This is an automated email from Gerrit. "zapb <d...@zapb.de>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/8999
-- gerrit commit c2b1f727942870d158f58ed9b188bedfb43ff5f8 Author: Marc Schink <d...@zapb.de> Date: Sun Jul 13 14:00:50 2025 +0000 flash/nor/stm32lx: Add 'option_{write,read}' functions Add functions to read and write the option bytes, inspired by the stm32l4x driver. Tested on STM32L072CZ, including regression tests for 'stm32lx (un)lock'. Change-Id: I73298e1a668b3f604cbe558422495719832644e8 Signed-off-by: Marc Schink <d...@zapb.de> diff --git a/doc/openocd.texi b/doc/openocd.texi index fb4664f3db..22095d4b27 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -8256,6 +8256,40 @@ Level is 2 which can't be unlocked at all). The @var{num} parameter is a value shown by @command{flash banks}. @end deffn +@deffn {Command} {stm32lx option_read} num reg_offset +Reads an option byte register from the 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 +stm32lx option_read 0 0x0 +# Option Register (for STM32L0): 0xaa +@end example + +The above example will read out the first 16-bit of the FLASH_OPTR register which contains the RDP and WPRMOD value. +@end deffn + +@deffn {Command} {stm324x option_write} num reg_offset value [reg_mask] +Write an option byte register of the 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. +@var{value} is the 16-bit value that is written into the register. +The complementary value for the option byte register is calculated automatically. +@var{reg_mask} is the mask to apply when writing the register (only bits with a '1' will be touched). + +@emph{Note:} To modify an entire 32-bit option byte register, two individual write operations are necessary. + +For example, to modify the first 16-bit of the FLASH_OPTR register use the following command: + +@example +stm32lx option_write 0 0x00 0x100 0x100 +@end example + +The above example sets bit 8 (WPRMOD) on the FLASH_OPTR option byte register which enables PCROP. +Due to the applied mask, all other bits are not changed. + +@emph{Note:} To apply the option bytes change immediately, use @command{stm32lx option_load}. +@end deffn + @deffn {Command} {stm32lx option_load} num Forces a re-load of the option byte registers. This command will cause a system reset of the device. diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c index bf283239c0..69e66e6c67 100644 --- a/src/flash/nor/stm32lx.c +++ b/src/flash/nor/stm32lx.c @@ -17,6 +17,7 @@ #include "imp.h" #include <helper/binarybuffer.h> +#include <helper/align.h> #include <target/algorithm.h> #include <target/armv7m.h> #include <target/cortex_m.h> @@ -82,8 +83,8 @@ /* option bytes */ #define OPTION_BYTES_ADDRESS 0x1FF80000 -#define OPTION_BYTE_0_PR1 0xFFFF0000 -#define OPTION_BYTE_0_PR0 0xFF5500AA +#define OPTION_BYTE_0_PR1 0x0000 +#define OPTION_BYTE_0_PR0 0x00AA static int stm32lx_unlock_program_memory(struct flash_bank *bank); static int stm32lx_lock_program_memory(struct flash_bank *bank); @@ -95,6 +96,8 @@ static int stm32lx_lock(struct flash_bank *bank); static int stm32lx_unlock(struct flash_bank *bank); static int stm32lx_mass_erase(struct flash_bank *bank); static int stm32lx_wait_until_bsy_clear_timeout(struct flash_bank *bank, int timeout); +static int stm32lx_write_option(struct flash_bank *bank, uint32_t reg_offset, + uint16_t value, uint16_t mask); static int stm32lx_update_part_info(struct flash_bank *bank, uint16_t flash_size_in_kb); struct stm32lx_rev { @@ -375,6 +378,76 @@ COMMAND_HANDLER(stm32lx_handle_option_load_command) return ERROR_OK; } +COMMAND_HANDLER(stm32lx_handle_option_read_command) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + + if (retval != ERROR_OK) + return retval; + + uint32_t reg_offset; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset); + + if (!IS_ALIGNED(reg_offset, 4)) { + command_print(CMD, "register offset must be word aligned"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + uint16_t value; + retval = target_read_u16(bank->target, OPTION_BYTES_ADDRESS + reg_offset, + &value); + + if (retval != ERROR_OK) { + command_print(CMD, "failed to read option bytes"); + return retval; + } + + command_print(CMD, "0x%" PRIx16 "", value); + + return retval; +} + +COMMAND_HANDLER(stm32lx_handle_option_write_command) +{ + if (CMD_ARGC != 3 && CMD_ARGC != 4) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + + if (retval != ERROR_OK) + return retval; + + uint32_t reg_offset; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], reg_offset); + + uint16_t value; + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[2], value); + + if (!IS_ALIGNED(reg_offset, 4)) { + command_print(CMD, "register offset must be word aligned"); + return ERROR_COMMAND_ARGUMENT_INVALID; + } + + uint16_t mask = 0xffff; + + if (CMD_ARGC > 3) + COMMAND_PARSE_NUMBER(u16, CMD_ARGV[3], mask); + + retval = stm32lx_write_option(bank, reg_offset, value, mask); + if (retval != ERROR_OK) { + command_print(CMD, "failed to write option bytes"); + return retval; + } + + return ERROR_OK; +} + static int stm32lx_protect_check(struct flash_bank *bank) { int retval; @@ -949,6 +1022,20 @@ static const struct command_registration stm32lx_exec_command_handlers[] = { .usage = "bank_id", .help = "Force re-load of device options (will cause device reset).", }, + { + .name = "option_read", + .handler = stm32lx_handle_option_read_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset", + .help = "Read & Display device option bytes.", + }, + { + .name = "option_write", + .handler = stm32lx_handle_option_write_command, + .mode = COMMAND_EXEC, + .usage = "bank_id reg_offset value [mask]", + .help = "Write device option bit fields with provided value.", + }, COMMAND_REGISTRATION_DONE }; @@ -1282,9 +1369,9 @@ static int stm32lx_obl_launch(struct flash_bank *bank) return tries ? ERROR_OK : ERROR_FAIL; } -static int stm32lx_lock(struct flash_bank *bank) +static int stm32lx_write_option(struct flash_bank *bank, uint32_t reg_offset, + uint16_t value, uint16_t mask) { - int retval; struct target *target = bank->target; if (target->state != TARGET_HALTED) { @@ -1292,34 +1379,23 @@ static int stm32lx_lock(struct flash_bank *bank) return ERROR_TARGET_NOT_HALTED; } - retval = stm32lx_unlock_options_bytes(bank); - if (retval != ERROR_OK) - return retval; + uint32_t option_data; + int retval = target_read_u32(target, OPTION_BYTES_ADDRESS + reg_offset, + &option_data); - /* set the RDP protection level to 1 */ - retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR1); if (retval != ERROR_OK) return retval; - return ERROR_OK; -} - -static int stm32lx_unlock(struct flash_bank *bank) -{ - int retval; - struct target *target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - retval = stm32lx_unlock_options_bytes(bank); if (retval != ERROR_OK) return retval; - /* set the RDP protection level to 0 */ - retval = target_write_u32(target, OPTION_BYTES_ADDRESS, OPTION_BYTE_0_PR0); + option_data = (option_data & ~mask) | (value & mask); + // Calculate the complementary value for both option bytes. + option_data = (~(option_data & 0xffff)) << 16 | (option_data & 0xffff); + + retval = target_write_u32(target, OPTION_BYTES_ADDRESS + reg_offset, + option_data); if (retval != ERROR_OK) return retval; @@ -1330,6 +1406,19 @@ static int stm32lx_unlock(struct flash_bank *bank) return ERROR_OK; } + +static int stm32lx_lock(struct flash_bank *bank) +{ + // Set the RDP protection level to 1. + return stm32lx_write_option(bank, 0x00, OPTION_BYTE_0_PR1, 0x00ff); +} + +static int stm32lx_unlock(struct flash_bank *bank) +{ + // Set the RDP protection level to 0. + return stm32lx_write_option(bank, 0x00, OPTION_BYTE_0_PR0, 0x00ff); +} + static int stm32lx_mass_erase(struct flash_bank *bank) { int retval; --