flash/nor/stellaris: implement protection statuses and procedures 67/2267/4
authorPaul Fertser <fercerpav@gmail.com>
Thu, 21 Aug 2014 11:16:55 +0000 (15:16 +0400)
committerSpencer Oliver <spen@spen-soft.co.uk>
Mon, 6 Oct 2014 18:40:46 +0000 (18:40 +0000)
This should make protection work as expected on all stellaris
families, including the latest Tiva C Snowflake.

Run-time tested on TM4C123x (Blizzard).

Change-Id: Ia017edb119bec32382b08fc037b5bbc02dd9000c
Signed-off-by: Paul Fertser <fercerpav@gmail.com>
Reviewed-on: http://openocd.zylin.com/2267
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
src/flash/nor/stellaris.c

index 4c819c6195d294946d521e132a8c614bc9b2846e..c4fe3be9edccf61edf6d8effb8529b70649a95e2 100644 (file)
@@ -107,14 +107,9 @@ struct stellaris_flash_bank {
        uint8_t target_class;
 
        uint32_t sramsiz;
-       uint32_t flshsz;
        /* flash geometry */
        uint32_t num_pages;
        uint32_t pagesize;
-       uint32_t pages_in_lockregion;
-
-       /* nv memory bits */
-       uint16_t num_lockbits;
 
        /* main clock status */
        uint32_t rcc;
@@ -524,24 +519,15 @@ static int get_stellaris_info(struct flash_bank *bank, char *buf, int buf_size)
        printed = snprintf(buf,
                           buf_size,
                           "master clock: %ikHz%s, "
-                          "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 "\n",
+                          "rcc is 0x%" PRIx32 ", rcc2 is 0x%" PRIx32 ", "
+                          "pagesize: %" PRIu32 ", pages: %" PRIu32,
                           (int)(stellaris_info->mck_freq / 1000),
                           stellaris_info->mck_desc,
                           stellaris_info->rcc,
-                          stellaris_info->rcc2);
-       buf += printed;
-       buf_size -= printed;
+                          stellaris_info->rcc2,
+                          stellaris_info->pagesize,
+                          stellaris_info->num_pages);
 
-       if (stellaris_info->num_lockbits > 0) {
-               snprintf(buf,
-                               buf_size,
-                               "pagesize: %" PRIi32 ", pages: %d, "
-                               "lockbits: %i, pages per lockbit: %i\n",
-                               stellaris_info->pagesize,
-                               (unsigned) stellaris_info->num_pages,
-                               stellaris_info->num_lockbits,
-                               (unsigned) stellaris_info->pages_in_lockregion);
-       }
        return ERROR_OK;
 }
 
@@ -782,11 +768,9 @@ static int stellaris_read_part_info(struct flash_bank *bank)
                target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
                target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
 
-               stellaris_info->num_lockbits = 1 + (stellaris_info->fsize & 0xFFFF);
                stellaris_info->num_pages = 2 * (1 + (stellaris_info->fsize & 0xFFFF));
                stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
                stellaris_info->pagesize = 1024;
-               stellaris_info->pages_in_lockregion = 2;
        } else if (stellaris_info->target_class == 0xa) { /* Snowflake */
                target_read_u32(target, FLASH_FSIZE, &stellaris_info->fsize);
                target_read_u32(target, FLASH_SSIZE, &stellaris_info->ssize);
@@ -794,17 +778,11 @@ static int stellaris_read_part_info(struct flash_bank *bank)
                stellaris_info->pagesize = (1 << ((stellaris_info->fsize >> 16) & 7)) * 1024;
                stellaris_info->num_pages = 2048 * (1 + (stellaris_info->fsize & 0xFFFF)) /
                        stellaris_info->pagesize;
-               stellaris_info->pages_in_lockregion = 1;
-
-               stellaris_info->num_lockbits = stellaris_info->pagesize * stellaris_info->num_pages /
-                       2048;
                stellaris_info->sramsiz = (1 + (stellaris_info->ssize & 0xFFFF)) / 4;
        } else {
-               stellaris_info->num_lockbits = 1 + (stellaris_info->dc0 & 0xFFFF);
                stellaris_info->num_pages = 2 * (1 + (stellaris_info->dc0 & 0xFFFF));
                stellaris_info->sramsiz = (1 + ((stellaris_info->dc0 >> 16) & 0xFFFF)) / 4;
                stellaris_info->pagesize = 1024;
-               stellaris_info->pages_in_lockregion = 2;
        }
 
        /* REVISIT for at least Tempest parts, read NVMSTAT.FWB too.
@@ -822,18 +800,16 @@ static int stellaris_read_part_info(struct flash_bank *bank)
 static int stellaris_protect_check(struct flash_bank *bank)
 {
        struct stellaris_flash_bank *stellaris = bank->driver_priv;
+       struct target *target = bank->target;
+       uint32_t flash_sizek = stellaris->pagesize / 1024 *
+               stellaris->num_pages;
+       uint32_t fmppe_addr;
        int status = ERROR_OK;
        unsigned i;
-       unsigned page;
 
        if (stellaris->did1 == 0)
                return ERROR_FLASH_BANK_NOT_PROBED;
 
-       if (stellaris->target_class == 0xa) {
-               LOG_WARNING("Assuming flash to be unprotected on Snowflake");
-               return ERROR_OK;
-       }
-
        for (i = 0; i < (unsigned) bank->num_sectors; i++)
                bank->sectors[i].is_protected = -1;
 
@@ -841,32 +817,32 @@ static int stellaris_protect_check(struct flash_bank *bank)
         * to report any pages that we can't write.  Ignore the Read Enable
         * register (FMPRE).
         */
