Added support for STM32L4X option bytes writing.

Enables the programming of Write protection lock bits.

- Updated/re-factored with option_read, option_write and option_load commands.

Change-Id: I86358c7eb1285c3c0baac1564e46da8ced5fd025
Signed-off-by: Thomas Søhus <tls@ceepro.dk>
Reviewed-on: http://openocd.zylin.com/4654
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
This commit is contained in:
Thomas Søhus 2018-08-16 14:04:45 +02:00 committed by Tomas Vanek
parent 4423a58b4d
commit b2d259f67c
2 changed files with 282 additions and 188 deletions

View File

@ -6596,6 +6596,42 @@ The @var{num} parameter is a value shown by @command{flash banks}.
Mass erases the entire stm32l4x device.
The @var{num} parameter is a value shown by @command{flash banks}.
@end deffn
@deffn Command {stm32l4x option_read} num reg_offset
Reads an option byte register from the stm32l4x device.
The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset}
is the register offset of the Option byte to read.
For example to read the FLASH_OPTR register:
@example
stm32l4x option_read 0 0x20
# Option Register: <0x40022020> = 0xffeff8aa
@end example
The above example will read out the FLASH_OPTR register which contains the RDP
option byte, Watchdog configuration, BOR level etc.
@end deffn
@deffn Command {stm32l4x option_write} num reg_offset reg_mask
Write an option byte register of the stm32l4x device.
The @var{num} parameter is a value shown by @command{flash banks}, @var{reg_offset}
is the register offset of the Option byte to write, and @var{reg_mask} is the mask
to apply when writing the register (only bits with a '1' will be touched).
For example to write the WRP1AR option bytes:
@example
stm32l4x option_write 0 0x28 0x00FF0000 0x00FF00FF
@end example
The above example will write the WRP1AR option register configuring the Write protection
Area A for bank 1. The above example set WRP1AR_END=255, WRP1AR_START=0.
This will effectively write protect all sectors in flash bank 1.
@end deffn
@deffn Command {stm32l4x option_load} num
Forces a re-load of the option byte registers. Will cause a reset of the device.
The @var{num} parameter is a value shown by @command{flash banks}.
@end deffn
@end deffn
@deffn {Flash Driver} str7x

View File

