This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Flash support part 2: flash programming
- From: Vladimir Prus <vladimir at codesourcery dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: Thu, 20 Jul 2006 13:42:30 +0400
- Subject: Flash support part 2: flash programming
Hello,
this patch is the second part of my work to add flash memory programming
support to gdb. Since the complete patch is large, it's split in 3 part for
easier review.
First part added support for getting target memory map.
This patch:
- Adds new target interfaces for working with flash.
- Implements those interfaces for remote protocol, using
previously discussed vFlashErase, vFlashWrite and vFlashDone packets.
- Introduces new function target_write_memory_blocks, that can take
several memory requests at once, and uses target memory map to
property drive flash
- Changes the 'generic_load' function to use the target_write_memory_blocks
function.
This patch was hand-tested with a real hardware.
- Volodya
2006-07-18 Vladimir Prus <vladimir@codesourcery.com>
* Makefile.in (SFILES): Add target-memory.c.
(COMMON_OBJS): Add target-memory.o.
(target-memory.o): New, specify dependencies.
* target.h (enum target_object): New value
TARGET_OBJECT_FLASH.
(struct target_ops): New methods to_flash_erase,
and to_flash_done.
(struct memory_write_request): New.
(typedef target_write_memory_2_progress_cb): New
(enum flash_preserve_mode): New.
(target_flash_erase, target_flash_done)
(target_write_memory_blocks): New.
(target_write_partial): Declare.
* target-memory.c: New.
* symfile.c (add_section_size_callback): Remove.
(struct load_section_data): New field memory_write_requests.
Remove write_count, data_count, and total_size fields.
(load_section_callback): Just record data to write, don't write.
(allow_flash_write, dont_preserve_flash)
(clear_memory_write_data): New.
(donwload_progress_cb): New;
(generic_load): Don't compute total size. Don't do memory
transfers here. Call target_write_memory_blocks.
--- target.c (revision 291)
+++ target.c (revision 292)
@@ -457,6 +457,9 @@ 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_write. */
+ /* Do not inherit to_flash_done. */
}
#undef INHERIT
@@ -1056,6 +1059,40 @@ target_memory_map (void)
tcomplain ();
}
+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)
@@ -1395,7 +1432,7 @@ target_read_partial (struct target_ops *
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,
--- target.h (revision 291)
+++ target.h (revision 292)
@@ -197,8 +197,13 @@ enum target_object
TARGET_OBJECT_AUXV,
/* StackGhost cookie. See "sparc-tdep.c". */
TARGET_OBJECT_WCOOKIE,
- /* Target memory map in XML format. */
+ /* Memory map. */
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, ... */
};
@@ -445,6 +450,20 @@ struct target_ops
could change, and so can't do caching itself. */
VEC(memory_region) * (*to_memory_map) (struct target_ops *);
+ /* 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?
*/
@@ -583,12 +602,96 @@ extern int target_read_memory_partial (C
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 ();
+
+/* 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)(),
+ enum flash_preserve_mode (*preserve_flash)(),
+ 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 *);
--- symfile.c (revision 291)
+++ symfile.c (revision 292)
@@ -1512,33 +1512,13 @@ load_command (char *arg, int from_tty)
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 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). */
@@ -1554,16 +1534,11 @@ load_section_callback (bfd *abfd, asecti
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;
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",
@@ -1571,79 +1546,88 @@ load_section_callback (bfd *abfd, asecti
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);
+ }
+ }
+ }
+}
- 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);
-
- 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 int
+allow_flash_write (void)
+{
+ return 1;
+}
- if (err != 0)
- error (_("Memory access error while loading section %s."), sect_name);
- do_cleanups (old_chain);
- }
+static enum flash_preserve_mode
+dont_preserve_flash (void)
+{
+ return flash_dont_preserve;
+}
+
+/* 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);
}
}
-void
-generic_load (char *args, int from_tty)
+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;
+}
+
+
+/* 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);
@@ -1689,34 +1673,52 @@ generic_load (char *args, int from_tty)
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);
}
--- target-memory.c (revision 291)
+++ target-memory.c (revision 292)
@@ -0,0 +1,558 @@
+/* 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 <stdio.h>
+#include <sys/time.h>
+
+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 by 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;
+}
+
+
--- Makefile.in (revision 291)
+++ Makefile.in (revision 292)
@@ -548,7 +548,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
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 \
@@ -955,7 +955,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
trad-frame.o \
tramp-frame.o \
solib.o solib-null.o \
- prologue-value.o vec.o memory-map.o
+ prologue-value.o vec.o target-memory.o memory-map.o
TSOBS = inflow.o
@@ -2745,6 +2745,8 @@ symtab.o: symtab.c $(defs_h) $(symtab_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) $(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) \