X-Git-Url: https://git.gag.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Fflash%2Fnor%2Fstm32l4x.c;h=db8d5e78d3175d5ecf010b5e06ce7acf37c594c0;hb=c9d40366ad55ea3a83f2ff438aab1e62da653169;hp=bfedc8d66d92001188b52779c182109474da9e48;hpb=88fa831839cf7e45f66064c9c74cfcf59b8d8c7e;p=fw%2Fopenocd diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index bfedc8d66..db8d5e78d 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -24,6 +24,7 @@ #endif #include "imp.h" +#include #include #include #include @@ -126,6 +127,8 @@ #define F_USE_ALL_WRPXX BIT(1) /* this flag indicates if the device embeds a TrustZone security feature */ #define F_HAS_TZ BIT(2) +/* this flag indicates if the device has the same flash registers as STM32L5 */ +#define F_HAS_L5_FLASH_REGS BIT(3) /* end of STM32L4 flags ******************************************************/ @@ -143,6 +146,13 @@ enum stm32l4_flash_reg_index { STM32_FLASH_REG_INDEX_NUM, }; +enum stm32l4_rdp { + RDP_LEVEL_0 = 0xAA, + RDP_LEVEL_0_5 = 0x55, /* for devices with TrustZone enabled */ + RDP_LEVEL_1 = 0x00, + RDP_LEVEL_2 = 0xCC +}; + static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { [STM32_FLASH_ACR_INDEX] = 0x000, [STM32_FLASH_KEYR_INDEX] = 0x008, @@ -158,10 +168,23 @@ static const uint32_t stm32l4_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { static const uint32_t stm32l5_ns_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { [STM32_FLASH_ACR_INDEX] = 0x000, - [STM32_FLASH_KEYR_INDEX] = 0x008, + [STM32_FLASH_KEYR_INDEX] = 0x008, /* NSKEYR */ + [STM32_FLASH_OPTKEYR_INDEX] = 0x010, + [STM32_FLASH_SR_INDEX] = 0x020, /* NSSR */ + [STM32_FLASH_CR_INDEX] = 0x028, /* NSCR */ + [STM32_FLASH_OPTR_INDEX] = 0x040, + [STM32_FLASH_WRP1AR_INDEX] = 0x058, + [STM32_FLASH_WRP1BR_INDEX] = 0x05C, + [STM32_FLASH_WRP2AR_INDEX] = 0x068, + [STM32_FLASH_WRP2BR_INDEX] = 0x06C, +}; + +static const uint32_t stm32l5_s_flash_regs[STM32_FLASH_REG_INDEX_NUM] = { + [STM32_FLASH_ACR_INDEX] = 0x000, + [STM32_FLASH_KEYR_INDEX] = 0x00C, /* SECKEYR */ [STM32_FLASH_OPTKEYR_INDEX] = 0x010, - [STM32_FLASH_SR_INDEX] = 0x020, - [STM32_FLASH_CR_INDEX] = 0x028, + [STM32_FLASH_SR_INDEX] = 0x024, /* SECSR */ + [STM32_FLASH_CR_INDEX] = 0x02C, /* SECCR */ [STM32_FLASH_OPTR_INDEX] = 0x040, [STM32_FLASH_WRP1AR_INDEX] = 0x058, [STM32_FLASH_WRP1BR_INDEX] = 0x05C, @@ -197,8 +220,12 @@ struct stm32l4_flash_bank { uint32_t user_bank_size; uint32_t wrpxxr_mask; const struct stm32l4_part_info *part_info; + uint32_t flash_regs_base; const uint32_t *flash_regs; bool otp_enabled; + enum stm32l4_rdp rdp; + bool tzen; + uint32_t optr; }; enum stm32_bank_id { @@ -433,7 +460,7 @@ static const struct stm32l4_part_info stm32l4_parts[] = { .num_revs = ARRAY_SIZE(stm32_472_revs), .device_str = "STM32L55/L56xx", .max_flash_size_kb = 512, - .flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX | F_HAS_TZ, + .flags = F_HAS_DUAL_BANK | F_USE_ALL_WRPXX | F_HAS_TZ | F_HAS_L5_FLASH_REGS, .flash_regs_base = 0x40022000, .default_flash_regs = stm32l5_ns_flash_regs, .fsize_addr = 0x0BFA05E0, @@ -610,10 +637,39 @@ static inline bool stm32l4_otp_is_enabled(struct flash_bank *bank) return stm32l4_info->otp_enabled; } +static void stm32l4_sync_rdp_tzen(struct flash_bank *bank) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + + bool tzen = false; + + if (stm32l4_info->part_info->flags & F_HAS_TZ) + tzen = (stm32l4_info->optr & FLASH_TZEN) != 0; + + uint32_t rdp = stm32l4_info->optr & FLASH_RDP_MASK; + + /* for devices without TrustZone: + * RDP level 0 and 2 values are to 0xAA and 0xCC + * Any other value corresponds to RDP level 1 + * for devices with TrusZone: + * RDP level 0 and 2 values are 0xAA and 0xCC + * RDP level 0.5 value is 0x55 only if TZEN = 1 + * Any other value corresponds to RDP level 1, including 0x55 if TZEN = 0 + */ + + if (rdp != RDP_LEVEL_0 && rdp != RDP_LEVEL_2) { + if (!tzen || (tzen && rdp != RDP_LEVEL_0_5)) + rdp = RDP_LEVEL_1; + } + + stm32l4_info->tzen = tzen; + stm32l4_info->rdp = rdp; +} + static inline uint32_t stm32l4_get_flash_reg(struct flash_bank *bank, uint32_t reg_offset) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; - return stm32l4_info->part_info->flash_regs_base + reg_offset; + return stm32l4_info->flash_regs_base + reg_offset; } static inline uint32_t stm32l4_get_flash_reg_by_index(struct flash_bank *bank, @@ -685,6 +741,49 @@ static int stm32l4_wait_status_busy(struct flash_bank *bank, int timeout) return retval; } +/** set all FLASH_SECBB registers to the same value */ +static int stm32l4_set_secbb(struct flash_bank *bank, uint32_t value) +{ + /* This function should be used only with device with TrustZone, do just a security check */ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + assert(stm32l4_info->part_info->flags & F_HAS_TZ); + + /* based on RM0438 Rev6 for STM32L5x devices: + * to modify a page block-based security attribution, it is recommended to + * 1- check that no flash operation is ongoing on the related page + * 2- add ISB instruction after modifying the page security attribute in SECBBxRy + * this step is not need in case of JTAG direct access + */ + int retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + /* write SECBBxRy registers */ + LOG_DEBUG("setting secure block-based areas registers (SECBBxRy) to 0x%08x", value); + + const uint8_t secbb_regs[] = { + FLASH_SECBB1(1), FLASH_SECBB1(2), FLASH_SECBB1(3), FLASH_SECBB1(4), /* bank 1 SECBB register offsets */ + FLASH_SECBB2(1), FLASH_SECBB2(2), FLASH_SECBB2(3), FLASH_SECBB2(4) /* bank 2 SECBB register offsets */ + }; + + + unsigned int num_secbb_regs = ARRAY_SIZE(secbb_regs); + + /* in single bank mode, it's useless to modify FLASH_SECBB2Rx registers + * then consider only the first half of secbb_regs + */ + if (!stm32l4_info->dual_bank_mode) + num_secbb_regs /= 2; + + for (unsigned int i = 0; i < num_secbb_regs; i++) { + retval = stm32l4_write_flash_reg(bank, secbb_regs[i], value); + if (retval != ERROR_OK) + return retval; + } + + return ERROR_OK; +} + static int stm32l4_unlock_reg(struct flash_bank *bank) { uint32_t ctrl; @@ -752,9 +851,46 @@ static int stm32l4_unlock_option_reg(struct flash_bank *bank) return ERROR_OK; } +static int stm32l4_perform_obl_launch(struct flash_bank *bank) +{ + int retval, retval2; + + retval = stm32l4_unlock_reg(bank); + if (retval != ERROR_OK) + goto err_lock; + + retval = stm32l4_unlock_option_reg(bank); + if (retval != ERROR_OK) + goto err_lock; + + /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload, + * but the RMs explicitly do *NOT* list this as power-on reset cause, and: + * "Note: If the read protection is set while the debugger is still + * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset." + */ + + /* "Setting OBL_LAUNCH generates a reset so the option byte loading is performed under system reset" */ + /* Due to this reset ST-Link reports an SWD_DP_ERROR, despite the write was successful, + * then just ignore the returned value */ + stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH); + + /* Need to re-probe after change */ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + stm32l4_info->probed = false; + +err_lock: + retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK); + + if (retval != ERROR_OK) + return retval; + + return retval2; +} + static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, uint32_t value, uint32_t mask) { + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; uint32_t optiondata; int retval, retval2; @@ -762,6 +898,12 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, if (retval != ERROR_OK) return retval; + /* for STM32L5 and similar devices, use always non-secure + * registers for option bytes programming */ + const uint32_t *saved_flash_regs = stm32l4_info->flash_regs; + if (stm32l4_info->part_info->flags & F_HAS_L5_FLASH_REGS) + stm32l4_info->flash_regs = stm32l5_ns_flash_regs; + retval = stm32l4_unlock_reg(bank); if (retval != ERROR_OK) goto err_lock; @@ -784,6 +926,7 @@ static int stm32l4_write_option(struct flash_bank *bank, uint32_t reg_offset, err_lock: retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK | FLASH_OPTLOCK); + stm32l4_info->flash_regs = saved_flash_regs; if (retval != ERROR_OK) return retval; @@ -931,6 +1074,16 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first, return ERROR_TARGET_NOT_HALTED; } + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* set all FLASH pages as secure */ + retval = stm32l4_set_secbb(bank, FLASH_SECBB_SECURE); + if (retval != ERROR_OK) { + /* restore all FLASH pages as non-secure */ + stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); /* ignore the return value */ + return retval; + } + } + retval = stm32l4_unlock_reg(bank); if (retval != ERROR_OK) goto err_lock; @@ -963,13 +1116,18 @@ static int stm32l4_erase(struct flash_bank *bank, unsigned int first, retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); if (retval != ERROR_OK) break; - - bank->sectors[i].is_erased = 1; } err_lock: retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* restore all FLASH pages as non-secure */ + int retval3 = stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); + if (retval3 != ERROR_OK) + return retval3; + } + if (retval != ERROR_OK) return retval; @@ -1207,6 +1365,7 @@ static int stm32l4_write_block(struct flash_bank *bank, const uint8_t *buffer, static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) { + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; int retval = ERROR_OK, retval2; if (stm32l4_is_otp(bank) && !stm32l4_otp_is_enabled(bank)) { @@ -1261,6 +1420,16 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, if (retval != ERROR_OK) return retval; + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* set all FLASH pages as secure */ + retval = stm32l4_set_secbb(bank, FLASH_SECBB_SECURE); + if (retval != ERROR_OK) { + /* restore all FLASH pages as non-secure */ + stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); /* ignore the return value */ + return retval; + } + } + retval = stm32l4_unlock_reg(bank); if (retval != ERROR_OK) goto err_lock; @@ -1270,6 +1439,13 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, err_lock: retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* restore all FLASH pages as non-secure */ + int retval3 = stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); + if (retval3 != ERROR_OK) + return retval3; + } + if (retval != ERROR_OK) { LOG_ERROR("block write failed"); return retval; @@ -1282,10 +1458,10 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) int retval; /* try reading possible IDCODE registers, in the following order */ - uint32_t DBGMCU_IDCODE[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5}; + uint32_t dbgmcu_idcode[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5}; - for (unsigned int i = 0; i < ARRAY_SIZE(DBGMCU_IDCODE); i++) { - retval = target_read_u32(bank->target, DBGMCU_IDCODE[i], id); + for (unsigned int i = 0; i < ARRAY_SIZE(dbgmcu_idcode); i++) { + retval = target_read_u32(bank->target, dbgmcu_idcode[i], id); if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff)) return ERROR_OK; } @@ -1294,14 +1470,35 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) return (retval == ERROR_OK) ? ERROR_FAIL : retval; } +static const char *get_stm32l4_rev_str(struct flash_bank *bank) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + const struct stm32l4_part_info *part_info = stm32l4_info->part_info; + assert(part_info); + + const uint16_t rev_id = stm32l4_info->idcode >> 16; + for (unsigned int i = 0; i < part_info->num_revs; i++) { + if (rev_id == part_info->revs[i].rev) + return part_info->revs[i].str; + } + return "'unknown'"; +} + +static const char *get_stm32l4_bank_type_str(struct flash_bank *bank) +{ + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + assert(stm32l4_info->part_info); + return stm32l4_is_otp(bank) ? "OTP" : + stm32l4_info->dual_bank_mode ? "Flash dual" : + "Flash single"; +} + static int stm32l4_probe(struct flash_bank *bank) { struct target *target = bank->target; struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; const struct stm32l4_part_info *part_info; uint16_t flash_size_kb = 0xffff; - uint32_t device_id; - uint32_t options; stm32l4_info->probed = false; @@ -1310,11 +1507,13 @@ static int stm32l4_probe(struct flash_bank *bank) if (retval != ERROR_OK) return retval; - device_id = stm32l4_info->idcode & 0xFFF; + const uint32_t device_id = stm32l4_info->idcode & 0xFFF; for (unsigned int n = 0; n < ARRAY_SIZE(stm32l4_parts); n++) { - if (device_id == stm32l4_parts[n].id) + if (device_id == stm32l4_parts[n].id) { stm32l4_info->part_info = &stm32l4_parts[n]; + break; + } } if (!stm32l4_info->part_info) { @@ -1323,14 +1522,30 @@ static int stm32l4_probe(struct flash_bank *bank) } part_info = stm32l4_info->part_info; + const char *rev_str = get_stm32l4_rev_str(bank); + const uint16_t rev_id = stm32l4_info->idcode >> 16; + + LOG_INFO("device idcode = 0x%08" PRIx32 " (%s - Rev %s : 0x%04x)", + stm32l4_info->idcode, part_info->device_str, rev_str, rev_id); + + stm32l4_info->flash_regs_base = stm32l4_info->part_info->flash_regs_base; stm32l4_info->flash_regs = stm32l4_info->part_info->default_flash_regs; - char device_info[1024]; - retval = bank->driver->info(bank, device_info, sizeof(device_info)); + /* read flash option register */ + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &stm32l4_info->optr); if (retval != ERROR_OK) return retval; - LOG_INFO("device idcode = 0x%08" PRIx32 " (%s)", stm32l4_info->idcode, device_info); + stm32l4_sync_rdp_tzen(bank); + + if (part_info->flags & F_HAS_TZ) + LOG_INFO("TZEN = %d : TrustZone %s by option bytes", + stm32l4_info->tzen, + stm32l4_info->tzen ? "enabled" : "disabled"); + + LOG_INFO("RDP level %s (0x%02X)", + stm32l4_info->rdp == RDP_LEVEL_0 ? "0" : stm32l4_info->rdp == RDP_LEVEL_0_5 ? "0.5" : "1", + stm32l4_info->rdp); if (stm32l4_is_otp(bank)) { bank->size = part_info->otp_size; @@ -1347,10 +1562,9 @@ static int stm32l4_probe(struct flash_bank *bank) return ERROR_FAIL; } - stm32l4_info->probed = true; return ERROR_OK; - } else if (bank->base != STM32_FLASH_BANK_BASE) { + } else if (bank->base != STM32_FLASH_BANK_BASE && bank->base != STM32_FLASH_S_BANK_BASE) { LOG_ERROR("invalid bank base address"); return ERROR_FAIL; } @@ -1379,11 +1593,6 @@ static int stm32l4_probe(struct flash_bank *bank) /* did we assign a flash size? */ assert((flash_size_kb != 0xffff) && flash_size_kb); - /* read flash option register */ - retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &options); - if (retval != ERROR_OK) - return retval; - stm32l4_info->bank1_sectors = 0; stm32l4_info->hole_sectors = 0; @@ -1409,7 +1618,7 @@ static int stm32l4_probe(struct flash_bank *bank) stm32l4_info->bank1_sectors = num_pages; /* check DUAL_BANK bit[21] if the flash is less than 1M */ - if (flash_size_kb == 1024 || (options & BIT(21))) { + if (flash_size_kb == 1024 || (stm32l4_info->optr & BIT(21))) { stm32l4_info->dual_bank_mode = true; stm32l4_info->bank1_sectors = num_pages / 2; } @@ -1435,7 +1644,7 @@ static int stm32l4_probe(struct flash_bank *bank) page_size_kb = 4; num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; - if (options & BIT(22)) { + if (stm32l4_info->optr & BIT(22)) { stm32l4_info->dual_bank_mode = true; page_size_kb = 2; num_pages = flash_size_kb / page_size_kb; @@ -1459,8 +1668,8 @@ static int stm32l4_probe(struct flash_bank *bank) num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; - if ((use_dbank_bit && (options & BIT(22))) || - (!use_dbank_bit && (options & BIT(21)))) { + if ((use_dbank_bit && (stm32l4_info->optr & BIT(22))) || + (!use_dbank_bit && (stm32l4_info->optr & BIT(21)))) { stm32l4_info->dual_bank_mode = true; page_size_kb = 4; num_pages = flash_size_kb / page_size_kb; @@ -1476,13 +1685,22 @@ static int stm32l4_probe(struct flash_bank *bank) num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages; use_dbank_bit = flash_size_kb == part_info->max_flash_size_kb; - if ((use_dbank_bit && (options & BIT(22))) || - (!use_dbank_bit && (options & BIT(21)))) { + if ((use_dbank_bit && (stm32l4_info->optr & BIT(22))) || + (!use_dbank_bit && (stm32l4_info->optr & BIT(21)))) { stm32l4_info->dual_bank_mode = true; page_size_kb = 2; num_pages = flash_size_kb / page_size_kb; stm32l4_info->bank1_sectors = num_pages / 2; } + + /** + * by default use the non-secure registers, + * switch secure registers if TZ is enabled and RDP is LEVEL_0 + */ + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + stm32l4_info->flash_regs_base |= 0x10000000; + stm32l4_info->flash_regs = stm32l5_s_flash_regs; + } break; case 0x495: /* STM32WB5x */ case 0x496: /* STM32WB3x */ @@ -1519,7 +1737,7 @@ static int stm32l4_probe(struct flash_bank *bank) * max_flash_size is always power of two, so max_pages too */ uint32_t max_pages = stm32l4_info->part_info->max_flash_size_kb / page_size_kb; - assert((max_pages & (max_pages - 1)) == 0); + assert(IS_PWR_OF_2(max_pages)); /* in dual bank mode number of pages is doubled, but extra bit is bank selection */ stm32l4_info->wrpxxr_mask = ((max_pages >> (stm32l4_info->dual_bank_mode ? 1 : 0)) - 1); @@ -1531,7 +1749,7 @@ static int stm32l4_probe(struct flash_bank *bank) bank->size = (flash_size_kb + gap_size_kb) * 1024; bank->num_sectors = num_pages; bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); - if (bank->sectors == NULL) { + if (!bank->sectors) { LOG_ERROR("failed to allocate bank sectors"); return ERROR_FAIL; } @@ -1554,39 +1772,34 @@ static int stm32l4_probe(struct flash_bank *bank) static int stm32l4_auto_probe(struct flash_bank *bank) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; - if (stm32l4_info->probed) - return ERROR_OK; + if (stm32l4_info->probed) { + uint32_t optr_cur; + + /* read flash option register and re-probe if optr value is changed */ + int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &optr_cur); + if (retval != ERROR_OK) + return retval; + + if (stm32l4_info->optr == optr_cur) + return ERROR_OK; + } return stm32l4_probe(bank); } -static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size) +static int get_stm32l4_info(struct flash_bank *bank, struct command_invocation *cmd) { struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; const struct stm32l4_part_info *part_info = stm32l4_info->part_info; if (part_info) { - const char *rev_str = NULL; - uint16_t rev_id = stm32l4_info->idcode >> 16; - for (unsigned int i = 0; i < part_info->num_revs; i++) { - if (rev_id == part_info->revs[i].rev) { - rev_str = part_info->revs[i].str; - break; - } - } - - int buf_len = snprintf(buf, buf_size, "%s - Rev %s : 0x%04x", - part_info->device_str, rev_str ? rev_str : "'unknown'", rev_id); - + const uint16_t rev_id = stm32l4_info->idcode >> 16; + command_print_sameline(cmd, "%s - Rev %s : 0x%04x", part_info->device_str, + get_stm32l4_rev_str(bank), rev_id); if (stm32l4_info->probed) - snprintf(buf + buf_len, buf_size - buf_len, " - %s-bank", - stm32l4_is_otp(bank) ? "OTP" : - stm32l4_info->dual_bank_mode ? "Flash dual" : "Flash single"); - - return ERROR_OK; + command_print_sameline(cmd, " - %s-bank", get_stm32l4_bank_type_str(bank)); } else { - snprintf(buf, buf_size, "Cannot identify target as an %s device", device_families); - return ERROR_FAIL; + command_print_sameline(cmd, "Cannot identify target as an %s device", device_families); } return ERROR_OK; @@ -1613,6 +1826,16 @@ static int stm32l4_mass_erase(struct flash_bank *bank) return ERROR_TARGET_NOT_HALTED; } + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* set all FLASH pages as secure */ + retval = stm32l4_set_secbb(bank, FLASH_SECBB_SECURE); + if (retval != ERROR_OK) { + /* restore all FLASH pages as non-secure */ + stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); /* ignore the return value */ + return retval; + } + } + retval = stm32l4_unlock_reg(bank); if (retval != ERROR_OK) goto err_lock; @@ -1635,6 +1858,13 @@ static int stm32l4_mass_erase(struct flash_bank *bank) err_lock: retval2 = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_LOCK); + if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { + /* restore all FLASH pages as non-secure */ + int retval3 = stm32l4_set_secbb(bank, FLASH_SECBB_NON_SECURE); + if (retval3 != ERROR_OK) + return retval3; + } + if (retval != ERROR_OK) return retval; @@ -1650,19 +1880,14 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; retval = stm32l4_mass_erase(bank); - if (retval == ERROR_OK) { - /* set all sectors as erased */ - for (unsigned int i = 0; i < bank->num_sectors; i++) - bank->sectors[i].is_erased = 1; - + if (retval == ERROR_OK) command_print(CMD, "stm32l4x mass erase complete"); - } else { + else command_print(CMD, "stm32l4x mass erase failed"); - } return retval; } @@ -1676,17 +1901,17 @@ COMMAND_HANDLER(stm32l4_handle_option_read_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; uint32_t reg_offset, reg_addr; uint32_t value = 0; - reg_offset = strtoul(CMD_ARGV[1], NULL, 16); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset); reg_addr = stm32l4_get_flash_reg(bank, reg_offset); retval = stm32l4_read_flash_reg(bank, reg_offset, &value); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; command_print(CMD, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value); @@ -1703,17 +1928,18 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; uint32_t reg_offset; uint32_t value = 0; uint32_t mask = 0xFFFFFFFF; - reg_offset = strtoul(CMD_ARGV[1], NULL, 16); - value = strtoul(CMD_ARGV[2], NULL, 16); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], value); + if (CMD_ARGC > 3) - mask = strtoul(CMD_ARGV[3], NULL, 16); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[3], mask); command_print(CMD, "%s Option written.\n" "INFO: a reset or power cycle is required " @@ -1723,38 +1949,87 @@ COMMAND_HANDLER(stm32l4_handle_option_write_command) return retval; } -COMMAND_HANDLER(stm32l4_handle_option_load_command) +COMMAND_HANDLER(stm32l4_handle_trustzone_command) { - if (CMD_ARGC != 1) + if (CMD_ARGC < 1 || CMD_ARGC > 2) return ERROR_COMMAND_SYNTAX_ERROR; struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; - retval = stm32l4_unlock_reg(bank); - if (ERROR_OK != retval) + struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; + if (!(stm32l4_info->part_info->flags & F_HAS_TZ)) { + LOG_ERROR("This device does not have a TrustZone"); + return ERROR_FAIL; + } + + retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &stm32l4_info->optr); + if (retval != ERROR_OK) return retval; - retval = stm32l4_unlock_option_reg(bank); - if (ERROR_OK != retval) + stm32l4_sync_rdp_tzen(bank); + + if (CMD_ARGC == 1) { + /* only display the TZEN value */ + LOG_INFO("Global TrustZone Security is %s", stm32l4_info->tzen ? "enabled" : "disabled"); + return ERROR_OK; + } + + bool new_tzen; + COMMAND_PARSE_ENABLE(CMD_ARGV[1], new_tzen); + + if (new_tzen == stm32l4_info->tzen) { + LOG_INFO("The requested TZEN is already programmed"); + return ERROR_OK; + } + + if (new_tzen) { + if (stm32l4_info->rdp != RDP_LEVEL_0) { + LOG_ERROR("TZEN can be set only when RDP level is 0"); + return ERROR_FAIL; + } + retval = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], + FLASH_TZEN, FLASH_TZEN); + } else { + /* Deactivation of TZEN (from 1 to 0) is only possible when the RDP is + * changing to level 0 (from level 1 to level 0 or from level 0.5 to level 0). */ + if (stm32l4_info->rdp != RDP_LEVEL_1 && stm32l4_info->rdp != RDP_LEVEL_0_5) { + LOG_ERROR("Deactivation of TZEN is only possible when the RDP is changing to level 0"); + return ERROR_FAIL; + } + + retval = stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], + RDP_LEVEL_0, FLASH_RDP_MASK | FLASH_TZEN); + } + + if (retval != ERROR_OK) return retval; - /* Set OBL_LAUNCH bit in CR -> system reset and option bytes reload, - * but the RMs explicitly do *NOT* list this as power-on reset cause, and: - * "Note: If the read protection is set while the debugger is still - * connected through JTAG/SWD, apply a POR (power-on reset) instead of a system reset." - */ - retval = stm32l4_write_flash_reg_by_index(bank, STM32_FLASH_CR_INDEX, FLASH_OBL_LAUNCH); + return stm32l4_perform_obl_launch(bank); +} - command_print(CMD, "stm32l4x option load completed. Power-on reset might be required"); +COMMAND_HANDLER(stm32l4_handle_option_load_command) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; - /* Need to re-probe after change */ - struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; - stm32l4_info->probed = false; + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (retval != ERROR_OK) + return retval; - return retval; + retval = stm32l4_perform_obl_launch(bank); + if (retval != ERROR_OK) { + command_print(CMD, "stm32l4x option load failed"); + return retval; + } + + + command_print(CMD, "stm32l4x option load completed. Power-on reset might be required"); + + return ERROR_OK; } COMMAND_HANDLER(stm32l4_handle_lock_command) @@ -1766,7 +2041,7 @@ COMMAND_HANDLER(stm32l4_handle_lock_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; if (stm32l4_is_otp(bank)) { @@ -1783,7 +2058,8 @@ COMMAND_HANDLER(stm32l4_handle_lock_command) /* set readout protection level 1 by erasing the RDP option byte */ struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; - if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], 0, 0x000000FF) != ERROR_OK) { + if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], + RDP_LEVEL_1, FLASH_RDP_MASK) != ERROR_OK) { command_print(CMD, "%s failed to lock device", bank->driver->name); return ERROR_OK; } @@ -1800,7 +2076,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; if (stm32l4_is_otp(bank)) { @@ -1817,7 +2093,7 @@ COMMAND_HANDLER(stm32l4_handle_unlock_command) struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; if (stm32l4_write_option(bank, stm32l4_info->flash_regs[STM32_FLASH_OPTR_INDEX], - RDP_LEVEL_0, 0x000000FF) != ERROR_OK) { + RDP_LEVEL_0, FLASH_RDP_MASK) != ERROR_OK) { command_print(CMD, "%s failed to unlock device", bank->driver->name); return ERROR_OK; } @@ -1832,7 +2108,7 @@ COMMAND_HANDLER(stm32l4_handle_wrp_info_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; if (stm32l4_is_otp(bank)) { @@ -1904,7 +2180,7 @@ COMMAND_HANDLER(stm32l4_handle_otp_command) struct flash_bank *bank; int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) + if (retval != ERROR_OK) return retval; if (!stm32l4_is_otp(bank)) { @@ -1960,6 +2236,13 @@ static const struct command_registration stm32l4_exec_command_handlers[] = { .usage = "bank_id reg_offset value mask", .help = "Write device option bit fields with provided value.", }, + { + .name = "trustzone", + .handler = stm32l4_handle_trustzone_command, + .mode = COMMAND_EXEC, + .usage = " [enable|disable]", + .help = "Configure TrustZone security", + }, { .name = "wrp_info", .handler = stm32l4_handle_wrp_info_command,