From: Daniel Jacobowitz Date: Thu, 21 Sep 2006 14:00:53 +0000 (+0000) Subject: * Makefile.in (SFILES): Add target-memory.c. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a76d924dffcb040b44a2bb5be026f0c974590c30;p=binutils-gdb.git * 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. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 10cb3655127..623e7f1d5c4 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,48 @@ +2006-09-21 Vladimir Prus + Daniel Jacobowitz + + * 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 Daniel Jacobowitz Nathan Sidwell diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 57f58403b27..394f4e060c6 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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) \ diff --git a/gdb/memattr.c b/gdb/memattr.c index 70f625bae46..889d03ac8f4 100644 --- a/gdb/memattr.c +++ b/gdb/memattr.c @@ -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; } } diff --git a/gdb/remote.c b/gdb/remote.c index e2716b53a8a..d05be841638 100644 --- a/gdb/remote.c +++ b/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 +
,: + + where encoding of is termined by PACKET_FORMAT. + + If USE_LENGTH is 0, then the 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,:...#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],:". */ + /* Construct the packet excluding the data: "
,:". */ - /* 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 . Retain the location/size of . It may need to - be adjusted once the packet body has been created. */ - plen = p; - plenlen = hexnumstr (p, (ULONGEST) todo); - p += plenlen; + /* Append . Retain the location/size of . 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 diff --git a/gdb/symfile.c b/gdb/symfile.c index 2036a073c14..8329aa5aa1e 100644 --- a/gdb/symfile.c +++ b/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; +}; + +/* Opaque data for load_progress for a single section. */ +struct load_progress_section_data { + struct load_progress_data *cumulative; - /* Per-section data for load_progress. */ + /* 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); +} + +/* Clean up an entire memory request vector, including load + data and progress records. */ - 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); +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; - do_cleanups (old_chain); + 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); } diff --git a/gdb/target-memory.c b/gdb/target-memory.c new file mode 100644 index 00000000000..64c16e1ac05 --- /dev/null +++ b/gdb/target-memory.c @@ -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 +#include + +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; +} diff --git a/gdb/target.c b/gdb/target.c index 3f6352774a7..690e4303b51 100644 --- a/gdb/target.c +++ b/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, diff --git a/gdb/target.h b/gdb/target.h index 67692163a41..ffbaddc5cee 100644 --- a/gdb/target.h +++ b/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 *);