From bceec38260f1d85c52eda1e3083af9ec9484ba79 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Thu, 10 Nov 2022 15:33:12 +0100 Subject: [PATCH 1/6] drivers: flash: Introduce write protection support for STM32F4 This patch adds sector write protection support for STM32F4 devices family. These devices can protect flash content with sector precision. Write protection functionality was exposed as vendor extended operation. To change write protection state, caller should provide mask of enabled and disabled sectors. Function responsible for locking/unlocking option bytes was implemented for all STM32 devices supported by this driver. Signed-off-by: Patryk Duda --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig.stm32 | 22 +++++ drivers/flash/flash_stm32.c | 95 +++++++++++++++++++ drivers/flash/flash_stm32.h | 21 ++++ drivers/flash/flash_stm32_ex_op.c | 85 +++++++++++++++++ drivers/flash/flash_stm32f4x.c | 71 +++++++++++++- .../flash/stm32_flash_api_extensions.h | 35 +++++++ 7 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 drivers/flash/flash_stm32_ex_op.c create mode 100644 include/zephyr/drivers/flash/stm32_flash_api_extensions.h diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 0d474daa43891..0470df9bf739b 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -47,6 +47,7 @@ if(CONFIG_SOC_FLASH_STM32) zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_STM32H7_FLASH_CONTROLLER_ENABLED flash_stm32h7x.c) else() zephyr_library_sources(flash_stm32.c) + zephyr_library_sources_ifdef(CONFIG_FLASH_EX_OP_ENABLED flash_stm32_ex_op.c) zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_STM32F1_FLASH_CONTROLLER_ENABLED flash_stm32f1x.c) zephyr_library_sources_ifdef(CONFIG_DT_HAS_ST_STM32F2_FLASH_CONTROLLER_ENABLED flash_stm32f2x.c) diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index 6aaa56a4ca264..5c5932ba40e14 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -3,6 +3,7 @@ # Copyright (c) 2016 RnDity Sp. z o.o. # Copyright (c) 2017 BayLibre, SAS # Copyright (c) 2022 Linaro Limited +# Copyright (c) 2023 Google Inc # SPDX-License-Identifier: Apache-2.0 config SOC_FLASH_STM32 @@ -13,6 +14,27 @@ config SOC_FLASH_STM32 default y select FLASH_PAGE_LAYOUT select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_EX_OP if SOC_SERIES_STM32F4X select MPU_ALLOW_FLASH_WRITE if ARM_MPU help Enable flash driver for STM32 series + +if SOC_FLASH_STM32 + +config FLASH_STM32_WRITE_PROTECT + bool "Extended operation for flash write protection control" + depends on SOC_SERIES_STM32F4X + default n + help + Enables flash extended operation for enabling/disabling flash write + protection. + +config FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION + bool "Prevent from disabling flash write protection" + depends on FLASH_STM32_WRITE_PROTECT + default n + help + If enabled, all requests to disable flash write protection will be + blocked. + +endif # SOC_FLASH_STM32 diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index 28ba84692e602..8f58625c9f939 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -2,6 +2,7 @@ * Copyright (c) 2017 Linaro Limited * Copyright (c) 2017 BayLibre, SAS. * Copyright (c) 2019 Centaur Analytics, Inc + * Copyright (c) 2023 Google Inc * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +14,7 @@ #include #include +#include #include #include #include @@ -306,6 +308,74 @@ static int flash_stm32_write_protection(const struct device *dev, bool enable) return rc; } +int flash_stm32_option_bytes_lock(const struct device *dev, bool enable) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + +#if defined(FLASH_OPTCR_OPTLOCK) /* F2, F4, F7 and H7 */ + if (enable) { + regs->OPTCR |= FLASH_OPTCR_OPTLOCK; + } else if (regs->OPTCR & FLASH_OPTCR_OPTLOCK) { + regs->OPTKEYR = FLASH_OPT_KEY1; + regs->OPTKEYR = FLASH_OPT_KEY2; + } +#else + int rc; + + /* Unlock CR/PECR/NSCR register if needed. */ + if (!enable) { + rc = flash_stm32_write_protection(dev, false); + if (rc) { + return rc; + } + } +#if defined(FLASH_CR_OPTWRE) /* F0, F1 and F3 */ + if (enable) { + regs->CR &= ~FLASH_CR_OPTWRE; + } else if (!(regs->CR & FLASH_CR_OPTWRE)) { + regs->OPTKEYR = FLASH_OPTKEY1; + regs->OPTKEYR = FLASH_OPTKEY2; + } +#elif defined(FLASH_CR_OPTLOCK) /* G0, G4, L4, WB and WL */ + if (enable) { + regs->CR |= FLASH_CR_OPTLOCK; + } else if (regs->CR & FLASH_CR_OPTLOCK) { + regs->OPTKEYR = FLASH_OPTKEY1; + regs->OPTKEYR = FLASH_OPTKEY2; + } +#elif defined(FLASH_PECR_OPTLOCK) /* L0 and L1 */ + if (enable) { + regs->PECR |= FLASH_PECR_OPTLOCK; + } else if (regs->PECR & FLASH_PECR_OPTLOCK) { + regs->OPTKEYR = FLASH_OPTKEY1; + regs->OPTKEYR = FLASH_OPTKEY2; + } +#elif defined(FLASH_NSCR_OPTLOCK) /* L5 and U5 */ + if (enable) { + regs->NSCR |= FLASH_NSCR_OPTLOCK; + } else if (regs->NSCR & FLASH_NSCR_OPTLOCK) { + regs->OPTKEYR = FLASH_OPTKEY1; + regs->OPTKEYR = FLASH_OPTKEY2; + } +#endif + /* Lock CR/PECR/NSCR register if needed. */ + if (enable) { + rc = flash_stm32_write_protection(dev, true); + if (rc) { + return rc; + } + } +#endif + + if (enable) { + LOG_DBG("Option bytes locked"); + } else { + LOG_DBG("Option bytes unlocked"); + } + + return 0; +} + static const struct flash_parameters * flash_stm32_get_parameters(const struct device *dev) { @@ -314,6 +384,28 @@ flash_stm32_get_parameters(const struct device *dev) return &flash_stm32_parameters; } +#ifdef CONFIG_FLASH_EX_OP_ENABLED +static int flash_stm32_ex_op(const struct device *dev, uint16_t code, + const uintptr_t in, void *out) +{ + int rv = -ENOTSUP; + + flash_stm32_sem_take(dev); + + switch (code) { +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) + case FLASH_STM32_EX_OP_SECTOR_WP: + rv = flash_stm32_ex_op_sector_wp(dev, in, out); + break; +#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ + } + + flash_stm32_sem_give(dev); + + return rv; +} +#endif + static struct flash_stm32_priv flash_data = { .regs = (FLASH_TypeDef *) DT_INST_REG_ADDR(0), /* Getting clocks information from device tree description depending @@ -335,6 +427,9 @@ static const struct flash_driver_api flash_stm32_api = { #ifdef CONFIG_FLASH_PAGE_LAYOUT .page_layout = flash_stm32_page_layout, #endif +#ifdef CONFIG_FLASH_EX_OP_ENABLED + .ex_op = flash_stm32_ex_op, +#endif }; static int stm32_flash_init(const struct device *dev) diff --git a/drivers/flash/flash_stm32.h b/drivers/flash/flash_stm32.h index a503d80d22d4e..3dee0425ef52c 100644 --- a/drivers/flash/flash_stm32.h +++ b/drivers/flash/flash_stm32.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2017 Linaro Limited * Copyright (c) 2017 BayLibre, SAS. + * Copyright (c) 2023 Google Inc * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,6 +9,8 @@ #ifndef ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_ #define ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_ +#include + #if DT_NODE_HAS_PROP(DT_INST(0, st_stm32_flash_controller), clocks) || \ DT_NODE_HAS_PROP(DT_INST(0, st_stm32h7_flash_controller), clocks) #include @@ -220,6 +223,8 @@ int flash_stm32_block_erase_loop(const struct device *dev, int flash_stm32_wait_flash_idle(const struct device *dev); +int flash_stm32_option_bytes_lock(const struct device *dev, bool enable); + #ifdef CONFIG_SOC_SERIES_STM32WBX int flash_stm32_check_status(const struct device *dev); #endif /* CONFIG_SOC_SERIES_STM32WBX */ @@ -230,4 +235,20 @@ void flash_stm32_page_layout(const struct device *dev, size_t *layout_size); #endif +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) + +int flash_stm32_update_wp_sectors(const struct device *dev, + uint32_t changed_sectors, + uint32_t protected_sectors); + +int flash_stm32_get_wp_sectors(const struct device *dev, + uint32_t *protected_sectors); +#endif + +/* Flash extended operations */ +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) +int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in, + void *out); +#endif + #endif /* ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_ */ diff --git a/drivers/flash/flash_stm32_ex_op.c b/drivers/flash/flash_stm32_ex_op.c new file mode 100644 index 0000000000000..430d8900cc80c --- /dev/null +++ b/drivers/flash/flash_stm32_ex_op.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Google Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#ifdef CONFIG_USERSPACE +#include +#include +#endif + +#include +#include "flash_stm32.h" + +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) +int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in, + void *out) +{ + const struct flash_stm32_ex_op_sector_wp_in *request = + (const struct flash_stm32_ex_op_sector_wp_in *)in; + struct flash_stm32_ex_op_sector_wp_out *result = + (struct flash_stm32_ex_op_sector_wp_out *)out; + uint32_t change_mask; + int rc = 0, rc2 = 0; +#ifdef CONFIG_USERSPACE + bool syscall_trap = z_syscall_trap(); +#endif + + if (request != NULL) { +#ifdef CONFIG_USERSPACE + struct flash_stm32_ex_op_sector_wp_in in_copy; + + if (syscall_trap) { + Z_OOPS(z_user_from_copy(&in_copy, request, + sizeof(in_copy))); + request = &in_copy; + } +#endif + change_mask = request->enable_mask; + + if (!IS_ENABLED( + CONFIG_FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION)) { + change_mask |= request->disable_mask; + } + + rc = flash_stm32_option_bytes_lock(dev, false); + if (rc == 0) { + rc = flash_stm32_update_wp_sectors( + dev, change_mask, request->enable_mask); + } + + rc2 = flash_stm32_option_bytes_lock(dev, true); + if (!rc) { + rc = rc2; + } + } + + if (result != NULL) { +#ifdef CONFIG_USERSPACE + struct flash_stm32_ex_op_sector_wp_out out_copy; + + if (syscall_trap) { + result = &out_copy; + } +#endif + rc2 = flash_stm32_get_wp_sectors(dev, &result->protected_mask); + if (!rc) { + rc = rc2; + } + +#ifdef CONFIG_USERSPACE + if (syscall_trap) { + Z_OOPS(z_user_to_copy(out, result, sizeof(out_copy))); + } +#endif + } + + return rc; +} +#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ diff --git a/drivers/flash/flash_stm32f4x.c b/drivers/flash/flash_stm32f4x.c index c58ad3ad2a354..67c82e559028a 100644 --- a/drivers/flash/flash_stm32f4x.c +++ b/drivers/flash/flash_stm32f4x.c @@ -1,14 +1,18 @@ /* * Copyright (c) 2017 Linaro Limited + * Copyright (c) 2023 Google Inc * * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include + +#include #include #include +#include +#include + #include #include "flash_stm32.h" @@ -203,6 +207,69 @@ int flash_stm32_write_range(const struct device *dev, unsigned int offset, return rc; } +static __unused int write_optb(const struct device *dev, uint32_t mask, + uint32_t value) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + int rc; + + if (regs->OPTCR & FLASH_OPTCR_OPTLOCK) { + return -EIO; + } + + if ((regs->OPTCR & mask) == value) { + return 0; + } + + rc = flash_stm32_wait_flash_idle(dev); + if (rc < 0) { + return rc; + } + + regs->OPTCR = (regs->OPTCR & ~mask) | value; + regs->OPTCR |= FLASH_OPTCR_OPTSTRT; + + /* Make sure previous write is completed. */ + __DSB(); + + rc = flash_stm32_wait_flash_idle(dev); + if (rc < 0) { + return rc; + } + + return 0; +} + +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) +int flash_stm32_update_wp_sectors(const struct device *dev, + uint32_t changed_sectors, + uint32_t protected_sectors) +{ + changed_sectors <<= FLASH_OPTCR_nWRP_Pos; + protected_sectors <<= FLASH_OPTCR_nWRP_Pos; + + if ((changed_sectors & FLASH_OPTCR_nWRP_Msk) != changed_sectors) { + return -EINVAL; + } + + /* Sector is protected when bit == 0. Flip protected_sectors bits */ + protected_sectors = ~protected_sectors & changed_sectors; + + return write_optb(dev, changed_sectors, protected_sectors); +} + +int flash_stm32_get_wp_sectors(const struct device *dev, + uint32_t *protected_sectors) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + + *protected_sectors = + (~regs->OPTCR & FLASH_OPTCR_nWRP_Msk) >> FLASH_OPTCR_nWRP_Pos; + + return 0; +} +#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ + /* * Different SoC flash layouts are specified in across various * reference manuals, but the flash layout for a given number of diff --git a/include/zephyr/drivers/flash/stm32_flash_api_extensions.h b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h new file mode 100644 index 0000000000000..b180135d1538a --- /dev/null +++ b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Google Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +enum stm32_ex_ops { + /* + * STM32 sector write protection control. + * + * As an input this operation takes a structure with two sector masks, + * first mask is used to enable protection on sectors, while second mask + * is used to do the opposite. If both masks are 0, then protection will + * remain unchanged. If same sector is set on both masks, protection + * will be enabled. + * + * As an output, sector mask with enabled protection is returned. + * Input can be NULL if we only want to get protected sectors. + * Output can be NULL if not needed. + */ + FLASH_STM32_EX_OP_SECTOR_WP = FLASH_EX_OP_VENDOR_BASE, +}; + +#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) +struct flash_stm32_ex_op_sector_wp_in { + uint32_t enable_mask; + uint32_t disable_mask; +}; + +struct flash_stm32_ex_op_sector_wp_out { + uint32_t protected_mask; +}; +#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ From b34b8bed769882a900cfe15f1d2765c602588cd1 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Thu, 10 Nov 2022 15:33:12 +0100 Subject: [PATCH 2/6] drivers: flash: Introduce readout protection support for STM32F4 This patch adds flash readout protection support for STM32F4 devices family. These devices can enable protection on entire flash content. Readout protection functionality was exposed as vendor extended operation. To change readout protection state, caller should provide a structure which describes desired RDP state. Enabling readout protection permanently or disabling readout protection (changing from level 1 to level 0) is guarded by CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW and CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW respectively. Signed-off-by: Patryk Duda --- drivers/flash/Kconfig.stm32 | 24 ++++ drivers/flash/flash_stm32.c | 5 + drivers/flash/flash_stm32.h | 15 +++ drivers/flash/flash_stm32_ex_op.c | 57 ++++++++++ drivers/flash/flash_stm32f4x.c | 104 ++++++++++++++++++ .../flash/stm32_flash_api_extensions.h | 15 +++ 6 files changed, 220 insertions(+) diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index 5c5932ba40e14..fe28a043653b0 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -37,4 +37,28 @@ config FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION If enabled, all requests to disable flash write protection will be blocked. +config FLASH_STM32_READOUT_PROTECTION + bool "Extended operation for flash readout protection control" + depends on SOC_SERIES_STM32F4X + default n + help + Enables flash extended operation for enabling/disabling flash readout + protection. + +config FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW + bool "Allow disabling readout protection" + depends on FLASH_STM32_READOUT_PROTECTION + default n + help + With this option enabled it will be possible to disable readout + protection. On STM32 devices it will trigger flash mass erase! + +config FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW + bool "Allow enabling readout protection permanently" + depends on FLASH_STM32_READOUT_PROTECTION + default n + help + With this option enabled it will be possible to enable readout + protection permanently. + endif # SOC_FLASH_STM32 diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index 8f58625c9f939..75613e3051578 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -398,6 +398,11 @@ static int flash_stm32_ex_op(const struct device *dev, uint16_t code, rv = flash_stm32_ex_op_sector_wp(dev, in, out); break; #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) + case FLASH_STM32_EX_OP_RDP: + rv = flash_stm32_ex_op_rdp(dev, in, out); + break; +#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ } flash_stm32_sem_give(dev); diff --git a/drivers/flash/flash_stm32.h b/drivers/flash/flash_stm32.h index 3dee0425ef52c..1e0dd397c50e8 100644 --- a/drivers/flash/flash_stm32.h +++ b/drivers/flash/flash_stm32.h @@ -198,6 +198,9 @@ struct flash_stm32_priv { FLASH_STM32_SR_RDERR | \ FLASH_STM32_SR_PGPERR) +#define FLASH_STM32_RDP0 0xAA +#define FLASH_STM32_RDP1 0x55 +#define FLASH_STM32_RDP2 0xCC #ifdef CONFIG_FLASH_PAGE_LAYOUT static inline bool flash_stm32_range_exists(const struct device *dev, @@ -244,11 +247,23 @@ int flash_stm32_update_wp_sectors(const struct device *dev, int flash_stm32_get_wp_sectors(const struct device *dev, uint32_t *protected_sectors); #endif +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) + +int flash_stm32_update_rdp(const struct device *dev, bool enable, + bool permanent); + +int flash_stm32_get_rdp(const struct device *dev, bool *enabled, + bool *permanent); +#endif /* Flash extended operations */ #if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in, void *out); #endif +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) +int flash_stm32_ex_op_rdp(const struct device *dev, const uintptr_t in, + void *out); +#endif #endif /* ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_ */ diff --git a/drivers/flash/flash_stm32_ex_op.c b/drivers/flash/flash_stm32_ex_op.c index 430d8900cc80c..1e2d801ffa59d 100644 --- a/drivers/flash/flash_stm32_ex_op.c +++ b/drivers/flash/flash_stm32_ex_op.c @@ -83,3 +83,60 @@ int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in, return rc; } #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ + +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) +int flash_stm32_ex_op_rdp(const struct device *dev, const uintptr_t in, + void *out) +{ + const struct flash_stm32_ex_op_rdp *request = + (const struct flash_stm32_ex_op_rdp *)in; + struct flash_stm32_ex_op_rdp *result = + (struct flash_stm32_ex_op_rdp *)out; + +#ifdef CONFIG_USERSPACE + struct flash_stm32_ex_op_rdp copy; + bool syscall_trap = z_syscall_trap(); +#endif + int rc = 0, rc2 = 0; + + if (request != NULL) { +#ifdef CONFIG_USERSPACE + if (syscall_trap) { + Z_OOPS(z_user_from_copy(©, request, sizeof(copy))); + request = © + } +#endif + rc = flash_stm32_option_bytes_lock(dev, false); + if (rc == 0) { + rc = flash_stm32_update_rdp(dev, request->enable, + request->permanent); + } + + rc2 = flash_stm32_option_bytes_lock(dev, true); + if (!rc) { + rc = rc2; + } + } + + if (result != NULL) { +#ifdef CONFIG_USERSPACE + if (syscall_trap) { + result = © + } +#endif + rc2 = flash_stm32_get_rdp(dev, &result->enable, + &result->permanent); + if (!rc) { + rc = rc2; + } + +#ifdef CONFIG_USERSPACE + if (syscall_trap) { + Z_OOPS(z_user_to_copy(out, result, sizeof(copy))); + } +#endif + } + + return rc; +} +#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ diff --git a/drivers/flash/flash_stm32f4x.c b/drivers/flash/flash_stm32f4x.c index 67c82e559028a..2f96390fd6b20 100644 --- a/drivers/flash/flash_stm32f4x.c +++ b/drivers/flash/flash_stm32f4x.c @@ -17,6 +17,8 @@ #include "flash_stm32.h" +LOG_MODULE_REGISTER(flash_stm32f4x, CONFIG_FLASH_LOG_LEVEL); + bool flash_stm32_valid_range(const struct device *dev, off_t offset, uint32_t len, bool write) @@ -270,6 +272,108 @@ int flash_stm32_get_wp_sectors(const struct device *dev, } #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) +int flash_stm32_update_rdp(const struct device *dev, bool enable, + bool permanent) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + uint8_t current_level, target_level; + + current_level = + (regs->OPTCR & FLASH_OPTCR_RDP_Msk) >> FLASH_OPTCR_RDP_Pos; + target_level = current_level; + + /* + * 0xAA = RDP level 0 (no protection) + * 0xCC = RDP level 2 (permanent protection) + * others = RDP level 1 (protection active) + */ + switch (current_level) { + case FLASH_STM32_RDP2: + if (!enable || !permanent) { + __ASSERT(false, "RDP level 2 is permanent and can't be " + "changed!"); + return -ENOTSUP; + } + break; + case FLASH_STM32_RDP0: + if (enable) { + target_level = FLASH_STM32_RDP1; + if (permanent) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) + target_level = FLASH_STM32_RDP2; +#else + __ASSERT(false, + "Permanent readout protection (RDP " + "level 0 -> 2) not allowed"); + return -ENOTSUP; +#endif + } + } + break; + default: /* FLASH_STM32_RDP1 */ + if (enable && permanent) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW) + target_level = FLASH_STM32_RDP2; +#else + __ASSERT(false, "Permanent readout protection (RDP " + "level 1 -> 2) not allowed"); + return -ENOTSUP; +#endif + } + if (!enable) { +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW) + target_level = FLASH_STM32_RDP0; +#else + __ASSERT(false, "Disabling readout protection (RDP " + "level 1 -> 0) not allowed"); + return -EACCES; +#endif + } + } + + /* Update RDP level if needed */ + if (current_level != target_level) { + LOG_INF("RDP changed from 0x%02x to 0x%02x", current_level, + target_level); + + write_optb(dev, FLASH_OPTCR_RDP_Msk, + (uint32_t)target_level << FLASH_OPTCR_RDP_Pos); + } + return 0; +} + +int flash_stm32_get_rdp(const struct device *dev, bool *enabled, + bool *permanent) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + uint8_t current_level; + + current_level = + (regs->OPTCR & FLASH_OPTCR_RDP_Msk) >> FLASH_OPTCR_RDP_Pos; + + /* + * 0xAA = RDP level 0 (no protection) + * 0xCC = RDP level 2 (permanent protection) + * others = RDP level 1 (protection active) + */ + switch (current_level) { + case FLASH_STM32_RDP2: + *enabled = true; + *permanent = true; + break; + case FLASH_STM32_RDP0: + *enabled = false; + *permanent = false; + break; + default: /* FLASH_STM32_RDP1 */ + *enabled = true; + *permanent = false; + } + return 0; +} +#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ + /* * Different SoC flash layouts are specified in across various * reference manuals, but the flash layout for a given number of diff --git a/include/zephyr/drivers/flash/stm32_flash_api_extensions.h b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h index b180135d1538a..db2e86af7bcca 100644 --- a/include/zephyr/drivers/flash/stm32_flash_api_extensions.h +++ b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h @@ -21,6 +21,14 @@ enum stm32_ex_ops { * Output can be NULL if not needed. */ FLASH_STM32_EX_OP_SECTOR_WP = FLASH_EX_OP_VENDOR_BASE, + /* + * STM32 sector readout protection control. + * + * As an input this operation takes structure with information about + * desired RDP state. As an output the status after applying changes + * is returned. + */ + FLASH_STM32_EX_OP_RDP, }; #if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) @@ -33,3 +41,10 @@ struct flash_stm32_ex_op_sector_wp_out { uint32_t protected_mask; }; #endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */ + +#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION) +struct flash_stm32_ex_op_rdp { + bool enable; + bool permanent; +}; +#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ From dfd775ce7fce64a62385230b4079cf260ce316e2 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Thu, 8 Dec 2022 14:16:11 +0100 Subject: [PATCH 3/6] drivers: flash: Add support for defining custom RDP1 byte value This patch makes possible to choose custom byte which should be used to enable non-permanent readout protection (RDP1). Actually, any byte except 0xAA and 0xCC (which are used by RDP0 and RDP2 respectively) can be used to enable RDP1 but in multi-image environment, some other image could check if RDP1 is enabled by comparing it to some hardcoded value. If property is not defined, 0x55 will be used to enable RDP1. The default value comes from STM32 HAL. Signed-off-by: Patryk Duda --- drivers/flash/flash_stm32.h | 7 ++++++- .../flash_controller/st,stm32-flash-controller.yaml | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/flash/flash_stm32.h b/drivers/flash/flash_stm32.h index 1e0dd397c50e8..e6cf7011d8a79 100644 --- a/drivers/flash/flash_stm32.h +++ b/drivers/flash/flash_stm32.h @@ -199,8 +199,13 @@ struct flash_stm32_priv { FLASH_STM32_SR_PGPERR) #define FLASH_STM32_RDP0 0xAA -#define FLASH_STM32_RDP1 0x55 #define FLASH_STM32_RDP2 0xCC +#define FLASH_STM32_RDP1 \ + DT_PROP(DT_INST(0, st_stm32_flash_controller), st_rdp1_enable_byte) + +#if FLASH_STM32_RDP1 == FLASH_STM32_RDP0 || FLASH_STM32_RDP1 == FLASH_STM32_RDP2 +#error RDP1 byte has to be different than RDP0 and RDP2 byte +#endif #ifdef CONFIG_FLASH_PAGE_LAYOUT static inline bool flash_stm32_range_exists(const struct device *dev, diff --git a/dts/bindings/flash_controller/st,stm32-flash-controller.yaml b/dts/bindings/flash_controller/st,stm32-flash-controller.yaml index 5156f4be3595d..34a28036e4228 100644 --- a/dts/bindings/flash_controller/st,stm32-flash-controller.yaml +++ b/dts/bindings/flash_controller/st,stm32-flash-controller.yaml @@ -3,3 +3,15 @@ description: STM32 Family flash controller compatible: "st,stm32-flash-controller" include: flash-controller.yaml + +properties: + st,rdp1-enable-byte: + type: int + default: 0x55 + description: | + This property provides a byte which should used to enable non-permanent + readout protection (RDP1). Actually, any byte except 0xAA and 0xCC + (which are used by RDP0 and RDP2 respectively) can be used to enable + RDP1 but in multi-image environment, some other image could check if + RDP1 is enabled by comparing it to some hardcoded value. The byte has to + be different than 0xAA and 0xCC. From 644d998c84cec4c3d8e1a0a2a533b18d03c78827 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Wed, 15 Mar 2023 14:11:35 +0100 Subject: [PATCH 4/6] drivers: flash: Add ex ops for STM32 option/control register block Introduce flash extended operations that can be used to disable access to option and control registers until reset. Disabling access to these registers improves system security, because flash content (or protection settings) can't be changed even when exploit was found. On STM32 devices, registers can be locked until reset by writing wrong key during unlock procedure. It triggers a bus fault, so during the procedure we need to ignore faults and clear bus fault pending bit. Please note that option register disabling was implemented for devices that have OPTCR register (F2, F4, F7 and H7). Implementation on other devices requires more testing, since documentation is not precise enough. Disabling control register was implemented for devices that have CR register. Signed-off-by: Patryk Duda --- drivers/flash/Kconfig.stm32 | 9 +++ drivers/flash/flash_stm32.c | 69 +++++++++++++++++++ .../flash/stm32_flash_api_extensions.h | 16 +++++ 3 files changed, 94 insertions(+) diff --git a/drivers/flash/Kconfig.stm32 b/drivers/flash/Kconfig.stm32 index fe28a043653b0..55aa1c7dcca44 100644 --- a/drivers/flash/Kconfig.stm32 +++ b/drivers/flash/Kconfig.stm32 @@ -61,4 +61,13 @@ config FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW With this option enabled it will be possible to enable readout protection permanently. +config FLASH_STM32_BLOCK_REGISTERS + bool "Extended operation for blocking option and control registers" + default n + help + Enables flash extended operations that can be used to disable access + to option and control registers until reset. Disabling access to these + registers improves system security, because flash content (or + protection settings) can't be changed even when exploit was found. + endif # SOC_FLASH_STM32 diff --git a/drivers/flash/flash_stm32.c b/drivers/flash/flash_stm32.c index 75613e3051578..1e9c2762a2012 100644 --- a/drivers/flash/flash_stm32.c +++ b/drivers/flash/flash_stm32.c @@ -376,6 +376,67 @@ int flash_stm32_option_bytes_lock(const struct device *dev, bool enable) return 0; } +#if defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS) +static int flash_stm32_control_register_disable(const struct device *dev) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + +#if defined(FLASH_CR_LOCK) /* F0, F1, F2, F3, F4, F7, L4, G0, G4, H7, WB, WL \ + */ + /* + * Access to control register can be disabled by writing wrong key to + * the key register. Option register will remain disabled until reset. + * Writing wrong key causes a bus fault, so we need to set FAULTMASK to + * disable faults, and clear bus fault pending bit before enabling them + * again. + */ + regs->CR |= FLASH_CR_LOCK; + + __set_FAULTMASK(1); + regs->KEYR = 0xffffffff; + + /* Clear Bus Fault pending bit */ + SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk; + __set_FAULTMASK(0); + + return 0; +#else + ARG_UNUSED(regs); + + return -ENOTSUP; +#endif +} + +static int flash_stm32_option_bytes_disable(const struct device *dev) +{ + FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); + +#if defined(FLASH_OPTCR_OPTLOCK) /* F2, F4, F7 and H7 */ + /* + * Access to option register can be disabled by writing wrong key to + * the key register. Option register will remain disabled until reset. + * Writing wrong key causes a bus fault, so we need to set FAULTMASK to + * disable faults, and clear bus fault pending bit before enabling them + * again. + */ + regs->OPTCR |= FLASH_OPTCR_OPTLOCK; + + __set_FAULTMASK(1); + regs->OPTKEYR = 0xffffffff; + + /* Clear Bus Fault pending bit */ + SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTPENDED_Msk; + __set_FAULTMASK(0); + + return 0; +#else + ARG_UNUSED(regs); + + return -ENOTSUP; +#endif +} +#endif /* CONFIG_FLASH_STM32_BLOCK_REGISTERS */ + static const struct flash_parameters * flash_stm32_get_parameters(const struct device *dev) { @@ -403,6 +464,14 @@ static int flash_stm32_ex_op(const struct device *dev, uint16_t code, rv = flash_stm32_ex_op_rdp(dev, in, out); break; #endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */ +#if defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS) + case FLASH_STM32_EX_OP_BLOCK_OPTION_REG: + rv = flash_stm32_option_bytes_disable(dev); + break; + case FLASH_STM32_EX_OP_BLOCK_CONTROL_REG: + rv = flash_stm32_control_register_disable(dev); + break; +#endif /* CONFIG_FLASH_STM32_BLOCK_REGISTERS */ } flash_stm32_sem_give(dev); diff --git a/include/zephyr/drivers/flash/stm32_flash_api_extensions.h b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h index db2e86af7bcca..0c6c8fc88ad14 100644 --- a/include/zephyr/drivers/flash/stm32_flash_api_extensions.h +++ b/include/zephyr/drivers/flash/stm32_flash_api_extensions.h @@ -29,6 +29,22 @@ enum stm32_ex_ops { * is returned. */ FLASH_STM32_EX_OP_RDP, + /* + * STM32 block option register. + * + * This operation causes option register to be locked until next boot. + * After calling, it's not possible to change option bytes (WP, RDP, + * user bytes). + */ + FLASH_STM32_EX_OP_BLOCK_OPTION_REG, + /* + * STM32 block control register. + * + * This operation causes control register to be locked until next boot. + * After calling, it's not possible to perform basic operation like + * erasing or writing. + */ + FLASH_STM32_EX_OP_BLOCK_CONTROL_REG, }; #if defined(CONFIG_FLASH_STM32_WRITE_PROTECT) From f8159b0820956e1f7f3d700ea97b891c4dee15a2 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Mon, 20 Mar 2023 18:53:06 +0100 Subject: [PATCH 5/6] test: drivers: Move existing flash tests to flash/common directory After 'flash_ex_op' syscall was added we are able to expose vendor specific features to the user. This patch moves existing tests to 'tests/drivers/common'. New tests for vendor specific operations should go to vendor directory under 'tests/drivers/flash'. Signed-off-by: Patryk Duda --- tests/drivers/flash/{ => common}/CMakeLists.txt | 0 .../boards/b_u585i_iot02a_ns.overlay | 0 .../{ => common}/boards/disco_l475_iot1.conf | 0 .../{ => common}/boards/esp32c3_devkitm.conf | 0 .../flash/{ => common}/boards/icev_wireless.conf | 0 .../{ => common}/boards/mimxrt1060_evk.conf | 0 .../{ => common}/boards/mimxrt595_evk_cm33.conf | 0 .../{ => common}/boards/mimxrt685_evk_cm33.conf | 0 .../{ => common}/boards/nrf52840_flash_qspi.conf | 0 .../{ => common}/boards/nrf52840_flash_soc.conf | 0 .../boards/nrf52840_size_in_bytes.overlay | 0 .../boards/nrf52840dk_flash_spi.conf | 0 .../boards/nrf52840dk_mx25l51245g.overlay | 0 .../boards/nrf52840dk_mx25r_high_perf.overlay | 0 .../{ => common}/boards/xmc45_relax_kit.conf | 0 tests/drivers/flash/{ => common}/prj.conf | 0 tests/drivers/flash/{ => common}/src/main.c | 0 tests/drivers/flash/{ => common}/testcase.yaml | 16 ++++++++-------- 18 files changed, 8 insertions(+), 8 deletions(-) rename tests/drivers/flash/{ => common}/CMakeLists.txt (100%) rename tests/drivers/flash/{ => common}/boards/b_u585i_iot02a_ns.overlay (100%) rename tests/drivers/flash/{ => common}/boards/disco_l475_iot1.conf (100%) rename tests/drivers/flash/{ => common}/boards/esp32c3_devkitm.conf (100%) rename tests/drivers/flash/{ => common}/boards/icev_wireless.conf (100%) rename tests/drivers/flash/{ => common}/boards/mimxrt1060_evk.conf (100%) rename tests/drivers/flash/{ => common}/boards/mimxrt595_evk_cm33.conf (100%) rename tests/drivers/flash/{ => common}/boards/mimxrt685_evk_cm33.conf (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840_flash_qspi.conf (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840_flash_soc.conf (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840_size_in_bytes.overlay (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840dk_flash_spi.conf (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840dk_mx25l51245g.overlay (100%) rename tests/drivers/flash/{ => common}/boards/nrf52840dk_mx25r_high_perf.overlay (100%) rename tests/drivers/flash/{ => common}/boards/xmc45_relax_kit.conf (100%) rename tests/drivers/flash/{ => common}/prj.conf (100%) rename tests/drivers/flash/{ => common}/src/main.c (100%) rename tests/drivers/flash/{ => common}/testcase.yaml (87%) diff --git a/tests/drivers/flash/CMakeLists.txt b/tests/drivers/flash/common/CMakeLists.txt similarity index 100% rename from tests/drivers/flash/CMakeLists.txt rename to tests/drivers/flash/common/CMakeLists.txt diff --git a/tests/drivers/flash/boards/b_u585i_iot02a_ns.overlay b/tests/drivers/flash/common/boards/b_u585i_iot02a_ns.overlay similarity index 100% rename from tests/drivers/flash/boards/b_u585i_iot02a_ns.overlay rename to tests/drivers/flash/common/boards/b_u585i_iot02a_ns.overlay diff --git a/tests/drivers/flash/boards/disco_l475_iot1.conf b/tests/drivers/flash/common/boards/disco_l475_iot1.conf similarity index 100% rename from tests/drivers/flash/boards/disco_l475_iot1.conf rename to tests/drivers/flash/common/boards/disco_l475_iot1.conf diff --git a/tests/drivers/flash/boards/esp32c3_devkitm.conf b/tests/drivers/flash/common/boards/esp32c3_devkitm.conf similarity index 100% rename from tests/drivers/flash/boards/esp32c3_devkitm.conf rename to tests/drivers/flash/common/boards/esp32c3_devkitm.conf diff --git a/tests/drivers/flash/boards/icev_wireless.conf b/tests/drivers/flash/common/boards/icev_wireless.conf similarity index 100% rename from tests/drivers/flash/boards/icev_wireless.conf rename to tests/drivers/flash/common/boards/icev_wireless.conf diff --git a/tests/drivers/flash/boards/mimxrt1060_evk.conf b/tests/drivers/flash/common/boards/mimxrt1060_evk.conf similarity index 100% rename from tests/drivers/flash/boards/mimxrt1060_evk.conf rename to tests/drivers/flash/common/boards/mimxrt1060_evk.conf diff --git a/tests/drivers/flash/boards/mimxrt595_evk_cm33.conf b/tests/drivers/flash/common/boards/mimxrt595_evk_cm33.conf similarity index 100% rename from tests/drivers/flash/boards/mimxrt595_evk_cm33.conf rename to tests/drivers/flash/common/boards/mimxrt595_evk_cm33.conf diff --git a/tests/drivers/flash/boards/mimxrt685_evk_cm33.conf b/tests/drivers/flash/common/boards/mimxrt685_evk_cm33.conf similarity index 100% rename from tests/drivers/flash/boards/mimxrt685_evk_cm33.conf rename to tests/drivers/flash/common/boards/mimxrt685_evk_cm33.conf diff --git a/tests/drivers/flash/boards/nrf52840_flash_qspi.conf b/tests/drivers/flash/common/boards/nrf52840_flash_qspi.conf similarity index 100% rename from tests/drivers/flash/boards/nrf52840_flash_qspi.conf rename to tests/drivers/flash/common/boards/nrf52840_flash_qspi.conf diff --git a/tests/drivers/flash/boards/nrf52840_flash_soc.conf b/tests/drivers/flash/common/boards/nrf52840_flash_soc.conf similarity index 100% rename from tests/drivers/flash/boards/nrf52840_flash_soc.conf rename to tests/drivers/flash/common/boards/nrf52840_flash_soc.conf diff --git a/tests/drivers/flash/boards/nrf52840_size_in_bytes.overlay b/tests/drivers/flash/common/boards/nrf52840_size_in_bytes.overlay similarity index 100% rename from tests/drivers/flash/boards/nrf52840_size_in_bytes.overlay rename to tests/drivers/flash/common/boards/nrf52840_size_in_bytes.overlay diff --git a/tests/drivers/flash/boards/nrf52840dk_flash_spi.conf b/tests/drivers/flash/common/boards/nrf52840dk_flash_spi.conf similarity index 100% rename from tests/drivers/flash/boards/nrf52840dk_flash_spi.conf rename to tests/drivers/flash/common/boards/nrf52840dk_flash_spi.conf diff --git a/tests/drivers/flash/boards/nrf52840dk_mx25l51245g.overlay b/tests/drivers/flash/common/boards/nrf52840dk_mx25l51245g.overlay similarity index 100% rename from tests/drivers/flash/boards/nrf52840dk_mx25l51245g.overlay rename to tests/drivers/flash/common/boards/nrf52840dk_mx25l51245g.overlay diff --git a/tests/drivers/flash/boards/nrf52840dk_mx25r_high_perf.overlay b/tests/drivers/flash/common/boards/nrf52840dk_mx25r_high_perf.overlay similarity index 100% rename from tests/drivers/flash/boards/nrf52840dk_mx25r_high_perf.overlay rename to tests/drivers/flash/common/boards/nrf52840dk_mx25r_high_perf.overlay diff --git a/tests/drivers/flash/boards/xmc45_relax_kit.conf b/tests/drivers/flash/common/boards/xmc45_relax_kit.conf similarity index 100% rename from tests/drivers/flash/boards/xmc45_relax_kit.conf rename to tests/drivers/flash/common/boards/xmc45_relax_kit.conf diff --git a/tests/drivers/flash/prj.conf b/tests/drivers/flash/common/prj.conf similarity index 100% rename from tests/drivers/flash/prj.conf rename to tests/drivers/flash/common/prj.conf diff --git a/tests/drivers/flash/src/main.c b/tests/drivers/flash/common/src/main.c similarity index 100% rename from tests/drivers/flash/src/main.c rename to tests/drivers/flash/common/src/main.c diff --git a/tests/drivers/flash/testcase.yaml b/tests/drivers/flash/common/testcase.yaml similarity index 87% rename from tests/drivers/flash/testcase.yaml rename to tests/drivers/flash/common/testcase.yaml index ac3a5affec93c..a24fad02cc19e 100644 --- a/tests/drivers/flash/testcase.yaml +++ b/tests/drivers/flash/common/testcase.yaml @@ -1,19 +1,19 @@ common: tags: drivers flash tests: - drivers.flash.nrf_qspi_nor: + drivers.flash.common.nrf_qspi_nor: platform_allow: nrf52840dk_nrf52840 extra_args: OVERLAY_CONFIG=boards/nrf52840_flash_qspi.conf integration_platforms: - nrf52840dk_nrf52840 - drivers.flash.nrf_qspi_nor.size_in_bytes: + drivers.flash.common.nrf_qspi_nor.size_in_bytes: platform_allow: nrf52840dk_nrf52840 extra_args: OVERLAY_CONFIG=boards/nrf52840_flash_qspi.conf DTC_OVERLAY_FILE=boards/nrf52840_size_in_bytes.overlay integration_platforms: - nrf52840dk_nrf52840 - drivers.flash.nrf_qspi_nor_4B_addr: + drivers.flash.common.nrf_qspi_nor_4B_addr: platform_allow: nrf52840dk_nrf52840 extra_args: OVERLAY_CONFIG=boards/nrf52840_flash_qspi.conf DTC_OVERLAY_FILE=boards/nrf52840dk_mx25l51245g.overlay @@ -21,16 +21,16 @@ tests: fixture: external_flash integration_platforms: - nrf52840dk_nrf52840 - drivers.flash.soc_flash_nrf: + drivers.flash.common.soc_flash_nrf: platform_allow: nrf52840dk_nrf52840 extra_args: OVERLAY_CONFIG=boards/nrf52840_flash_soc.conf integration_platforms: - nrf52840dk_nrf52840 - drivers.flash.default: + drivers.flash.common.default: filter: ((CONFIG_FLASH_HAS_DRIVER_ENABLED and not CONFIG_TRUSTED_EXECUTION_NONSECURE) and dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions")) - drivers.flash.tfm_ns: + drivers.flash.common.tfm_ns: build_only: true filter: (CONFIG_FLASH_HAS_DRIVER_ENABLED and CONFIG_TRUSTED_EXECUTION_NONSECURE and dt_label_with_parent_compat_enabled("slot1_ns_partition", "fixed-partitions")) @@ -38,7 +38,7 @@ tests: platform_allow: mimxrt1060_evk it8xxx2_evb mimxrt685_evk_cm33 mimxrt595_evk_cm33 integration_platforms: - mimxrt1060_evk - drivers.flash.stm32: + drivers.flash.common.stm32: platform_allow: nucleo_f103rb nucleo_f207zg stm32f3_disco nucleo_f429zi stm32f746g_disco nucleo_g0b1re nucleo_g474re nucleo_h743zi nucleo_l152re disco_l475_iot1 nucleo_wb55rg nucleo_wl55jc stm32l562e_dk stm32l562e_dk_ns @@ -47,7 +47,7 @@ tests: filter: (dt_compat_enabled("st,stm32-flash-controller") or dt_compat_enabled("st,stm32h7-flash-controller")) and dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions") - drivers.flash.mx25r_high_perf: + drivers.flash.common.mx25r_high_perf: platform_allow: nrf52840dk_nrf52840 extra_args: OVERLAY_CONFIG=boards/nrf52840dk_flash_spi.conf From 42cdf7218fabfb78ac358b17397540d6edc769a4 Mon Sep 17 00:00:00 2001 From: Patryk Duda Date: Mon, 20 Mar 2023 19:17:44 +0100 Subject: [PATCH 6/6] drivers: flash: Add tests for STM32 extended operations Introduced tests covers following features: - Write protection - enabling, disabling, confirming that it works. - Readout protection - checking current status. These features are implemented on STM32F4 boards, so we only allow these boards. Signed-off-by: Patryk Duda --- tests/drivers/flash/stm32/CMakeLists.txt | 8 + .../stm32/boards/google_dragonclaw.overlay | 19 ++ tests/drivers/flash/stm32/prj.conf | 11 ++ tests/drivers/flash/stm32/src/main.c | 171 ++++++++++++++++++ tests/drivers/flash/stm32/testcase.yaml | 7 + 5 files changed, 216 insertions(+) create mode 100644 tests/drivers/flash/stm32/CMakeLists.txt create mode 100644 tests/drivers/flash/stm32/boards/google_dragonclaw.overlay create mode 100644 tests/drivers/flash/stm32/prj.conf create mode 100644 tests/drivers/flash/stm32/src/main.c create mode 100644 tests/drivers/flash/stm32/testcase.yaml diff --git a/tests/drivers/flash/stm32/CMakeLists.txt b/tests/drivers/flash/stm32/CMakeLists.txt new file mode 100644 index 0000000000000..2d8bb41ee9a84 --- /dev/null +++ b/tests/drivers/flash/stm32/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(stm32_extended_operations) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/drivers/flash/stm32/boards/google_dragonclaw.overlay b/tests/drivers/flash/stm32/boards/google_dragonclaw.overlay new file mode 100644 index 0000000000000..e9bbc1b87bf12 --- /dev/null +++ b/tests/drivers/flash/stm32/boards/google_dragonclaw.overlay @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Google Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserve last 4KiB of flash for storage_partition. */ + storage_partition: partition@ff000 { + label = "storage"; + reg = <0x000ff000 DT_SIZE_K(4)>; + }; + }; +}; diff --git a/tests/drivers/flash/stm32/prj.conf b/tests/drivers/flash/stm32/prj.conf new file mode 100644 index 0000000000000..4f43063f0cd7c --- /dev/null +++ b/tests/drivers/flash/stm32/prj.conf @@ -0,0 +1,11 @@ +CONFIG_TEST=y +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y + +CONFIG_FLASH=y +CONFIG_FLASH_EX_OP_ENABLED=y +CONFIG_FLASH_STM32_READOUT_PROTECTION=y +CONFIG_FLASH_STM32_WRITE_PROTECT=y +CONFIG_FLASH_STM32_BLOCK_REGISTERS=y + +CONFIG_MAIN_STACK_SIZE=2048 diff --git a/tests/drivers/flash/stm32/src/main.c b/tests/drivers/flash/stm32/src/main.c new file mode 100644 index 0000000000000..b5770dd6b7fbd --- /dev/null +++ b/tests/drivers/flash/stm32/src/main.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023 Google Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define TEST_AREA storage_partition +#define TEST_AREA_OFFSET FIXED_PARTITION_OFFSET(TEST_AREA) +#define TEST_AREA_SIZE FIXED_PARTITION_SIZE(TEST_AREA) +#define TEST_AREA_MAX (TEST_AREA_OFFSET + TEST_AREA_SIZE) +#define TEST_AREA_DEVICE FIXED_PARTITION_DEVICE(TEST_AREA) + +#define EXPECTED_SIZE 512 + +static const struct device *const flash_dev = TEST_AREA_DEVICE; +static const struct flash_parameters *flash_params; +static uint32_t sector_mask; +static uint8_t __aligned(4) expected[EXPECTED_SIZE]; + +static int sector_mask_from_offset(const struct device *dev, off_t offset, + size_t size, uint32_t *sector_mask) +{ + struct flash_pages_info start_page, end_page; + + if (flash_get_page_info_by_offs(dev, offset, &start_page) || + flash_get_page_info_by_offs(dev, offset + size - 1, &end_page)) { + return -EINVAL; + } + + *sector_mask = ((1UL << (end_page.index + 1)) - 1) & + ~((1UL << start_page.index) - 1); + + return 0; +} + +static void *flash_stm32_setup(void) +{ + struct flash_stm32_ex_op_sector_wp_out wp_status; + struct flash_stm32_ex_op_sector_wp_in wp_request; + uint8_t buf[EXPECTED_SIZE]; + bool is_buf_clear = true; + int rc; + + /* Check if tested region fits in flash. */ + zassert_true((TEST_AREA_OFFSET + EXPECTED_SIZE) < TEST_AREA_MAX, + "Test area exceeds flash size"); + + zassert_true(device_is_ready(flash_dev)); + + flash_params = flash_get_parameters(flash_dev); + + rc = sector_mask_from_offset(flash_dev, TEST_AREA_OFFSET, EXPECTED_SIZE, + §or_mask); + zassert_equal(rc, 0, "Cannot get sector mask"); + + TC_PRINT("Sector mask for offset 0x%x size 0x%x is 0x%x\n", + TEST_AREA_OFFSET, EXPECTED_SIZE, sector_mask); + + /* Check if region is not write protected. */ + rc = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_SECTOR_WP, + (uintptr_t)NULL, &wp_status); + zassert_equal(rc, 0, "Cannot get write protect status"); + + TC_PRINT("Protected sectors: 0x%x\n", wp_status.protected_mask); + + /* Remove protection if needed. */ + if (wp_status.protected_mask & sector_mask) { + TC_PRINT("Removing write protection\n"); + wp_request.disable_mask = sector_mask; + + rc = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_SECTOR_WP, + (uintptr_t)&wp_request, NULL); + zassert_equal(rc, 0, "Cannot remove write protection"); + } + + /* Check if test region is not empty. */ + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + /* Check if flash is cleared. */ + for (off_t i = 0; i < EXPECTED_SIZE; i++) { + if (buf[i] != flash_params->erase_value) { + is_buf_clear = false; + break; + } + } + + if (!is_buf_clear) { + TC_PRINT("Test area is not empty. Clear it before continuing.\n"); + rc = flash_erase(flash_dev, TEST_AREA_OFFSET, EXPECTED_SIZE); + zassert_equal(rc, 0, "Flash memory not properly erased"); + } + + /* Fill test buffer with some pattern. */ + for (int i = 0; i < EXPECTED_SIZE; i++) { + expected[i] = i; + } + + return NULL; +} + +ZTEST(flash_stm32, test_stm32_write_protection) +{ + struct flash_stm32_ex_op_sector_wp_in wp_request; + uint8_t buf[EXPECTED_SIZE]; + int rc; + + TC_PRINT("Enabling write protection..."); + wp_request.disable_mask = 0; + wp_request.enable_mask = sector_mask; + + rc = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_SECTOR_WP, + (uintptr_t)&wp_request, NULL); + zassert_equal(rc, 0, "Cannot enable write protection"); + TC_PRINT("Done\n"); + + rc = flash_write(flash_dev, TEST_AREA_OFFSET, expected, EXPECTED_SIZE); + zassert_not_equal(rc, 0, "Write suceeded"); + TC_PRINT("Write failed as expected, error %d\n", rc); + + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + for (off_t i = 0; i < EXPECTED_SIZE; i++) { + zassert_true(buf[i] == flash_params->erase_value, + "Buffer is not empty after write with protected " + "sectors"); + } + + TC_PRINT("Disabling write protection..."); + wp_request.disable_mask = sector_mask; + wp_request.enable_mask = 0; + + rc = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_SECTOR_WP, + (uintptr_t)&wp_request, NULL); + zassert_equal(rc, 0, "Cannot disable write protection"); + TC_PRINT("Done\n"); + + rc = flash_write(flash_dev, TEST_AREA_OFFSET, expected, EXPECTED_SIZE); + zassert_equal(rc, 0, "Write failed"); + TC_PRINT("Write suceeded\n"); + + rc = flash_read(flash_dev, TEST_AREA_OFFSET, buf, EXPECTED_SIZE); + zassert_equal(rc, 0, "Cannot read flash"); + + zassert_equal(memcmp(buf, expected, EXPECTED_SIZE), 0, + "Read data doesn't match expected data"); +} + +ZTEST(flash_stm32, test_stm32_readout_protection_disabled) +{ + struct flash_stm32_ex_op_rdp rdp_status; + int rc; + + rc = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_RDP, (uintptr_t)NULL, + &rdp_status); + zassert_equal(rc, 0, "Failed to get RDP status"); + zassert_false(rdp_status.enable, "RDP is enabled"); + zassert_false(rdp_status.permanent, "RDP is enabled permanently"); + + TC_PRINT("RDP is disabled\n"); +} + +ZTEST_SUITE(flash_stm32, NULL, flash_stm32_setup, NULL, NULL, NULL); diff --git a/tests/drivers/flash/stm32/testcase.yaml b/tests/drivers/flash/stm32/testcase.yaml new file mode 100644 index 0000000000000..a438934bd75f9 --- /dev/null +++ b/tests/drivers/flash/stm32/testcase.yaml @@ -0,0 +1,7 @@ +common: + tags: drivers flash +tests: + drivers.flash.stm32.default: + platform_allow: nucleo_f429zi google_dragonclaw + filter: dt_compat_enabled("st,stm32-flash-controller") and + dt_label_with_parent_compat_enabled("storage_partition", "fixed-partitions")