This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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, &regular);
+  make_cleanup (cleanup_write_requests_vector, &flash);
+
+  split_regular_and_flash_blocks (blocks, &regular, &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, &regular_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 (&current_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) \

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]