[arm][cortex-m4] Add hardware stack guard support

Enable RT_USING_HW_STACK_GUARD on Cortex-M4, which was previously only
available on Cortex-M7 and Cortex-M33.

Core porting (libcpu/arm/cortex-m4/):
- Add mpu.c/mpu.h/mputype.h aligned with the Cortex-M7 MPU layer. The only
  difference is the default memory type attribute: Cortex-M4 has no L1 cache,
  so the cacheability bits are reduced to three cases.
- Add rt_hw_stack_guard_init() to cpuport.c.
- Add missing #include <rtconfig.h> to context_gcc.S and invoke
  rt_hw_mpu_table_switch() in the PendSV context switch path.
- Update SConscript to exclude mpu.c when memory protection is disabled.

BSP reference (bsp/stm32/stm32f407-fk407m2-zgt6/):
- board.h: include rtthread.h and define NUM_STATIC_REGIONS.
- board.c: define static_regions[] marking the Flash region read-only,
  guarded by RT_USING_MEM_PROTECTION so it is not enabled by default.
This commit is contained in:
hanzhijian
2026-08-26 10:37:02 +08:00
committed by Rbb666
parent 45679c79de
commit e2e6245aac
8 changed files with 607 additions and 117 deletions
+53 -42
View File
@@ -11,50 +11,61 @@
#include <board.h>
#include <drv_common.h>
#ifdef RT_USING_MEM_PROTECTION
#include "mprotect.h"
rt_mem_region_t static_regions[NUM_STATIC_REGIONS] = {
/* Flash region, read only */
{
.start = (void *)STM32_FLASH_START_ADRESS,
.size = (rt_size_t)STM32_FLASH_SIZE,
.attr = RT_MEM_REGION_P_RX_U_RX,
},
};
#endif
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
/**Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE
|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
/**Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_HSE | RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
+15 -10
View File
@@ -11,31 +11,36 @@
#ifndef __BOARD_H__
#define __BOARD_H__
#include <rtthread.h>
#include <stm32f4xx.h>
#ifdef __cplusplus
extern "C" {
#endif
#define STM32_SRAM_SIZE (128)
#define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024)
#define STM32_SRAM_SIZE (128)
#define STM32_SRAM_END (0x20000000 + STM32_SRAM_SIZE * 1024)
#define STM32_FLASH_START_ADRESS ((uint32_t)0x08000000)
#define STM32_FLASH_SIZE (1024 * 1024)
#define STM32_FLASH_END_ADDRESS ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE))
#define STM32_FLASH_START_ADRESS ((uint32_t)0x08000000)
#define STM32_FLASH_SIZE (1024 * 1024)
#define STM32_FLASH_END_ADDRESS ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE))
#if defined(__ARMCC_VERSION)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="CSTACK"
#define HEAP_BEGIN (__segment_end("CSTACK"))
#pragma section = "CSTACK"
#define HEAP_BEGIN (__segment_end("CSTACK"))
#else
extern int __bss_end;
#define HEAP_BEGIN ((void *)&__bss_end)
#define HEAP_BEGIN ((void *)&__bss_end)
#endif
#define HEAP_END STM32_SRAM_END
#define HEAP_END STM32_SRAM_END
#ifdef RT_USING_MEM_PROTECTION
#define NUM_STATIC_REGIONS 1
#endif
void SystemClock_Config(void);
+13
View File
@@ -21,6 +21,19 @@ if rtconfig.PLATFORM in ['gcc', 'llvm-arm']:
if rtconfig.PLATFORM in ['iccarm']:
src += Glob('*_iar.S')
using_mpu = (
GetDepend('RT_USING_MEM_PROTECTION') or
GetDepend('RT_USING_HW_STACK_GUARD')
)
if using_mpu:
if rtconfig.PLATFORM not in ['gcc', 'llvm-arm']:
raise RuntimeError(
'Cortex-M4 memory protection currently supports GCC only'
)
else:
SrcRemove(src, 'mpu.c')
group = DefineGroup('CPU', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+9
View File
@@ -18,6 +18,8 @@
*/
/*@{*/
#include <rtconfig.h>
.cpu cortex-m4
.syntax unified
.thumb
@@ -155,6 +157,13 @@ switch_to_thread:
BICNE lr, lr, #0x10 /* lr &= ~(1 << 4), set FPCA. */
#endif
#if defined (RT_USING_MEM_PROTECTION)
PUSH {r0-r3, r12, lr}
BL rt_thread_self
BL rt_hw_mpu_table_switch
POP {r0-r3, r12, lr}
#endif
pendsv_exit:
/* restore interrupt */
MSR PRIMASK, r2
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-25 tangzz98 the first version
* 2026-08-23 RT-Thread add cortex-m4 support
*/
#ifndef __MPU_H__
#define __MPU_H__
#ifdef RT_USING_MEM_PROTECTION
#include <board.h>
#define MPU_MIN_REGION_SIZE 32U
/* MPU attributes for configuring data region permission.
* These are identical to Cortex-M7 because both cores implement the
* ARMv7-M MPU with the same AP/XN bit layout in the RASR register. */
/* Privileged No Access, Unprivileged No Access */
#define P_NA_U_NA ((0x0 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* Privileged Read Write, Unprivileged No Access */
#define P_RW_U_NA ((0x1 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* Privileged Read Write, Unprivileged Read Only */
#define P_RW_U_RO ((0x2 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* Privileged Read Write, Unprivileged Read Write */
#define P_RW_U_RW ((0x3 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* Privileged Read Only, Unprivileged No Access */
#define P_RO_U_NA ((0x5 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* Privileged Read Only, Unprivileged Read Only */
#define P_RO_U_RO ((0x6 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk | MPU_RASR_XN_Msk)
/* MPU attributes for configuring code region permission */
/* Privileged Read Write Execute, Unprivileged Read Write Execute */
#define P_RWX_U_RWX ((0x3 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
/* Privileged Read Write Execute, Unprivileged Read Execute */
#define P_RWX_U_RX ((0x2 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
/* Privileged Read Write Execute, Unprivileged No Access */
#define P_RWX_U_NA ((0x1 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
/* Privileged Read Execute, Unprivileged Read Execute */
#define P_RX_U_RX ((0x6 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
/* Privileged Read Execute, Unprivileged No Access */
#define P_RX_U_NA ((0x5 << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk)
/* MPU attributes for configuring memory type, cacheability and shareability.
*
* Unlike Cortex-M7, Cortex-M4 has no L1 cache, so the cacheability bits in
* RASR only control memory ordering (strongly-ordered / device / normal)
* rather than an actual cache. The full write-through / write-back taxonomy
* used on M7 is therefore not needed here and is reduced to three types. */
#define STRONGLY_ORDERED_SHAREABLE MPU_RASR_S_Msk
#define STRONGLY_ORDERED_NON_SHAREABLE 0U
#define DEVICE_SHAREABLE (MPU_RASR_B_Msk | MPU_RASR_S_Msk)
#define DEVICE_NON_SHAREABLE MPU_RASR_B_Msk
#define NORMAL_NON_CACHEABLE_SHAREABLE (MPU_RASR_C_Msk | MPU_RASR_S_Msk)
#define NORMAL_NON_CACHEABLE MPU_RASR_C_Msk
/* Sentinel marking an attribute that only carries the permission bits and
* therefore needs the default memory type filled in later. */
#define RESERVED ((2 << MPU_RASR_TEX_Pos) | MPU_RASR_B_Msk)
typedef struct
{
rt_thread_t thread; /* Thread that triggered exception */
void *addr; /* Address of faulting memory access */
rt_mem_region_t region; /* Configurations of the memory region containing the address */
rt_uint8_t mmfsr; /* Content of MemManage Status Register */
} rt_mem_exception_info_t;
typedef void (*rt_hw_mpu_exception_hook_t)(rt_mem_exception_info_t *);
#define RT_ARM_MEM_ATTR(perm, type) ((rt_mem_attr_t){ (perm) | (type) })
/* Convenient macros for configuring data region attributes with default memory type */
#define RT_MEM_REGION_P_NA_U_NA RT_ARM_MEM_ATTR(P_NA_U_NA, RESERVED)
#define RT_MEM_REGION_P_RW_U_RW RT_ARM_MEM_ATTR(P_RW_U_RW, RESERVED)
#define RT_MEM_REGION_P_RW_U_RO RT_ARM_MEM_ATTR(P_RW_U_RO, RESERVED)
#define RT_MEM_REGION_P_RW_U_NA RT_ARM_MEM_ATTR(P_RW_U_NA, RESERVED)
#define RT_MEM_REGION_P_RO_U_RO RT_ARM_MEM_ATTR(P_RO_U_RO, RESERVED)
#define RT_MEM_REGION_P_RO_U_NA RT_ARM_MEM_ATTR(P_RO_U_NA, RESERVED)
/* Convenient macros for configuring code region attributes with default memory type */
#define RT_MEM_REGION_P_RWX_U_RWX RT_ARM_MEM_ATTR(P_RWX_U_RWX, RESERVED)
#define RT_MEM_REGION_P_RWX_U_RX RT_ARM_MEM_ATTR(P_RWX_U_RX, RESERVED)
#define RT_MEM_REGION_P_RWX_U_NA RT_ARM_MEM_ATTR(P_RWX_U_NA, RESERVED)
#define RT_MEM_REGION_P_RX_U_RX RT_ARM_MEM_ATTR(P_RX_U_RX, RESERVED)
#define RT_MEM_REGION_P_RX_U_NA RT_ARM_MEM_ATTR(P_RX_U_NA, RESERVED)
rt_bool_t rt_hw_mpu_region_valid(rt_mem_region_t *region);
rt_err_t rt_hw_mpu_init(void);
rt_err_t rt_hw_mpu_add_region(rt_thread_t thread, rt_mem_region_t *region);
rt_err_t rt_hw_mpu_delete_region(rt_thread_t thread, rt_mem_region_t *region);
rt_err_t rt_hw_mpu_update_region(rt_thread_t thread, rt_mem_region_t *region);
rt_err_t rt_hw_mpu_exception_set_hook(rt_hw_mpu_exception_hook_t hook);
void rt_hw_mpu_table_switch(rt_thread_t thread);
#endif /* RT_USING_MEM_PROTECTION */
#endif /* __MPU_H__ */
+33
View File
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-09-25 tangzz98 the first version
* 2026-08-23 RT-Thread add cortex-m4 support
*/
#ifndef __MPUTYPE_H__
#define __MPUTYPE_H__
#ifdef RT_USING_MEM_PROTECTION
#ifdef RT_USING_HW_STACK_GUARD
#define NUM_DYNAMIC_REGIONS (2 + NUM_CONFIGURABLE_REGIONS)
#else
#define NUM_DYNAMIC_REGIONS (NUM_CONFIGURABLE_REGIONS)
#endif
/* Cortex-M4 follows ARMv7-M, whose MPU programs the full attribute set
* (access permission + memory type) through the single RASR register,
* just like Cortex-M7. */
typedef struct
{
rt_uint32_t rasr;
} rt_mem_attr_t;
#endif /* RT_USING_MEM_PROTECTION */
#endif /* __MPUTYPE_H__ */