=== gdb/target.c ================================================================== --- gdb/target.c (/mirrors/gdb) (revision 336) +++ gdb/target.c (/patches/flash_memory/gdb) (revision 336) @@ -456,6 +456,9 @@ INHERIT (to_make_corefile_notes, t); INHERIT (to_get_thread_local_address, t); INHERIT (to_magic, t); + /* Do not inherit to_flash_erase. */ + /* Do not inherit to_flash_write. */ + /* Do not inherit to_flash_done. */ } #undef INHERIT @@ -1011,6 +1014,40 @@ return target_xfer_memory (memaddr, bytes, len, 1); } +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) @@ -1350,7 +1387,7 @@ return target_xfer_partial (ops, object, annex, buf, NULL, offset, len); } -static LONGEST +LONGEST target_write_partial (struct target_ops *ops, enum target_object object, const char *annex, const gdb_byte *buf, === gdb/target.h ================================================================== --- gdb/target.h (/mirrors/gdb) (revision 336) +++ gdb/target.h (/patches/flash_memory/gdb) (revision 336) @@ -194,7 +194,14 @@ /* Transfer auxilliary vector. */ TARGET_OBJECT_AUXV, /* StackGhost cookie. See "sparc-tdep.c". */ - TARGET_OBJECT_WCOOKIE + TARGET_OBJECT_WCOOKIE, + /* Target memory map in XML format. */ + TARGET_OBJECT_MEMORY_MAP, + /* Flash memory. This object can be used to write content to + a previously erased flash memory. Using it without erasing + flash has undefined results. Addresses are physicall + address on target, and not relative to flash start. */ + TARGET_OBJECT_FLASH /* Possible future objects: TARGET_OBJECT_FILE, TARGET_OBJECT_PROC, ... */ }; @@ -437,6 +444,20 @@ gdb_byte *readbuf, const gdb_byte *writebuf, ULONGEST offset, LONGEST len); + /* Erases the region of flash memory starting ad ADDRESS, or + length LENGTH. + Precondition: both ADDRESS and ADDRESS+LENGTH are aligned + on flash block boundary, as reported by 'to_memory_map'. */ + void (*to_flash_erase) (struct target_ops *, + ULONGEST address, LONGEST length); + + /* Finishes flash memory write. After this operation all flash + memory should be available for writing and the content of + areas written by 'to_flash_write' should be equal to what's + written. */ + void (*to_flash_done) (struct target_ops *); + + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ @@ -575,6 +596,96 @@ extern int target_write_memory_partial (CORE_ADDR addr, gdb_byte *buf, int len, int *err); +LONGEST +target_write_partial (struct target_ops *ops, + enum target_object object, + const char *annex, const gdb_byte *buf, + ULONGEST offset, LONGEST len); + +/* Calls the first non-null to_memory_map pointer in target_stack. + Sorts the result by starting address and returns it. Additionally + checks that memory regions do not overlap. If they do, issues + a warning and returns empty vector. */ +VEC(memory_region) *target_memory_map (void); + +/* Calls the first non-null to_flash_erase pointer in target_stack. */ +void target_flash_erase (ULONGEST address, LONGEST length); + +/* Calls the first non-null to_flash_done pointer in target_stack. */ +void target_flash_done (void); + +/* Describes a request for memory write operation. */ +typedef 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; + /* The name of this memory block. The name will be only + passed to progress callback rountine and no semantic + is attached to it. + */ + char *name; + } memory_write_request; + +DEF_VEC_O(memory_write_request); + +/* The type of progress callback for 'target_write_memory_blocks' below + NAME is the name of the memory block as specified by the user. + SENT is the number of bytes transferred for the current block. + SIZE is the size of the the current block in bytes. + TOTAL_SENT is the number of bytes already sent by target_write_memory_blocks + call. + TOTAL_SIZE is the total number of bytes in all blocks passed to + target_write_memory_blocks.. + + The function should return 0 if memory write is to continue and + non-zero value to terminate. */ +typedef int (*target_write_memory_blocks_progress_cb) + (const char* name, + ULONGEST sent, ULONGEST size, + ULONGEST total_sent, ULONGEST total_size); + +/* Enumeration specifying different flash preservation behaviour. */ +enum flash_preserve_mode + { + flash_preserve = 1, + flash_dont_preserve, + flash_abort + }; + +/* Writes several memory blocks at once. This version is more efficient + than making several calls to 'target_write_memory', in particular because + it can optimize accesses to flash memory. + + Moreover, it's 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 possible. + + MEMORY_WRITE_REQUESTS is the vector (see vec.h) of memory_write_request. + FLASH_WRITE_ALLOWED is a function that will be called before writing + any flash memory. If it returns 1, flash is written. Otherwise, + write is aborted. The function can opt to call error, if it returns + 0 write is aborted without diagnostic. + PRESERVE_FLASH is a function that will be called if any block of flash + needs to be erased, but is not completely written, and should + return a value indicating what to do. + PROGRESS_CB is a function that will be periodically called to provide + feedback to user. + + Note that using FLASH_WRITE_ALLOWED and PRESERVE_FLASH callbacks makes + it possible to interactively ask the user. + + The function returns 0 on success, and error otherwise. */ +extern int +target_write_memory_blocks (VEC(memory_write_request) *memory_write_requests, + int (*flash_write_allowed) (void), + enum flash_preserve_mode (*preserve_flash) (void), + target_write_memory_blocks_progress_cb progress_cb); + + extern char *child_pid_to_exec_file (int); extern char *child_core_file_to_sym_file (char *); === gdb/symfile.c ================================================================== --- gdb/symfile.c (/mirrors/gdb) (revision 336) +++ gdb/symfile.c (/patches/flash_memory/gdb) (revision 336) @@ -1512,15 +1512,6 @@ overlay_cache_invalid = 1; } -/* This version of "load" should be usable for any target. Currently - it is just used for remote targets, not inftarg.c or core files, - on the theory that only in that case is it useful. - - Avoiding xmodem and the like seems like a win (a) because we don't have - to worry about finding it, and (b) On VMS, fork() is very slow and so - we don't want to run a subprocess. On the other hand, I'm not sure how - performance compares. */ - static int download_write_size = 512; static void show_download_write_size (struct ui_file *file, int from_tty, @@ -1532,22 +1523,11 @@ } static int validate_download = 0; -/* Callback service function for generic_load (bfd_map_over_sections). */ -static void -add_section_size_callback (bfd *abfd, asection *asec, void *data) -{ - bfd_size_type *sum = data; - - *sum += bfd_get_section_size (asec); -} - /* Opaque data for load_section_callback. */ struct load_section_data { unsigned long load_offset; - unsigned long write_count; - unsigned long data_count; - bfd_size_type total_size; + VEC(memory_write_request) *memory_write_requests; }; /* Callback service function for generic_load (bfd_map_over_sections). */ @@ -1563,12 +1543,9 @@ if (size > 0) { gdb_byte *buffer; - struct cleanup *old_chain; + CORE_ADDR lma = bfd_section_lma (abfd, asec) + args->load_offset; - bfd_size_type block_size; - int err; const char *sect_name = bfd_get_section_name (abfd, asec); - bfd_size_type sent; if (download_write_size > 0 && size > download_write_size) block_size = download_write_size; @@ -1576,8 +1553,6 @@ block_size = size; buffer = xmalloc (size); - old_chain = make_cleanup (xfree, buffer); - /* 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", @@ -1585,81 +1560,88 @@ bfd_get_section_contents (abfd, asec, buffer, 0, size); - sent = 0; - do - { - int len; - bfd_size_type this_transfer = size - sent; + { + memory_write_request *n = + VEC_safe_push (memory_write_request, + args->memory_write_requests, 0); + n->begin = lma; + n->end = lma + size; + n->data = buffer; + n->name = strdup (sect_name); + } + } + } +} - if (this_transfer >= block_size) - this_transfer = block_size; - len = target_write_memory_partial (lma, buffer, - this_transfer, &err); - if (err) - break; - if (validate_download) - { - /* Broken memories and broken monitors manifest - themselves here when bring new computers to - life. This doubles already slow downloads. */ - /* NOTE: cagney/1999-10-18: A more efficient - implementation might add a verify_memory() - method to the target vector and then use - that. remote.c could implement that method - using the ``qCRC'' packet. */ - gdb_byte *check = xmalloc (len); - struct cleanup *verify_cleanups = - make_cleanup (xfree, check); +static int +allow_flash_write (void) +{ + return 1; +} - if (target_read_memory (lma, check, len) != 0) - error (_("Download verify read failed at 0x%s"), - paddr (lma)); - if (memcmp (buffer, check, len) != 0) - error (_("Download verify compare failed at 0x%s"), - paddr (lma)); - do_cleanups (verify_cleanups); - } - args->data_count += len; - lma += len; - buffer += len; - args->write_count += 1; - sent += len; - if (quit_flag - || (deprecated_ui_load_progress_hook != NULL - && deprecated_ui_load_progress_hook (sect_name, sent))) - error (_("Canceled the download")); - if (deprecated_show_load_progress != NULL) - deprecated_show_load_progress (sect_name, sent, size, - args->data_count, - args->total_size); - } - while (sent < size); +static enum flash_preserve_mode +dont_preserve_flash (void) +{ + return flash_dont_preserve; +} - if (err != 0) - error (_("Memory access error while loading section %s."), sect_name); +/* Casts 'p' to vector of memory_write_request object and + frees that DATA field in all such objects. */ +static void +clear_memory_write_data (void *p) +{ + VEC(memory_write_request) *vec = p; + int i; + memory_write_request *mr; + for (i = 0; VEC_iterate (memory_write_request, vec, i, mr); ++i) + { + xfree (mr->data); + xfree (mr->name); + } +} - do_cleanups (old_chain); - } +static int +download_progress_cb (const char* name, + ULONGEST sent, ULONGEST size, + ULONGEST total_sent, ULONGEST total_size) +{ + int ret = 0; + if (deprecated_ui_load_progress_hook != NULL + && deprecated_ui_load_progress_hook (name, sent)) + { + /* Stop download. */ + return 1; } + + if (deprecated_show_load_progress != NULL) + deprecated_show_load_progress (name, sent, size, + total_sent, total_size); + + return 0; } -void -generic_load (char *args, int from_tty) + +/* This version of "load" should be usable for any target. Currently + it is just used for remote targets, not inftarg.c or core files, + on the theory that only in that case is it useful. */ +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; CORE_ADDR entry; char **argv; + unsigned long total_count = 0; + memory_write_request *mr; + int i; + struct timeval start_time, end_time; + 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. */ + cbdata.memory_write_requests = VEC_alloc (memory_write_request, 1); argv = buildargv (args); @@ -1705,34 +1687,52 @@ bfd_errmsg (bfd_get_error ())); } - bfd_map_over_sections (loadfile_bfd, add_section_size_callback, - (void *) &cbdata.total_size); - gettimeofday (&start_time, NULL); bfd_map_over_sections (loadfile_bfd, load_section_callback, &cbdata); + for (i = 0; + VEC_iterate (memory_write_request, cbdata.memory_write_requests, i, mr); + ++i) + { + total_count += (mr->end - mr->begin); + } + + /* Note: cleanups are run in reverse order, so first register code + for cleaning the array, and then for cleaning the pointers contained + in array. */ + make_cleanup ((void (*)(void*))VEC_OP(memory_write_request, free), + &cbdata.memory_write_requests); + make_cleanup (clear_memory_write_data, cbdata.memory_write_requests); + + + if (target_write_memory_blocks + (cbdata.memory_write_requests, + &allow_flash_write, &dont_preserve_flash, &download_progress_cb) != 0) + { + error (_("Failed to write memory")); + } + gettimeofday (&end_time, NULL); + print_transfer_performance (gdb_stdout, total_count, + 0 /* Write count not known. */, + &start_time, &end_time); + + entry = bfd_get_start_address (loadfile_bfd); + + 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_count); ui_out_text (uiout, "\n"); + /* We were doing this in remote-mips.c, I suspect it is right for other targets too. */ write_pc (entry); - /* FIXME: are we supposed to call symbol_file_add or not? According - to a comment from remote-mips.c (where a call to symbol_file_add - was commented out), making the call confuses GDB if more than one - 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); - do_cleanups (old_cleanups); } === gdb/target-memory.c ================================================================== --- gdb/target-memory.c (/mirrors/gdb) (revision 336) +++ gdb/target-memory.c (/patches/flash_memory/gdb) (revision 336) @@ -0,0 +1,556 @@ +/* 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 "config.h" +#include "vec.h" +#include "target.h" +#include "gdb_assert.h" +#include "memory-map.h" + +#include +#include + +static int +compare_block_starting_address (const void* a, const void *b) +{ + ULONGEST a_begin = ((memory_write_request*)a)->begin; + ULONGEST b_begin = ((memory_write_request*)b)->begin; + return a_begin - b_begin; +} + +/* 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, + a part of memory request will be added. */ +static void +claim_memory (VEC(memory_write_request) *blocks, + VEC(memory_write_request) **result, + ULONGEST begin, + ULONGEST end) +{ + int i; + ULONGEST claimed_begin; + ULONGEST claimed_end; + memory_write_request *r; + + for (i = 0; VEC_iterate (memory_write_request, blocks, i, r); ++i) + { + if (begin >= r->end || end <= r->begin) + continue; + + claimed_begin = max (begin, r->begin); + claimed_end = min (end, r->end); + + if (claimed_begin == r->begin && claimed_end == r->end) + VEC_safe_push (memory_write_request, *result, r); + else + { + memory_write_request *n = + VEC_safe_push (memory_write_request, *result, 0); + n->begin = claimed_begin; + n->end = claimed_end; + n->data = r->data + (claimed_begin - r->begin); + } + } +} + +/* Given an array of 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) *blocks, + VEC(memory_write_request) **regular_blocks, + VEC(memory_write_request) **flash_blocks) +{ + VEC(memory_region) *regions = target_memory_map (); + int i; + memory_region *cur; + + /* 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. */ + if (VEC_empty (memory_region, regions)) + { + int i; + memory_write_request* ptr; + for (i = 0; i < VEC_iterate (memory_write_request, blocks, i, ptr); ++i) + VEC_safe_push (memory_write_request, *regular_blocks, ptr); + return; + } + + if (VEC_index (memory_region, regions, 0)->begin > 0) + { + /* Nothing is said about (0, regions.begin) region. Assume regular. */ + claim_memory (blocks, regular_blocks, 0, + VEC_index (memory_region, regions, 0)->begin); + } + + for (i = 0; VEC_iterate (memory_region, regions, i, cur); ++i) + { + VEC(memory_write_request) **r = + (cur->memory_type == TARGET_MEMORY_FLASH) ? + flash_blocks : regular_blocks; + + claim_memory (blocks, r, cur->begin, cur->end); + + if (i < VEC_length (memory_region, regions) - 1) + { + memory_region *next = VEC_index (memory_region, regions, i+1); + if (cur->end < next->begin) + { + claim_memory (blocks, regular_blocks, + cur->end, next->begin); + } + } + else + { + claim_memory (blocks, regular_blocks, cur->end, ULONGEST_MAX); + } + } +} + +/* Given 'address', return begin and end addresses for the flash segments + that address is in. */ +static void +segment_boundaries (unsigned address, unsigned *begin, unsigned *end) +{ + VEC(memory_region) *regions = target_memory_map (); + unsigned i; + memory_region *r; + + for(i = 0; VEC_iterate (memory_region, regions, i, r); ++i) + { + if (r->begin <= address && address < r->end) + { + unsigned block_size = r->flash_block_size; + + if (begin) + *begin = address/block_size*block_size; + if (end) + *end = (address + block_size - 1)/block_size*block_size; + return; + } + } + if (begin) + *begin = (unsigned)-1; + if (end) + *end = (unsigned)-1; +} + +/* Returns the list of flash blocks that must be erased. */ +static VEC(memory_write_request) * +blocks_to_erase (VEC(memory_write_request)* written) +{ + unsigned page_size = 1024; + unsigned i; + memory_write_request* ptr; + + VEC(memory_write_request) *result = NULL; + + for(i = 0; VEC_iterate (memory_write_request, written, i, ptr); ++i) + { + unsigned begin; + segment_boundaries (ptr->begin, &begin, 0); + unsigned end; + segment_boundaries (ptr->end, 0, &end); + + if (!VEC_empty(memory_write_request, result) + && VEC_last (memory_write_request, result)->end >= begin) + { + VEC_last (memory_write_request, result)->end = end; + } + else + { + memory_write_request* n = + VEC_safe_push (memory_write_request, result, 0); + n->begin = begin; + n->end = end; + } + } + + return result; +} + + +/* Given a list of blocks that will be erased with flash erase commands, + and the list of blocks that will be written, computed the set + of regions that will have its content erased and not written. */ +static VEC(memory_write_request) * +compute_garbled_blocks (VEC(memory_write_request)* erased_blocks, + VEC(memory_write_request)* written_blocks) +{ + VEC(memory_write_request) *result = NULL; + + unsigned i, j; + unsigned je = VEC_length (memory_write_request, written_blocks); + memory_write_request *erased_p; + + /* Look at each erased memory_write_request in turn, and + see if what part of it is subsequently written to. */ + for(i = 0; + VEC_iterate (memory_write_request, 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. */ + memory_write_request erased = *erased_p; + for(j = 0; j != je;) + { + memory_write_request* written = VEC_index (memory_write_request, + written_blocks, j); + + /* Now try various cases. */ + + /* 1. written is fully to the left of erased. + Check next written memory_write_request. */ + if (written->end <= erased.begin) + { + ++j; + continue; + } + + /* 2. written is fully to the right of erased. Erased + is not written at all. written might affect other blocks. */ + if (written->begin >= erased.end) + { + VEC_safe_push (memory_write_request, result, &erased); + goto next_erased; + } + + /* 3. All of 'erased' is completely written. */ + if (written->begin <= erased.begin + && written->end >= erased.end) + { + goto next_erased; + } + + /* 4. Is there unwritten part at the beginning? */ + if (written->begin > erased.begin) + { + memory_write_request *n = + VEC_safe_push (memory_write_request, result, 0); + n->begin = erased.begin; + n->end = written->end; + erased.begin = written->begin; + continue; + } + + /* 5. Is there unwritten part at the end? */ + if (written->end < erased.end) + { + /* Unwritten memory_write_request at the end. However, next write + memory_write_request can touch it, so wait a bit. */ + erased.begin = written->end; + ++j; + continue; + } + } + + /* Run out of loop without doing anything about 'erased'. + That means it's really erased. */ + VEC_safe_push (memory_write_request, result, &erased); + + next_erased: + ; + } + + + return result; +} + +static VEC(memory_write_request) * +join_blocks(VEC(memory_write_request)* blocks) +{ + VEC(memory_write_request) *result = NULL; + unsigned i; + memory_write_request *b; + + for(i = 0; VEC_iterate (memory_write_request, blocks, i, b); ++i) + { + if (VEC_empty (memory_write_request, result) + || VEC_last (memory_write_request, result)->end != b->begin) + { + VEC_safe_push (memory_write_request, result, b); + } + else + { + VEC_last (memory_write_request, result)->end = b->end; + } + } + + return result; +} + +DEF_VEC_I(int); + +/* Call progress callback after next data transfer is done. + This function is called with the range of addresses [BEGIN, END) + that was just transferred. + MEMORY_WRITE_REQUESTS is the vector of original write requests passed + to target_write_memory_blocks. + TRANSFERRED is a vector of the same size as MEMORY_WRITE_REQUESTS, + that contains the number of bytes already transferred for the + parallel element of MEMORY_WRITE_REQUESTS. + TOTAL count should contain the sum of data sizes for all requests. + TRANSFERRED_TOTAL is the amount of data transferred so far. + PROGRESS_CB is the callback to be called. + + The function determines all memory write requests which overlap + with just written range, recomputes written bytes count for + each, and calls progress_cb. + + Returns 0 is operation is to continue, and 1 if operation should + be terminated. */ +static int +report_progress (ULONGEST begin, ULONGEST end, + VEC(memory_write_request) *memory_write_requests, + VEC(int) *transferred, + ULONGEST total_count, + ULONGEST *transferred_total, + target_write_memory_blocks_progress_cb progress_cb) +{ + int i; + memory_write_request *r; + + for (i = 0; + VEC_iterate (memory_write_request, memory_write_requests, i, r); + ++i) + { + ULONGEST common_begin, common_end; + common_begin = max (begin, r->begin); + common_end = min (end, r->end); + if (common_begin < common_end) + { + int ret; + unsigned size = (common_end - common_begin); + *transferred_total += size; + *(VEC_address (int, transferred) + i) += size; + + ret = (*progress_cb) (r->name, VEC_index (int, transferred, i), + r->end - r->begin, + *transferred_total, total_count); + if (ret) + return ret; + } + } + return 0; +} + +static void +cleanup_write_requests_vector (void *p) +{ + VEC(memory_write_request) **v = p; + VEC_free (memory_write_request, *v); +} + +static void +cleanup_int_vector (void *p) +{ + VEC(int) **v = p; + VEC_free (int, *v); +} + + +int +target_write_memory_blocks (VEC(memory_write_request) *memory_write_requests, + int (*flash_write_allowed)(), + enum flash_preserve_mode (*preserve_flash)(), + target_write_memory_blocks_progress_cb progress_cb) +{ + struct cleanup *back_to = make_cleanup (null_cleanup, NULL); + VEC(memory_write_request) *blocks = VEC_copy (memory_write_request, + memory_write_requests); + unsigned i; + unsigned long total_data_count = 0; + int err = 0; + memory_write_request* r; + VEC(int) *transferred = 0; + ULONGEST transferred_total = 0; + + make_cleanup (cleanup_write_requests_vector, &blocks); + + for (i = 0; VEC_iterate (memory_write_request, blocks, i, r); ++i) + { + total_data_count += (r->end - r->begin); + } + + /* Sort the blocks by the start address. */ + qsort (VEC_address (memory_write_request, blocks), + VEC_length (memory_write_request, blocks), + sizeof (memory_write_request), compare_block_starting_address); + + /* Split blocks into list of regular memory blocks, + and list of flash memory blocks. */ + VEC(memory_write_request) *regular = VEC_alloc (memory_write_request, 1); + VEC(memory_write_request) *flash = VEC_alloc (memory_write_request, 1); + + make_cleanup (cleanup_write_requests_vector, ®ular); + make_cleanup (cleanup_write_requests_vector, &flash); + + split_regular_and_flash_blocks (blocks, ®ular, &flash); + + + /* Find out if flash write is allowed. */ + if (!VEC_empty (memory_write_request, flash)) + { + if (!flash_write_allowed) + { + error(_("Write to flash memory is not allowed")); + } + if (!flash_write_allowed()) + { + error(_("Write to flash memory is not allowed")); + } + } + + /* Find flash blocks to erase. */ + VEC(memory_write_request) *erased = blocks_to_erase (flash); + make_cleanup (cleanup_write_requests_vector, &erased); + + /* Find what flash regions will be erased, and not written, + and decide if we error out, save them, or go on anyway. */ + VEC(memory_write_request) *garbled = compute_garbled_blocks (erased, flash); + make_cleanup (cleanup_write_requests_vector, &garbled); + + if (!VEC_empty (memory_write_request, garbled)) + { + /* If no callback is provided, play safe. */ + enum flash_preserve_mode ret = + preserve_flash ? (*preserve_flash)() : flash_preserve; + + if (ret == flash_abort) + { + error(_("Flash region erased but not written")); + } + + /* Read in regions that must be preserved and add them to the + list of blocks we read. */ + if (ret == flash_preserve) + { + memory_write_request *r; + for(i = 0; VEC_iterate (memory_write_request, garbled, i, r); ++i) + { + gdb_assert (r->data == 0); + r->data = malloc (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, flash, r); + } + + qsort (VEC_address (memory_write_request, flash), + VEC_length (memory_write_request, flash), + sizeof (memory_write_request), compare_block_starting_address); + } + } + + /* Join adjacent memory blocks. */ + VEC(memory_write_request) *regular_final = join_blocks (regular); + make_cleanup (cleanup_write_requests_vector, ®ular_final); + + VEC(memory_write_request) *flash_final = join_blocks (flash); + make_cleanup (cleanup_write_requests_vector, &flash_final); + + /* Prepare for progress reporting. */ + for (i = 0; i < VEC_length (memory_write_request, memory_write_requests); ++i) + VEC_safe_push (int, transferred, 0); + make_cleanup (cleanup_int_vector, &transferred); + + /* Write regular blocks. */ + for (i = 0; VEC_iterate (memory_write_request, regular_final, i, r); ++i) + { + err = target_write_memory (r->begin, r->data, r->end - r->begin); + if (err != 0) + goto out; + + if (progress_cb) + { + err = report_progress (r->begin, r->end, memory_write_requests, + transferred, total_data_count, + &transferred_total, progress_cb); + if (err) + goto out; + } + } + + if (!VEC_empty (memory_write_request, erased)) + { + /* Erase all pages. */ + for(i = 0; i < VEC_iterate (memory_write_request, erased, i, r); ++i) + { + target_flash_erase (r->begin, r->end - r->begin); + } + /* Write flash data. */ + for(i = 0; i < VEC_iterate (memory_write_request, flash_final, i, r); + ++i) + { + int len; + + while(r->begin < r->end) + { + len = target_write_partial (¤t_target, + TARGET_OBJECT_FLASH, 0, + r->data, + r->begin, r->end - r->begin); + if (len <= 0) + error (_("Error writing data to flash")); + + if (progress_cb) + { + err = report_progress (r->begin, r->begin + len, + memory_write_requests, + transferred, total_data_count, + &transferred_total, progress_cb); + if (err) + goto out; + } + + r->begin += len; + r->data += len; + } + + if (progress_cb) + { + err = report_progress (r->begin, r->end, memory_write_requests, + transferred, total_data_count, + &transferred_total, progress_cb); + if (err) + goto out; + } + } + + target_flash_done (); + } + + out: + do_cleanups (back_to); + + return err; +} === gdb/Makefile.in ================================================================== --- gdb/Makefile.in (/mirrors/gdb) (revision 336) +++ gdb/Makefile.in (/patches/flash_memory/gdb) (revision 336) @@ -548,7 +548,7 @@ 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 \ @@ -953,7 +953,7 @@ trad-frame.o \ tramp-frame.o \ solib.o solib-null.o \ - prologue-value.o + prologue-value.o vec.o target-memory.o memory-map.o TSOBS = inflow.o @@ -2737,6 +2737,8 @@ 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) $(config_h) $(vec_h) $(target_t) \ + $(gdb_assert_t) $(memory_map_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) \