--- /dev/null
+/*\r
+ * FreeRTOS Kernel V10.2.1\r
+ * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://www.FreeRTOS.org\r
+ * http://aws.amazon.com/freertos\r
+ *\r
+ * 1 tab == 4 spaces!\r
+ */\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+/** ARMv7 MPU Details:\r
+ *\r
+ * - ARMv7 MPU requires that the size of a MPU region is a power of 2.\r
+ * - Smallest supported region size is 32 bytes.\r
+ * - Start address of a region must be aligned to an integer multiple of the\r
+ * region size. For example, if the region size is 4 KB(0x1000), the starting\r
+ * address must be N x 0x1000, where N is an integer.\r
+ */\r
+\r
+/**\r
+ * @brief Size of the shared memory region.\r
+ */\r
+#define SHARED_MEMORY_SIZE 32\r
+\r
+/**\r
+ * @brief Memory region shared between two tasks.\r
+ */\r
+static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) );\r
+\r
+/**\r
+ * @brief Memory region used to track Memory Fault intentionally caused by the\r
+ * RO Access task.\r
+ *\r
+ * RO Access task sets ucROTaskFaultTracker[ 0 ] to 1 before accessing illegal\r
+ * memory. Illegal memory access causes Memory Fault and the fault handler\r
+ * checks ucROTaskFaultTracker[ 0 ] to see if this is an expected fault. We\r
+ * recover gracefully from an expected fault by jumping to the next instruction.\r
+ *\r
+ * @note We are declaring a region of 32 bytes even though we need only one.\r
+ * The reason is that the smallest supported MPU region size is 32 bytes.\r
+ */\r
+static volatile uint8_t ucROTaskFaultTracker[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) ) = { 0 };\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Implements the task which has Read Only access to the memory region\r
+ * ucSharedMemory.\r
+ *\r
+ * @param pvParameters[in] Parameters as passed during task creation.\r
+ */\r
+static void prvROAccessTask( void * pvParameters );\r
+\r
+/**\r
+ * @brief Implements the task which has Read Write access to the memory region\r
+ * ucSharedMemory.\r
+ *\r
+ * @param pvParameters[in] Parameters as passed during task creation.\r
+ */\r
+static void prvRWAccessTask( void * pvParameters );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvROAccessTask( void * pvParameters )\r
+{\r
+uint8_t ucVal;\r
+\r
+ /* Unused parameters. */\r
+ ( void ) pvParameters;\r
+\r
+ for( ; ; )\r
+ {\r
+ /* This task has RO access to ucSharedMemory and therefore it can read\r
+ * it but cannot modify it. */\r
+ ucVal = ucSharedMemory[ 0 ];\r
+\r
+ /* Silent compiler warnings about unused variables. */\r
+ ( void ) ucVal;\r
+\r
+ /* Since this task has Read Only access to the ucSharedMemory region,\r
+ * writing to it results in Memory Fault. Set ucROTaskFaultTracker[ 0 ]\r
+ * to 1 to tell the Memory Fault Handler that this is an expected fault.\r
+ * The handler will recover from this fault gracefully by jumping to the\r
+ * next instruction. */\r
+ ucROTaskFaultTracker[ 0 ] = 1;\r
+\r
+ /* Illegal access to generate Memory Fault. */\r
+ ucSharedMemory[ 0 ] = 0;\r
+\r
+ /* Ensure that the above line did generate MemFault and the fault\r
+ * handler did clear the ucROTaskFaultTracker[ 0 ]. */\r
+ configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );\r
+\r
+ #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 )\r
+ {\r
+ /* Generate an SVC to raise the privilege. Since privilege\r
+ * escalation is only allowed from kernel code, this request must\r
+ * get rejected and the task must remain unprivileged. As a result,\r
+ * trying to write to ucSharedMemory will still result in Memory\r
+ * Fault. */\r
+ portRAISE_PRIVILEGE();\r
+\r
+ /* Set ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault\r
+ * Handler that this is an expected fault. The handler will then be\r
+ * able to recover from this fault gracefully by jumping to the\r
+ * next instruction.*/\r
+ ucROTaskFaultTracker[ 0 ] = 1;\r
+\r
+ /* The following must still result in Memory Fault since the task\r
+ * is still running unprivileged. */\r
+ ucSharedMemory[ 0 ] = 0;\r
+\r
+ /* Ensure that the above line did generate MemFault and the fault\r
+ * handler did clear the ucROTaskFaultTracker[ 0 ]. */\r
+ configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );\r
+ }\r
+ #else\r
+ {\r
+ /* Generate an SVC to raise the privilege. Since\r
+ * configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY is not enabled, the\r
+ * task will be able to escalate privilege. */\r
+ portRAISE_PRIVILEGE();\r
+\r
+ /* At this point, the task is running privileged. The following\r
+ * access must not result in Memory Fault. If something goes\r
+ * wrong and we do get a fault, the execution will stop in fault\r
+ * handler as ucROTaskFaultTracker[ 0 ] is not set (i.e.\r
+ * un-expected fault). */\r
+ ucSharedMemory[ 0 ] = 0;\r
+\r
+ /* Lower down the privilege. */\r
+ portSWITCH_TO_USER_MODE();\r
+\r
+ /* Now the task is running unprivileged and therefore an attempt to\r
+ * write to ucSharedMemory will result in a Memory Fault. Set\r
+ * ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault Handler\r
+ * that this is an expected fault. The handler will then be able to\r
+ * recover from this fault gracefully by jumping to the next\r
+ * instruction.*/\r
+ ucROTaskFaultTracker[ 0 ] = 1;\r
+\r
+ /* The following must result in Memory Fault since the task is now\r
+ * running unprivileged. */\r
+ ucSharedMemory[ 0 ] = 0;\r
+\r
+ /* Ensure that the above line did generate MemFault and the fault\r
+ * handler did clear the ucROTaskFaultTracker[ 0 ]. */\r
+ configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );\r
+ }\r
+ #endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */\r
+\r
+ /* Wait for a second. */\r
+ vTaskDelay( pdMS_TO_TICKS( 1000 ) );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvRWAccessTask( void * pvParameters )\r
+{\r
+ /* Unused parameters. */\r
+ ( void ) pvParameters;\r
+\r
+ for( ; ; )\r
+ {\r
+ /* This task has RW access to ucSharedMemory and therefore can write to\r
+ * it. */\r
+ ucSharedMemory[ 0 ] = 0;\r
+\r
+ /* Wait for a second. */\r
+ vTaskDelay( pdMS_TO_TICKS( 1000 ) );\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vStartMPUDemo( void )\r
+{\r
+/**\r
+ * Since stack of a task is protected using MPU, it must satisfy MPU\r
+ * requirements as mentioned at the top of this file.\r
+ */\r
+static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( configMINIMAL_STACK_SIZE * sizeof( StackType_t ) ) ) );\r
+static StackType_t xRWAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( configMINIMAL_STACK_SIZE * sizeof( StackType_t ) ) ) );\r
+TaskParameters_t xROAccessTaskParameters =\r
+{\r
+ .pvTaskCode = prvROAccessTask,\r
+ .pcName = "ROAccess",\r
+ .usStackDepth = configMINIMAL_STACK_SIZE,\r
+ .pvParameters = NULL,\r
+ .uxPriority = tskIDLE_PRIORITY,\r
+ .puxStackBuffer = xROAccessTaskStack,\r
+ .xRegions = {\r
+ { ucSharedMemory, SHARED_MEMORY_SIZE, portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY | portMPU_REGION_EXECUTE_NEVER },\r
+ { ( void * ) ucROTaskFaultTracker, SHARED_MEMORY_SIZE, portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER },\r
+ { 0, 0, 0 },\r
+ }\r
+};\r
+TaskParameters_t xRWAccessTaskParameters =\r
+{\r
+ .pvTaskCode = prvRWAccessTask,\r
+ .pcName = "RWAccess",\r
+ .usStackDepth = configMINIMAL_STACK_SIZE,\r
+ .pvParameters = NULL,\r
+ .uxPriority = tskIDLE_PRIORITY,\r
+ .puxStackBuffer = xRWAccessTaskStack,\r
+ .xRegions = {\r
+ { ucSharedMemory, SHARED_MEMORY_SIZE, portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER},\r
+ { 0, 0, 0 },\r
+ { 0, 0, 0 },\r
+ }\r
+};\r
+\r
+ /* Create an unprivileged task with RO access to ucSharedMemory. */\r
+ xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL );\r
+\r
+ /* Create an unprivileged task with RW access to ucSharedMemory. */\r
+ xTaskCreateRestricted( &( xRWAccessTaskParameters ), NULL );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+portDONT_DISCARD void vHandleMemoryFault( uint32_t * pulFaultStackAddress )\r
+{\r
+uint32_t ulPC;\r
+uint16_t usOffendingInstruction;\r
+\r
+ /* Is this an expected fault? */\r
+ if( ucROTaskFaultTracker[ 0 ] == 1 )\r
+ {\r
+ /* Read program counter. */\r
+ ulPC = pulFaultStackAddress[ 6 ];\r
+\r
+ /* Read the offending instruction. */\r
+ usOffendingInstruction = *( uint16_t * )ulPC;\r
+\r
+ /* From ARM docs:\r
+ * If the value of bits[15:11] of the halfword being decoded is one of\r
+ * the following, the halfword is the first halfword of a 32-bit\r
+ * instruction:\r
+ * - 0b11101.\r
+ * - 0b11110.\r
+ * - 0b11111.\r
+ * Otherwise, the halfword is a 16-bit instruction.\r
+ */\r
+\r
+ /* Extract bits[15:11] of the offending instruction. */\r
+ usOffendingInstruction = usOffendingInstruction & 0xF800;\r
+ usOffendingInstruction = ( usOffendingInstruction >> 11 );\r
+\r
+ /* Determine if the offending instruction is a 32-bit instruction or\r
+ * a 16-bit instruction. */\r
+ if( usOffendingInstruction == 0x001F ||\r
+ usOffendingInstruction == 0x001E ||\r
+ usOffendingInstruction == 0x001D )\r
+ {\r
+ /* Since the offending instruction is a 32-bit instruction,\r
+ * increment the program counter by 4 to move to the next\r
+ * instruction. */\r
+ ulPC += 4;\r
+ }\r
+ else\r
+ {\r
+ /* Since the offending instruction is a 16-bit instruction,\r
+ * increment the program counter by 2 to move to the next\r
+ * instruction. */\r
+ ulPC += 2;\r
+ }\r
+\r
+ /* Save the new program counter on the stack. */\r
+ pulFaultStackAddress[ 6 ] = ulPC;\r
+\r
+ /* Mark the fault as handled. */\r
+ ucROTaskFaultTracker[ 0 ] = 0;\r
+ }\r
+ else\r
+ {\r
+ /* This is an unexpected fault - loop forever. */\r
+ for( ; ; )\r
+ {\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r