From 6790372f908a864c8dee1a4ad84772bad4ed7a22 Mon Sep 17 00:00:00 2001 From: Ching-Hsin Lee Date: Thu, 31 Aug 2023 16:47:16 +0800 Subject: [PATCH] Add mpu_demo in pmp-test --- FreeRTOS/Demo/PMP-test/FreeRTOSConfig.h | 1 + FreeRTOS/Demo/PMP-test/Makefile | 1 + FreeRTOS/Demo/PMP-test/exception_handler.c | 25 +- FreeRTOS/Demo/PMP-test/main.c | 10 +- FreeRTOS/Demo/PMP-test/mpu_demo.c | 271 +++++++++++++++++++++ FreeRTOS/Demo/PMP-test/privilege_apis.c | 21 +- FreeRTOS/Demo/PMP-test/privilege_apis.h | 2 +- FreeRTOS/Source | 2 +- 8 files changed, 322 insertions(+), 11 deletions(-) create mode 100644 FreeRTOS/Demo/PMP-test/mpu_demo.c diff --git a/FreeRTOS/Demo/PMP-test/FreeRTOSConfig.h b/FreeRTOS/Demo/PMP-test/FreeRTOSConfig.h index 9151f66c429..936930fd0c0 100644 --- a/FreeRTOS/Demo/PMP-test/FreeRTOSConfig.h +++ b/FreeRTOS/Demo/PMP-test/FreeRTOSConfig.h @@ -108,5 +108,6 @@ to exclude the API function. */ #define configENABLE_MPU 1 #define configUSE_MPU_WRAPPERS_V1 1 +#define configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY 1 #endif /* FREERTOS_CONFIG_H */ diff --git a/FreeRTOS/Demo/PMP-test/Makefile b/FreeRTOS/Demo/PMP-test/Makefile index dc679cb0bc6..bd58c7a652f 100644 --- a/FreeRTOS/Demo/PMP-test/Makefile +++ b/FreeRTOS/Demo/PMP-test/Makefile @@ -43,6 +43,7 @@ SRCS = main.c \ pmp_stage0_demo.c \ pmp_stage1_demo.c \ pmp_stage2_demo.c \ + mpu_demo.c \ $(RTOS_SOURCE_DIR)/event_groups.c \ $(RTOS_SOURCE_DIR)/list.c \ $(RTOS_SOURCE_DIR)/queue.c \ diff --git a/FreeRTOS/Demo/PMP-test/exception_handler.c b/FreeRTOS/Demo/PMP-test/exception_handler.c index 55ee8751164..68e4e4d6162 100644 --- a/FreeRTOS/Demo/PMP-test/exception_handler.c +++ b/FreeRTOS/Demo/PMP-test/exception_handler.c @@ -46,6 +46,11 @@ static void user_mode_trap_handler( int xSyscallNumber ) char temp[64]; uint32_t * uxTaskStackPointer; + #if ( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) + extern uint32_t __syscalls_flash_start__[]; + extern uint32_t __syscalls_flash_end__[]; + #endif + /* Get the mstatus from the stack. */ uxTaskStackPointer = pxCurrentTCB[ 0 ]; @@ -56,11 +61,20 @@ static void user_mode_trap_handler( int xSyscallNumber ) } else if( xSyscallNumber == portECALL_RAISE_PRIORITY ) { + #if ( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) + if( ( uxTaskStackPointer[ 0 ] >= ( uint32_t ) __syscalls_flash_start__ ) && + ( uxTaskStackPointer[ 0 ] <= ( uint32_t ) __syscalls_flash_end__ ) ) + #endif /* Update the mstatus.MPP on to stack to return to M-mode. */ - uxTaskStackPointer[ 30 ] = uxTaskStackPointer[ 30 ] | ( 0x3 << 11 ); + { + uxTaskStackPointer[ 30 ] = uxTaskStackPointer[ 30 ] | ( 0x3 << 11 ); + } } } +#define SHARED_MEMORY_SIZE 32 +extern volatile uint8_t ucROTaskFaultTracker[ SHARED_MEMORY_SIZE ]; +extern void vHandleMemoryFault( uint32_t * pulFaultStackAddress ); void freertos_risc_v_application_exception_handler( void ) { volatile int xSyscallNumber; @@ -98,9 +112,18 @@ void freertos_risc_v_application_exception_handler( void ) switch( mCause ) { + case METAL_II_EXCEPTION_CODE: + /* User mode tries to set the priority. */ + break; case METAL_LAF_EXCEPTION_CODE: case METAL_SAMOAF_EXCEPTION_CODE : /* memory access violation. */ xMemoryAcceccExceptionFlag = 1; + // vHandleMemoryFault( uxTaskStackPointer ); + if( ucROTaskFaultTracker[ 0 ] == 1 ) + { + /* Mark the fault as handled. */ + ucROTaskFaultTracker[ 0 ] = 0; + } break; case METAL_ECALL_U_EXCEPTION_CODE : /* ECALL from u-mode. */ user_mode_trap_handler( xSyscallNumber ); diff --git a/FreeRTOS/Demo/PMP-test/main.c b/FreeRTOS/Demo/PMP-test/main.c index a68856446f3..5c8256d2f3e 100644 --- a/FreeRTOS/Demo/PMP-test/main.c +++ b/FreeRTOS/Demo/PMP-test/main.c @@ -31,7 +31,7 @@ /* Run a simple demo just prints 'Blink' */ #define mainVECTOR_MODE_DIRECT 1 -#define mainPMP_DEMO_STAGE 2 +#define mainPMP_DEMO_STAGE 3 extern void freertos_risc_v_trap_handler( void ); extern void freertos_vector_table( void ); @@ -44,12 +44,13 @@ void vApplicationTickHook( void ); extern int pmp_stage0_demo( void ); extern int pmp_stage1_demo( void ); extern int pmp_stage2_demo( void ); +extern void vStartMPUDemo( void ); /*-----------------------------------------------------------*/ int main( void ) { - int ret; + int ret = 0; // trap handler initialization #if( mainVECTOR_MODE_DIRECT == 1 ) { @@ -66,8 +67,11 @@ int main( void ) ret = pmp_stage0_demo(); #elif mainPMP_DEMO_STAGE == 1 ret = pmp_stage1_demo(); -#else +#elif mainPMP_DEMO_STAGE == 2 ret = pmp_stage2_demo(); +#else + vStartMPUDemo(); + vTaskStartScheduler(); #endif return ret; diff --git a/FreeRTOS/Demo/PMP-test/mpu_demo.c b/FreeRTOS/Demo/PMP-test/mpu_demo.c new file mode 100644 index 00000000000..a6075ea7191 --- /dev/null +++ b/FreeRTOS/Demo/PMP-test/mpu_demo.c @@ -0,0 +1,271 @@ +/* + * FreeRTOS V202212.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "FreeRTOSConfig.h" +#include "task.h" + +#include + +/** ARMv7 MPU Details: + * + * - ARMv7 MPU requires that the size of a MPU region is a power of 2. + * - Smallest supported region size is 32 bytes. + * - Start address of a region must be aligned to an integer multiple of the + * region size. For example, if the region size is 4 KB(0x1000), the starting + * address must be N x 0x1000, where N is an integer. + */ + +/** + * @brief Size of the shared memory region. + */ +#define SHARED_MEMORY_SIZE 32 + +/** + * @brief Memory region shared between two tasks. + */ +static volatile uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) ); + +/** + * @brief Memory region used to track Memory Fault intentionally caused by the + * RO Access task. + * + * RO Access task sets ucROTaskFaultTracker[ 0 ] to 1 before accessing illegal + * memory. Illegal memory access causes Memory Fault and the fault handler + * checks ucROTaskFaultTracker[ 0 ] to see if this is an expected fault. We + * recover gracefully from an expected fault by jumping to the next instruction. + * + * @note We are declaring a region of 32 bytes even though we need only one. + * The reason is that the smallest supported MPU region size is 32 bytes. + */ +volatile uint8_t ucROTaskFaultTracker[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) ) = { 0 }; +/*-----------------------------------------------------------*/ + +/** + * @brief Implements the task which has Read Only access to the memory region + * ucSharedMemory. + * + * @param pvParameters[in] Parameters as passed during task creation. + */ +static void prvROAccessTask( void * pvParameters ); + +/** + * @brief Implements the task which has Read Write access to the memory region + * ucSharedMemory. + * + * @param pvParameters[in] Parameters as passed during task creation. + */ +static void prvRWAccessTask( void * pvParameters ); + +/*-----------------------------------------------------------*/ + +static void prvROAccessTask( void * pvParameters ) +{ + uint8_t ucVal; + + /* Unused parameters. */ + ( void ) pvParameters; + + portRAISE_PRIVILEGE(); + + for( ; ; ) + { + /* This task has RO access to ucSharedMemory and therefore it can read + * it but cannot modify it. */ + ucVal = ucSharedMemory[ 0 ]; + + /* Silent compiler warnings about unused variables. */ + ( void ) ucVal; + + /* Since this task has Read Only access to the ucSharedMemory region, + * writing to it results in Memory Fault. Set ucROTaskFaultTracker[ 0 ] + * to 1 to tell the Memory Fault Handler that this is an expected fault. + * The handler will recover from this fault gracefully by jumping to the + * next instruction. */ + ucROTaskFaultTracker[ 0 ] = 1; + + /* Illegal access to generate Memory Fault. */ + vSendString( "Write memory in RO task sould fail!" ); + ucSharedMemory[ 0 ] = 0; + + /* Ensure that the above line did generate MemFault and the fault + * handler did clear the ucROTaskFaultTracker[ 0 ]. */ + /*configASSERT( ucROTaskFaultTracker[ 0 ] == 0 ); */ + + #if ( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) + { + /* Generate an SVC to raise the privilege. Since privilege + * escalation is only allowed from kernel code, this request must + * get rejected and the task must remain unprivileged. As a result, + * trying to write to ucSharedMemory will still result in Memory + * Fault. */ + portRAISE_PRIVILEGE(); + + /* Set ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault + * Handler that this is an expected fault. The handler will then be + * able to recover from this fault gracefully by jumping to the + * next instruction.*/ + ucROTaskFaultTracker[ 0 ] = 1; + + /* The following must still result in Memory Fault since the task + * is still running unprivileged. */ + vSendString( "Write memory in RO task sould fail!" ); + ucSharedMemory[ 0 ] = 0; + + /* Ensure that the above line did generate MemFault and the fault + * handler did clear the ucROTaskFaultTracker[ 0 ]. */ + configASSERT( ucROTaskFaultTracker[ 0 ] == 0 ); + } + #else /* if ( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */ + { + /* Generate an SVC to raise the privilege. Since + * configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY is not enabled, the + * task will be able to escalate privilege. */ + portRAISE_PRIVILEGE(); + + /* At this point, the task is running privileged. The following + * access must not result in Memory Fault. If something goes + * wrong and we do get a fault, the execution will stop in fault + * handler as ucROTaskFaultTracker[ 0 ] is not set (i.e. + * un-expected fault). */ + vSendString( "Write memory in privileged RO task sould not fail!" ); + ucSharedMemory[ 0 ] = 0; + + /* Lower down the privilege. */ + portSWITCH_TO_USER_MODE(); + + /* Now the task is running unprivileged and therefore an attempt to + * write to ucSharedMemory will result in a Memory Fault. Set + * ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault Handler + * that this is an expected fault. The handler will then be able to + * recover from this fault gracefully by jumping to the next + * instruction.*/ + ucROTaskFaultTracker[ 0 ] = 1; + + /* The following must result in Memory Fault since the task is now + * running unprivileged. */ + ucSharedMemory[ 0 ] = 0; + + /* Ensure that the above line did generate MemFault and the fault + * handler did clear the ucROTaskFaultTracker[ 0 ]. */ + configASSERT( ucROTaskFaultTracker[ 0 ] == 0 ); + } + #endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */ + + /* Wait for a second. */ + vTaskDelay( pdMS_TO_TICKS( 1000 ) ); + } +} + +/*-----------------------------------------------------------*/ + +static void prvRWAccessTask( void * pvParameters ) +{ + /* Unused parameters. */ + ( void ) pvParameters; + + for( ; ; ) + { + vSendString( "Write memory in RW task sould not fail!" ); + /* This task has RW access to ucSharedMemory and therefore can write to + * it. */ + ucSharedMemory[ 0 ] = 0; + + /* Wait for a second. */ + vTaskDelay( pdMS_TO_TICKS( 1000 ) ); + } +} + +/*-----------------------------------------------------------*/ + +void vStartMPUDemo( void ) +{ +/** + * Since stack of a task is protected using MPU, it must satisfy MPU + * requirements as mentioned at the top of this file. + */ + static StackType_t xROAccessTaskStack[ 2048 ] + __attribute__( ( aligned( 1024 * sizeof( StackType_t ) ) ) ); + static StackType_t xRWAccessTaskStack[ 2048 ] + __attribute__( ( aligned( 1024 * sizeof( StackType_t ) ) ) ); + + TaskParameters_t xROAccessTaskParameters = + { + .pvTaskCode = prvROAccessTask, + .pcName = "ROAccess", + .usStackDepth = 2048, + .pvParameters = NULL, + .uxPriority = tskIDLE_PRIORITY, + .puxStackBuffer = xROAccessTaskStack, + .xRegions = + { + { ucSharedMemory, SHARED_MEMORY_SIZE, + portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY | + portMPU_REGION_EXECUTE_NEVER }, + { ( void * ) ucROTaskFaultTracker, SHARED_MEMORY_SIZE, + portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER }, + { 0, 0, 0}, + } + }; + + TaskParameters_t xRWAccessTaskParameters = + { + .pvTaskCode = prvRWAccessTask, + .pcName = "RWAccess", + .usStackDepth = 2048, + .pvParameters = NULL, + .uxPriority = tskIDLE_PRIORITY, + .puxStackBuffer = xRWAccessTaskStack, + .xRegions = + { + { ucSharedMemory, SHARED_MEMORY_SIZE, + portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER }, + { 0, 0, 0 }, + { 0, 0, 0 }, + } + }; + + /* Create an unprivileged task with RO access to ucSharedMemory. */ + xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL ); + + /* Create an unprivileged task with RW access to ucSharedMemory. */ + xTaskCreateRestricted( &( xRWAccessTaskParameters ), NULL ); +} +/*-----------------------------------------------------------*/ + +portDONT_DISCARD void vHandleMemoryFault( uint32_t * pulFaultStackAddress ) +{ + ( void )pulFaultStackAddress; + + /* Is this an expected fault? */ + if( ucROTaskFaultTracker[ 0 ] == 1 ) + { + /* Mark the fault as handled. */ + ucROTaskFaultTracker[ 0 ] = 0; + } +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS/Demo/PMP-test/privilege_apis.c b/FreeRTOS/Demo/PMP-test/privilege_apis.c index b52caf81f22..49d924c7dfe 100644 --- a/FreeRTOS/Demo/PMP-test/privilege_apis.c +++ b/FreeRTOS/Demo/PMP-test/privilege_apis.c @@ -70,6 +70,8 @@ BaseType_t portIS_PRIVILEGED( void ) return ret; } +#if 0 +/* Use macro implementation in port. */ void portRAISE_PRIVILEGE( void ) { if( xPortStarted == pdTRUE ) @@ -78,13 +80,21 @@ void portRAISE_PRIVILEGE( void ) __internal_syscall_0( portECALL_RAISE_PRIORITY ); } } +#endif void portRESET_PRIVILEGE( void ) { + /* User mode still using the same stack. */ + BaseType_t xIsPrivileged; + + /* User mode entry point is the return address. */ if( xPortStarted == pdTRUE ) { - /* User mode entry point is the return address. */ - prvPrivilegeDropToMode( METAL_PRIVILEGE_USER ); + xIsPrivileged = portIS_PRIVILEGED(); + if( xIsPrivileged == pdTRUE ) + { + prvPrivilegeDropToMode( METAL_PRIVILEGE_USER ); + } } } @@ -97,8 +107,9 @@ void portSWITCH_TO_USER_MODE( void ) if( xPortStarted == pdTRUE ) { xIsPrivileged = portIS_PRIVILEGED(); - while( xIsPrivileged == pdFALSE ); - - prvPrivilegeDropToMode( METAL_PRIVILEGE_USER ); + if( xIsPrivileged == pdTRUE ) + { + prvPrivilegeDropToMode( METAL_PRIVILEGE_USER ); + } } } diff --git a/FreeRTOS/Demo/PMP-test/privilege_apis.h b/FreeRTOS/Demo/PMP-test/privilege_apis.h index 87847891268..8ff49a76248 100644 --- a/FreeRTOS/Demo/PMP-test/privilege_apis.h +++ b/FreeRTOS/Demo/PMP-test/privilege_apis.h @@ -4,7 +4,7 @@ #include "FreeRTOS.h" BaseType_t portIS_PRIVILEGED( void ); -void portRAISE_PRIVILEGE( void ); +// void portRAISE_PRIVILEGE( void ); void portRESET_PRIVILEGE( void ); void portSWITCH_TO_USER_MODE( void ); diff --git a/FreeRTOS/Source b/FreeRTOS/Source index c069f16a7d0..e89548643ea 160000 --- a/FreeRTOS/Source +++ b/FreeRTOS/Source @@ -1 +1 @@ -Subproject commit c069f16a7d0e11d4529b40f248c01f891a3402d2 +Subproject commit e89548643ea9fd65de76a3d9d3694f54e728c677