cpu/stm32: Add hardening changes to stm32
Initialize STM32 RDP in a glitch-resistant fashion to prevent debugger use when restrictions are set by the designer.
This commit is contained in:
parent
a85853ae30
commit
ee832148b3
@ -39,6 +39,26 @@ orsource "kconfigs/*/Kconfig"
|
|||||||
orsource "kconfigs/*/Kconfig.lines"
|
orsource "kconfigs/*/Kconfig.lines"
|
||||||
orsource "kconfigs/*/Kconfig.models"
|
orsource "kconfigs/*/Kconfig.models"
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "ReaDout Protection level"
|
||||||
|
default RDP0
|
||||||
|
help
|
||||||
|
Set minimum running RDP level.
|
||||||
|
RDP0 is full debug permissions, RDP1 disables read from Flash but
|
||||||
|
otherwise leaves debug enabled, RDP2 disables JTAG completely. If
|
||||||
|
there is a mismatch between desired RDP level here and RDP level
|
||||||
|
set on the chip, early cpu init will hang. This ensures production
|
||||||
|
devices with the wrong RDP level, by fault or malace intent, will
|
||||||
|
not run. See cpu manual for further details on RDP.
|
||||||
|
depends on (CPU_FAM_F1 || CPU_FAM_F2 || CPU_FAM_F3 || CPU_FAM_F4 || CPU_FAM_F5 || CPU_FAM_F6 || CPU_FAM_F7)
|
||||||
|
config RDP0
|
||||||
|
bool "RDP0"
|
||||||
|
config RDP1
|
||||||
|
bool "RDP1"
|
||||||
|
config RDP2
|
||||||
|
bool "RDP2"
|
||||||
|
endchoice
|
||||||
|
|
||||||
if TEST_KCONFIG
|
if TEST_KCONFIG
|
||||||
|
|
||||||
rsource "periph/Kconfig"
|
rsource "periph/Kconfig"
|
||||||
|
|||||||
@ -36,6 +36,16 @@ endif
|
|||||||
include $(RIOTCPU)/stm32/stm32_line.mk
|
include $(RIOTCPU)/stm32/stm32_line.mk
|
||||||
CPU_LINE ?= $(shell echo $(CPU_MODEL) | cut -c -9 | tr 'a-z-' 'A-Z_')xx
|
CPU_LINE ?= $(shell echo $(CPU_MODEL) | cut -c -9 | tr 'a-z-' 'A-Z_')xx
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_RDP0),y)
|
||||||
|
CFLAGS += -DCONFIG_STM32_RDP=0
|
||||||
|
endif
|
||||||
|
ifeq ($(CONFIG_RDP1),y)
|
||||||
|
CFLAGS += -DCONFIG_STM32_RDP=1
|
||||||
|
endif
|
||||||
|
ifeq ($(CONFIG_RDP2),y)
|
||||||
|
CFLAGS += -DCONFIG_STM32_RDP=2
|
||||||
|
endif
|
||||||
|
|
||||||
# Set CFLAGS
|
# Set CFLAGS
|
||||||
CFLAGS += -D$(CPU_LINE) -DCPU_LINE_$(CPU_LINE)
|
CFLAGS += -D$(CPU_LINE) -DCPU_LINE_$(CPU_LINE)
|
||||||
CFLAGS += -DSTM32_FLASHSIZE=$(FLASHSIZE)U
|
CFLAGS += -DSTM32_FLASHSIZE=$(FLASHSIZE)U
|
||||||
|
|||||||
@ -39,6 +39,7 @@
|
|||||||
#include "periph/init.h"
|
#include "periph/init.h"
|
||||||
#include "periph/gpio.h"
|
#include "periph/gpio.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
|
#include "pm_layered.h"
|
||||||
|
|
||||||
#if defined (CPU_FAM_STM32L4) || defined (CPU_FAM_STM32G4) || \
|
#if defined (CPU_FAM_STM32L4) || defined (CPU_FAM_STM32G4) || \
|
||||||
defined(CPU_FAM_STM32L5)
|
defined(CPU_FAM_STM32L5)
|
||||||
@ -152,6 +153,138 @@ static void _gpio_init_ain(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the value of a register in a glitch resistant fashion
|
||||||
|
*
|
||||||
|
* This very teniously avoids optimization, even optimized it's better than
|
||||||
|
* nothing but periodic review should establish that it doesn't get optimized.
|
||||||
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
|
static inline uint32_t _multi_read_reg32(volatile uint32_t *addr, bool *glitch)
|
||||||
|
{
|
||||||
|
uint32_t value = *addr;
|
||||||
|
// cppcheck-suppress duplicateExpression
|
||||||
|
// cppcheck-suppress knownConditionTrueFalse
|
||||||
|
if (*addr != value || *addr != value) {
|
||||||
|
/* (reason: volatile pointer forces multiple reads for glitch resistance,
|
||||||
|
glitch may force different value) */
|
||||||
|
*glitch = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check RDP level is what the designer intended.
|
||||||
|
*
|
||||||
|
* RDP stands for "ReaDout Protection."
|
||||||
|
*
|
||||||
|
* The STM32L4 readout protection feature offers three levels of protection
|
||||||
|
* for all SRAM2 and Flash memory as well as the backup registers:
|
||||||
|
*
|
||||||
|
* - Level 0 (RDP0) means “no protection”. This is the factory default. Read,
|
||||||
|
* Write and Erase operations are permitted in the SRAM2 and Flash memory
|
||||||
|
* as well as the backup registers. Option bytes are changeable in Level 0.
|
||||||
|
*
|
||||||
|
* - Level 1 (RDP1) ensures read protection of the chip’s memories which
|
||||||
|
* includes the Flash memory and the backup registers as well as the SRAM2
|
||||||
|
* content. Whenever a debugger access is detected or Boot mode is not set
|
||||||
|
* to a Flash memory area, any access to the Flash memory, the backup
|
||||||
|
* registers or to the SRAM2 generates a system hard fault which blocks all
|
||||||
|
* code execution until the next power-on reset. Option bytes can still be
|
||||||
|
* modified in Level 1.
|
||||||
|
*
|
||||||
|
* - Level 2 (RDP2) provides the same protection features for the SRAM2,
|
||||||
|
* Flash memory and Backup registers as described for Level 1. However,
|
||||||
|
* there are three major differences. The JTAG/SWD debugger connection is
|
||||||
|
* disabled (even at the ST factory, to ensure that there are no
|
||||||
|
* backdoors), the Boot mode is forced to User Flash memory REGARDLESS of
|
||||||
|
* what the boot 0/1 settings are, and Level 2 is permanent. Once set to
|
||||||
|
* Level 2, there is no going back; RDP/WRP option bytes can no longer be
|
||||||
|
* changed, as well as ALL the other option bytes.
|
||||||
|
*
|
||||||
|
* By way of background, changing the level of RDP protection is only
|
||||||
|
* permitted when the current protection level is ‘1’. Changing the protection
|
||||||
|
* level from '1' to '0' should automatically erase the entire user flash
|
||||||
|
* memory, SRAM2 and backup registers.
|
||||||
|
*
|
||||||
|
* The issue is that while Level 0 is 0xAA and Level 2 is 0xCC, Level 1 is any
|
||||||
|
* other number. So when OxCC is set and the chip is physically or
|
||||||
|
* electrically perturbed, flipping any bit will "fool" the CPU into thinking
|
||||||
|
* that it is in Level 1, allowing JTAG access and the changing of option
|
||||||
|
* bits.
|
||||||
|
*
|
||||||
|
* Think of this as a STM32-specific version of the Rowhammer attack.
|
||||||
|
*
|
||||||
|
* RDP may not be set correctly due to manufacturing error, glitch or
|
||||||
|
* intentional attack. It's done thrice to reduce the probablility of a
|
||||||
|
* glitch attack succeeding amongst all of the multireads desgned to make it
|
||||||
|
* tougher.
|
||||||
|
*
|
||||||
|
* This would be best served with a random delay at the beginning of the
|
||||||
|
* function. But a consistent strategy for all chips is tough.
|
||||||
|
*
|
||||||
|
* To set the RDP bytes, the J-Flash utility or the STM32 Unlock (from J-Link)
|
||||||
|
* utility, both provided by the manufacturer.
|
||||||
|
*
|
||||||
|
* You can also set the option bytes from code:
|
||||||
|
*
|
||||||
|
* 1. Unlock the option bytes by writing the correct keys to FLASH_OPTKEYR and
|
||||||
|
* clearing OPTLOCK
|
||||||
|
* 2. Set the desired option values in FLASH_OPTCR
|
||||||
|
* 3. Set OPTSTRT in FLASH_OPTCR
|
||||||
|
*
|
||||||
|
* This is the generic procedure for all option bytes. However, setting the
|
||||||
|
* RDP level in this fashion will immediately lock the CPU and force a reboot
|
||||||
|
* (and in some cases a clearing of the flash memory).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* RDP only defined for particular families. Kconfig sets this as necessary */
|
||||||
|
#if defined(STM32_OPTION_BYTES)
|
||||||
|
|
||||||
|
#ifndef CONFIG_STM32_RDP
|
||||||
|
#define CONFIG_STM32_RDP 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool _rdp_ok(void)
|
||||||
|
{
|
||||||
|
if (CONFIG_STM32_RDP == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool glitch = false;
|
||||||
|
uint32_t read1 = _multi_read_reg32(STM32_OPTION_BYTES, &glitch);
|
||||||
|
uint32_t read2 = _multi_read_reg32(STM32_OPTION_BYTES, &glitch);
|
||||||
|
uint32_t read3 = _multi_read_reg32(STM32_OPTION_BYTES, &glitch);
|
||||||
|
if (glitch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (CONFIG_STM32_RDP) {
|
||||||
|
case 1:
|
||||||
|
return GET_RDP(read1) == 0xAA ||
|
||||||
|
GET_RDP(read2) == 0xAA ||
|
||||||
|
GET_RDP(read3) == 0xAA;
|
||||||
|
case 2:
|
||||||
|
return GET_RDP(read1) != 0xCC ||
|
||||||
|
GET_RDP(read2) != 0xCC ||
|
||||||
|
GET_RDP(read3) != 0xCC;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _rdp_check(void)
|
||||||
|
{
|
||||||
|
if (!_rdp_ok()) {
|
||||||
|
/* halt execution */
|
||||||
|
while (1) {
|
||||||
|
pm_set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* STM32_OPTION_BYTES */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize HW debug pins for Sub-GHz Radio
|
* @brief Initialize HW debug pins for Sub-GHz Radio
|
||||||
*/
|
*/
|
||||||
@ -212,6 +345,7 @@ void cpu_init(void)
|
|||||||
defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7) || \
|
defined(CPU_FAM_STM32F4) || defined(CPU_FAM_STM32F7) || \
|
||||||
defined(CPU_FAM_STM32L1)
|
defined(CPU_FAM_STM32L1)
|
||||||
_gpio_init_ain();
|
_gpio_init_ain();
|
||||||
|
_rdp_check();
|
||||||
#endif
|
#endif
|
||||||
#if !defined(CPU_FAM_STM32MP1) || IS_USED(MODULE_STM32MP1_ENG_MODE)
|
#if !defined(CPU_FAM_STM32MP1) || IS_USED(MODULE_STM32MP1_ENG_MODE)
|
||||||
/* initialize the system clock as configured in the periph_conf.h */
|
/* initialize the system clock as configured in the periph_conf.h */
|
||||||
|
|||||||
@ -41,6 +41,12 @@ extern "C" {
|
|||||||
#define STM32_BOOTLOADER_ADDR (0x1FFFC400)
|
#define STM32_BOOTLOADER_ADDR (0x1FFFC400)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFFF800)
|
||||||
|
#define GET_RDP(x) (x & 0xFF)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override ADC resolution values
|
* @brief Override ADC resolution values
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -33,6 +33,12 @@ extern "C" {
|
|||||||
#define STM32_BOOTLOADER_ADDR (0x1FFFF000)
|
#define STM32_BOOTLOADER_ADDR (0x1FFFF000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFFF800)
|
||||||
|
#define GET_RDP(x) (x & 0xFF)
|
||||||
|
|
||||||
#endif /* ndef DOXYGEN */
|
#endif /* ndef DOXYGEN */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -38,6 +38,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define STM32_BOOTLOADER_ADDR (0x1FFF0000)
|
#define STM32_BOOTLOADER_ADDR (0x1FFF0000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFFC000)
|
||||||
|
#define GET_RDP(x) ((x & 0xFF00) >> 8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override the ADC resolution configuration
|
* @brief Override the ADC resolution configuration
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -49,6 +49,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define STM32_BOOTLOADER_ADDR (0x1FFFD800)
|
#define STM32_BOOTLOADER_ADDR (0x1FFFD800)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFFF800)
|
||||||
|
#define GET_RDP(x) (x & 0xFF)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override ADC resolution values
|
* @brief Override ADC resolution values
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -49,6 +49,12 @@ extern "C" {
|
|||||||
#define STM32_BOOTLOADER_ADDR (0x1FFF0000)
|
#define STM32_BOOTLOADER_ADDR (0x1FFF0000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFFC000)
|
||||||
|
#define GET_RDP(x) ((x & 0xFF00) >> 8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override the ADC resolution configuration
|
* @brief Override the ADC resolution configuration
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -32,6 +32,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define STM32_BOOTLOADER_ADDR (0x1FF00000)
|
#define STM32_BOOTLOADER_ADDR (0x1FF00000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FFF0000)
|
||||||
|
#define GET_RDP(x) ((x & 0xFF00) >> 8)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override the ADC resolution configuration
|
* @brief Override the ADC resolution configuration
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
@ -33,6 +33,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define STM32_BOOTLOADER_ADDR (0x1FF00000)
|
#define STM32_BOOTLOADER_ADDR (0x1FF00000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Readout Protection (RDP) option bytes
|
||||||
|
*/
|
||||||
|
#define STM32_OPTION_BYTES ((uint32_t*) 0x1FF80000)
|
||||||
|
#define GET_RDP(x) (x & 0xFF)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Override the ADC resolution configuration
|
* @brief Override the ADC resolution configuration
|
||||||
* @{
|
* @{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user