2 * FreeRTOS Kernel V10.3.0
\r
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
28 /* FreeRTOS includes. */
\r
29 #include "FreeRTOS.h"
\r
32 /** ARMv7 MPU Details:
\r
34 * - ARMv7 MPU requires that the size of a MPU region is a power of 2.
\r
35 * - Smallest supported region size is 32 bytes.
\r
36 * - Start address of a region must be aligned to an integer multiple of the
\r
37 * region size. For example, if the region size is 4 KB(0x1000), the starting
\r
38 * address must be N x 0x1000, where N is an integer.
\r
42 * @brief Size of the shared memory region.
\r
44 #define SHARED_MEMORY_SIZE 32
\r
47 * @brief Memory region shared between two tasks.
\r
49 static uint8_t ucSharedMemory[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) );
\r
52 * @brief Memory region used to track Memory Fault intentionally caused by the
\r
55 * RO Access task sets ucROTaskFaultTracker[ 0 ] to 1 before accessing illegal
\r
56 * memory. Illegal memory access causes Memory Fault and the fault handler
\r
57 * checks ucROTaskFaultTracker[ 0 ] to see if this is an expected fault. We
\r
58 * recover gracefully from an expected fault by jumping to the next instruction.
\r
60 * @note We are declaring a region of 32 bytes even though we need only one.
\r
61 * The reason is that the smallest supported MPU region size is 32 bytes.
\r
63 static volatile uint8_t ucROTaskFaultTracker[ SHARED_MEMORY_SIZE ] __attribute__( ( aligned( SHARED_MEMORY_SIZE ) ) ) = { 0 };
\r
64 /*-----------------------------------------------------------*/
\r
67 * @brief Implements the task which has Read Only access to the memory region
\r
70 * @param pvParameters[in] Parameters as passed during task creation.
\r
72 static void prvROAccessTask( void * pvParameters );
\r
75 * @brief Implements the task which has Read Write access to the memory region
\r
78 * @param pvParameters[in] Parameters as passed during task creation.
\r
80 static void prvRWAccessTask( void * pvParameters );
\r
82 /*-----------------------------------------------------------*/
\r
84 static void prvROAccessTask( void * pvParameters )
\r
88 /* Unused parameters. */
\r
89 ( void ) pvParameters;
\r
93 /* This task has RO access to ucSharedMemory and therefore it can read
\r
94 * it but cannot modify it. */
\r
95 ucVal = ucSharedMemory[ 0 ];
\r
97 /* Silent compiler warnings about unused variables. */
\r
100 /* Since this task has Read Only access to the ucSharedMemory region,
\r
101 * writing to it results in Memory Fault. Set ucROTaskFaultTracker[ 0 ]
\r
102 * to 1 to tell the Memory Fault Handler that this is an expected fault.
\r
103 * The handler will recover from this fault gracefully by jumping to the
\r
104 * next instruction. */
\r
105 ucROTaskFaultTracker[ 0 ] = 1;
\r
107 /* Illegal access to generate Memory Fault. */
\r
108 ucSharedMemory[ 0 ] = 0;
\r
110 /* Ensure that the above line did generate MemFault and the fault
\r
111 * handler did clear the ucROTaskFaultTracker[ 0 ]. */
\r
112 configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );
\r
114 #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 )
\r
116 /* Generate an SVC to raise the privilege. Since privilege
\r
117 * escalation is only allowed from kernel code, this request must
\r
118 * get rejected and the task must remain unprivileged. As a result,
\r
119 * trying to write to ucSharedMemory will still result in Memory
\r
121 portRAISE_PRIVILEGE();
\r
123 /* Set ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault
\r
124 * Handler that this is an expected fault. The handler will then be
\r
125 * able to recover from this fault gracefully by jumping to the
\r
126 * next instruction.*/
\r
127 ucROTaskFaultTracker[ 0 ] = 1;
\r
129 /* The following must still result in Memory Fault since the task
\r
130 * is still running unprivileged. */
\r
131 ucSharedMemory[ 0 ] = 0;
\r
133 /* Ensure that the above line did generate MemFault and the fault
\r
134 * handler did clear the ucROTaskFaultTracker[ 0 ]. */
\r
135 configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );
\r
139 /* Generate an SVC to raise the privilege. Since
\r
140 * configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY is not enabled, the
\r
141 * task will be able to escalate privilege. */
\r
142 portRAISE_PRIVILEGE();
\r
144 /* At this point, the task is running privileged. The following
\r
145 * access must not result in Memory Fault. If something goes
\r
146 * wrong and we do get a fault, the execution will stop in fault
\r
147 * handler as ucROTaskFaultTracker[ 0 ] is not set (i.e.
\r
148 * un-expected fault). */
\r
149 ucSharedMemory[ 0 ] = 0;
\r
151 /* Lower down the privilege. */
\r
152 portSWITCH_TO_USER_MODE();
\r
154 /* Now the task is running unprivileged and therefore an attempt to
\r
155 * write to ucSharedMemory will result in a Memory Fault. Set
\r
156 * ucROTaskFaultTracker[ 0 ] to 1 to tell the Memory Fault Handler
\r
157 * that this is an expected fault. The handler will then be able to
\r
158 * recover from this fault gracefully by jumping to the next
\r
160 ucROTaskFaultTracker[ 0 ] = 1;
\r
162 /* The following must result in Memory Fault since the task is now
\r
163 * running unprivileged. */
\r
164 ucSharedMemory[ 0 ] = 0;
\r
166 /* Ensure that the above line did generate MemFault and the fault
\r
167 * handler did clear the ucROTaskFaultTracker[ 0 ]. */
\r
168 configASSERT( ucROTaskFaultTracker[ 0 ] == 0 );
\r
170 #endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */
\r
172 /* Wait for a second. */
\r
173 vTaskDelay( pdMS_TO_TICKS( 1000 ) );
\r
176 /*-----------------------------------------------------------*/
\r
178 static void prvRWAccessTask( void * pvParameters )
\r
180 /* Unused parameters. */
\r
181 ( void ) pvParameters;
\r
185 /* This task has RW access to ucSharedMemory and therefore can write to
\r
187 ucSharedMemory[ 0 ] = 0;
\r
189 /* Wait for a second. */
\r
190 vTaskDelay( pdMS_TO_TICKS( 1000 ) );
\r
193 /*-----------------------------------------------------------*/
\r
195 void vStartMPUDemo( void )
\r
198 * Since stack of a task is protected using MPU, it must satisfy MPU
\r
199 * requirements as mentioned at the top of this file.
\r
201 static StackType_t xROAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( configMINIMAL_STACK_SIZE * sizeof( StackType_t ) ) ) );
\r
202 static StackType_t xRWAccessTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( configMINIMAL_STACK_SIZE * sizeof( StackType_t ) ) ) );
\r
203 TaskParameters_t xROAccessTaskParameters =
\r
205 .pvTaskCode = prvROAccessTask,
\r
206 .pcName = "ROAccess",
\r
207 .usStackDepth = configMINIMAL_STACK_SIZE,
\r
208 .pvParameters = NULL,
\r
209 .uxPriority = tskIDLE_PRIORITY,
\r
210 .puxStackBuffer = xROAccessTaskStack,
\r
212 { ucSharedMemory, SHARED_MEMORY_SIZE, portMPU_REGION_PRIVILEGED_READ_WRITE_UNPRIV_READ_ONLY | portMPU_REGION_EXECUTE_NEVER },
\r
213 { ( void * ) ucROTaskFaultTracker, SHARED_MEMORY_SIZE, portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER },
\r
217 TaskParameters_t xRWAccessTaskParameters =
\r
219 .pvTaskCode = prvRWAccessTask,
\r
220 .pcName = "RWAccess",
\r
221 .usStackDepth = configMINIMAL_STACK_SIZE,
\r
222 .pvParameters = NULL,
\r
223 .uxPriority = tskIDLE_PRIORITY,
\r
224 .puxStackBuffer = xRWAccessTaskStack,
\r
226 { ucSharedMemory, SHARED_MEMORY_SIZE, portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER},
\r
232 /* Create an unprivileged task with RO access to ucSharedMemory. */
\r
233 xTaskCreateRestricted( &( xROAccessTaskParameters ), NULL );
\r
235 /* Create an unprivileged task with RW access to ucSharedMemory. */
\r
236 xTaskCreateRestricted( &( xRWAccessTaskParameters ), NULL );
\r
238 /*-----------------------------------------------------------*/
\r
240 portDONT_DISCARD void vHandleMemoryFault( uint32_t * pulFaultStackAddress )
\r
243 uint16_t usOffendingInstruction;
\r
245 /* Is this an expected fault? */
\r
246 if( ucROTaskFaultTracker[ 0 ] == 1 )
\r
248 /* Read program counter. */
\r
249 ulPC = pulFaultStackAddress[ 6 ];
\r
251 /* Read the offending instruction. */
\r
252 usOffendingInstruction = *( uint16_t * )ulPC;
\r
255 * If the value of bits[15:11] of the halfword being decoded is one of
\r
256 * the following, the halfword is the first halfword of a 32-bit
\r
261 * Otherwise, the halfword is a 16-bit instruction.
\r
264 /* Extract bits[15:11] of the offending instruction. */
\r
265 usOffendingInstruction = usOffendingInstruction & 0xF800;
\r
266 usOffendingInstruction = ( usOffendingInstruction >> 11 );
\r
268 /* Determine if the offending instruction is a 32-bit instruction or
\r
269 * a 16-bit instruction. */
\r
270 if( usOffendingInstruction == 0x001F ||
\r
271 usOffendingInstruction == 0x001E ||
\r
272 usOffendingInstruction == 0x001D )
\r
274 /* Since the offending instruction is a 32-bit instruction,
\r
275 * increment the program counter by 4 to move to the next
\r
281 /* Since the offending instruction is a 16-bit instruction,
\r
282 * increment the program counter by 2 to move to the next
\r
287 /* Save the new program counter on the stack. */
\r
288 pulFaultStackAddress[ 6 ] = ulPC;
\r
290 /* Mark the fault as handled. */
\r
291 ucROTaskFaultTracker[ 0 ] = 0;
\r
295 /* This is an unexpected fault - loop forever. */
\r
301 /*-----------------------------------------------------------*/
\r