flash/nor/nrf5: improve handling of nRF52 flash errors
[openocd.git] / src / flash / nor / nrf5.c
index 0ceb8d7544831453eb8142e2487c4dab52e0568c..02c144810608ab5a66a22e5cde10e490ba69e89d 100644 (file)
@@ -328,7 +328,7 @@ static int nrf5_wait_for_nvmc(struct nrf5_info *chip)
        do {
                res = target_read_u32(chip->target, NRF5_NVMC_READY, &ready);
                if (res != ERROR_OK) {
-                       LOG_ERROR("Couldn't read NVMC_READY register");
+                       LOG_ERROR("Error waiting NVMC_READY: generic flash write/erase error (check protection etc...)");
                        return res;
                }
 
@@ -440,6 +440,38 @@ error:
        return ERROR_FAIL;
 }
 
+static int nrf5_protect_check_clenr0(struct flash_bank *bank)
+{
+       int res;
+       uint32_t clenr0;
+       struct nrf5_bank *nbank = bank->driver_priv;
+       struct nrf5_info *chip = nbank->chip;
+
+       assert(chip != NULL);
+
+       res = target_read_u32(chip->target, NRF51_FICR_CLENR0,
+                             &clenr0);
+       if (res != ERROR_OK) {
+               LOG_ERROR("Couldn't read code region 0 size[FICR]");
+               return res;
+       }
+
+       if (clenr0 == 0xFFFFFFFF) {
+               res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
+                                     &clenr0);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Couldn't read code region 0 size[UICR]");
+                       return res;
+               }
+       }
+
+       for (unsigned int i = 0; i < bank->num_sectors; i++)
+               bank->sectors[i].is_protected =
+                       clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
+
+       return ERROR_OK;
+}
+
 static int nrf5_protect_check_bprot(struct flash_bank *bank)
 {
        struct nrf5_bank *nbank = bank->driver_priv;
@@ -469,9 +501,6 @@ static int nrf5_protect_check_bprot(struct flash_bank *bank)
 
 static int nrf5_protect_check(struct flash_bank *bank)
 {
-       int res;
-       uint32_t clenr0;
-
        /* UICR cannot be write protected so just return early */
        if (bank->base == NRF5_UICR_BASE)
                return ERROR_OK;
@@ -484,53 +513,20 @@ static int nrf5_protect_check(struct flash_bank *bank)
        if (chip->features & NRF5_FEATURE_BPROT)
                return nrf5_protect_check_bprot(bank);
 
-       if (!(chip->features & NRF5_FEATURE_SERIES_51)) {
-               LOG_WARNING("Flash protection of this nRF device is not supported");
-               return ERROR_FLASH_OPER_UNSUPPORTED;
-       }
-
-       res = target_read_u32(chip->target, NRF51_FICR_CLENR0,
-                             &clenr0);
-       if (res != ERROR_OK) {
-               LOG_ERROR("Couldn't read code region 0 size[FICR]");
-               return res;
-       }
-
-       if (clenr0 == 0xFFFFFFFF) {
-               res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
-                                     &clenr0);
-               if (res != ERROR_OK) {
-                       LOG_ERROR("Couldn't read code region 0 size[UICR]");
-                       return res;
-               }
-       }
-
-       for (unsigned int i = 0; i < bank->num_sectors; i++)
-               bank->sectors[i].is_protected =
-                       clenr0 != 0xFFFFFFFF && bank->sectors[i].offset < clenr0;
+       if (chip->features & NRF5_FEATURE_SERIES_51)
+               return nrf5_protect_check_clenr0(bank);
 
-       return ERROR_OK;
+       LOG_WARNING("Flash protection of this nRF device is not supported");
+       return ERROR_FLASH_OPER_UNSUPPORTED;
 }
 
