From 7c057d03184dae9430973db2e0a9e1bc76eee04a Mon Sep 17 00:00:00 2001 From: hanzhijian Date: Tue, 25 Aug 2026 09:19:51 +0800 Subject: [PATCH] [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 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. --- .../stm32f407-fk407m2-zgt6/board/board.c | 95 +++--- .../stm32f407-fk407m2-zgt6/board/board.h | 25 +- libcpu/arm/cortex-m4/SConscript | 13 + libcpu/arm/cortex-m4/context_gcc.S | 9 + libcpu/arm/cortex-m4/cpuport.c | 159 ++++++---- libcpu/arm/cortex-m4/mpu.c | 288 ++++++++++++++++++ libcpu/arm/cortex-m4/mpu.h | 102 +++++++ libcpu/arm/cortex-m4/mputype.h | 33 ++ 8 files changed, 607 insertions(+), 117 deletions(-) create mode 100644 libcpu/arm/cortex-m4/mpu.c create mode 100644 libcpu/arm/cortex-m4/mpu.h create mode 100644 libcpu/arm/cortex-m4/mputype.h diff --git a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c index 702980ab3e06..34f974369c51 100644 --- a/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c +++ b/bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c @@ -11,50 +11,61 @@ #include #include +#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 3d6b28229710..3fdd7f3afd5b 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 #include #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 3ecbea8de355..2ac22c8e4d59 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 188482ce090d..4fd570b797c6 100644 --- a/libcpu/arm/cortex-m4/context_gcc.S +++ b/libcpu/arm/cortex-m4/context_gcc.S @@ -18,6 +18,8 @@ */ /*@{*/ +#include + .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 5db2fca7f3ec..37733dcd384f 100644 --- a/libcpu/arm/cortex-m4/cpuport.c +++ b/libcpu/arm/cortex-m4/cpuport.c @@ -23,9 +23,12 @@ #ifdef RT_USING_DM #include #endif +#ifdef RT_USING_HW_STACK_GUARD +#include +#endif -#define DBG_TAG "cortex.m4" -#define DBG_LVL DBG_INFO +#define DBG_TAG "cortex.m4" +#define DBG_LVL DBG_INFO #include 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 000000000000..83859766a55f --- /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 +#include + +#define DBG_ENABLE +#define DBG_SECTION_NAME "MEMORY PROTECTION" +#define DBG_LEVEL DBG_ERROR +#include + +#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 000000000000..d2cfd1ac2f92 --- /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 + +#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 000000000000..083335402e36 --- /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__ */