mirror of
https://github.com/darlinghq/darling-gdb.git
synced 2024-11-28 06:20:30 +00:00
* Makefile.in (SFILES): Add target-memory.c.
(COMMON_OBS): Add target-memory.o. * memattr.c (lookup_mem_region): Adjust handling for the top of memory. Improve comments. * remote.c (packet_check_result): New function, split out from packet_ok. Recognize "E." as an error prefix. (packet_ok): Use it. (remote_write_bytes_aux): New function, renamed from remote_write_bytes. Take packet header, packet format, and length flag as arguments. (remote_write_bytes): Rewrite to use remote_write_bytes_aux. (remote_send_printf, restore_remote_timeout) (remote_flash_timeout, remote_flash_erase, remote_flash_write) (remote_flash_done): New. (remote_xfer_partial): Handle flash writes. (init_remote_ops, init_remote_async_ops): Set to_flash_erase and to_flash_done. * symfile.c (struct load_section_data): Include a pointer to the cumulative stats and a request queue. Move most members to other types. (struct load_progress_data, struct load_progress_section_data): New types. (load_progress): Handle a NULL baton and zero bytes. Update for type changes. (load_section_callback): Create memory write requests instead of writing to memory. Don't print the progress message here. (clear_memory_write_data): New function. (generic_load): Use target_write_memory_blocks. * target-memory.c: New file. * target.c (update_current_target): Mention new uninherited methods. (memory_xfer_partial): Issue an error for flash writes. (target_flash_erase, target_flash_done): New functions. (target_write_with_progress): Call the progress callback at the start also. * target.h (enum target_object): Add TARGET_OBJECT_FLASH. (target_write_with_progress): Update comment. (struct target_ops): Add to_flash_erase and to_flash_done. (target_flash_erase, target_flash_done, struct memory_write_request) (memory_write_request_s, enum flash_preserve_mode) (target_write_memory_blocks): New, including a vector type for memory_write_request_s.
This commit is contained in:
parent
fd79eceebf
commit
a76d924dff
@ -1,3 +1,48 @@
|
||||
2006-09-21 Vladimir Prus <vladimir@codesourcery.com>
|
||||
Daniel Jacobowitz <dan@codesourcery.com>
|
||||
|
||||
* Makefile.in (SFILES): Add target-memory.c.
|
||||
(COMMON_OBS): Add target-memory.o.
|
||||
* memattr.c (lookup_mem_region): Adjust handling for
|
||||
the top of memory. Improve comments.
|
||||
* remote.c (packet_check_result): New function, split out
|
||||
from packet_ok. Recognize "E." as an error prefix.
|
||||
(packet_ok): Use it.
|
||||
(remote_write_bytes_aux): New function, renamed from
|
||||
remote_write_bytes. Take packet header, packet format,
|
||||
and length flag as arguments.
|
||||
(remote_write_bytes): Rewrite to use remote_write_bytes_aux.
|
||||
(remote_send_printf, restore_remote_timeout)
|
||||
(remote_flash_timeout, remote_flash_erase, remote_flash_write)
|
||||
(remote_flash_done): New.
|
||||
(remote_xfer_partial): Handle flash writes.
|
||||
(init_remote_ops, init_remote_async_ops): Set to_flash_erase
|
||||
and to_flash_done.
|
||||
* symfile.c (struct load_section_data): Include a pointer to
|
||||
the cumulative stats and a request queue. Move most members
|
||||
to other types.
|
||||
(struct load_progress_data, struct load_progress_section_data): New
|
||||
types.
|
||||
(load_progress): Handle a NULL baton and zero bytes. Update for
|
||||
type changes.
|
||||
(load_section_callback): Create memory write requests instead of
|
||||
writing to memory. Don't print the progress message here.
|
||||
(clear_memory_write_data): New function.
|
||||
(generic_load): Use target_write_memory_blocks.
|
||||
* target-memory.c: New file.
|
||||
* target.c (update_current_target): Mention new uninherited methods.
|
||||
(memory_xfer_partial): Issue an error for flash writes.
|
||||
(target_flash_erase, target_flash_done): New functions.
|
||||
(target_write_with_progress): Call the progress callback at the
|
||||
start also.
|
||||
* target.h (enum target_object): Add TARGET_OBJECT_FLASH.
|
||||
(target_write_with_progress): Update comment.
|
||||
(struct target_ops): Add to_flash_erase and to_flash_done.
|
||||
(target_flash_erase, target_flash_done, struct memory_write_request)
|
||||
(memory_write_request_s, enum flash_preserve_mode)
|
||||
(target_write_memory_blocks): New, including a vector type
|
||||
for memory_write_request_s.
|
||||
|
||||
2006-09-21 Vladimir Prus <vladimir@codesourcery.com>
|
||||
Daniel Jacobowitz <dan@codesourcery.com>
|
||||
Nathan Sidwell <nathan@codesourcery.com>
|
||||
|
@ -554,7 +554,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \
|
||||
solib.c solib-null.c source.c \
|
||||
stabsread.c stack.c std-regs.c symfile.c symfile-mem.c symmisc.c \
|
||||
symtab.c \
|
||||
target.c thread.c top.c tracepoint.c \
|
||||
target.c target-memory.c thread.c top.c tracepoint.c \
|
||||
trad-frame.c \
|
||||
tramp-frame.c \
|
||||
typeprint.c \
|
||||
@ -964,7 +964,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
||||
trad-frame.o \
|
||||
tramp-frame.o \
|
||||
solib.o solib-null.o \
|
||||
prologue-value.o memory-map.o xml-support.o
|
||||
prologue-value.o memory-map.o xml-support.o target-memory.o
|
||||
|
||||
TSOBS = inflow.o
|
||||
|
||||
@ -2757,6 +2757,8 @@ symtab.o: symtab.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(gdbcore_h) \
|
||||
target.o: target.c $(defs_h) $(gdb_string_h) $(target_h) $(gdbcmd_h) \
|
||||
$(symtab_h) $(inferior_h) $(bfd_h) $(symfile_h) $(objfiles_h) \
|
||||
$(gdb_wait_h) $(dcache_h) $(regcache_h) $(gdb_assert_h) $(gdbcore_h)
|
||||
target-memory.o: target-memory.c $(defs_h) $(vec_h) $(target_h) \
|
||||
$(memory_map_h) $(gdb_assert_h)
|
||||
thread.o: thread.c $(defs_h) $(symtab_h) $(frame_h) $(inferior_h) \
|
||||
$(environ_h) $(value_h) $(target_h) $(gdbthread_h) $(exceptions_h) \
|
||||
$(command_h) $(gdbcmd_h) $(regcache_h) $(gdb_h) $(gdb_string_h) \
|
||||
|
@ -209,10 +209,11 @@ lookup_mem_region (CORE_ADDR addr)
|
||||
redefined to describe the minimal region containing ADDR. LO
|
||||
and HI are used in the case where no memory region is defined
|
||||
that contains ADDR. If a memory region is disabled, it is
|
||||
treated as if it does not exist. */
|
||||
treated as if it does not exist. The initial values for LO
|
||||
and HI represent the bottom and top of memory. */
|
||||
|
||||
lo = (CORE_ADDR) 0;
|
||||
hi = (CORE_ADDR) ~ 0;
|
||||
lo = 0;
|
||||
hi = 0;
|
||||
|
||||
/* If we ever want to support a huge list of memory regions, this
|
||||
check should be replaced with a binary search (probably using
|
||||
@ -224,10 +225,16 @@ lookup_mem_region (CORE_ADDR addr)
|
||||
if (addr >= m->lo && (addr < m->hi || m->hi == 0))
|
||||
return m;
|
||||
|
||||
/* This (correctly) won't match if m->hi == 0, representing
|
||||
the top of the address space, because CORE_ADDR is unsigned;
|
||||
no value of LO is less than zero. */
|
||||
if (addr >= m->hi && lo < m->hi)
|
||||
lo = m->hi;
|
||||
|
||||
if (addr <= m->lo && hi > m->lo)
|
||||
/* This will never set HI to zero; if we're here and ADDR
|
||||
is at or below M, and the region starts at zero, then ADDR
|
||||
would have been in the region. */
|
||||
if (addr <= m->lo && (hi == 0 || hi > m->lo))
|
||||
hi = m->lo;
|
||||
}
|
||||
}
|
||||
|
324
gdb/remote.c
324
gdb/remote.c
@ -753,12 +753,42 @@ add_packet_config_cmd (struct packet_config *config, const char *name,
|
||||
}
|
||||
|
||||
static enum packet_result
|
||||
packet_ok (const char *buf, struct packet_config *config)
|
||||
packet_check_result (const char *buf)
|
||||
{
|
||||
if (buf[0] != '\0')
|
||||
{
|
||||
/* The stub recognized the packet request. Check that the
|
||||
operation succeeded. */
|
||||
if (buf[0] == 'E'
|
||||
&& isxdigit (buf[1]) && isxdigit (buf[2])
|
||||
&& buf[3] == '\0')
|
||||
/* "Enn" - definitly an error. */
|
||||
return PACKET_ERROR;
|
||||
|
||||
/* Always treat "E." as an error. This will be used for
|
||||
more verbose error messages, such as E.memtypes. */
|
||||
if (buf[0] == 'E' && buf[1] == '.')
|
||||
return PACKET_ERROR;
|
||||
|
||||
/* The packet may or may not be OK. Just assume it is. */
|
||||
return PACKET_OK;
|
||||
}
|
||||
else
|
||||
/* The stub does not support the packet. */
|
||||
return PACKET_UNKNOWN;
|
||||
}
|
||||
|
||||
static enum packet_result
|
||||
packet_ok (const char *buf, struct packet_config *config)
|
||||
{
|
||||
enum packet_result result;
|
||||
|
||||
result = packet_check_result (buf);
|
||||
switch (result)
|
||||
{
|
||||
case PACKET_OK:
|
||||
case PACKET_ERROR:
|
||||
/* The stub recognized the packet request. */
|
||||
switch (config->support)
|
||||
{
|
||||
case PACKET_SUPPORT_UNKNOWN:
|
||||
@ -775,19 +805,8 @@ packet_ok (const char *buf, struct packet_config *config)
|
||||
case PACKET_ENABLE:
|
||||
break;
|
||||
}
|
||||
if (buf[0] == 'O' && buf[1] == 'K' && buf[2] == '\0')
|
||||
/* "OK" - definitly OK. */
|
||||
return PACKET_OK;
|
||||
if (buf[0] == 'E'
|
||||
&& isxdigit (buf[1]) && isxdigit (buf[2])
|
||||
&& buf[3] == '\0')
|
||||
/* "Enn" - definitly an error. */
|
||||
return PACKET_ERROR;
|
||||
/* The packet may or may not be OK. Just assume it is. */
|
||||
return PACKET_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
case PACKET_UNKNOWN:
|
||||
/* The stub does not support the packet. */
|
||||
switch (config->support)
|
||||
{
|
||||
@ -812,8 +831,10 @@ packet_ok (const char *buf, struct packet_config *config)
|
||||
case PACKET_DISABLE:
|
||||
break;
|
||||
}
|
||||
return PACKET_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum {
|
||||
@ -3852,24 +3873,44 @@ check_binary_download (CORE_ADDR addr)
|
||||
|
||||
/* Write memory data directly to the remote machine.
|
||||
This does not inform the data cache; the data cache uses this.
|
||||
HEADER is the starting part of the packet.
|
||||
MEMADDR is the address in the remote memory space.
|
||||
MYADDR is the address of the buffer in our space.
|
||||
LEN is the number of bytes.
|
||||
PACKET_FORMAT should be either 'X' or 'M', and indicates if we
|
||||
should send data as binary ('X'), or hex-encoded ('M').
|
||||
|
||||
The function creates packet of the form
|
||||
<HEADER><ADDRESS>,<LENGTH>:<DATA>
|
||||
|
||||
where encoding of <DATA> is termined by PACKET_FORMAT.
|
||||
|
||||
If USE_LENGTH is 0, then the <LENGTH> field and the preceding comma
|
||||
are omitted.
|
||||
|
||||
Returns the number of bytes transferred, or 0 (setting errno) for
|
||||
|
||||
Returns number of bytes transferred, or 0 (setting errno) for
|
||||
error. Only transfer a single packet. */
|
||||
|
||||
int
|
||||
remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
static int
|
||||
remote_write_bytes_aux (const char *header, CORE_ADDR memaddr,
|
||||
const gdb_byte *myaddr, int len,
|
||||
char packet_format, int use_length)
|
||||
{
|
||||
struct remote_state *rs = get_remote_state ();
|
||||
char *p;
|
||||
char *plen;
|
||||
int plenlen;
|
||||
char *plen = NULL;
|
||||
int plenlen = 0;
|
||||
int todo;
|
||||
int nr_bytes;
|
||||
int payload_size;
|
||||
int payload_length;
|
||||
int header_length;
|
||||
|
||||
if (packet_format != 'X' && packet_format != 'M')
|
||||
internal_error (__FILE__, __LINE__,
|
||||
"remote_write_bytes_aux: bad packet format");
|
||||
|
||||
/* Should this be the selected frame? */
|
||||
gdbarch_remote_translate_xfer_address (current_gdbarch,
|
||||
@ -3880,47 +3921,46 @@ remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
/* Verify that the target can support a binary download. */
|
||||
check_binary_download (memaddr);
|
||||
|
||||
payload_size = get_memory_write_packet_size ();
|
||||
|
||||
/* The packet buffer will be large enough for the payload;
|
||||
get_memory_packet_size ensures this. */
|
||||
rs->buf[0] = '\0';
|
||||
|
||||
/* Compute the size of the actual payload by subtracting out the
|
||||
packet header and footer overhead: "$M<memaddr>,<len>:...#nn".
|
||||
*/
|
||||
payload_size -= strlen ("$M,:#NN");
|
||||
payload_size -= strlen ("$,:#NN");
|
||||
if (!use_length)
|
||||
/* The comma won't be used. */
|
||||
payload_size += 1;
|
||||
header_length = strlen (header);
|
||||
payload_size -= header_length;
|
||||
payload_size -= hexnumlen (memaddr);
|
||||
|
||||
/* Construct the packet header: "[MX]<memaddr>,<len>:". */
|
||||
/* Construct the packet excluding the data: "<header><memaddr>,<len>:". */
|
||||
|
||||
/* Append "[XM]". Compute a best guess of the number of bytes
|
||||
actually transfered. */
|
||||
p = rs->buf;
|
||||
switch (remote_protocol_packets[PACKET_X].support)
|
||||
strcat (rs->buf, header);
|
||||
p = rs->buf + strlen (header);
|
||||
|
||||
/* Compute a best guess of the number of bytes actually transfered. */
|
||||
if (packet_format == 'X')
|
||||
{
|
||||
case PACKET_ENABLE:
|
||||
*p++ = 'X';
|
||||
/* Best guess at number of bytes that will fit. */
|
||||
todo = min (len, payload_size);
|
||||
payload_size -= hexnumlen (todo);
|
||||
if (use_length)
|
||||
payload_size -= hexnumlen (todo);
|
||||
todo = min (todo, payload_size);
|
||||
break;
|
||||
case PACKET_DISABLE:
|
||||
*p++ = 'M';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Num bytes that will fit. */
|
||||
todo = min (len, payload_size / 2);
|
||||
payload_size -= hexnumlen (todo);
|
||||
if (use_length)
|
||||
payload_size -= hexnumlen (todo);
|
||||
todo = min (todo, payload_size / 2);
|
||||
break;
|
||||
case PACKET_SUPPORT_UNKNOWN:
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("remote_write_bytes: bad internal state"));
|
||||
default:
|
||||
internal_error (__FILE__, __LINE__, _("bad switch"));
|
||||
}
|
||||
|
||||
if (todo <= 0)
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("minumum packet size too small to write data"));
|
||||
@ -3934,23 +3974,25 @@ remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
memaddr = remote_address_masked (memaddr);
|
||||
p += hexnumstr (p, (ULONGEST) memaddr);
|
||||
|
||||
/* Append ",". */
|
||||
*p++ = ',';
|
||||
if (use_length)
|
||||
{
|
||||
/* Append ",". */
|
||||
*p++ = ',';
|
||||
|
||||
/* Append <len>. Retain the location/size of <len>. It may need to
|
||||
be adjusted once the packet body has been created. */
|
||||
plen = p;
|
||||
plenlen = hexnumstr (p, (ULONGEST) todo);
|
||||
p += plenlen;
|
||||
/* Append <len>. Retain the location/size of <len>. It may need to
|
||||
be adjusted once the packet body has been created. */
|
||||
plen = p;
|
||||
plenlen = hexnumstr (p, (ULONGEST) todo);
|
||||
p += plenlen;
|
||||
}
|
||||
|
||||
/* Append ":". */
|
||||
*p++ = ':';
|
||||
*p = '\0';
|
||||
|
||||
/* Append the packet body. */
|
||||
switch (remote_protocol_packets[PACKET_X].support)
|
||||
if (packet_format == 'X')
|
||||
{
|
||||
case PACKET_ENABLE:
|
||||
/* Binary mode. Send target system values byte by byte, in
|
||||
increasing byte addresses. Only escape certain critical
|
||||
characters. */
|
||||
@ -3972,7 +4014,7 @@ remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
}
|
||||
|
||||
p += payload_length;
|
||||
if (nr_bytes < todo)
|
||||
if (use_length && nr_bytes < todo)
|
||||
{
|
||||
/* Escape chars have filled up the buffer prematurely,
|
||||
and we have actually sent fewer bytes than planned.
|
||||
@ -3981,19 +4023,14 @@ remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
plen += hexnumnstr (plen, (ULONGEST) nr_bytes, plenlen);
|
||||
*plen = ':'; /* overwrite \0 from hexnumnstr() */
|
||||
}
|
||||
break;
|
||||
case PACKET_DISABLE:
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normal mode: Send target system values byte by byte, in
|
||||
increasing byte addresses. Each byte is encoded as a two hex
|
||||
value. */
|
||||
nr_bytes = bin2hex (myaddr, p, todo);
|
||||
p += 2 * nr_bytes;
|
||||
break;
|
||||
case PACKET_SUPPORT_UNKNOWN:
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("remote_write_bytes: bad internal state"));
|
||||
default:
|
||||
internal_error (__FILE__, __LINE__, _("bad switch"));
|
||||
}
|
||||
|
||||
putpkt_binary (rs->buf, (int) (p - rs->buf));
|
||||
@ -4014,6 +4051,42 @@ remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
return nr_bytes;
|
||||
}
|
||||
|
||||
/* Write memory data directly to the remote machine.
|
||||
This does not inform the data cache; the data cache uses this.
|
||||
MEMADDR is the address in the remote memory space.
|
||||
MYADDR is the address of the buffer in our space.
|
||||
LEN is the number of bytes.
|
||||
|
||||
Returns number of bytes transferred, or 0 (setting errno) for
|
||||
error. Only transfer a single packet. */
|
||||
|
||||
int
|
||||
remote_write_bytes (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
|
||||
{
|
||||
char *packet_format = 0;
|
||||
|
||||
/* Check whether the target supports binary download. */
|
||||
check_binary_download (memaddr);
|
||||
|
||||
switch (remote_protocol_packets[PACKET_X].support)
|
||||
{
|
||||
case PACKET_ENABLE:
|
||||
packet_format = "X";
|
||||
break;
|
||||
case PACKET_DISABLE:
|
||||
packet_format = "M";
|
||||
break;
|
||||
case PACKET_SUPPORT_UNKNOWN:
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("remote_write_bytes: bad internal state"));
|
||||
default:
|
||||
internal_error (__FILE__, __LINE__, _("bad switch"));
|
||||
}
|
||||
|
||||
return remote_write_bytes_aux (packet_format,
|
||||
memaddr, myaddr, len, packet_format[0], 1);
|
||||
}
|
||||
|
||||
/* Read memory data directly from the remote machine.
|
||||
This does not use the data cache; the data cache uses this.
|
||||
MEMADDR is the address in the remote memory space.
|
||||
@ -4121,6 +4194,111 @@ remote_xfer_memory (CORE_ADDR mem_addr, gdb_byte *buffer, int mem_len,
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Sends a packet with content determined by the printf format string
|
||||
FORMAT and the remaining arguments, then gets the reply. Returns
|
||||
whether the packet was a success, a failure, or unknown. */
|
||||
|
||||
enum packet_result
|
||||
remote_send_printf (const char *format, ...)
|
||||
{
|
||||
struct remote_state *rs = get_remote_state ();
|
||||
int max_size = get_remote_packet_size ();
|
||||
|
||||
va_list ap;
|
||||
va_start (ap, format);
|
||||
|
||||
rs->buf[0] = '\0';
|
||||
if (vsnprintf (rs->buf, max_size, format, ap) >= max_size)
|
||||
internal_error (__FILE__, __LINE__, "Too long remote packet.");
|
||||
|
||||
if (putpkt (rs->buf) < 0)
|
||||
error (_("Communication problem with target."));
|
||||
|
||||
rs->buf[0] = '\0';
|
||||
getpkt (&rs->buf, &rs->buf_size, 0);
|
||||
|
||||
return packet_check_result (rs->buf);
|
||||
}
|
||||
|
||||
static void
|
||||
restore_remote_timeout (void *p)
|
||||
{
|
||||
int value = *(int *)p;
|
||||
remote_timeout = value;
|
||||
}
|
||||
|
||||
/* Flash writing can take quite some time. We'll set
|
||||
effectively infinite timeout for flash operations.
|
||||
In future, we'll need to decide on a better approach. */
|
||||
static const int remote_flash_timeout = 1000;
|
||||
|
||||
static void
|
||||
remote_flash_erase (struct target_ops *ops,
|
||||
ULONGEST address, LONGEST length)
|
||||
{
|
||||
int saved_remote_timeout = remote_timeout;
|
||||
enum packet_result ret;
|
||||
|
||||
struct cleanup *back_to = make_cleanup (restore_remote_timeout,
|
||||
&saved_remote_timeout);
|
||||
remote_timeout = remote_flash_timeout;
|
||||
|
||||
ret = remote_send_printf ("vFlashErase:%s,%s",
|
||||
paddr (address),
|
||||
phex (length, 4));
|
||||
switch (ret)
|
||||
{
|
||||
case PACKET_UNKNOWN:
|
||||
error (_("Remote target does not support flash erase"));
|
||||
case PACKET_ERROR:
|
||||
error (_("Error erasing flash with vFlashErase packet"));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
do_cleanups (back_to);
|
||||
}
|
||||
|
||||
static LONGEST
|
||||
remote_flash_write (struct target_ops *ops,
|
||||
ULONGEST address, LONGEST length,
|
||||
const gdb_byte *data)
|
||||
{
|
||||
int saved_remote_timeout = remote_timeout;
|
||||
int ret;
|
||||
struct cleanup *back_to = make_cleanup (restore_remote_timeout,
|
||||
&saved_remote_timeout);
|
||||
|
||||
remote_timeout = remote_flash_timeout;
|
||||
ret = remote_write_bytes_aux ("vFlashWrite:", address, data, length, 'X', 0);
|
||||
do_cleanups (back_to);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
remote_flash_done (struct target_ops *ops)
|
||||
{
|
||||
int saved_remote_timeout = remote_timeout;
|
||||
int ret;
|
||||
struct cleanup *back_to = make_cleanup (restore_remote_timeout,
|
||||
&saved_remote_timeout);
|
||||
|
||||
remote_timeout = remote_flash_timeout;
|
||||
ret = remote_send_printf ("vFlashDone");
|
||||
do_cleanups (back_to);
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case PACKET_UNKNOWN:
|
||||
error (_("Remote target does not support vFlashDone"));
|
||||
case PACKET_ERROR:
|
||||
error (_("Error finishing flash operation"));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
remote_files_info (struct target_ops *ignore)
|
||||
{
|
||||
@ -5300,9 +5478,27 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Only handle reads. */
|
||||
if (writebuf != NULL || readbuf == NULL)
|
||||
return -1;
|
||||
/* Only handle flash writes. */
|
||||
if (writebuf != NULL)
|
||||
{
|
||||
LONGEST xfered;
|
||||
|
||||
switch (object)
|
||||
{
|
||||
case TARGET_OBJECT_FLASH:
|
||||
xfered = remote_flash_write (ops, offset, len, writebuf);
|
||||
|
||||
if (xfered > 0)
|
||||
return xfered;
|
||||
else if (xfered == 0 && errno == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Map pre-existing objects onto letters. DO NOT do this for new
|
||||
objects!!! Instead specify new query packets. */
|
||||
@ -5722,6 +5918,8 @@ Specify the serial device it is connected to\n\
|
||||
remote_ops.to_has_thread_control = tc_schedlock; /* can lock scheduler */
|
||||
remote_ops.to_magic = OPS_MAGIC;
|
||||
remote_ops.to_memory_map = remote_memory_map;
|
||||
remote_ops.to_flash_erase = remote_flash_erase;
|
||||
remote_ops.to_flash_done = remote_flash_done;
|
||||
}
|
||||
|
||||
/* Set up the extended remote vector by making a copy of the standard
|
||||
@ -5852,6 +6050,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
|
||||
remote_async_ops.to_async_mask_value = 1;
|
||||
remote_async_ops.to_magic = OPS_MAGIC;
|
||||
remote_async_ops.to_memory_map = remote_memory_map;
|
||||
remote_async_ops.to_flash_erase = remote_flash_erase;
|
||||
remote_async_ops.to_flash_done = remote_flash_done;
|
||||
}
|
||||
|
||||
/* Set up the async extended remote vector by making a copy of the standard
|
||||
|
125
gdb/symfile.c
125
gdb/symfile.c
@ -1537,11 +1537,23 @@ add_section_size_callback (bfd *abfd, asection *asec, void *data)
|
||||
/* Opaque data for load_section_callback. */
|
||||
struct load_section_data {
|
||||
unsigned long load_offset;
|
||||
struct load_progress_data *progress_data;
|
||||
VEC(memory_write_request_s) *requests;
|
||||
};
|
||||
|
||||
/* Opaque data for load_progress. */
|
||||
struct load_progress_data {
|
||||
/* Cumulative data. */
|
||||
unsigned long write_count;
|
||||
unsigned long data_count;
|
||||
bfd_size_type total_size;
|
||||
};
|
||||
|
||||
/* Per-section data for load_progress. */
|
||||
/* Opaque data for load_progress for a single section. */
|
||||
struct load_progress_section_data {
|
||||
struct load_progress_data *cumulative;
|
||||
|
||||
/* Per-section data. */
|
||||
const char *section_name;
|
||||
ULONGEST section_sent;
|
||||
ULONGEST section_size;
|
||||
@ -1549,12 +1561,30 @@ struct load_section_data {
|
||||
gdb_byte *buffer;
|
||||
};
|
||||
|
||||
/* Target write callback routine for load_section_callback. */
|
||||
/* Target write callback routine for progress reporting. */
|
||||
|
||||
static void
|
||||
load_progress (ULONGEST bytes, void *untyped_arg)
|
||||
{
|
||||
struct load_section_data *args = untyped_arg;
|
||||
struct load_progress_section_data *args = untyped_arg;
|
||||
struct load_progress_data *totals;
|
||||
|
||||
if (args == NULL)
|
||||
/* Writing padding data. No easy way to get at the cumulative
|
||||
stats, so just ignore this. */
|
||||
return;
|
||||
|
||||
totals = args->cumulative;
|
||||
|
||||
if (bytes == 0 && args->section_sent == 0)
|
||||
{
|
||||
/* The write is just starting. Let the user know we've started
|
||||
this section. */
|
||||
ui_out_message (uiout, 0, "Loading section %s, size 0x%s lma 0x%s\n",
|
||||
args->section_name, paddr_nz (args->section_size),
|
||||
paddr_nz (args->lma));
|
||||
return;
|
||||
}
|
||||
|
||||
if (validate_download)
|
||||
{
|
||||
@ -1576,10 +1606,10 @@ load_progress (ULONGEST bytes, void *untyped_arg)
|
||||
paddr (args->lma));
|
||||
do_cleanups (verify_cleanups);
|
||||
}
|
||||
args->data_count += bytes;
|
||||
totals->data_count += bytes;
|
||||
args->lma += bytes;
|
||||
args->buffer += bytes;
|
||||
args->write_count += 1;
|
||||
totals->write_count += 1;
|
||||
args->section_sent += bytes;
|
||||
if (quit_flag
|
||||
|| (deprecated_ui_load_progress_hook != NULL
|
||||
@ -1591,8 +1621,8 @@ load_progress (ULONGEST bytes, void *untyped_arg)
|
||||
deprecated_show_load_progress (args->section_name,
|
||||
args->section_sent,
|
||||
args->section_size,
|
||||
args->data_count,
|
||||
args->total_size);
|
||||
totals->data_count,
|
||||
totals->total_size);
|
||||
}
|
||||
|
||||
/* Callback service function for generic_load (bfd_map_over_sections). */
|
||||
@ -1600,12 +1630,12 @@ load_progress (ULONGEST bytes, void *untyped_arg)
|
||||
static void
|
||||
load_section_callback (bfd *abfd, asection *asec, void *data)
|
||||
{
|
||||
struct memory_write_request *new_request;
|
||||
struct load_section_data *args = data;
|
||||
struct load_progress_section_data *section_data;
|
||||
bfd_size_type size = bfd_get_section_size (asec);
|
||||
gdb_byte *buffer;
|
||||
struct cleanup *old_chain;
|
||||
const char *sect_name = bfd_get_section_name (abfd, asec);
|
||||
LONGEST transferred;
|
||||
|
||||
if ((bfd_get_section_flags (abfd, asec) & SEC_LOAD) == 0)
|
||||
return;
|
||||
@ -1613,49 +1643,63 @@ load_section_callback (bfd *abfd, asection *asec, void *data)
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
buffer = xmalloc (size);
|
||||
old_chain = make_cleanup (xfree, buffer);
|
||||
new_request = VEC_safe_push (memory_write_request_s,
|
||||
args->requests, NULL);
|
||||
memset (new_request, 0, sizeof (struct memory_write_request));
|
||||
section_data = xcalloc (1, sizeof (struct load_progress_section_data));
|
||||
new_request->begin = bfd_section_lma (abfd, asec) + args->load_offset;
|
||||
new_request->end = new_request->begin + size; /* FIXME Should size be in instead? */
|
||||
new_request->data = xmalloc (size);
|
||||
new_request->baton = section_data;
|
||||
|
||||
args->section_name = sect_name;
|
||||
args->section_sent = 0;
|
||||
args->section_size = size;
|
||||
args->lma = bfd_section_lma (abfd, asec) + args->load_offset;
|
||||
args->buffer = buffer;
|
||||
buffer = new_request->data;
|
||||
|
||||
/* Is this really necessary? I guess it gives the user something
|
||||
to look at during a long download. */
|
||||
ui_out_message (uiout, 0, "Loading section %s, size 0x%s lma 0x%s\n",
|
||||
sect_name, paddr_nz (size), paddr_nz (args->lma));
|
||||
section_data->cumulative = args->progress_data;
|
||||
section_data->section_name = sect_name;
|
||||
section_data->section_size = size;
|
||||
section_data->lma = new_request->begin;
|
||||
section_data->buffer = buffer;
|
||||
|
||||
bfd_get_section_contents (abfd, asec, buffer, 0, size);
|
||||
}
|
||||
|
||||
transferred = target_write_with_progress (¤t_target,
|
||||
TARGET_OBJECT_MEMORY,
|
||||
NULL, buffer, args->lma,
|
||||
size, load_progress, args);
|
||||
if (transferred < size)
|
||||
error (_("Memory access error while loading section %s."),
|
||||
sect_name);
|
||||
/* Clean up an entire memory request vector, including load
|
||||
data and progress records. */
|
||||
|
||||
do_cleanups (old_chain);
|
||||
static void
|
||||
clear_memory_write_data (void *arg)
|
||||
{
|
||||
VEC(memory_write_request_s) **vec_p = arg;
|
||||
VEC(memory_write_request_s) *vec = *vec_p;
|
||||
int i;
|
||||
struct memory_write_request *mr;
|
||||
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, vec, i, mr); ++i)
|
||||
{
|
||||
xfree (mr->data);
|
||||
xfree (mr->baton);
|
||||
}
|
||||
VEC_free (memory_write_request_s, vec);
|
||||
}
|
||||
|
||||
void
|
||||
generic_load (char *args, int from_tty)
|
||||
{
|
||||
asection *s;
|
||||
bfd *loadfile_bfd;
|
||||
struct timeval start_time, end_time;
|
||||
char *filename;
|
||||
struct cleanup *old_cleanups = make_cleanup (null_cleanup, 0);
|
||||
struct load_section_data cbdata;
|
||||
struct load_progress_data total_progress;
|
||||
|
||||
CORE_ADDR entry;
|
||||
char **argv;
|
||||
|
||||
cbdata.load_offset = 0; /* Offset to add to vma for each section. */
|
||||
cbdata.write_count = 0; /* Number of writes needed. */
|
||||
cbdata.data_count = 0; /* Number of bytes written to target memory. */
|
||||
cbdata.total_size = 0; /* Total size of all bfd sectors. */
|
||||
memset (&cbdata, 0, sizeof (cbdata));
|
||||
memset (&total_progress, 0, sizeof (total_progress));
|
||||
cbdata.progress_data = &total_progress;
|
||||
|
||||
make_cleanup (clear_memory_write_data, &cbdata.requests);
|
||||
|
||||
argv = buildargv (args);
|
||||
|
||||
@ -1702,11 +1746,15 @@ generic_load (char *args, int from_tty)
|
||||
}
|
||||
|
||||
bfd_map_over_sections (loadfile_bfd, add_section_size_callback,
|
||||
(void *) &cbdata.total_size);
|
||||
(void *) &total_progress.total_size);
|
||||
|
||||
bfd_map_over_sections (loadfile_bfd, load_section_callback, &cbdata);
|
||||
|
||||
gettimeofday (&start_time, NULL);
|
||||
|
||||
bfd_map_over_sections (loadfile_bfd, load_section_callback, &cbdata);
|
||||
if (target_write_memory_blocks (cbdata.requests, flash_discard,
|
||||
load_progress) != 0)
|
||||
error (_("Load failed"));
|
||||
|
||||
gettimeofday (&end_time, NULL);
|
||||
|
||||
@ -1714,7 +1762,7 @@ generic_load (char *args, int from_tty)
|
||||
ui_out_text (uiout, "Start address ");
|
||||
ui_out_field_fmt (uiout, "address", "0x%s", paddr_nz (entry));
|
||||
ui_out_text (uiout, ", load size ");
|
||||
ui_out_field_fmt (uiout, "load-size", "%lu", cbdata.data_count);
|
||||
ui_out_field_fmt (uiout, "load-size", "%lu", total_progress.data_count);
|
||||
ui_out_text (uiout, "\n");
|
||||
/* We were doing this in remote-mips.c, I suspect it is right
|
||||
for other targets too. */
|
||||
@ -1726,8 +1774,9 @@ generic_load (char *args, int from_tty)
|
||||
file is loaded in. Some targets do (e.g., remote-vx.c) but
|
||||
others don't (or didn't - perhaps they have all been deleted). */
|
||||
|
||||
print_transfer_performance (gdb_stdout, cbdata.data_count,
|
||||
cbdata.write_count, &start_time, &end_time);
|
||||
print_transfer_performance (gdb_stdout, total_progress.data_count,
|
||||
total_progress.write_count,
|
||||
&start_time, &end_time);
|
||||
|
||||
do_cleanups (old_cleanups);
|
||||
}
|
||||
|
437
gdb/target-memory.c
Normal file
437
gdb/target-memory.c
Normal file
@ -0,0 +1,437 @@
|
||||
/* Parts of target interface that deal with accessing memory and memory-like
|
||||
objects.
|
||||
|
||||
Copyright (C) 2006
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA. */
|
||||
|
||||
#include "defs.h"
|
||||
#include "vec.h"
|
||||
#include "target.h"
|
||||
#include "memory-map.h"
|
||||
|
||||
#include "gdb_assert.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static int
|
||||
compare_block_starting_address (const void *a, const void *b)
|
||||
{
|
||||
const struct memory_write_request *a_req = a;
|
||||
const struct memory_write_request *b_req = b;
|
||||
|
||||
if (a_req->begin < b_req->begin)
|
||||
return -1;
|
||||
else if (a_req->begin == b_req->begin)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Adds to RESULT all memory write requests from BLOCK that are
|
||||
in [BEGIN, END) range.
|
||||
|
||||
If any memory request is only partially in the specified range,
|
||||
that part of the memory request will be added. */
|
||||
|
||||
static void
|
||||
claim_memory (VEC(memory_write_request_s) *blocks,
|
||||
VEC(memory_write_request_s) **result,
|
||||
ULONGEST begin,
|
||||
ULONGEST end)
|
||||
{
|
||||
int i;
|
||||
ULONGEST claimed_begin;
|
||||
ULONGEST claimed_end;
|
||||
struct memory_write_request *r;
|
||||
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, blocks, i, r); ++i)
|
||||
{
|
||||
/* If the request doesn't overlap [BEGIN, END), skip it. We
|
||||
must handle END == 0 meaning the top of memory; we don't yet
|
||||
check for R->end == 0, which would also mean the top of
|
||||
memory, but there's an assertion in
|
||||
target_write_memory_blocks which checks for that. */
|
||||
|
||||
if (begin >= r->end)
|
||||
continue;
|
||||
if (end != 0 && end <= r->begin)
|
||||
continue;
|
||||
|
||||
claimed_begin = max (begin, r->begin);
|
||||
if (end == 0)
|
||||
claimed_end = r->end;
|
||||
else
|
||||
claimed_end = min (end, r->end);
|
||||
|
||||
if (claimed_begin == r->begin && claimed_end == r->end)
|
||||
VEC_safe_push (memory_write_request_s, *result, r);
|
||||
else
|
||||
{
|
||||
struct memory_write_request *n =
|
||||
VEC_safe_push (memory_write_request_s, *result, NULL);
|
||||
memset (n, 0, sizeof (struct memory_write_request));
|
||||
n->begin = claimed_begin;
|
||||
n->end = claimed_end;
|
||||
n->data = r->data + (claimed_begin - r->begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Given a vector of struct memory_write_request objects in BLOCKS,
|
||||
add memory requests for flash memory into FLASH_BLOCKS, and for
|
||||
regular memory to REGULAR_BLOCKS. */
|
||||
|
||||
static void
|
||||
split_regular_and_flash_blocks (VEC(memory_write_request_s) *blocks,
|
||||
VEC(memory_write_request_s) **regular_blocks,
|
||||
VEC(memory_write_request_s) **flash_blocks)
|
||||
{
|
||||
struct mem_region *region;
|
||||
CORE_ADDR cur_address;
|
||||
|
||||
/* This implementation runs in O(length(regions)*length(blocks)) time.
|
||||
However, in most cases the number of blocks will be small, so this does
|
||||
not matter.
|
||||
|
||||
Note also that it's extremely unlikely that a memory write request
|
||||
will span more than one memory region, however for safety we handle
|
||||
such situations. */
|
||||
|
||||
cur_address = 0;
|
||||
while (1)
|
||||
{
|
||||
VEC(memory_write_request_s) **r;
|
||||
region = lookup_mem_region (cur_address);
|
||||
|
||||
r = region->attrib.mode == MEM_FLASH ? flash_blocks : regular_blocks;
|
||||
cur_address = region->hi;
|
||||
claim_memory (blocks, r, region->lo, region->hi);
|
||||
|
||||
if (cur_address == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Given an ADDRESS, if BEGIN is non-NULL this function sets *BEGIN
|
||||
to the start of the flash block containing the address. Similarly,
|
||||
if END is non-NULL *END will be set to the address one past the end
|
||||
of the block containing the address. */
|
||||
|
||||
static void
|
||||
block_boundaries (CORE_ADDR address, CORE_ADDR *begin, CORE_ADDR *end)
|
||||
{
|
||||
struct mem_region *region;
|
||||
unsigned blocksize;
|
||||
|
||||
region = lookup_mem_region (address);
|
||||
gdb_assert (region->attrib.mode == MEM_FLASH);
|
||||
blocksize = region->attrib.blocksize;
|
||||
if (begin)
|
||||
*begin = address / blocksize * blocksize;
|
||||
if (end)
|
||||
*end = (address + blocksize - 1) / blocksize * blocksize;
|
||||
}
|
||||
|
||||
/* Given the list of memory requests to be WRITTEN, this function
|
||||
returns write requests covering each group of flash blocks which must
|
||||
be erased. */
|
||||
|
||||
static VEC(memory_write_request_s) *
|
||||
blocks_to_erase (VEC(memory_write_request_s) *written)
|
||||
{
|
||||
unsigned i;
|
||||
struct memory_write_request *ptr;
|
||||
|
||||
VEC(memory_write_request_s) *result = NULL;
|
||||
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, written, i, ptr); ++i)
|
||||
{
|
||||
CORE_ADDR begin, end;
|
||||
|
||||
block_boundaries (ptr->begin, &begin, 0);
|
||||
block_boundaries (ptr->end, 0, &end);
|
||||
|
||||
if (!VEC_empty (memory_write_request_s, result)
|
||||
&& VEC_last (memory_write_request_s, result)->end >= begin)
|
||||
{
|
||||
VEC_last (memory_write_request_s, result)->end = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct memory_write_request *n =
|
||||
VEC_safe_push (memory_write_request_s, result, NULL);
|
||||
memset (n, 0, sizeof (struct memory_write_request));
|
||||
n->begin = begin;
|
||||
n->end = end;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Given ERASED_BLOCKS, a list of blocks that will be erased with
|
||||
flash erase commands, and WRITTEN_BLOCKS, the list of memory
|
||||
addresses that will be written, compute the set of memory addresses
|
||||
that will be erased but not rewritten (e.g. padding within a block
|
||||
which is only partially filled by "load"). */
|
||||
|
||||
static VEC(memory_write_request_s) *
|
||||
compute_garbled_blocks (VEC(memory_write_request_s) *erased_blocks,
|
||||
VEC(memory_write_request_s) *written_blocks)
|
||||
{
|
||||
VEC(memory_write_request_s) *result = NULL;
|
||||
|
||||
unsigned i, j;
|
||||
unsigned je = VEC_length (memory_write_request_s, written_blocks);
|
||||
struct memory_write_request *erased_p;
|
||||
|
||||
/* Look at each erased memory_write_request in turn, and
|
||||
see what part of it is subsequently written to.
|
||||
|
||||
This implementation is O(length(erased) * length(written)). If
|
||||
the lists are sorted at this point it could be rewritten more
|
||||
efficiently, but the complexity is not generally worthwhile. */
|
||||
|
||||
for (i = 0;
|
||||
VEC_iterate (memory_write_request_s, erased_blocks, i, erased_p);
|
||||
++i)
|
||||
{
|
||||
/* Make a deep copy -- it will be modified inside the loop, but
|
||||
we don't want to modify original vector. */
|
||||
struct memory_write_request erased = *erased_p;
|
||||
|
||||
for (j = 0; j != je;)
|
||||
{
|
||||
struct memory_write_request *written
|
||||
= VEC_index (memory_write_request_s,
|
||||
written_blocks, j);
|
||||
|
||||
/* Now try various cases. */
|
||||
|
||||
/* If WRITTEN is fully to the left of ERASED, check the next
|
||||
written memory_write_request. */
|
||||
if (written->end <= erased.begin)
|
||||
{
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If WRITTEN is fully to the right of ERASED, then ERASED
|
||||
is not written at all. WRITTEN might affect other
|
||||
blocks. */
|
||||
if (written->begin >= erased.end)
|
||||
{
|
||||
VEC_safe_push (memory_write_request_s, result, &erased);
|
||||
goto next_erased;
|
||||
}
|
||||
|
||||
/* If all of ERASED is completely written, we can move on to
|
||||
the next erased region. */
|
||||
if (written->begin <= erased.begin
|
||||
&& written->end >= erased.end)
|
||||
{
|
||||
goto next_erased;
|
||||
}
|
||||
|
||||
/* If there is an unwritten part at the beginning of ERASED,
|
||||
then we should record that part and try this inner loop
|
||||
again for the remainder. */
|
||||
if (written->begin > erased.begin)
|
||||
{
|
||||
struct memory_write_request *n =
|
||||
VEC_safe_push (memory_write_request_s, result, NULL);
|
||||
memset (n, 0, sizeof (struct memory_write_request));
|
||||
n->begin = erased.begin;
|
||||
n->end = written->begin;
|
||||
erased.begin = written->begin;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If there is an unwritten part at the end of ERASED, we
|
||||
forget about the part that was written to and wait to see
|
||||
if the next write request writes more of ERASED. We can't
|
||||
push it yet. */
|
||||
if (written->end < erased.end)
|
||||
{
|
||||
erased.begin = written->end;
|
||||
++j;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we ran out of write requests without doing anything about
|
||||
ERASED, then that means it's really erased. */
|
||||
VEC_safe_push (memory_write_request_s, result, &erased);
|
||||
|
||||
next_erased:
|
||||
;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_request_data (void *p)
|
||||
{
|
||||
VEC(memory_write_request_s) **v = p;
|
||||
struct memory_write_request *r;
|
||||
int i;
|
||||
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, *v, i, r); ++i)
|
||||
xfree (r->data);
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_write_requests_vector (void *p)
|
||||
{
|
||||
VEC(memory_write_request_s) **v = p;
|
||||
VEC_free (memory_write_request_s, *v);
|
||||
}
|
||||
|
||||
int
|
||||
target_write_memory_blocks (VEC(memory_write_request_s) *requests,
|
||||
enum flash_preserve_mode preserve_flash_p,
|
||||
void (*progress_cb) (ULONGEST, void *))
|
||||
{
|
||||
struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
|
||||
VEC(memory_write_request_s) *blocks = VEC_copy (memory_write_request_s,
|
||||
requests);
|
||||
unsigned i;
|
||||
int err = 0;
|
||||
struct memory_write_request *r;
|
||||
VEC(memory_write_request_s) *regular = NULL;
|
||||
VEC(memory_write_request_s) *flash = NULL;
|
||||
VEC(memory_write_request_s) *erased, *garbled;
|
||||
|
||||
/* END == 0 would represent wraparound: a write to the very last
|
||||
byte of the address space. This file was not written with that
|
||||
possibility in mind. This is fixable, but a lot of work for a
|
||||
rare problem; so for now, fail noisily here instead of obscurely
|
||||
later. */
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, requests, i, r); ++i)
|
||||
gdb_assert (r->end != 0);
|
||||
|
||||
make_cleanup (cleanup_write_requests_vector, &blocks);
|
||||
|
||||
/* Sort the blocks by their start address. */
|
||||
qsort (VEC_address (memory_write_request_s, blocks),
|
||||
VEC_length (memory_write_request_s, blocks),
|
||||
sizeof (struct memory_write_request), compare_block_starting_address);
|
||||
|
||||
/* Split blocks into list of regular memory blocks,
|
||||
and list of flash memory blocks. */
|
||||
make_cleanup (cleanup_write_requests_vector, ®ular);
|
||||
make_cleanup (cleanup_write_requests_vector, &flash);
|
||||
split_regular_and_flash_blocks (blocks, ®ular, &flash);
|
||||
|
||||
/* If a variable is added to forbid flash write, even during "load",
|
||||
it should be checked here. Similarly, if this function is used
|
||||
for other situations besides "load" in which writing to flash
|
||||
is undesirable, that should be checked here. */
|
||||
|
||||
/* Find flash blocks to erase. */
|
||||
erased = blocks_to_erase (flash);
|
||||
make_cleanup (cleanup_write_requests_vector, &erased);
|
||||
|
||||
/* Find what flash regions will be erased, and not overwritten; then
|
||||
either preserve or discard the old contents. */
|
||||
garbled = compute_garbled_blocks (erased, flash);
|
||||
make_cleanup (cleanup_request_data, &garbled);
|
||||
make_cleanup (cleanup_write_requests_vector, &garbled);
|
||||
|
||||
if (!VEC_empty (memory_write_request_s, garbled))
|
||||
{
|
||||
if (preserve_flash_p == flash_preserve)
|
||||
{
|
||||
struct memory_write_request *r;
|
||||
|
||||
/* Read in regions that must be preserved and add them to
|
||||
the list of blocks we read. */
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, garbled, i, r); ++i)
|
||||
{
|
||||
gdb_assert (r->data == NULL);
|
||||
r->data = xmalloc (r->end - r->begin);
|
||||
err = target_read_memory (r->begin, r->data, r->end - r->begin);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
VEC_safe_push (memory_write_request_s, flash, r);
|
||||
}
|
||||
|
||||
qsort (VEC_address (memory_write_request_s, flash),
|
||||
VEC_length (memory_write_request_s, flash),
|
||||
sizeof (struct memory_write_request), compare_block_starting_address);
|
||||
}
|
||||
}
|
||||
|
||||
/* We could coalesce adjacent memory blocks here, to reduce the
|
||||
number of write requests for small sections. However, we would
|
||||
have to reallocate and copy the data pointers, which could be
|
||||
large; large sections are more common in loadable objects than
|
||||
large numbers of small sections (although the reverse can be true
|
||||
in object files). So, we issue at least one write request per
|
||||
passed struct memory_write_request. The remote stub will still
|
||||
have the opportunity to batch flash requests. */
|
||||
|
||||
/* Write regular blocks. */
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, regular, i, r); ++i)
|
||||
{
|
||||
LONGEST len;
|
||||
|
||||
len = target_write_with_progress (¤t_target,
|
||||
TARGET_OBJECT_MEMORY, NULL,
|
||||
r->data, r->begin, r->end - r->begin,
|
||||
progress_cb, r->baton);
|
||||
if (len < (LONGEST) (r->end - r->begin))
|
||||
{
|
||||
/* Call error? */
|
||||
err = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!VEC_empty (memory_write_request_s, erased))
|
||||
{
|
||||
/* Erase all pages. */
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, erased, i, r); ++i)
|
||||
target_flash_erase (r->begin, r->end - r->begin);
|
||||
|
||||
/* Write flash data. */
|
||||
for (i = 0; VEC_iterate (memory_write_request_s, flash, i, r); ++i)
|
||||
{
|
||||
LONGEST len;
|
||||
|
||||
len = target_write_with_progress (¤t_target,
|
||||
TARGET_OBJECT_FLASH, NULL,
|
||||
r->data, r->begin, r->end - r->begin,
|
||||
progress_cb, r->baton);
|
||||
if (len < (LONGEST) (r->end - r->begin))
|
||||
error (_("Error writing data to flash"));
|
||||
}
|
||||
|
||||
target_flash_done ();
|
||||
}
|
||||
|
||||
out:
|
||||
do_cleanups (back_to);
|
||||
|
||||
return err;
|
||||
}
|
46
gdb/target.c
46
gdb/target.c
@ -465,6 +465,8 @@ update_current_target (void)
|
||||
INHERIT (to_get_thread_local_address, t);
|
||||
INHERIT (to_magic, t);
|
||||
/* Do not inherit to_memory_map. */
|
||||
/* Do not inherit to_flash_erase. */
|
||||
/* Do not inherit to_flash_done. */
|
||||
}
|
||||
#undef INHERIT
|
||||
|
||||
@ -892,6 +894,12 @@ memory_xfer_partial (struct target_ops *ops, void *readbuf, const void *writebuf
|
||||
if (readbuf != NULL)
|
||||
return -1;
|
||||
break;
|
||||
|
||||
case MEM_FLASH:
|
||||
/* We only support writing to flash during "load" for now. */
|
||||
if (writebuf != NULL)
|
||||
error (_("Writing to flash memory forbidden in this context"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (region->attrib.cache)
|
||||
@ -1089,6 +1097,39 @@ target_memory_map (void)
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
target_flash_erase (ULONGEST address, LONGEST length)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
if (t->to_flash_erase != NULL)
|
||||
{
|
||||
if (targetdebug)
|
||||
fprintf_unfiltered (gdb_stdlog, "target_flash_erase (%s, %s)\n",
|
||||
paddr (address), phex (length, 0));
|
||||
return t->to_flash_erase (t, address, length);
|
||||
}
|
||||
|
||||
tcomplain ();
|
||||
}
|
||||
|
||||
void
|
||||
target_flash_done (void)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
if (t->to_flash_done != NULL)
|
||||
{
|
||||
if (targetdebug)
|
||||
fprintf_unfiltered (gdb_stdlog, "target_flash_done\n");
|
||||
return t->to_flash_done (t);
|
||||
}
|
||||
|
||||
tcomplain ();
|
||||
}
|
||||
|
||||
#ifndef target_stopped_data_address_p
|
||||
int
|
||||
target_stopped_data_address_p (struct target_ops *target)
|
||||
@ -1229,6 +1270,11 @@ target_write_with_progress (struct target_ops *ops,
|
||||
void (*progress) (ULONGEST, void *), void *baton)
|
||||
{
|
||||
LONGEST xfered = 0;
|
||||
|
||||
/* Give the progress callback a chance to set up. */
|
||||
if (progress)
|
||||
(*progress) (0, baton);
|
||||
|
||||
while (xfered < len)
|
||||
{
|
||||
LONGEST xfer = target_write_partial (ops, object, annex,
|
||||
|
81
gdb/target.h
81
gdb/target.h
@ -202,6 +202,11 @@ enum target_object
|
||||
TARGET_OBJECT_WCOOKIE,
|
||||
/* Target memory map in XML format. */
|
||||
TARGET_OBJECT_MEMORY_MAP,
|
||||
/* Flash memory. This object can be used to write contents to
|
||||
a previously erased flash memory. Using it without erasing
|
||||
flash can have unexpected results. Addresses are physical
|
||||
address on target, and not relative to flash start. */
|
||||
TARGET_OBJECT_FLASH
|
||||
|
||||
/* Possible future objects: TARGET_OBJECT_FILE, TARGET_OBJECT_PROC, ... */
|
||||
};
|
||||
@ -227,11 +232,13 @@ extern LONGEST target_write (struct target_ops *ops,
|
||||
const char *annex, const gdb_byte *buf,
|
||||
ULONGEST offset, LONGEST len);
|
||||
|
||||
/* Similar to target_write, except that it also calls PROGRESS
|
||||
with the number of bytes written and the opaque BATON after
|
||||
every partial write. This is useful for progress reporting
|
||||
and user interaction while writing data. To abort the transfer,
|
||||
the progress callback can throw an exception. */
|
||||
/* Similar to target_write, except that it also calls PROGRESS with
|
||||
the number of bytes written and the opaque BATON after every
|
||||
successful partial write (and before the first write). This is
|
||||
useful for progress reporting and user interaction while writing
|
||||
data. To abort the transfer, the progress callback can throw an
|
||||
exception. */
|
||||
|
||||
LONGEST target_write_with_progress (struct target_ops *ops,
|
||||
enum target_object object,
|
||||
const char *annex, const gdb_byte *buf,
|
||||
@ -471,6 +478,20 @@ struct target_ops
|
||||
layers will re-fetch it. */
|
||||
VEC(mem_region_s) *(*to_memory_map) (struct target_ops *);
|
||||
|
||||
/* Erases the region of flash memory starting at ADDRESS, of
|
||||
length LENGTH.
|
||||
|
||||
Precondition: both ADDRESS and ADDRESS+LENGTH should be aligned
|
||||
on flash block boundaries, as reported by 'to_memory_map'. */
|
||||
void (*to_flash_erase) (struct target_ops *,
|
||||
ULONGEST address, LONGEST length);
|
||||
|
||||
/* Finishes a flash memory write sequence. After this operation
|
||||
all flash memory should be available for writing and the result
|
||||
of reading from areas written by 'to_flash_write' should be
|
||||
equal to what was written. */
|
||||
void (*to_flash_done) (struct target_ops *);
|
||||
|
||||
int to_magic;
|
||||
/* Need sub-structure for target machine related rather than comm related?
|
||||
*/
|
||||
@ -599,6 +620,56 @@ extern int child_xfer_memory (CORE_ADDR, gdb_byte *, int, int,
|
||||
is returned. */
|
||||
VEC(mem_region_s) *target_memory_map (void);
|
||||
|
||||
/* Erase the specified flash region. */
|
||||
void target_flash_erase (ULONGEST address, LONGEST length);
|
||||
|
||||
/* Finish a sequence of flash operations. */
|
||||
void target_flash_done (void);
|
||||
|
||||
/* Describes a request for a memory write operation. */
|
||||
struct memory_write_request
|
||||
{
|
||||
/* Begining address that must be written. */
|
||||
ULONGEST begin;
|
||||
/* Past-the-end address. */
|
||||
ULONGEST end;
|
||||
/* The data to write. */
|
||||
gdb_byte *data;
|
||||
/* A callback baton for progress reporting for this request. */
|
||||
void *baton;
|
||||
};
|
||||
typedef struct memory_write_request memory_write_request_s;
|
||||
DEF_VEC_O(memory_write_request_s);
|
||||
|
||||
/* Enumeration specifying different flash preservation behaviour. */
|
||||
enum flash_preserve_mode
|
||||
{
|
||||
flash_preserve,
|
||||
flash_discard
|
||||
};
|
||||
|
||||
/* Write several memory blocks at once. This version can be more
|
||||
efficient than making several calls to target_write_memory, in
|
||||
particular because it can optimize accesses to flash memory.
|
||||
|
||||
Moreover, this is currently the only memory access function in gdb
|
||||
that supports writing to flash memory, and it should be used for
|
||||
all cases where access to flash memory is desirable.
|
||||
|
||||
REQUESTS is the vector (see vec.h) of memory_write_request.
|
||||
PRESERVE_FLASH_P indicates what to do with blocks which must be
|
||||
erased, but not completely rewritten.
|
||||
PROGRESS_CB is a function that will be periodically called to provide
|
||||
feedback to user. It will be called with the baton corresponding
|
||||
to the request currently being written. It may also be called
|
||||
with a NULL baton, when preserved flash sectors are being rewritten.
|
||||
|
||||
The function returns 0 on success, and error otherwise. */
|
||||
int target_write_memory_blocks (VEC(memory_write_request_s) *requests,
|
||||
enum flash_preserve_mode preserve_flash_p,
|
||||
void (*progress_cb) (ULONGEST, void *));
|
||||
|
||||
|
||||
extern char *child_pid_to_exec_file (int);
|
||||
|
||||
extern char *child_core_file_to_sym_file (char *);
|
||||
|
Loading…
Reference in New Issue
Block a user