aboutsummaryrefslogtreecommitdiff
path: root/src/flash/nor
diff options
context:
space:
mode:
authorTomas Vanek <vanekt@fbl.cz>2020-03-16 15:49:54 +0100
committerTomas Vanek <vanekt@fbl.cz>2020-10-28 10:51:29 +0000
commit491636c8b8328a54bef41b68771c2965dd468e05 (patch)
tree67b892726af225b89ecedbd2980d822962935c50 /src/flash/nor
parentc97ccc8971b44fbce28438cb926990a6e5a63450 (diff)
downloadriscv-openocd-491636c8b8328a54bef41b68771c2965dd468e05.zip
riscv-openocd-491636c8b8328a54bef41b68771c2965dd468e05.tar.gz
riscv-openocd-491636c8b8328a54bef41b68771c2965dd468e05.tar.bz2
flash/nor/nrf5: check protection before flash erase/write on nRF51
nRF51 devices have a clumsy flash protection based on UICR CLENR0. A code running in RAM can write to a protected flash region without any hint the protection gets violated. NVMC flash page erase obeys protection setting but fails absolutely silently. Before this change the first problem was not addressed in the code. To justify the second one, protection was loaded during probe, after protection setting and after a mass erase. Move protection updates to the beginning of erase/write operation and limit them to nRF51 series only. Check for protected sectors before write. The change also fixes the problem of 'nrf5 mass_erase' on nRF52833/840 devices. They use ACL flash protection, which is not supported in nrf5 driver. mass_erase then looked like it failed. Change-Id: Ie58cda68eb104d410b02777c3df5b343408e2666 Signed-off-by: Tomas Vanek <vanekt@fbl.cz> Reviewed-on: http://openocd.zylin.com/5522 Tested-by: jenkins
Diffstat (limited to 'src/flash/nor')
-rw-r--r--src/flash/nor/nrf5.c146
1 files changed, 98 insertions, 48 deletions
diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c
index 657af9b..f56f32e 100644
--- a/src/flash/nor/nrf5.c
+++ b/src/flash/nor/nrf5.c
@@ -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,32 +513,11 @@ 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_clenr0(struct flash_bank *bank, int set, unsigned int first,
@@ -569,8 +577,6 @@ static int nrf5_protect_clenr0(struct flash_bank *bank, int set, unsigned int fi
error:
nrf5_nvmc_read_only(chip);
- nrf5_protect_check(bank);
-
return res;
}
@@ -845,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 {
@@ -1036,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;
@@ -1062,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)
@@ -1183,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)