-       for (i = 0, page = 0;
-                       i < DIV_ROUND_UP(stellaris->num_lockbits, 32u);
-                       i++) {
-               uint32_t lockbits;
-
-               status = target_read_u32(bank->target,
-                               SCB_BASE + (i ? (FMPPE0 + 4 * i) : FMPPE),
-                               &lockbits);
-               LOG_DEBUG("FMPPE%d = %#8.8x (status %d)", i,
-                               (unsigned) lockbits, status);
-               if (status != ERROR_OK)
-                       goto done;
-
-               for (unsigned j = 0; j < 32; j++) {
-                       unsigned k;
 
-                       for (k = 0; k < stellaris->pages_in_lockregion; k++) {
-                               if (page >= (unsigned) bank->num_sectors)
-                                       goto done;
-                               bank->sectors[page++].is_protected =
-                                               !(lockbits & (1 << j));
+       if (stellaris->target_class >= 0x0a || flash_sizek > 64)
+               fmppe_addr = SCB_BASE | FMPPE0;
+       else
+               fmppe_addr = SCB_BASE | FMPPE;
+
+       unsigned int page = 0, lockbitnum, lockbitcnt = flash_sizek / 2;
+       unsigned int bits_per_page = stellaris->pagesize / 2048;
+       /* Every lock bit always corresponds to a 2k region */
+       for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
+               uint32_t fmppe;
+
+               target_read_u32(target, fmppe_addr, &fmppe);
+               for (i = 0; i < 32 && lockbitnum + i < lockbitcnt; i++) {
+                       bool protect = !(fmppe & (1 << i));
+                       if (bits_per_page) {
+                               bank->sectors[page++].is_protected = protect;
+                               i += bits_per_page - 1;
+                       } else { /* 1024k pages, every lockbit covers 2 pages */
+                               bank->sectors[page++].is_protected = protect;
+                               bank->sectors[page++].is_protected = protect;
                        }
                }
+               fmppe_addr += 4;
        }
 
-done:
        return status;
 }
 
@@ -930,13 +906,12 @@ static int stellaris_erase(struct flash_bank *bank, int first, int last)
 
 static int stellaris_protect(struct flash_bank *bank, int set, int first, int last)
 {
-       uint32_t fmppe, flash_fmc, flash_cris;
-       int lockregion;
-
-       struct stellaris_flash_bank *stellaris_info = bank->driver_priv;
+       struct stellaris_flash_bank *stellaris = bank->driver_priv;
        struct target *target = bank->target;
+       uint32_t flash_fmc, flash_cris;
+       unsigned int bits_per_page = stellaris->pagesize / 2048;
 
-       if (bank->target->state != TARGET_HALTED) {
+       if (target->state != TARGET_HALTED) {
                LOG_ERROR("Target not halted");
                return ERROR_TARGET_NOT_HALTED;
        }
@@ -947,26 +922,18 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       if (stellaris_info->did1 == 0)
+       if (stellaris->did1 == 0)
                return ERROR_FLASH_BANK_NOT_PROBED;
 
-       if (stellaris_info->target_class == 0x03 &&
-           !((stellaris_info->did0 >> 8) & 0xFF) &&
-           !((stellaris_info->did0) & 0xFF)) {
+       if (stellaris->target_class == 0x03 &&
+           !((stellaris->did0 >> 8) & 0xFF) &&
+           !((stellaris->did0) & 0xFF)) {
                LOG_ERROR("DustDevil A0 parts can't be unprotected, see errata; refusing to proceed");
                return ERROR_FLASH_OPERATION_FAILED;
        }
 
-       if (stellaris_info->target_class == 0xa) {
-               LOG_ERROR("Protection on Snowflake is not supported yet");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
-
-       /* lockregions are 2 pages ... must protect [even..odd] */
-       if ((first < 0) || (first & 1)
-                       || (last < first) || !(last & 1)
-                       || (last >= 2 * stellaris_info->num_lockbits)) {
-               LOG_ERROR("Can't protect unaligned or out-of-range pages.");
+       if (!bits_per_page && (first % 2 || !(last % 2))) {
+               LOG_ERROR("Can't protect unaligned pages");
                return ERROR_FLASH_SECTOR_INVALID;
        }
 
@@ -974,50 +941,60 @@ static int stellaris_protect(struct flash_bank *bank, int set, int first, int la
        stellaris_read_clock_info(bank);
        stellaris_set_flash_timing(bank);
 
-       /* convert from pages to lockregions */
-       first /= 2;
-       last /= 2;
-
-       /* FIXME this assumes single FMPPE, for a max of 64K of flash!!
-        * Current parts can be much bigger.
-        */
-       if (last >= 32) {
-               LOG_ERROR("No support yet for protection > 64K");
-               return ERROR_FLASH_OPERATION_FAILED;
-       }
-
-       target_read_u32(target, SCB_BASE | FMPPE, &fmppe);
-
-       for (lockregion = first; lockregion <= last; lockregion++)
-               fmppe &= ~(1 << lockregion);
-
        /* Clear and disable flash programming interrupts */
        target_write_u32(target, FLASH_CIM, 0);
        target_write_u32(target, FLASH_MISC, PMISC | AMISC);
 
-       /* REVISIT this clobbers state set by any halted firmware ...
-        * it might want to process those IRQs.
-        */
+       uint32_t flash_sizek = stellaris->pagesize / 1024 *
+               stellaris->num_pages;
+       uint32_t fmppe_addr;
 
-       LOG_DEBUG("fmppe 0x%" PRIx32 "", fmppe);
-       target_write_u32(target, SCB_BASE | FMPPE, fmppe);
+       if (stellaris->target_class >= 0x0a || flash_sizek > 64)
+               fmppe_addr = SCB_BASE | FMPPE0;
+       else
+               fmppe_addr = SCB_BASE | FMPPE;
+
+       int page = 0;
+       unsigned int lockbitnum, lockbitcnt = flash_sizek / 2;
+       /* Every lock bit always corresponds to a 2k region */
+       for (lockbitnum = 0; lockbitnum < lockbitcnt; lockbitnum += 32) {
+               uint32_t fmppe;
+
+               target_read_u32(target, fmppe_addr, &fmppe);
+               for (unsigned int i = 0;
+                    i < 32 && lockbitnum + i < lockbitcnt;
+                    i++) {
+                       if (page >= first && page <= last)
+                               fmppe &= ~(1 << i);
+
+                       if (bits_per_page) {
+                               if (!((i + 1) % bits_per_page))
+                                       page++;
+                       } else { /* 1024k pages, every lockbit covers 2 pages */
+                               page += 2;
+                       }
+               }
+               target_write_u32(target, fmppe_addr, fmppe);
 
-       /* Commit FMPPE */
-       target_write_u32(target, FLASH_FMA, 1);
-       /* Write commit command */
-       target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
+               /* Commit FMPPE* */
+               target_write_u32(target, FLASH_FMA, 1 + lockbitnum / 16);
+               /* Write commit command */
+               target_write_u32(target, FLASH_FMC, FMC_WRKEY | FMC_COMT);
 
-       /* Wait until erase complete */
-       do {
-               target_read_u32(target, FLASH_FMC, &flash_fmc);
-       } while (flash_fmc & FMC_COMT);
+               /* Wait until commit complete */
+               do {
+                       target_read_u32(target, FLASH_FMC, &flash_fmc);
+               } while (flash_fmc & FMC_COMT);
 
-       /* Check acess violations */
-       target_read_u32(target, FLASH_CRIS, &flash_cris);
-       if (flash_cris & (AMASK)) {
-               LOG_WARNING("Error setting flash page protection,  flash_cris 0x%" PRIx32 "", flash_cris);
-               target_write_u32(target, FLASH_CRIS, 0);
-               return ERROR_FLASH_OPERATION_FAILED;
+               /* Check access violations */
+               target_read_u32(target, FLASH_CRIS, &flash_cris);
+               if (flash_cris & (AMASK)) {
+                       LOG_WARNING("Error setting flash page protection,  flash_cris 0x%" PRIx32 "", flash_cris);
+                       target_write_u32(target, FLASH_CRIS, 0);
+                       return ERROR_FLASH_OPERATION_FAILED;
+               }
+
+               fmppe_addr += 4;
        }
 
        return ERROR_OK;
@@ -1382,6 +1359,9 @@ COMMAND_HANDLER(stellaris_handle_recover_command)
        struct flash_bank *bank;
        int retval;
 
+       if (CMD_ARGC < 1)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
        retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
        if (retval != ERROR_OK)
                return retval;

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)