NOR FLASH: only erase/unlock whole sectors
authorDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 18 Dec 2009 18:16:52 +0000 (10:16 -0800)
committerDavid Brownell <dbrownell@users.sourceforge.net>
Fri, 18 Dec 2009 18:16:52 +0000 (10:16 -0800)
Much to my surprise, I observed a "flash erase_address ..."
command erasing data which I said should not be erased.

The issue turns out to be generic NOR flash code which was
silently, and rather dangerously, morphing partial-sector
references into unrequested whole-sector ones.

This patch removes that low-level morphing.  If desired, it
can and should be done in higher level code.  (We might need
to fix some stuff in the GDB server code.)

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
NEWS
src/flash/nor/core.c

diff --git a/NEWS b/NEWS
index 498797b..2d01f00 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,8 @@ Flash Layer:
                - <bank_name>: reference the bank with its defined name
                - <driver_name>[.N]: reference the driver's Nth bank
        New 'nand verify' command to check bank against an image file.
+       The "flash erase_address" command now rejects partial sectors;
+               previously it would silently erase extra data.
 
 Board, Target, and Interface Configuration Scripts:
        ARM9
index c2ea134..1873dee 100644 (file)
@@ -279,11 +279,13 @@ int default_flash_blank_check(struct flash_bank *bank)
 
        return ERROR_OK;
 }
+
 /* erase given flash region, selects proper bank according to target and address */
 static int flash_iterate_address_range(struct target *target, uint32_t addr, uint32_t length,
                int (*callback)(struct flash_bank *bank, int first, int last))
 {
        struct flash_bank *c;
+       uint32_t last_addr = addr + length;     /* first address AFTER end */
        int first = -1;
        int last = -1;
        int i;
@@ -306,26 +308,52 @@ static int flash_iterate_address_range(struct target *target, uint32_t addr, uin
                return callback(c, 0, c->num_sectors - 1);
        }
 
-       /* check whether it fits */
+       /* check whether it all fits in this bank */
        if (addr + length - 1 > c->base + c->size - 1)
                return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
 
+       /** @todo: handle erasures that cross into adjacent banks */
+
        addr -= c->base;
 
        for (i = 0; i < c->num_sectors; i++)
        {
-               /* check whether sector overlaps with the given range and is not yet erased */
-               if (addr < c->sectors[i].offset + c->sectors[i].size && addr + length > c->sectors[i].offset && c->sectors[i].is_erased != 1) {
-                       /* if first is not set yet then this is the first sector */
-                       if (first == -1)
+               struct flash_sector *f = c->sectors + i;
+
+               /* start only on a sector boundary */
+               if (first < 0) {
+                       /* is this the first sector? */
+                       if (addr == f->offset)
                                first = i;
-                       last = i; /* and it is the last one so far in any case */
+                       else if (addr < f->offset)
+                               break;
                }
+
+               /* is this (also?) the last sector? */
+               if (last_addr == f->offset + f->size) {
+                       last = i;
+                       break;
+               }
+
+               /* MUST finish on a sector boundary */
+               if (last_addr <= f->offset)
+                       break;
        }
 
-       if (first == -1 || last == -1)
-               return ERROR_OK;
+       /* invalid start or end address? */
+       if (first == -1 || last == -1) {
+               LOG_ERROR("address range 0x%8.8x .. 0x%8.8x "
+                               "is not sector-aligned",
+                               (unsigned) c->base + addr,
+                               (unsigned) last_addr - 1);
+               return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+       }
 
+       /* The NOR driver may trim this range down, based on
+        * whether or not a given sector is already erased.
+        *
+        * REVISIT should *we* trim it... ?
+        */
        return callback(c, first, last);
 }