Skip to content

Commit 07b426d

Browse files
committed
[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.
1 parent 7d1e551 commit 07b426d

8 files changed

Lines changed: 478 additions & 0 deletions

File tree

bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,19 @@
1111
#include <board.h>
1212
#include <drv_common.h>
1313

14+
#ifdef RT_USING_MEM_PROTECTION
15+
#include "mprotect.h"
16+
17+
rt_mem_region_t static_regions[NUM_STATIC_REGIONS] = {
18+
/* Flash region, read only */
19+
{
20+
.start = (void *)STM32_FLASH_START_ADRESS,
21+
.size = (rt_size_t)STM32_FLASH_SIZE,
22+
.attr = RT_MEM_REGION_P_RX_U_RX,
23+
},
24+
};
25+
#endif
26+
1427
void SystemClock_Config(void)
1528
{
1629
RCC_OscInitTypeDef RCC_OscInitStruct = {0};

bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#ifndef __BOARD_H__
1212
#define __BOARD_H__
1313

14+
#include <rtthread.h>
1415
#include <stm32f4xx.h>
1516

1617
#ifdef __cplusplus
@@ -37,6 +38,10 @@ extern int __bss_end;
3738

3839
#define HEAP_END STM32_SRAM_END
3940

41+
#ifdef RT_USING_MEM_PROTECTION
42+
#define NUM_STATIC_REGIONS 1
43+
#endif
44+
4045
void SystemClock_Config(void);
4146

4247
#ifdef __cplusplus

libcpu/arm/cortex-m4/SConscript

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ if rtconfig.PLATFORM in ['gcc', 'llvm-arm']:
2121
if rtconfig.PLATFORM in ['iccarm']:
2222
src += Glob('*_iar.S')
2323

24+
if not GetDepend('RT_USING_MEM_PROTECTION') and not GetDepend('RT_USING_HW_STACK_GUARD'):
25+
SrcRemove(src, 'mpu.c')
26+
2427
group = DefineGroup('CPU', src, depend = [''], CPPPATH = CPPPATH)
2528

2629
Return('group')

libcpu/arm/cortex-m4/context_gcc.S

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
/*@{*/
2020

21+
#include <rtconfig.h>
22+
2123
.cpu cortex-m4
2224
.syntax unified
2325
.thumb
@@ -155,6 +157,13 @@ switch_to_thread:
155157
BICNE lr, lr, #0x10 /* lr &= ~(1 << 4), set FPCA. */
156158
#endif
157159

160+
#if defined (RT_USING_MEM_PROTECTION)
161+
PUSH {r0-r3, r12, lr}
162+
BL rt_thread_self
163+
BL rt_hw_mpu_table_switch
164+
POP {r0-r3, r12, lr}
165+
#endif
166+
158167
pendsv_exit:
159168
/* restore interrupt */
160169
MSR PRIMASK, r2

libcpu/arm/cortex-m4/cpuport.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#ifdef RT_USING_DM
2424
#include <drivers/core/power.h>
2525
#endif
26+
#ifdef RT_USING_HW_STACK_GUARD
27+
#include <mprotect.h>
28+
#endif
2629

2730
#define DBG_TAG "cortex.m4"
2831
#define DBG_LVL DBG_INFO
@@ -189,6 +192,28 @@ rt_uint8_t *rt_hw_stack_init(void *tentry,
189192
return stk;
190193
}
191194

195+
#ifdef RT_USING_HW_STACK_GUARD
196+
void rt_hw_stack_guard_init(rt_thread_t thread)
197+
{
198+
rt_mem_region_t stack_top_region, stack_bottom_region;
199+
rt_ubase_t stack_bottom = (rt_ubase_t)thread->stack_addr;
200+
rt_ubase_t stack_top = (rt_ubase_t)((rt_uint8_t *)thread->stack_addr + thread->stack_size);
201+
rt_ubase_t stack_bottom_region_start = RT_ALIGN(stack_bottom, MPU_MIN_REGION_SIZE);
202+
rt_ubase_t stack_top_region_start = RT_ALIGN_DOWN(stack_top - MPU_MIN_REGION_SIZE, MPU_MIN_REGION_SIZE);
203+
stack_top_region.start = (void *)stack_top_region_start;
204+
stack_top_region.size = MPU_MIN_REGION_SIZE;
205+
stack_top_region.attr = RT_MEM_REGION_P_NA_U_NA;
206+
stack_bottom_region.start = (void *)stack_bottom_region_start;
207+
stack_bottom_region.size = MPU_MIN_REGION_SIZE;
208+
stack_bottom_region.attr = RT_MEM_REGION_P_NA_U_NA;
209+
rt_mprotect_add_region(thread, &stack_top_region);
210+
rt_mprotect_add_region(thread, &stack_bottom_region);
211+
thread->stack_buf = thread->stack_addr;
212+
thread->stack_addr = (void *)(stack_bottom_region_start + MPU_MIN_REGION_SIZE);
213+
thread->stack_size = (rt_uint32_t)(stack_top_region_start - stack_bottom_region_start - MPU_MIN_REGION_SIZE);
214+
}
215+
#endif /* RT_USING_HW_STACK_GUARD */
216+
192217
/**
193218
* This function set the hook, which is invoked on fault exception handling.
194219
*

libcpu/arm/cortex-m4/mpu.c

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
/*
2+
* Copyright (c) 2006-2026, RT-Thread Development Team
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Change Logs:
7+
* Date Author Notes
8+
* 2023-09-25 tangzz98 the first version
9+
* 2026-08-23 RT-Thread add cortex-m4 support
10+
*/
11+
12+
#include <rtdef.h>
13+
#include <mprotect.h>
14+
15+
#define DBG_ENABLE
16+
#define DBG_SECTION_NAME "MEMORY PROTECTION"
17+
#define DBG_LEVEL DBG_ERROR
18+
#include <rtdbg.h>
19+
20+
#define MEM_REGION_TO_MPU_INDEX(thread, region) ((((rt_size_t)region - (rt_size_t)(thread->mem_regions)) / sizeof(rt_mem_region_t)) + NUM_STATIC_REGIONS)
21+
22+
extern rt_mem_region_t *rt_mprotect_find_free_region(rt_thread_t thread);
23+
extern rt_mem_region_t *rt_mprotect_find_region(rt_thread_t thread, rt_mem_region_t *region);
24+
25+
static rt_hw_mpu_exception_hook_t mem_manage_hook = RT_NULL;
26+
27+
/* Cortex-M4 has no L1 cache, so unlike Cortex-M7 the RASR cacheability bits
28+
* only select the memory ordering model. This reduces the default memory type
29+
* to three cases instead of the per-segment table used on M7. */
30+
rt_weak rt_uint32_t rt_hw_mpu_region_default_attr(rt_mem_region_t *region)
31+
{
32+
if ((rt_uint32_t)region->start >= 0xE0000000U)
33+
{
34+
return ((rt_uint32_t)region->start >= 0xE0100000U) ? STRONGLY_ORDERED_SHAREABLE : DEVICE_SHAREABLE;
35+
}
36+
return NORMAL_NON_CACHEABLE_SHAREABLE;
37+
}
38+
39+
static rt_uint32_t _mpu_rasr(rt_mem_region_t *region)
40+
{
41+
rt_uint32_t rasr = 0U;
42+
if ((region->attr.rasr & RESERVED) == RESERVED)
43+
{
44+
rasr |= rt_hw_mpu_region_default_attr(region);
45+
rasr |= region->attr.rasr & (MPU_RASR_XN_Msk | MPU_RASR_AP_Msk);
46+
}
47+
else
48+
{
49+
rasr |= region->attr.rasr & MPU_RASR_ATTRS_Msk;
50+
}
51+
rasr |= ((32U - __builtin_clz(region->size - 1U) - 2U + 1U) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk;
52+
rasr |= MPU_RASR_ENABLE_Msk;
53+
return rasr;
54+
}
55+
56+
rt_bool_t rt_hw_mpu_region_valid(rt_mem_region_t *region)
57+
{
58+
if (region->size < MPU_MIN_REGION_SIZE)
59+
{
60+
LOG_E("Region size is too small");
61+
return RT_FALSE;
62+
}
63+
if ((region->size & (region->size - 1U)) != 0U)
64+
{
65+
LOG_E("Region size is not power of 2");
66+
return RT_FALSE;
67+
}
68+
if (((rt_uint32_t)region->start & (region->size - 1U)) != 0U)
69+
{
70+
LOG_E("Region is not naturally aligned");
71+
return RT_FALSE;
72+
}
73+
return RT_TRUE;
74+
}
75+
76+
rt_err_t rt_hw_mpu_init(void)
77+
{
78+
extern rt_mem_region_t static_regions[NUM_STATIC_REGIONS];
79+
rt_uint8_t num_mpu_regions;
80+
rt_uint8_t num_dynamic_regions;
81+
rt_uint8_t index;
82+
num_mpu_regions = (rt_uint8_t)((MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos);
83+
if (num_mpu_regions == 0U)
84+
{
85+
LOG_E("Hardware does not support MPU");
86+
return RT_ERROR;
87+
}
88+
if (num_mpu_regions != NUM_MEM_REGIONS)
89+
{
90+
LOG_E("Incorrect setting of NUM_MEM_REGIONS");
91+
LOG_E("NUM_MEM_REGIONS = %d, hardware support %d MPU regions", NUM_MEM_REGIONS, num_mpu_regions);
92+
return RT_ERROR;
93+
}
94+
95+
num_dynamic_regions = NUM_DYNAMIC_REGIONS + NUM_EXCLUSIVE_REGIONS;
96+
if (num_dynamic_regions + NUM_STATIC_REGIONS > num_mpu_regions)
97+
{
98+
LOG_E("Insufficient MPU regions: %d hardware MPU regions", num_mpu_regions);
99+
#ifdef RT_USING_HW_STACK_GUARD
100+
LOG_E("Current configuration requires %d static regions + %d configurable regions + %d exclusive regions + %d stack guard regions", NUM_STATIC_REGIONS, NUM_CONFIGURABLE_REGIONS, NUM_EXCLUSIVE_REGIONS, 2);
101+
#else
102+
LOG_E("Current configuration requires %d static regions + %d configurable regions + %d exclusive regions", NUM_STATIC_REGIONS, NUM_CONFIGURABLE_REGIONS, NUM_EXCLUSIVE_REGIONS);
103+
#endif
104+
return RT_ERROR;
105+
}
106+
107+
ARM_MPU_Disable();
108+
for (index = 0U; index < NUM_STATIC_REGIONS; index++)
109+
{
110+
if (rt_hw_mpu_region_valid(&(static_regions[index])) == RT_FALSE)
111+
{
112+
return RT_ERROR;
113+
}
114+
static_regions[index].attr.rasr = _mpu_rasr(&(static_regions[index]));
115+
ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)static_regions[index].start), static_regions[index].attr.rasr);
116+
}
117+
/* Enable background region. */
118+
ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk);
119+
120+
return RT_EOK;
121+
}
122+
123+
rt_err_t rt_hw_mpu_add_region(rt_thread_t thread, rt_mem_region_t *region)
124+
{
125+
rt_uint8_t index;
126+
rt_mem_region_t *free_region;
127+
if (rt_hw_mpu_region_valid(region) == RT_FALSE)
128+
{
129+
return RT_ERROR;
130+
}
131+
region->attr.rasr = _mpu_rasr(region);
132+
if (thread == RT_NULL)
133+
{
134+
return RT_EOK;
135+
}
136+
rt_enter_critical();
137+
free_region = rt_mprotect_find_free_region(thread);
138+
if (free_region == RT_NULL)
139+
{
140+
rt_exit_critical();
141+
LOG_E("Insufficient regions");
142+
return RT_ERROR;
143+
}
144+
rt_memcpy(free_region, region, sizeof(rt_mem_region_t));
145+
if (thread == rt_thread_self())
146+
{
147+
index = MEM_REGION_TO_MPU_INDEX(thread, free_region);
148+
ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)region->start), region->attr.rasr);
149+
}
150+
rt_exit_critical();
151+
return RT_EOK;
152+
}
153+
154+
rt_err_t rt_hw_mpu_delete_region(rt_thread_t thread, rt_mem_region_t *region)
155+
{
156+
rt_uint8_t index;
157+
rt_enter_critical();
158+
rt_mem_region_t *found_region = rt_mprotect_find_region(thread, region);
159+
if (found_region == RT_NULL)
160+
{
161+
rt_exit_critical();
162+
LOG_E("Region not found");
163+
return RT_ERROR;
164+
}
165+
rt_memset(found_region, 0, sizeof(rt_mem_region_t));
166+
if (thread == rt_thread_self())
167+
{
168+
index = MEM_REGION_TO_MPU_INDEX(thread, found_region);
169+
ARM_MPU_ClrRegion(index);
170+
}
171+
rt_exit_critical();
172+
return RT_EOK;
173+
}
174+
175+
rt_err_t rt_hw_mpu_update_region(rt_thread_t thread, rt_mem_region_t *region)
176+
{
177+
rt_uint8_t index;
178+
if (rt_hw_mpu_region_valid(region) == RT_FALSE)
179+
{
180+
return RT_ERROR;
181+
}
182+
region->attr.rasr = _mpu_rasr(region);
183+
rt_enter_critical();
184+
rt_mem_region_t *old_region = rt_mprotect_find_region(thread, region);
185+
if (old_region == RT_NULL)
186+
{
187+
rt_exit_critical();
188+
LOG_E("Region not found");
189+
return RT_ERROR;
190+
}
191+
rt_memcpy(old_region, region, sizeof(rt_mem_region_t));
192+
if (thread == rt_thread_self())
193+
{
194+
index = MEM_REGION_TO_MPU_INDEX(thread, old_region);
195+
ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)region->start), region->attr.rasr);
196+
}
197+
rt_exit_critical();
198+
return RT_EOK;
199+
}
200+
201+
rt_err_t rt_hw_mpu_exception_set_hook(rt_hw_mpu_exception_hook_t hook)
202+
{
203+
mem_manage_hook = hook;
204+
return RT_EOK;
205+
}
206+
207+
void rt_hw_mpu_table_switch(rt_thread_t thread)
208+
{
209+
extern rt_mem_exclusive_region_t exclusive_regions[NUM_EXCLUSIVE_REGIONS];
210+
rt_uint8_t i;
211+
rt_uint8_t index = NUM_STATIC_REGIONS;
212+
if (thread->mem_regions != RT_NULL)
213+
{
214+
for (i = 0U; i < NUM_DYNAMIC_REGIONS; i++)
215+
{
216+
if (((rt_mem_region_t *)thread->mem_regions)[i].size != 0U)
217+
{
218+
ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)(((rt_mem_region_t *)thread->mem_regions)[i].start)), ((rt_mem_region_t *)thread->mem_regions)[i].attr.rasr);
219+
index += 1U;
220+
}
221+
}
222+
}
223+
for (i = 0U; i < NUM_EXCLUSIVE_REGIONS; i++)
224+
{
225+
if ((exclusive_regions[i].owner != RT_NULL) && (exclusive_regions[i].owner != thread))
226+
{
227+
ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)(exclusive_regions[i].region.start)), exclusive_regions[i].region.attr.rasr);
228+
index += 1U;
229+
}
230+
}
231+
for ( ; index < NUM_MEM_REGIONS; index++)
232+
{
233+
ARM_MPU_ClrRegion(index);
234+
}
235+
}
236+
237+
void MemManage_Handler(void)
238+
{
239+
extern rt_mem_region_t static_regions[NUM_STATIC_REGIONS];
240+
extern rt_mem_exclusive_region_t exclusive_regions[NUM_EXCLUSIVE_REGIONS];
241+
rt_mem_exception_info_t info;
242+
rt_int8_t i;
243+
rt_memset(&info, 0, sizeof(rt_mem_exception_info_t));
244+
info.thread = rt_thread_self();
245+
if (SCB->CFSR & SCB_CFSR_MMARVALID_Msk)
246+
{
247+
info.addr = (void *)(SCB->MMFAR);
248+
for (i = NUM_EXCLUSIVE_REGIONS - 1; i >= 0; i--)
249+
{
250+
if ((exclusive_regions[i].owner != RT_NULL) && ((exclusive_regions[i].owner != rt_thread_self())) && ADDR_IN_REGION(info.addr, (rt_mem_region_t *)&(exclusive_regions[i])))
251+
{
252+
rt_memcpy(&(info.region), &(exclusive_regions[i]), sizeof(rt_mem_region_t));
253+
break;
254+
}
255+
}
256+
if (info.region.size == 0U)
257+
{
258+
if (info.thread->mem_regions != RT_NULL)
259+
{
260+
for (i = NUM_DYNAMIC_REGIONS - 1; i >= 0; i--)
261+
{
262+
if ((((rt_mem_region_t *)info.thread->mem_regions)[i].size != 0U) && ADDR_IN_REGION(info.addr, &(((rt_mem_region_t *)info.thread->mem_regions)[i])))
263+
{
264+
rt_memcpy(&(info.region), &(((rt_mem_region_t *)info.thread->mem_regions)[i]), sizeof(rt_mem_region_t));
265+
break;
266+
}
267+
}
268+
}
269+
if (info.region.size == 0U)
270+
{
271+
for (i = NUM_STATIC_REGIONS - 1; i >= 0; i--)
272+
{
273+
if (ADDR_IN_REGION(info.addr, &(static_regions[i])))
274+
{
275+
rt_memcpy(&(info.region), &(static_regions[i]), sizeof(rt_mem_region_t));
276+
break;
277+
}
278+
}
279+
}
280+
}
281+
}
282+
info.mmfsr = (SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk) >> SCB_CFSR_MEMFAULTSR_Pos;
283+
if (mem_manage_hook != RT_NULL)
284+
{
285+
mem_manage_hook(&info);
286+
}
287+
while (1);
288+
}

0 commit comments

Comments
 (0)