summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhanzhijian <[email protected]>2026-08-25 09:19:51 +0800
committerRbb666 <[email protected]>2026-08-26 10:37:02 +0800
commite2e6245aace07b545b8dbcef8fbcdb4469f55d9f (patch)
treeb17de7aed93f98eeded0d760553e212d40df5ca6
parent45679c79de6d60f2e8345928c49671936f6ff008 (diff)
[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.
-rw-r--r--bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c95
-rw-r--r--bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h25
-rw-r--r--libcpu/arm/cortex-m4/SConscript13
-rw-r--r--libcpu/arm/cortex-m4/context_gcc.S9
-rw-r--r--libcpu/arm/cortex-m4/cpuport.c159
-rw-r--r--libcpu/arm/cortex-m4/mpu.c288
-rw-r--r--libcpu/arm/cortex-m4/mpu.h102
-rw-r--r--libcpu/arm/cortex-m4/mputype.h33
8 files changed, 607 insertions, 117 deletions
diff --git a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c
index 702980ab3e..34f974369c 100644
--- a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c
+++ b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c
@@ -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();
+ }
}
diff --git a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h
index 3d6b282297..3fdd7f3afd 100644
--- a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h
+++ b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h
@@ -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);
diff --git a/libcpu/arm/cortex-m4/SConscript b/libcpu/arm/cortex-m4/SConscript
index 3ecbea8de3..2ac22c8e4d 100644
--- a/libcpu/arm/cortex-m4/SConscript
+++ b/libcpu/arm/cortex-m4/SConscript
@@ -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')
diff --git a/libcpu/arm/cortex-m4/context_gcc.S b/libcpu/arm/cortex-m4/context_gcc.S
index 188482ce09..4fd570b797 100644
--- a/libcpu/arm/cortex-m4/context_gcc.S
+++ b/libcpu/arm/cortex-m4/context_gcc.S
@@ -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
diff --git a/libcpu/arm/cortex-m4/cpuport.c b/libcpu/arm/cortex-m4/cpuport.c
index 5db2fca7f3..37733dcd38 100644
--- a/libcpu/arm/cortex-m4/cpuport.c
+++ b/libcpu/arm/cortex-m4/cpuport.c
@@ -23,9 +23,12 @@
#ifdef RT_USING_DM
#include <drivers/core/power.h>
#endif
+#ifdef RT_USING_HW_STACK_GUARD
+#include <mprotect.h>
+#endif
-#define DBG_TAG "cortex.m4"
-#define DBG_LVL DBG_INFO
+#define DBG_TAG "cortex.m4"
+#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_weak int rt_hw_cpu_id(void)
@@ -33,13 +36,10 @@ rt_weak int rt_hw_cpu_id(void)
return 0;
}
-#if /* ARMCC */ ( (defined ( __CC_ARM ) && defined ( __TARGET_FPU_VFP )) \
- /* Clang */ || (defined ( __clang__ ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) \
- /* IAR */ || (defined ( __ICCARM__ ) && defined ( __ARMVFP__ )) \
- /* GNU */ || (defined ( __GNUC__ ) && defined ( __VFP_FP__ ) && !defined(__SOFTFP__)) )
-#define USE_FPU 1
+#if /* ARMCC */ ((defined(__CC_ARM) && defined(__TARGET_FPU_VFP)) /* Clang */ || (defined(__clang__) && defined(__VFP_FP__) && !defined(__SOFTFP__)) /* IAR */ || (defined(__ICCARM__) && defined(__ARMVFP__)) /* GNU */ || (defined(__GNUC__) && defined(__VFP_FP__) && !defined(__SOFTFP__)))
+#define USE_FPU 1
#else
-#define USE_FPU 0
+#define USE_FPU 0
#endif
/* exception and interrupt handler table */
@@ -151,34 +151,34 @@ struct stack_frame_fpu
struct exception_stack_frame_fpu exception_stack_frame;
};
-rt_uint8_t *rt_hw_stack_init(void *tentry,
- void *parameter,
+rt_uint8_t *rt_hw_stack_init(void *tentry,
+ void *parameter,
rt_uint8_t *stack_addr,
- void *texit)
+ void *texit)
{
struct stack_frame *stack_frame;
- rt_uint8_t *stk;
- unsigned long i;
+ rt_uint8_t *stk;
+ unsigned long i;
- stk = stack_addr + sizeof(rt_uint32_t);
- stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
+ stk = stack_addr + sizeof(rt_uint32_t);
+ stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
stk -= sizeof(struct stack_frame);
stack_frame = (struct stack_frame *)stk;
/* init all register */
- for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
+ for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i++)
{
((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
}
- stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
- stack_frame->exception_stack_frame.r1 = 0; /* r1 */
- stack_frame->exception_stack_frame.r2 = 0; /* r2 */
- stack_frame->exception_stack_frame.r3 = 0; /* r3 */
+ stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
+ stack_frame->exception_stack_frame.r1 = 0; /* r1 */
+ stack_frame->exception_stack_frame.r2 = 0; /* r2 */
+ stack_frame->exception_stack_frame.r3 = 0; /* r3 */
stack_frame->exception_stack_frame.r12 = 0; /* r12 */
- stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
- stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
+ stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */
+ stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */
#if USE_FPU
@@ -189,6 +189,28 @@ rt_uint8_t *rt_hw_stack_init(void *tentry,
return stk;
}
+#ifdef RT_USING_HW_STACK_GUARD
+void rt_hw_stack_guard_init(rt_thread_t thread)
+{
+ rt_mem_region_t stack_top_region, stack_bottom_region;
+ rt_ubase_t stack_bottom = (rt_ubase_t)thread->stack_addr;
+ rt_ubase_t stack_top = (rt_ubase_t)((rt_uint8_t *)thread->stack_addr + thread->stack_size);
+ rt_ubase_t stack_bottom_region_start = RT_ALIGN(stack_bottom, MPU_MIN_REGION_SIZE);
+ rt_ubase_t stack_top_region_start = RT_ALIGN_DOWN(stack_top - MPU_MIN_REGION_SIZE, MPU_MIN_REGION_SIZE);
+ stack_top_region.start = (void *)stack_top_region_start;
+ stack_top_region.size = MPU_MIN_REGION_SIZE;
+ stack_top_region.attr = RT_MEM_REGION_P_NA_U_NA;
+ stack_bottom_region.start = (void *)stack_bottom_region_start;
+ stack_bottom_region.size = MPU_MIN_REGION_SIZE;
+ stack_bottom_region.attr = RT_MEM_REGION_P_NA_U_NA;
+ rt_mprotect_add_region(thread, &stack_top_region);
+ rt_mprotect_add_region(thread, &stack_bottom_region);
+ thread->stack_buf = thread->stack_addr;
+ thread->stack_addr = (void *)(stack_bottom_region_start + MPU_MIN_REGION_SIZE);
+ thread->stack_size = (rt_uint32_t)(stack_top_region_start - stack_bottom_region_start - MPU_MIN_REGION_SIZE);
+}
+#endif /* RT_USING_HW_STACK_GUARD */
+
/**
* This function set the hook, which is invoked on fault exception handling.
*
@@ -206,9 +228,9 @@ void rt_hw_exception_install(rt_err_t (*exception_handle)(void *context))
#define SCB_AIRCR (*(volatile unsigned long *)0xE000ED0C) /* Reset control Address Register */
#define SCB_RESET_VALUE 0x05FA0004 /* Reset value, write to SCB_AIRCR can reset cpu */
-#define SCB_CFSR_MFSR (*(volatile const unsigned char*)0xE000ED28) /* Memory-management Fault Status Register */
-#define SCB_CFSR_BFSR (*(volatile const unsigned char*)0xE000ED29) /* Bus Fault Status Register */
-#define SCB_CFSR_UFSR (*(volatile const unsigned short*)0xE000ED2A) /* Usage Fault Status Register */
+#define SCB_CFSR_MFSR (*(volatile const unsigned char *)0xE000ED28) /* Memory-management Fault Status Register */
+#define SCB_CFSR_BFSR (*(volatile const unsigned char *)0xE000ED29) /* Bus Fault Status Register */
+#define SCB_CFSR_UFSR (*(volatile const unsigned short *)0xE000ED2A) /* Usage Fault Status Register */
#ifdef RT_USING_FINSH
static void usage_fault_track(void)
@@ -216,37 +238,37 @@ static void usage_fault_track(void)
rt_kprintf("usage fault:\n");
rt_kprintf("SCB_CFSR_UFSR:0x%02X ", SCB_CFSR_UFSR);
- if(SCB_CFSR_UFSR & (1<<0))
+ if (SCB_CFSR_UFSR & (1 << 0))
{
/* [0]:UNDEFINSTR */
rt_kprintf("UNDEFINSTR ");
}
- if(SCB_CFSR_UFSR & (1<<1))
+ if (SCB_CFSR_UFSR & (1 << 1))
{
/* [1]:INVSTATE */
rt_kprintf("INVSTATE ");
}
- if(SCB_CFSR_UFSR & (1<<2))
+ if (SCB_CFSR_UFSR & (1 << 2))
{
/* [2]:INVPC */
rt_kprintf("INVPC ");
}
- if(SCB_CFSR_UFSR & (1<<3))
+ if (SCB_CFSR_UFSR & (1 << 3))
{
/* [3]:NOCP */
rt_kprintf("NOCP ");
}
- if(SCB_CFSR_UFSR & (1<<8))
+ if (SCB_CFSR_UFSR & (1 << 8))
{
/* [8]:UNALIGNED */
rt_kprintf("UNALIGNED ");
}
- if(SCB_CFSR_UFSR & (1<<9))
+ if (SCB_CFSR_UFSR & (1 << 9))
{
/* [9]:DIVBYZERO */
rt_kprintf("DIVBYZERO ");
@@ -260,37 +282,37 @@ static void bus_fault_track(void)
rt_kprintf("bus fault:\n");
rt_kprintf("SCB_CFSR_BFSR:0x%02X ", SCB_CFSR_BFSR);
- if(SCB_CFSR_BFSR & (1<<0))
+ if (SCB_CFSR_BFSR & (1 << 0))
{
/* [0]:IBUSERR */
rt_kprintf("IBUSERR ");
}
- if(SCB_CFSR_BFSR & (1<<1))
+ if (SCB_CFSR_BFSR & (1 << 1))
{
/* [1]:PRECISERR */
rt_kprintf("PRECISERR ");
}
- if(SCB_CFSR_BFSR & (1<<2))
+ if (SCB_CFSR_BFSR & (1 << 2))
{
/* [2]:IMPRECISERR */
rt_kprintf("IMPRECISERR ");
}
- if(SCB_CFSR_BFSR & (1<<3))
+ if (SCB_CFSR_BFSR & (1 << 3))
{
/* [3]:UNSTKERR */
rt_kprintf("UNSTKERR ");
}
- if(SCB_CFSR_BFSR & (1<<4))
+ if (SCB_CFSR_BFSR & (1 << 4))
{
/* [4]:STKERR */
rt_kprintf("STKERR ");
}
- if(SCB_CFSR_BFSR & (1<<7))
+ if (SCB_CFSR_BFSR & (1 << 7))
{
rt_kprintf("SCB->BFAR:%08X\n", SCB_BFAR);
}
@@ -305,31 +327,31 @@ static void mem_manage_fault_track(void)
rt_kprintf("mem manage fault:\n");
rt_kprintf("SCB_CFSR_MFSR:0x%02X ", SCB_CFSR_MFSR);
- if(SCB_CFSR_MFSR & (1<<0))
+ if (SCB_CFSR_MFSR & (1 << 0))
{
/* [0]:IACCVIOL */
rt_kprintf("IACCVIOL ");
}
- if(SCB_CFSR_MFSR & (1<<1))
+ if (SCB_CFSR_MFSR & (1 << 1))
{
/* [1]:DACCVIOL */
rt_kprintf("DACCVIOL ");
}
- if(SCB_CFSR_MFSR & (1<<3))
+ if (SCB_CFSR_MFSR & (1 << 3))
{
/* [3]:MUNSTKERR */
rt_kprintf("MUNSTKERR ");
}
- if(SCB_CFSR_MFSR & (1<<4))
+ if (SCB_CFSR_MFSR & (1 << 4))
{
/* [4]:MSTKERR */
rt_kprintf("MSTKERR ");
}
- if(SCB_CFSR_MFSR & (1<<7))
+ if (SCB_CFSR_MFSR & (1 << 7))
{
/* [7]:MMARVALID */
rt_kprintf("SCB->MMAR:%08X\n", SCB_MMAR);
@@ -342,33 +364,33 @@ static void mem_manage_fault_track(void)
static void hard_fault_track(void)
{
- if(SCB_HFSR & (1UL<<1))
+ if (SCB_HFSR & (1UL << 1))
{
/* [1]:VECTBL, Indicates hard fault is caused by failed vector fetch. */
rt_kprintf("failed vector fetch\n");
}
- if(SCB_HFSR & (1UL<<30))
+ if (SCB_HFSR & (1UL << 30))
{
/* [30]:FORCED, Indicates hard fault is taken because of bus fault,
memory management fault, or usage fault. */
- if(SCB_CFSR_BFSR)
+ if (SCB_CFSR_BFSR)
{
bus_fault_track();
}
- if(SCB_CFSR_MFSR)
+ if (SCB_CFSR_MFSR)
{
mem_manage_fault_track();
}
- if(SCB_CFSR_UFSR)
+ if (SCB_CFSR_UFSR)
{
usage_fault_track();
}
}
- if(SCB_HFSR & (1UL<<31))
+ if (SCB_HFSR & (1UL << 31))
{
/* [31]:DEBUGEVT, Indicates hard fault is triggered by debug event. */
rt_kprintf("debug event\n");
@@ -395,7 +417,10 @@ void rt_hw_hard_fault_exception(struct exception_info *exception_info)
rt_err_t result;
result = rt_exception_hook(exception_stack);
- if (result == RT_EOK) return;
+ if (result == RT_EOK)
+ {
+ return;
+ }
}
rt_kprintf("psr: 0x%08x\n", context->exception_stack_frame.psr);
@@ -429,7 +454,7 @@ void rt_hw_hard_fault_exception(struct exception_info *exception_info)
rt_kprintf("hard fault on handler\r\n\r\n");
}
- if ( (exception_info->exc_return & 0x10) == 0)
+ if ((exception_info->exc_return & 0x10) == 0)
{
rt_kprintf("FPU active!\r\n");
}
@@ -476,19 +501,19 @@ rt_inline rt_uint32_t rt_hw_get_ipsr(void)
{
#if defined(__CC_ARM)
register uint32_t __regIPSR __asm("ipsr");
- return(__regIPSR);
+ return (__regIPSR);
#elif defined(__clang__)
uint32_t result;
- __asm volatile ("MRS %0, ipsr" : "=r" (result) );
- return(result);
+ __asm volatile("MRS %0, ipsr" : "=r"(result));
+ return (result);
#elif defined(__IAR_SYSTEMS_ICC__)
uint32_t result;
- __asm volatile ("MRS %0, ipsr" : "=r" (result));
+ __asm volatile("MRS %0, ipsr" : "=r"(result));
return result;
-#elif defined ( __GNUC__ )
+#elif defined(__GNUC__)
uint32_t result;
- __asm volatile ("MRS %0, ipsr" : "=r" (result) );
- return(result);
+ __asm volatile("MRS %0, ipsr" : "=r"(result));
+ return (result);
#endif
}
@@ -502,7 +527,7 @@ rt_inline rt_uint32_t rt_hw_get_ipsr(void)
void rt_interrupt_enter(void)
{
extern void (*rt_interrupt_enter_hook)(void);
- RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
+ RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook, ());
LOG_D("irq has come...");
}
@@ -517,7 +542,7 @@ void rt_interrupt_leave(void)
{
extern void (*rt_interrupt_leave_hook)(void);
LOG_D("irq is going to leave");
- RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,());
+ RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook, ());
}
/**
@@ -545,15 +570,15 @@ rt_inline rt_uint32_t rt_hw_get_primask_value(void)
return (__regPRIMASK);
#elif defined(__clang__)
uint32_t result;
- __asm volatile ("MRS %0, primask" : "=r" (result));
+ __asm volatile("MRS %0, primask" : "=r"(result));
return result;
#elif defined(__IAR_SYSTEMS_ICC__)
uint32_t result;
- __asm volatile ("MRS %0, primask" : "=r" (result));
+ __asm volatile("MRS %0, primask" : "=r"(result));
return result;
#elif defined(__GNUC__)
uint32_t result;
- __asm volatile ("MRS %0, primask" : "=r" (result));
+ __asm volatile("MRS %0, primask" : "=r"(result));
return result;
#endif
}
@@ -584,6 +609,7 @@ rt_bool_t rt_hw_interrupt_is_disabled(void)
* shall return 0.
*/
#if defined(__CC_ARM)
+// clang-format off
__asm int __rt_ffs(int value)
{
CMP r0, #0x00
@@ -596,6 +622,7 @@ __asm int __rt_ffs(int value)
exit
BX lr
}
+// clang-format on
#elif defined(__clang__)
int __rt_ffs(int value)
{
@@ -610,14 +637,16 @@ int __rt_ffs(int value)
"1: \n"
: "=r"(value)
- : "r"(value)
- );
+ : "r"(value));
return value;
}
#elif defined(__IAR_SYSTEMS_ICC__)
int __rt_ffs(int value)
{
- if (value == 0) return value;
+ if (value == 0)
+ {
+ return value;
+ }
asm("RBIT %0, %1" : "=r"(value) : "r"(value));
asm("CLZ %0, %1" : "=r"(value) : "r"(value));
diff --git a/libcpu/arm/cortex-m4/mpu.c b/libcpu/arm/cortex-m4/mpu.c
new file mode 100644
index 0000000000..83859766a5
--- /dev/null
+++ b/libcpu/arm/cortex-m4/mpu.c
@@ -0,0 +1,288 @@
+/*
+ * 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
+ */
+
+#include <rtdef.h>
+#include <mprotect.h>
+
+#define DBG_ENABLE
+#define DBG_SECTION_NAME "MEMORY PROTECTION"
+#define DBG_LEVEL DBG_ERROR
+#include <rtdbg.h>
+
+#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)
+
+extern rt_mem_region_t *rt_mprotect_find_free_region(rt_thread_t thread);
+extern rt_mem_region_t *rt_mprotect_find_region(rt_thread_t thread, rt_mem_region_t *region);
+
+static rt_hw_mpu_exception_hook_t mem_manage_hook = RT_NULL;
+
+/* Cortex-M4 has no L1 cache, so unlike Cortex-M7 the RASR cacheability bits
+ * only select the memory ordering model. This reduces the default memory type
+ * to three cases instead of the per-segment table used on M7. */
+rt_weak rt_uint32_t rt_hw_mpu_region_default_attr(rt_mem_region_t *region)
+{
+ if ((rt_uint32_t)region->start >= 0xE0000000U)
+ {
+ return ((rt_uint32_t)region->start >= 0xE0100000U) ? STRONGLY_ORDERED_SHAREABLE : DEVICE_SHAREABLE;
+ }
+ return NORMAL_NON_CACHEABLE_SHAREABLE;
+}
+
+static rt_uint32_t _mpu_rasr(rt_mem_region_t *region)
+{
+ rt_uint32_t rasr = 0U;
+ if ((region->attr.rasr & RESERVED) == RESERVED)
+ {
+ rasr |= rt_hw_mpu_region_default_attr(region);
+ rasr |= region->attr.rasr & (MPU_RASR_XN_Msk | MPU_RASR_AP_Msk);
+ }
+ else
+ {
+ rasr |= region->attr.rasr & MPU_RASR_ATTRS_Msk;
+ }
+ rasr |= ((32U - __builtin_clz(region->size - 1U) - 2U + 1U) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk;
+ rasr |= MPU_RASR_ENABLE_Msk;
+ return rasr;
+}
+
+rt_bool_t rt_hw_mpu_region_valid(rt_mem_region_t *region)
+{
+ if (region->size < MPU_MIN_REGION_SIZE)
+ {
+ LOG_E("Region size is too small");
+ return RT_FALSE;
+ }
+ if ((region->size & (region->size - 1U)) != 0U)
+ {
+ LOG_E("Region size is not power of 2");
+ return RT_FALSE;
+ }
+ if (((rt_uint32_t)region->start & (region->size - 1U)) != 0U)
+ {
+ LOG_E("Region is not naturally aligned");
+ return RT_FALSE;
+ }
+ return RT_TRUE;
+}
+
+rt_err_t rt_hw_mpu_init(void)
+{
+ extern rt_mem_region_t static_regions[NUM_STATIC_REGIONS];
+ rt_uint8_t num_mpu_regions;
+ rt_uint8_t num_dynamic_regions;
+ rt_uint8_t index;
+ num_mpu_regions = (rt_uint8_t)((MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos);
+ if (num_mpu_regions == 0U)
+ {
+ LOG_E("Hardware does not support MPU");
+ return RT_ERROR;
+ }
+ if (num_mpu_regions != NUM_MEM_REGIONS)
+ {
+ LOG_E("Incorrect setting of NUM_MEM_REGIONS");
+ LOG_E("NUM_MEM_REGIONS = %d, hardware support %d MPU regions", NUM_MEM_REGIONS, num_mpu_regions);
+ return RT_ERROR;
+ }
+
+ num_dynamic_regions = NUM_DYNAMIC_REGIONS + NUM_EXCLUSIVE_REGIONS;
+ if (num_dynamic_regions + NUM_STATIC_REGIONS > num_mpu_regions)
+ {
+ LOG_E("Insufficient MPU regions: %d hardware MPU regions", num_mpu_regions);
+#ifdef RT_USING_HW_STACK_GUARD
+ 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);
+#else
+ LOG_E("Current configuration requires %d static regions + %d configurable regions + %d exclusive regions", NUM_STATIC_REGIONS, NUM_CONFIGURABLE_REGIONS, NUM_EXCLUSIVE_REGIONS);
+#endif
+ return RT_ERROR;
+ }
+
+ ARM_MPU_Disable();
+ for (index = 0U; index < NUM_STATIC_REGIONS; index++)
+ {
+ if (rt_hw_mpu_region_valid(&(static_regions[index])) == RT_FALSE)
+ {
+ return RT_ERROR;
+ }
+ static_regions[index].attr.rasr = _mpu_rasr(&(static_regions[index]));
+ ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)static_regions[index].start), static_regions[index].attr.rasr);
+ }
+ /* Enable background region. */
+ ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk);
+
+ return RT_EOK;
+}
+
+rt_err_t rt_hw_mpu_add_region(rt_thread_t thread, rt_mem_region_t *region)
+{
+ rt_uint8_t index;
+ rt_mem_region_t *free_region;
+ if (rt_hw_mpu_region_valid(region) == RT_FALSE)
+ {
+ return RT_ERROR;
+ }
+ region->attr.rasr = _mpu_rasr(region);
+ if (thread == RT_NULL)
+ {
+ return RT_EOK;
+ }
+ rt_enter_critical();
+ free_region = rt_mprotect_find_free_region(thread);
+ if (free_region == RT_NULL)
+ {
+ rt_exit_critical();
+ LOG_E("Insufficient regions");
+ return RT_ERROR;
+ }
+ rt_memcpy(free_region, region, sizeof(rt_mem_region_t));
+ if (thread == rt_thread_self())
+ {
+ index = MEM_REGION_TO_MPU_INDEX(thread, free_region);
+ ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)region->start), region->attr.rasr);
+ }
+ rt_exit_critical();
+ return RT_EOK;
+}
+
+rt_err_t rt_hw_mpu_delete_region(rt_thread_t thread, rt_mem_region_t *region)
+{
+ rt_uint8_t index;
+ rt_enter_critical();
+ rt_mem_region_t *found_region = rt_mprotect_find_region(thread, region);
+ if (found_region == RT_NULL)
+ {
+ rt_exit_critical();
+ LOG_E("Region not found");
+ return RT_ERROR;
+ }
+ rt_memset(found_region, 0, sizeof(rt_mem_region_t));
+ if (thread == rt_thread_self())
+ {
+ index = MEM_REGION_TO_MPU_INDEX(thread, found_region);
+ ARM_MPU_ClrRegion(index);
+ }
+ rt_exit_critical();
+ return RT_EOK;
+}
+
+rt_err_t rt_hw_mpu_update_region(rt_thread_t thread, rt_mem_region_t *region)
+{
+ rt_uint8_t index;
+ if (rt_hw_mpu_region_valid(region) == RT_FALSE)
+ {
+ return RT_ERROR;
+ }
+ region->attr.rasr = _mpu_rasr(region);
+ rt_enter_critical();
+ rt_mem_region_t *old_region = rt_mprotect_find_region(thread, region);
+ if (old_region == RT_NULL)
+ {
+ rt_exit_critical();
+ LOG_E("Region not found");
+ return RT_ERROR;
+ }
+ rt_memcpy(old_region, region, sizeof(rt_mem_region_t));
+ if (thread == rt_thread_self())
+ {
+ index = MEM_REGION_TO_MPU_INDEX(thread, old_region);
+ ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)region->start), region->attr.rasr);
+ }
+ rt_exit_critical();
+ return RT_EOK;
+}
+
+rt_err_t rt_hw_mpu_exception_set_hook(rt_hw_mpu_exception_hook_t hook)
+{
+ mem_manage_hook = hook;
+ return RT_EOK;
+}
+
+void rt_hw_mpu_table_switch(rt_thread_t thread)
+{
+ extern rt_mem_exclusive_region_t exclusive_regions[NUM_EXCLUSIVE_REGIONS];
+ rt_uint8_t i;
+ rt_uint8_t index = NUM_STATIC_REGIONS;
+ if (thread->mem_regions != RT_NULL)
+ {
+ for (i = 0U; i < NUM_DYNAMIC_REGIONS; i++)
+ {
+ if (((rt_mem_region_t *)thread->mem_regions)[i].size != 0U)
+ {
+ 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);
+ index += 1U;
+ }
+ }
+ }
+ for (i = 0U; i < NUM_EXCLUSIVE_REGIONS; i++)
+ {
+ if ((exclusive_regions[i].owner != RT_NULL) && (exclusive_regions[i].owner != thread))
+ {
+ ARM_MPU_SetRegion(ARM_MPU_RBAR(index, (rt_uint32_t)(exclusive_regions[i].region.start)), exclusive_regions[i].region.attr.rasr);
+ index += 1U;
+ }
+ }
+ for (; index < NUM_MEM_REGIONS; index++)
+ {
+ ARM_MPU_ClrRegion(index);
+ }
+}
+
+void MemManage_Handler(void)
+{
+ extern rt_mem_region_t static_regions[NUM_STATIC_REGIONS];
+ extern rt_mem_exclusive_region_t exclusive_regions[NUM_EXCLUSIVE_REGIONS];
+ rt_mem_exception_info_t info;
+ rt_int8_t i;
+ rt_memset(&info, 0, sizeof(rt_mem_exception_info_t));
+ info.thread = rt_thread_self();
+ if (SCB->CFSR & SCB_CFSR_MMARVALID_Msk)
+ {
+ info.addr = (void *)(SCB->MMFAR);
+ for (i = NUM_EXCLUSIVE_REGIONS - 1; i >= 0; i--)
+ {
+ 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])))
+ {
+ rt_memcpy(&(info.region), &(exclusive_regions[i]), sizeof(rt_mem_region_t));
+ break;
+ }
+ }
+ if (info.region.size == 0U)
+ {
+ if (info.thread->mem_regions != RT_NULL)
+ {
+ for (i = NUM_DYNAMIC_REGIONS - 1; i >= 0; i--)
+ {
+ 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])))
+ {
+ rt_memcpy(&(info.region), &(((rt_mem_region_t *)info.thread->mem_regions)[i]), sizeof(rt_mem_region_t));
+ break;
+ }
+ }
+ }
+ if (info.region.size == 0U)
+ {
+ for (i = NUM_STATIC_REGIONS - 1; i >= 0; i--)
+ {
+ if (ADDR_IN_REGION(info.addr, &(static_regions[i])))
+ {
+ rt_memcpy(&(info.region), &(static_regions[i]), sizeof(rt_mem_region_t));
+ break;
+ }
+ }
+ }
+ }
+ }
+ info.mmfsr = (SCB->CFSR & SCB_CFSR_MEMFAULTSR_Msk) >> SCB_CFSR_MEMFAULTSR_Pos;
+ if (mem_manage_hook != RT_NULL)
+ {
+ mem_manage_hook(&info);
+ }
+ while (1);
+}
diff --git a/libcpu/arm/cortex-m4/mpu.h b/libcpu/arm/cortex-m4/mpu.h
new file mode 100644
index 0000000000..d2cfd1ac2f
--- /dev/null
+++ b/libcpu/arm/cortex-m4/mpu.h
@@ -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__ */
diff --git a/libcpu/arm/cortex-m4/mputype.h b/libcpu/arm/cortex-m4/mputype.h
new file mode 100644
index 0000000000..083335402e
--- /dev/null
+++ b/libcpu/arm/cortex-m4/mputype.h
@@ -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__ */