-static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
+static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int first,
                unsigned int last)
 {
        int res;
        uint32_t clenr0, ppfc;
-       struct nrf5_info *chip;
-
-       /* UICR cannot be write protected so just bail out early */
-       if (bank->base == NRF5_UICR_BASE)
-               return ERROR_FAIL;
-
-       res = nrf5_get_probed_chip_if_halted(bank, &chip);
-       if (res != ERROR_OK)
-               return res;
-
-       if (!(chip->features & NRF5_FEATURE_SERIES_51)) {
-               LOG_ERROR("Flash protection setting of this nRF device is not supported");
-               return ERROR_FLASH_OPER_UNSUPPORTED;
-       }
+       struct nrf5_bank *nbank = bank->driver_priv;
+       struct nrf5_info *chip = nbank->chip;
 
        if (first != 0) {
                LOG_ERROR("Code region 0 must start at the beginning of the bank");
@@ -552,25 +548,59 @@ static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
        res = target_read_u32(chip->target, NRF51_UICR_CLENR0,
                              &clenr0);
        if (res != ERROR_OK) {
-               LOG_ERROR("Couldn't read code region 0 size[UICR]");
+               LOG_ERROR("Couldn't read code region 0 size from UICR");
                return res;
        }
 
-       if (clenr0 == 0xFFFFFFFF) {
-               res = target_write_u32(chip->target, NRF51_UICR_CLENR0,
-                                      clenr0);
-               if (res != ERROR_OK) {
-                       LOG_ERROR("Couldn't write code region 0 size[UICR]");
-                       return res;
-               }
-
-       } else {
+       if (!set || clenr0 != 0xFFFFFFFF) {
                LOG_ERROR("You need to perform chip erase before changing the protection settings");
+               return ERROR_FAIL;
        }
 
-       nrf5_protect_check(bank);
+       res = nrf5_nvmc_write_enable(chip);
+       if (res != ERROR_OK)
+               goto error;
 
-       return ERROR_OK;
+       clenr0 = bank->sectors[last].offset + bank->sectors[last].size;
+       res = target_write_u32(chip->target, NRF51_UICR_CLENR0, clenr0);
+
+       int res2 = nrf5_wait_for_nvmc(chip);
+
+       if (res == ERROR_OK)
+               res = res2;
+
+       if (res == ERROR_OK)
+               LOG_INFO("A reset or power cycle is required for the new protection settings to take effect.");
+       else
+               LOG_ERROR("Couldn't write code region 0 size to UICR");
+
+error:
+       nrf5_nvmc_read_only(chip);
+
+       return res;
+}
+
+static int nrf5_protect(struct flash_bank *bank, int set, unsigned int first,
+               unsigned int last)
+{
+       int res;
+       struct nrf5_info *chip;
+
+       /* UICR cannot be write protected so just bail out early */
+       if (bank->base == NRF5_UICR_BASE) {
+               LOG_ERROR("UICR page does not support protection");
+               return ERROR_FLASH_OPER_UNSUPPORTED;
+       }
+
+       res = nrf5_get_probed_chip_if_halted(bank, &chip);
+       if (res != ERROR_OK)
+               return res;
+
+       if (chip->features & NRF5_FEATURE_SERIES_51)
+               return nrf5_protect_clenr0(bank, set, first, last);
+
+       LOG_ERROR("Flash protection setting is not supported on this nRF5 device");
+       return ERROR_FLASH_OPER_UNSUPPORTED;
 }
 
 static bool nrf5_info_variant_to_str(uint32_t variant, char *bf)
@@ -821,8 +851,6 @@ static int nrf5_probe(struct flash_bank *bank)
                if (!bank->sectors)
                        return ERROR_FAIL;
 
-               nrf5_protect_check(bank);
-
                chip->bank[0].probed = true;
 
        } else {
@@ -984,7 +1012,7 @@ static int nrf5_ll_flash_write(struct nrf5_info *chip, uint32_t address, const u
                        0, NULL,
                        ARRAY_SIZE(reg_params), reg_params,
                        source->address, source->size,
-                       write_algorithm->address, 0,
+                       write_algorithm->address, write_algorithm->address + sizeof(nrf5_flash_write_code) - 2,
                        &armv7m_info);
 
        target_free_working_area(target, source);
@@ -1012,6 +1040,34 @@ static int nrf5_write(struct flash_bank *bank, const uint8_t *buffer,
        assert(offset % 4 == 0);
        assert(count % 4 == 0);
 
+       /* UICR CLENR0 based protection used on nRF51 is somewhat clumsy:
+        * RM reads: Code running from code region 1 will not be able to write
+        * to code region 0.
+        * Unfortunately the flash loader running from RAM can write to both
+        * code regions whithout any hint the protection is violated.
+        *
+        * Update protection state and check if any flash sector to be written
+        * is protected. */
+       if (chip->features & NRF5_FEATURE_SERIES_51) {
+
+               res = nrf5_protect_check_clenr0(bank);
+               if (res != ERROR_OK)
+                       return res;
+
+               for (unsigned int sector = 0; sector < bank->num_sectors; sector++) {
+                       struct flash_sector *bs = &bank->sectors[sector];
+
+                       /* Start offset in or before this sector? */
+                       /* End offset in or behind this sector? */
+                       if ((offset < (bs->offset + bs->size))
+                                       && ((offset + count - 1) >= bs->offset)
+                                       && bs->is_protected == 1) {
+                               LOG_ERROR("Write refused, sector %d is protected", sector);
+                               return ERROR_FLASH_PROTECTED;
+                       }
+               }
+       }
+
        res = nrf5_nvmc_write_enable(chip);
        if (res != ERROR_OK)
                goto error;
@@ -1038,11 +1094,36 @@ static int nrf5_erase(struct flash_bank *bank, unsigned int first,
        if (res != ERROR_OK)
                return res;
 
+       /* UICR CLENR0 based protection used on nRF51 prevents erase
+        * absolutely silently. NVMC has no flag to indicate the protection
+        * was violated.
+        *
+        * Update protection state and check if any flash sector to be erased
+        * is protected. */
+       if (chip->features & NRF5_FEATURE_SERIES_51) {
+
+               res = nrf5_protect_check_clenr0(bank);
+               if (res != ERROR_OK)
+                       return res;
+       }
+
        /* For each sector to be erased */
-       for (unsigned int s = first; s <= last && res == ERROR_OK; s++)
+       for (unsigned int s = first; s <= last && res == ERROR_OK; s++) {
+
+               if (chip->features & NRF5_FEATURE_SERIES_51
+                               && bank->sectors[s].is_protected == 1) {
+                       LOG_ERROR("Flash sector %d is protected", s);
+                       return ERROR_FLASH_PROTECTED;
+               }
+
                res = nrf5_erase_page(bank, chip, &bank->sectors[s]);
+               if (res != ERROR_OK) {
+                       LOG_ERROR("Error erasing sector %d", s);
+                       return res;
+               }
+       }
 
-       return res;
+       return ERROR_OK;
 }
 
 static void nrf5_free_driver_priv(struct flash_bank *bank)
@@ -1159,23 +1240,16 @@ COMMAND_HANDLER(nrf5_handle_mass_erase_command)
        }
 
        res = nrf5_erase_all(chip);
-       if (res != ERROR_OK) {
-               LOG_ERROR("Failed to erase the chip");
-               nrf5_protect_check(bank);
-               return res;
-       }
+       if (res == ERROR_OK) {
+               LOG_INFO("Mass erase completed.");
+               if (chip->features & NRF5_FEATURE_SERIES_51)
+                       LOG_INFO("A reset or power cycle is required if the flash was protected before.");
 
-       res = nrf5_protect_check(bank);
-       if (res != ERROR_OK) {
-               LOG_ERROR("Failed to check chip's write protection");
-               return res;
+       } else {
+               LOG_ERROR("Failed to erase the chip");
        }
 
-       res = get_flash_bank_by_addr(target, NRF5_UICR_BASE, true, &bank);
-       if (res != ERROR_OK)
-               return res;
-
-       return ERROR_OK;
+       return res;
 }
 
 COMMAND_HANDLER(nrf5_handle_info_command)

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)