Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
55 changes: 55 additions & 0 deletions drivers/flash/Kconfig.stm32
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,6 +14,60 @@ 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.

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.

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
169 changes: 169 additions & 0 deletions drivers/flash/flash_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -13,6 +14,7 @@

#include <string.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/drivers/flash/stm32_flash_api_extensions.h>
#include <zephyr/init.h>
#include <soc.h>
#include <stm32_ll_bus.h>
Expand Down Expand Up @@ -306,6 +308,135 @@ 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;
}

#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)
{
Expand All @@ -314,6 +445,41 @@ 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 */
#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 */
#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);

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
Expand All @@ -335,6 +501,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)
Expand Down
41 changes: 41 additions & 0 deletions drivers/flash/flash_stm32.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
/*
* Copyright (c) 2017 Linaro Limited
* Copyright (c) 2017 BayLibre, SAS.
* Copyright (c) 2023 Google Inc
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_
#define ZEPHYR_DRIVERS_FLASH_FLASH_STM32_H_

#include <zephyr/drivers/flash.h>

#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 <zephyr/drivers/clock_control.h>
Expand Down Expand Up @@ -195,6 +198,14 @@ struct flash_stm32_priv {
FLASH_STM32_SR_RDERR | \
FLASH_STM32_SR_PGPERR)

#define FLASH_STM32_RDP0 0xAA
#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,
Expand All @@ -220,6 +231,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 */
Expand All @@ -230,4 +243,32 @@ 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
#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_ */
Loading