* 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:
Daniel Jacobowitz 2006-09-21 14:00:53 +00:00
parent fd79eceebf
commit a76d924dff
8 changed files with 968 additions and 111 deletions

View File

@ -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>

View File

@ -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) \

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 (&current_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
View 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, &regular);
make_cleanup (cleanup_write_requests_vector, &flash);
split_regular_and_flash_blocks (blocks, &regular, &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 (&current_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 (&current_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;
}

View File

@ -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,

View File

@ -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 *);