aboutsummaryrefslogtreecommitdiff
path: root/src/flash/nor/nrf5.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/flash/nor/nrf5.c')
-rw-r--r--src/flash/nor/nrf5.c235
1 files changed, 155 insertions, 80 deletions
diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c
index 0b65367..45d3d09 100644
--- a/src/flash/nor/nrf5.c
+++ b/src/flash/nor/nrf5.c
@@ -158,7 +158,7 @@ struct nrf5_info {
bool ficr_info_valid;
struct nrf52_ficr_info ficr_info;
const struct nrf5_device_spec *spec;
- uint32_t hwid;
+ uint16_t hwid;
enum nrf5_features features;
unsigned int flash_size_kb;
unsigned int ram_size_kb;
@@ -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 (chip->features & NRF5_FEATURE_SERIES_51)
+ return nrf5_protect_check_clenr0(bank);
- 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;
+ 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)
@@ -618,7 +648,7 @@ static int nrf5_info(struct flash_bank *bank, char *buf, int buf_size)
variant, &variant[2]);
} else {
- res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%08" PRIx32 ")",
+ res = snprintf(buf, buf_size, "nRF51xxx (HWID 0x%04" PRIx16 ")",
chip->hwid);
}
if (res <= 0)
@@ -735,14 +765,15 @@ static int nrf5_probe(struct flash_bank *bank)
struct nrf5_info *chip = nbank->chip;
struct target *target = chip->target;
- res = target_read_u32(target, NRF5_FICR_CONFIGID, &chip->hwid);
+ uint32_t configid;
+ res = target_read_u32(target, NRF5_FICR_CONFIGID, &configid);
if (res != ERROR_OK) {
LOG_ERROR("Couldn't read CONFIGID register");
return res;
}
- chip->hwid &= 0xFFFF; /* HWID is stored in the lower two
- * bytes of the CONFIGID register */
+ /* HWID is stored in the lower two bytes of the CONFIGID register */
+ chip->hwid = configid & 0xFFFF;
/* guess a nRF51 series if the device has no FICR INFO and we don't know HWID */
chip->features = NRF5_FEATURE_SERIES_51;
@@ -820,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 {
@@ -983,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);
@@ -1011,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;
@@ -1037,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)
@@ -1158,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)