@ -57,8 +57,8 @@
#define STM32_FLASH_CR 0x40022014
#define STM32_FLASH_OPTR 0x40022020
#define STM32_FLASH_WRP1AR 0x4002202c
#define STM32_FLASH_WRP2AR 0x40022030
#define STM32_FLASH_WRP1BR 0x4002204c
#define STM32_FLASH_WRP1BR 0x40022030
#define STM32_FLASH_WRP2AR 0x4002204c
#define STM32_FLASH_WRP2BR 0x40022050
/* FLASH_CR register bits */
@ -70,8 +70,10 @@
#define FLASH_CR_BKER (1 << 11)
#define FLASH_MER2 (1 << 15)
#define FLASH_STRT (1 << 16)
#define FLASH_OPTSTRT (1 << 17)
#define FLASH_EOPIE (1 << 24)
#define FLASH_ERRIE (1 << 25)
#define FLASH_OBLLAUNCH (1 << 27)
#define FLASH_OPTLOCK (1 << 30)
#define FLASH_LOCK (1 << 31)
@ -102,28 +104,17 @@
#define OPTKEY1 0x08192A3B
#define OPTKEY2 0x4C5D6E7F
#define RDP_LEVEL_0 0xAA
#define RDP_LEVEL_1 0xBB
#define RDP_LEVEL_2 0xCC
/* other registers */
#define DBGMCU_IDCODE 0xE0042000
#define FLASH_SIZE_REG 0x1FFF75E0
struct stm32l4_options {
uint8_t RDP;
uint16_t bank_b_start;
uint8_t user_options;
uint8_t wpr1a_start;
uint8_t wpr1a_end;
uint8_t wpr1b_start;
uint8_t wpr1b_end;
uint8_t wpr2a_start;
uint8_t wpr2a_end;
uint8_t wpr2b_start;
uint8_t wpr2b_end;
/* Fixme: Handle PCROP */
};
struct stm32l4_flash_bank {
struct stm32l4_options option_bytes;
uint16_t bank2_start;
int probed;
};
@ -265,97 +256,80 @@ static int stm32l4_unlock_option_reg(struct target *target)
return ERROR_OK;
}
static int stm32l4_read_options(struct flash_bank *bank)
static int stm32l4_read_option(struct flash_bank *bank, uint32_t address, uint32_t* value)
{
uint32_t optiondata;
struct stm32l4_flash_bank *stm32l4_info = NULL;
struct target *target = bank->target;
stm32l4_info = bank->driver_priv;
/* read current option bytes */
int retval = target_read_u32(target, STM32_FLASH_OPTR, &optiondata);
if (retval != ERROR_OK)
return retval;
stm32l4_info->option_bytes.user_options = (optiondata >> 8) & 0x3ffff;
stm32l4_info->option_bytes.RDP = optiondata & 0xff;
retval = target_read_u32(target, STM32_FLASH_WRP1AR, &optiondata);
if (retval != ERROR_OK)
return retval;
stm32l4_info->option_bytes.wpr1a_start = optiondata & 0xff;
stm32l4_info->option_bytes.wpr1a_end = (optiondata >> 16) & 0xff;
retval = target_read_u32(target, STM32_FLASH_WRP2AR, &optiondata);
if (retval != ERROR_OK)
return retval;
stm32l4_info->option_bytes.wpr2a_start = optiondata & 0xff;
stm32l4_info->option_bytes.wpr2a_end = (optiondata >> 16) & 0xff;
retval = target_read_u32(target, STM32_FLASH_WRP1BR, &optiondata);
if (retval != ERROR_OK)
return retval;
stm32l4_info->option_bytes.wpr1b_start = optiondata & 0xff;
stm32l4_info->option_bytes.wpr1b_end = (optiondata >> 16) & 0xff;
retval = target_read_u32(target, STM32_FLASH_WRP2BR, &optiondata);
if (retval != ERROR_OK)
return retval;
stm32l4_info->option_bytes.wpr2b_start = optiondata & 0xff;
stm32l4_info->option_bytes.wpr2b_end = (optiondata >> 16) & 0xff;
if (stm32l4_info->option_bytes.RDP != 0xAA)
LOG_INFO("Device Security Bit Set");
return ERROR_OK;
return target_read_u32(target, address, value);
}
static int stm32l4_write_options(struct flash_bank *bank)
static int stm32l4_write_option(struct flash_bank *bank, uint32_t address, uint32_t value, uint32_t mask)
{
struct stm32l4_flash_bank *stm32l4_info = NULL;
struct target *target = bank->target;
uint32_t optiondata;
stm32l4_info = bank->driver_priv;
(void) optiondata;
(void) stm32l4_info;
int retval = stm32l4_unlock_option_reg(target);
int retval = target_read_u32(target, address, &optiondata);
if (retval != ERROR_OK)
return retval;
/* FIXME: Implement Option writing!*/
return ERROR_OK;
retval = stm32l4_unlock_reg(target);
if (retval != ERROR_OK)
return retval;
retval = stm32l4_unlock_option_reg(target);
if (retval != ERROR_OK)
return retval;
optiondata = (optiondata & ~mask) | (value & mask);
retval = target_write_u32(target, address, optiondata);
if (retval != ERROR_OK)
return retval;
retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OPTSTRT);
if (retval != ERROR_OK)
return retval;
retval = stm32l4_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
if (retval != ERROR_OK)
return retval;
return retval;
}
static int stm32l4_protect_check(struct flash_bank *bank)
{
struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv;
uint32_t wrp1ar, wrp1br, wrp2ar, wrp2br;
stm32l4_read_option(bank, STM32_FLASH_WRP1AR, &wrp1ar);
stm32l4_read_option(bank, STM32_FLASH_WRP1BR, &wrp1br);
stm32l4_read_option(bank, STM32_FLASH_WRP2AR, &wrp2ar);
stm32l4_read_option(bank, STM32_FLASH_WRP2BR, &wrp2br);
/* read write protection settings */
int retval = stm32l4_read_options(bank);
if (retval != ERROR_OK) {
LOG_DEBUG("unable to read option bytes");
return retval;
}
const uint8_t wrp1a_start = wrp1ar & 0xFF;
const uint8_t wrp1a_end = (wrp1ar >> 16) & 0xFF;
const uint8_t wrp1b_start = wrp1br & 0xFF;
const uint8_t wrp1b_end = (wrp1br >> 16) & 0xFF;
const uint8_t wrp2a_start = wrp2ar & 0xFF;
const uint8_t wrp2a_end = (wrp2ar >> 16) & 0xFF;
const uint8_t wrp2b_start = wrp2br & 0xFF;
const uint8_t wrp2b_end = (wrp2br >> 16) & 0xFF;
for (int i = 0; i < bank->num_sectors; i++) {
if (i < stm32l4_info->option_bytes.bank_b_start) {
if (((i >= stm32l4_info->option_bytes.wpr1a_start) &&
(i <= stm32l4_info->option_bytes.wpr1a_end)) ||
((i >= stm32l4_info->option_bytes.wpr2a_start) &&
(i <= stm32l4_info->option_bytes.wpr2a_end)))
if (i < stm32l4_info->bank2_start) {
if (((i >= wrp1a_start) &&
(i <= wrp1a_end)) ||
((i >= wrp1b_start) &&
(i <= wrp1b_end)))
bank->sectors[i].is_protected = 1;
else
bank->sectors[i].is_protected = 0;
} else {
uint8_t snb;
snb = i - stm32l4_info->option_bytes.bank_b_start + 256;
if (((snb >= stm32l4_info->option_bytes.wpr1b_start) &&
(snb <= stm32l4_info->option_bytes.wpr1b_end)) ||
((snb >= stm32l4_info->option_bytes.wpr2b_start) &&
(snb <= stm32l4_info->option_bytes.wpr2b_end)))
snb = i - stm32l4_info->bank2_start + 256;
if (((snb >= wrp2a_start) &&
(snb <= wrp2a_end)) ||
((snb >= wrp2b_start) &&
(snb <= wrp2b_end)))
bank->sectors[i].is_protected = 1;
else
bank->sectors[i].is_protected = 0;
@ -398,9 +372,9 @@ static int stm32l4_erase(struct flash_bank *bank, int first, int last)
uint32_t erase_flags;
erase_flags = FLASH_PER | FLASH_STRT;
if (i >= stm32l4_info->option_bytes.bank_b_start) {
if (i >= stm32l4_info->bank2_start) {
uint8_t snb;
snb = (i - stm32l4_info->option_bytes.bank_b_start) + 256;
snb = (i - stm32l4_info->bank2_start) + 256;
erase_flags |= snb << FLASH_PAGE_SHIFT | FLASH_CR_BKER;
} else
erase_flags |= i << FLASH_PAGE_SHIFT;
@ -434,20 +408,29 @@ static int stm32l4_protect(struct flash_bank *bank, int set, int first, int last
return ERROR_TARGET_NOT_HALTED;
}
/* read protection settings */
int retval = stm32l4_read_options(bank);
if (retval != ERROR_OK) {
LOG_DEBUG("unable to read option bytes");
return retval;
int ret = ERROR_OK;
/* Bank 2 */
uint32_t reg_value = 0xFF; /* Default to bank un-protected */
if (last >= stm32l4_info->bank2_start) {
if (set == 1) {
uint8_t begin = first > stm32l4_info->bank2_start ? first : 0x00;
reg_value = ((last & 0xFF) << 16) | begin;
}
(void)stm32l4_info;
/* FIXME: Write First and last in a valid WRPxx_start/end combo*/
retval = stm32l4_write_options(bank);
if (retval != ERROR_OK)
return retval;
ret = stm32l4_write_option(bank, STM32_FLASH_WRP2AR, reg_value, 0xffffffff);
}
/* Bank 1 */
reg_value = 0xFF; /* Default to bank un-protected */
if (first < stm32l4_info->bank2_start) {
if (set == 1) {
uint8_t end = last >= stm32l4_info->bank2_start ? 0xFF : last;
reg_value = (end << 16) | (first & 0xFF);
}
return ERROR_OK;
ret = stm32l4_write_option(bank, STM32_FLASH_WRP1AR, reg_value, 0xffffffff);
}
return ret;
}
/* Count is in halfwords */
@ -650,9 +633,9 @@ static int stm32l4_probe(struct flash_bank *bank)
/* only devices with < 1024 kiB may be set to single bank dual banks */
if ((flash_size_in_kb == 1024) || !(options & OPT_DUALBANK))
stm32l4_info->option_bytes.bank_b_start = 256;
stm32l4_info->bank2_start = 256;
else
stm32l4_info->option_bytes.bank_b_start = flash_size_in_kb << 9;
stm32l4_info->bank2_start = flash_size_in_kb << 9;
/* did we assign flash size? */
assert((flash_size_in_kb != 0xffff) && flash_size_in_kb);
@ -747,89 +730,6 @@ static int get_stm32l4_info(struct flash_bank *bank, char *buf, int buf_size)
return ERROR_OK;
}
COMMAND_HANDLER(stm32l4_handle_lock_command)
{
struct target *target = NULL;
struct stm32l4_flash_bank *stm32l4_info = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
stm32l4_info = bank->driver_priv;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (stm32l4_read_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to read options",
bank->driver->name);
return ERROR_OK;
}
/* set readout protection */
stm32l4_info->option_bytes.RDP = 0;
if (stm32l4_write_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to lock device", bank->driver->name);
return ERROR_OK;
}
command_print(CMD_CTX, "%s locked", bank->driver->name);
return ERROR_OK;
}
COMMAND_HANDLER(stm32l4_handle_unlock_command)
{
struct target *target = NULL;
struct stm32l4_flash_bank *stm32l4_info = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
stm32l4_info = bank->driver_priv;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (stm32l4_read_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to read options", bank->driver->name);
return ERROR_OK;
}
/* clear readout protection and complementary option bytes
* this will also force a device unlock if set */
stm32l4_info->option_bytes.RDP = 0xAA;
if (stm32l4_write_options(bank) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to unlock device",
bank->driver->name);
return ERROR_OK;
}
command_print(CMD_CTX, "%s unlocked.\n"
"INFO: a reset or power cycle is required "
"for the new settings to take effect.", bank->driver->name);
return ERROR_OK;
}
static int stm32l4_mass_erase(struct flash_bank *bank, uint32_t action)
{
int retval;
@ -873,7 +773,7 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
uint32_t action;
if (CMD_ARGC < 1) {
command_print(CMD_CTX, "stm32x mass_erase <STM32L4 bank>");
command_print(CMD_CTX, "stm32l4x mass_erase <STM32L4 bank>");
return ERROR_COMMAND_SYNTAX_ERROR;
}
@ -889,14 +789,151 @@ COMMAND_HANDLER(stm32l4_handle_mass_erase_command)
for (i = 0; i < bank->num_sectors; i++)
bank->sectors[i].is_erased = 1;
command_print(CMD_CTX, "stm32x mass erase complete");
command_print(CMD_CTX, "stm32l4x mass erase complete");
} else {
command_print(CMD_CTX, "stm32x mass erase failed");
command_print(CMD_CTX, "stm32l4x mass erase failed");
}
return retval;
}
COMMAND_HANDLER(stm32l4_handle_option_read_command)
{
if (CMD_ARGC < 2) {
command_print(CMD_CTX, "stm32l4x option_read <STM32L4 bank> <option_reg offset>");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
uint32_t reg_addr = STM32_FLASH_BASE;
uint32_t value = 0;
reg_addr += strtoul(CMD_ARGV[1], NULL, 16);
retval = stm32l4_read_option(bank, reg_addr, &value);
if (ERROR_OK != retval)
return retval;
command_print(CMD_CTX, "Option Register: <0x%" PRIx32 "> = 0x%" PRIx32 "", reg_addr, value);
return retval;
}
COMMAND_HANDLER(stm32l4_handle_option_write_command)
{
if (CMD_ARGC < 3) {
command_print(CMD_CTX, "stm32l4x option_write <STM32L4 bank> <option_reg offset> <value> [mask]");
return ERROR_COMMAND_SYNTAX_ERROR;
}
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
uint32_t reg_addr = STM32_FLASH_BASE;
uint32_t value = 0;
uint32_t mask = 0xFFFFFFFF;
reg_addr += strtoul(CMD_ARGV[1], NULL, 16);
value = strtoul(CMD_ARGV[2], NULL, 16);
if (CMD_ARGC > 3)
mask = strtoul(CMD_ARGV[3], NULL, 16);
command_print(CMD_CTX, "%s Option written.\n"
"INFO: a reset or power cycle is required "
"for the new settings to take effect.", bank->driver->name);
retval = stm32l4_write_option(bank, reg_addr, value, mask);
return retval;
}
COMMAND_HANDLER(stm32l4_handle_option_load_command)
{
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
struct target *target = bank->target;
retval = stm32l4_unlock_reg(target);
if (ERROR_OK != retval)
return retval;
retval = stm32l4_unlock_option_reg(target);
if (ERROR_OK != retval)
return retval;
/* Write the OBLLAUNCH bit in CR -> Cause device "POR" and option bytes reload */
retval = target_write_u32(target, stm32l4_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBLLAUNCH);
command_print(CMD_CTX, "stm32l4x option load (POR) completed.");
return retval;
}
COMMAND_HANDLER(stm32l4_handle_lock_command)
{
struct target *target = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
/* set readout protection level 1 by erasing the RDP option byte */
if (stm32l4_write_option(bank, STM32_FLASH_OPTR, 0, 0x000000FF) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to lock device", bank->driver->name);
return ERROR_OK;
}
return ERROR_OK;
}
COMMAND_HANDLER(stm32l4_handle_unlock_command)
{
struct target *target = NULL;
if (CMD_ARGC < 1)
return ERROR_COMMAND_SYNTAX_ERROR;
struct flash_bank *bank;
int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
if (ERROR_OK != retval)
return retval;
target = bank->target;
if (target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (stm32l4_write_option(bank, STM32_FLASH_OPTR, RDP_LEVEL_0, 0x000000FF) != ERROR_OK) {
command_print(CMD_CTX, "%s failed to unlock device", bank->driver->name);
return ERROR_OK;
}
return ERROR_OK;
}
static const struct command_registration stm32l4_exec_command_handlers[] = {
{
.name = "lock",
@ -919,6 +956,27 @@ static const struct command_registration stm32l4_exec_command_handlers[] = {
.usage = "bank_id",
.help = "Erase entire flash device.",
},
{
.name = "option_read",
.handler = stm32l4_handle_option_read_command,
.mode = COMMAND_EXEC,
.usage = "bank_id reg_offset",
.help = "Read & Display device option bytes.",
},
{
.name = "option_write",
.handler = stm32l4_handle_option_write_command,
.mode = COMMAND_EXEC,
.usage = "bank_id reg_offset value mask",
.help = "Write device option bit fields with provided value.",
},
{
.name = "option_load",
.handler = stm32l4_handle_option_load_command,
.mode = COMMAND_EXEC,
.usage = "bank_id",
.help = "Force re-load of device options (will cause device reset).",
},
COMMAND_REGISTRATION_DONE
};