1 /*
2 * FreeRTOS Kernel V10.6.2
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
26 *
27 */
28
29 /* Standard includes. */
30 #include <stdlib.h>
31
32 /* Scheduler includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35
36 #ifndef configUNIQUE_INTERRUPT_PRIORITIES
37 #error configUNIQUE_INTERRUPT_PRIORITIES must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
38 #endif
39
40 #ifndef configSETUP_TICK_INTERRUPT
41 #error configSETUP_TICK_INTERRUPT() must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
42 #endif /* configSETUP_TICK_INTERRUPT */
43
44 #ifndef configMAX_API_CALL_INTERRUPT_PRIORITY
45 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
46 #endif
47
48 #if configMAX_API_CALL_INTERRUPT_PRIORITY == 0
49 #error configMAX_API_CALL_INTERRUPT_PRIORITY must not be set to 0
50 #endif
51
52 #if configMAX_API_CALL_INTERRUPT_PRIORITY > configUNIQUE_INTERRUPT_PRIORITIES
53 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be less than or equal to configUNIQUE_INTERRUPT_PRIORITIES as the lower the numeric priority value the higher the logical interrupt priority
54 #endif
55
56 #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
57 /* Check the configuration. */
58 #if( configMAX_PRIORITIES > 32 )
59 #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
60 #endif
61 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
62
63 /* In case security extensions are implemented. */
64 #if configMAX_API_CALL_INTERRUPT_PRIORITY <= ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
65 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be greater than ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
66 #endif
67
68 /* Some vendor specific files default configCLEAR_TICK_INTERRUPT() in
69 portmacro.h. */
70 #ifndef configCLEAR_TICK_INTERRUPT
71 #define configCLEAR_TICK_INTERRUPT()
72 #endif
73
74 /* A critical section is exited when the critical section nesting count reaches
75 this value. */
76 #define portNO_CRITICAL_NESTING ( ( size_t ) 0 )
77
78 /* In all GICs 255 can be written to the priority mask register to unmask all
79 (but the lowest) interrupt priority. */
80 #define portUNMASK_VALUE ( 0xFFUL )
81
82 /* Tasks are not created with a floating point context, but can be given a
83 floating point context after they have been created. A variable is stored as
84 part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
85 does not have an FPU context, or any other value if the task does have an FPU
86 context. */
87 #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
88
89 /* Constants required to setup the initial task context. */
90 #define portSP_ELx ( ( StackType_t ) 0x01 )
91 #define portSP_EL0 ( ( StackType_t ) 0x00 )
92
93 #if defined( GUEST )
94 #define portEL1 ( ( StackType_t ) 0x04 )
95 #define portINITIAL_PSTATE ( portEL1 | portSP_EL0 )
96 #else
97 #define portEL3 ( ( StackType_t ) 0x0c )
98 /* At the time of writing, the BSP only supports EL3. */
99 #define portINITIAL_PSTATE ( portEL3 | portSP_EL0 )
100 #endif
101
102 /* Masks all bits in the APSR other than the mode bits. */
103 #define portAPSR_MODE_BITS_MASK ( 0x0C )
104
105 /* The I bit in the DAIF bits. */
106 #define portDAIF_I ( 0x80 )
107
108 /* Macro to unmask all interrupt priorities. */
109 /* s3_0_c4_c6_0 is ICC_PMR_EL1. */
110 #define portCLEAR_INTERRUPT_MASK() \
111 { \
112 __asm volatile ( "MSR DAIFSET, #2 \n" \
113 "DSB SY \n" \
114 "ISB SY \n" \
115 "MSR s3_0_c4_c6_0, %0 \n" \
116 "DSB SY \n" \
117 "ISB SY \n" \
118 "MSR DAIFCLR, #2 \n" \
119 "DSB SY \n" \
120 "ISB SY \n" \
121 ::"r"( portUNMASK_VALUE ) ); \
122 }
123
124 /*-----------------------------------------------------------*/
125
126 /*
127 * Starts the first task executing. This function is necessarily written in
128 * assembly code so is implemented in portASM.s.
129 */
130 extern void vPortRestoreTaskContext( void );
131
132 /*-----------------------------------------------------------*/
133
134 /* A variable is used to keep track of the critical section nesting. This
135 variable has to be stored as part of the task context and must be initialised to
136 a non zero value to ensure interrupts don't inadvertently become unmasked before
137 the scheduler starts. As it is stored as part of the task context it will
138 automatically be set to 0 when the first task is started. */
139 volatile uint64_t ullCriticalNesting = 9999ULL;
140
141 /* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero
142 then floating point context must be saved and restored for the task. */
143 uint64_t ullPortTaskHasFPUContext = pdFALSE;
144
145 /* Set to 1 to pend a context switch from an ISR. */
146 uint64_t ullPortYieldRequired = pdFALSE;
147
148 /* Counts the interrupt nesting depth. A context switch is only performed if
149 if the nesting depth is 0. */
150 uint64_t ullPortInterruptNesting = 0;
151
152 /* Used in the ASM code. */
153 __attribute__(( used )) const uint64_t ullMaxAPIPriorityMask = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
154
155 /*-----------------------------------------------------------*/
156
157 /*
158 * See header file for description.
159 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)160 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
161 {
162 /* Setup the initial stack of the task. The stack is set exactly as
163 expected by the portRESTORE_CONTEXT() macro. */
164
165 /* First all the general purpose registers. */
166 pxTopOfStack--;
167 *pxTopOfStack = 0x0101010101010101ULL; /* R1 */
168 pxTopOfStack--;
169 *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
170 pxTopOfStack--;
171 *pxTopOfStack = 0x0303030303030303ULL; /* R3 */
172 pxTopOfStack--;
173 *pxTopOfStack = 0x0202020202020202ULL; /* R2 */
174 pxTopOfStack--;
175 *pxTopOfStack = 0x0505050505050505ULL; /* R5 */
176 pxTopOfStack--;
177 *pxTopOfStack = 0x0404040404040404ULL; /* R4 */
178 pxTopOfStack--;
179 *pxTopOfStack = 0x0707070707070707ULL; /* R7 */
180 pxTopOfStack--;
181 *pxTopOfStack = 0x0606060606060606ULL; /* R6 */
182 pxTopOfStack--;
183 *pxTopOfStack = 0x0909090909090909ULL; /* R9 */
184 pxTopOfStack--;
185 *pxTopOfStack = 0x0808080808080808ULL; /* R8 */
186 pxTopOfStack--;
187 *pxTopOfStack = 0x1111111111111111ULL; /* R11 */
188 pxTopOfStack--;
189 *pxTopOfStack = 0x1010101010101010ULL; /* R10 */
190 pxTopOfStack--;
191 *pxTopOfStack = 0x1313131313131313ULL; /* R13 */
192 pxTopOfStack--;
193 *pxTopOfStack = 0x1212121212121212ULL; /* R12 */
194 pxTopOfStack--;
195 *pxTopOfStack = 0x1515151515151515ULL; /* R15 */
196 pxTopOfStack--;
197 *pxTopOfStack = 0x1414141414141414ULL; /* R14 */
198 pxTopOfStack--;
199 *pxTopOfStack = 0x1717171717171717ULL; /* R17 */
200 pxTopOfStack--;
201 *pxTopOfStack = 0x1616161616161616ULL; /* R16 */
202 pxTopOfStack--;
203 *pxTopOfStack = 0x1919191919191919ULL; /* R19 */
204 pxTopOfStack--;
205 *pxTopOfStack = 0x1818181818181818ULL; /* R18 */
206 pxTopOfStack--;
207 *pxTopOfStack = 0x2121212121212121ULL; /* R21 */
208 pxTopOfStack--;
209 *pxTopOfStack = 0x2020202020202020ULL; /* R20 */
210 pxTopOfStack--;
211 *pxTopOfStack = 0x2323232323232323ULL; /* R23 */
212 pxTopOfStack--;
213 *pxTopOfStack = 0x2222222222222222ULL; /* R22 */
214 pxTopOfStack--;
215 *pxTopOfStack = 0x2525252525252525ULL; /* R25 */
216 pxTopOfStack--;
217 *pxTopOfStack = 0x2424242424242424ULL; /* R24 */
218 pxTopOfStack--;
219 *pxTopOfStack = 0x2727272727272727ULL; /* R27 */
220 pxTopOfStack--;
221 *pxTopOfStack = 0x2626262626262626ULL; /* R26 */
222 pxTopOfStack--;
223 *pxTopOfStack = 0x2929292929292929ULL; /* R29 */
224 pxTopOfStack--;
225 *pxTopOfStack = 0x2828282828282828ULL; /* R28 */
226 pxTopOfStack--;
227 *pxTopOfStack = ( StackType_t ) 0x00; /* XZR - has no effect, used so there are an even number of registers. */
228 pxTopOfStack--;
229 *pxTopOfStack = ( StackType_t ) 0x00; /* R30 - procedure call link register. */
230 pxTopOfStack--;
231
232 *pxTopOfStack = portINITIAL_PSTATE;
233 pxTopOfStack--;
234
235 *pxTopOfStack = ( StackType_t ) pxCode; /* Exception return address. */
236 pxTopOfStack--;
237
238 /* The task will start with a critical nesting count of 0 as interrupts are
239 enabled. */
240 *pxTopOfStack = portNO_CRITICAL_NESTING;
241 pxTopOfStack--;
242
243 /* The task will start without a floating point context. A task that uses
244 the floating point hardware must call vPortTaskUsesFPU() before executing
245 any floating point instructions. */
246 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
247
248 return pxTopOfStack;
249 }
250 /*-----------------------------------------------------------*/
251
xPortStartScheduler(void)252 BaseType_t xPortStartScheduler( void )
253 {
254 uint32_t ulAPSR;
255
256 __asm volatile ( "MRS %0, CurrentEL" : "=r" ( ulAPSR ) );
257 ulAPSR &= portAPSR_MODE_BITS_MASK;
258
259 #if defined( GUEST )
260 configASSERT( ulAPSR == portEL1 );
261 if( ulAPSR == portEL1 )
262 #else
263 configASSERT( ulAPSR == portEL3 );
264 if( ulAPSR == portEL3 )
265 #endif
266 {
267 /* Interrupts are turned off in the CPU itself to ensure a tick does
268 not execute while the scheduler is being started. Interrupts are
269 automatically turned back on in the CPU when the first task starts
270 executing. */
271 portDISABLE_INTERRUPTS();
272
273 /* Start the timer that generates the tick ISR. */
274 configSETUP_TICK_INTERRUPT();
275
276 /* Start the first task executing. */
277 vPortRestoreTaskContext();
278 }
279
280 return 0;
281 }
282 /*-----------------------------------------------------------*/
283
vPortEndScheduler(void)284 void vPortEndScheduler( void )
285 {
286 /* Not implemented in ports where there is nothing to return to.
287 Artificially force an assert. */
288 configASSERT( ullCriticalNesting == 1000ULL );
289 }
290 /*-----------------------------------------------------------*/
291
vPortEnterCritical(void)292 void vPortEnterCritical( void )
293 {
294 /* Mask interrupts up to the max syscall interrupt priority. */
295 uxPortSetInterruptMask();
296
297 /* Now interrupts are disabled ullCriticalNesting can be accessed
298 directly. Increment ullCriticalNesting to keep a count of how many times
299 portENTER_CRITICAL() has been called. */
300 ullCriticalNesting++;
301
302 /* This is not the interrupt safe version of the enter critical function so
303 assert() if it is being called from an interrupt context. Only API
304 functions that end in "FromISR" can be used in an interrupt. Only assert if
305 the critical nesting count is 1 to protect against recursive calls if the
306 assert function also uses a critical section. */
307 if( ullCriticalNesting == 1ULL )
308 {
309 configASSERT( ullPortInterruptNesting == 0 );
310 }
311 }
312 /*-----------------------------------------------------------*/
313
vPortExitCritical(void)314 void vPortExitCritical( void )
315 {
316 if( ullCriticalNesting > portNO_CRITICAL_NESTING )
317 {
318 /* Decrement the nesting count as the critical section is being
319 exited. */
320 ullCriticalNesting--;
321
322 /* If the nesting level has reached zero then all interrupt
323 priorities must be re-enabled. */
324 if( ullCriticalNesting == portNO_CRITICAL_NESTING )
325 {
326 /* Critical nesting has reached zero so all interrupt priorities
327 should be unmasked. */
328 portCLEAR_INTERRUPT_MASK();
329 }
330 }
331 }
332 /*-----------------------------------------------------------*/
333
FreeRTOS_Tick_Handler(void)334 void FreeRTOS_Tick_Handler( void )
335 {
336 /* Must be the lowest possible priority. */
337 #if !defined( QEMU )
338 {
339 uint64_t ullRunningInterruptPriority;
340 /* s3_0_c12_c11_3 is ICC_RPR_EL1. */
341 __asm volatile ( "MRS %0, s3_0_c12_c11_3" : "=r" ( ullRunningInterruptPriority ) );
342 configASSERT( ullRunningInterruptPriority == ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) );
343 }
344 #endif
345
346 /* Interrupts should not be enabled before this point. */
347 #if( configASSERT_DEFINED == 1 )
348 {
349 uint32_t ulMaskBits;
350
351 __asm volatile( "MRS %0, DAIF" : "=r"( ulMaskBits ) :: "memory" );
352 configASSERT( ( ulMaskBits & portDAIF_I ) != 0 );
353 }
354 #endif /* configASSERT_DEFINED */
355
356 /* Set interrupt mask before altering scheduler structures. The tick
357 handler runs at the lowest priority, so interrupts cannot already be masked,
358 so there is no need to save and restore the current mask value. It is
359 necessary to turn off interrupts in the CPU itself while the ICCPMR is being
360 updated. */
361 /* s3_0_c4_c6_0 is ICC_PMR_EL1. */
362 __asm volatile ( "MSR s3_0_c4_c6_0, %0 \n"
363 "DSB SY \n"
364 "ISB SY \n"
365 :: "r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
366
367 /* Ok to enable interrupts after the interrupt source has been cleared. */
368 configCLEAR_TICK_INTERRUPT();
369 portENABLE_INTERRUPTS();
370
371 /* Increment the RTOS tick. */
372 if( xTaskIncrementTick() != pdFALSE )
373 {
374 ullPortYieldRequired = pdTRUE;
375 }
376
377 /* Ensure all interrupt priorities are active again. */
378 portCLEAR_INTERRUPT_MASK();
379 }
380 /*-----------------------------------------------------------*/
381
vPortTaskUsesFPU(void)382 void vPortTaskUsesFPU( void )
383 {
384 /* A task is registering the fact that it needs an FPU context. Set the
385 FPU flag (which is saved as part of the task context). */
386 ullPortTaskHasFPUContext = pdTRUE;
387
388 /* Consider initialising the FPSR here - but probably not necessary in
389 AArch64. */
390 }
391 /*-----------------------------------------------------------*/
392
vPortClearInterruptMask(UBaseType_t uxNewMaskValue)393 void vPortClearInterruptMask( UBaseType_t uxNewMaskValue )
394 {
395 if( uxNewMaskValue == pdFALSE )
396 {
397 portCLEAR_INTERRUPT_MASK();
398 }
399 }
400 /*-----------------------------------------------------------*/
401
uxPortSetInterruptMask(void)402 UBaseType_t uxPortSetInterruptMask( void )
403 {
404 uint32_t ulReturn;
405 uint64_t ullPMRValue;
406
407 /* Interrupt in the CPU must be turned off while the ICCPMR is being
408 updated. */
409 portDISABLE_INTERRUPTS();
410 /* s3_0_c4_c6_0 is ICC_PMR_EL1. */
411 __asm volatile ( "MRS %0, s3_0_c4_c6_0" : "=r" ( ullPMRValue ) );
412 if( ullPMRValue == ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) )
413 {
414 /* Interrupts were already masked. */
415 ulReturn = pdTRUE;
416 }
417 else
418 {
419 ulReturn = pdFALSE;
420 /* s3_0_c4_c6_0 is ICC_PMR_EL1. */
421 __asm volatile ( "MSR s3_0_c4_c6_0, %0 \n"
422 "DSB SY \n"
423 "ISB SY \n"
424 :: "r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" );
425 }
426
427 portENABLE_INTERRUPTS();
428
429 return ulReturn;
430 }
431 /*-----------------------------------------------------------*/
432
433 #if( configASSERT_DEFINED == 1 )
434
vPortValidateInterruptPriority(void)435 void vPortValidateInterruptPriority( void )
436 {
437 /* The following assertion will fail if a service routine (ISR) for
438 an interrupt that has been assigned a priority above
439 configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
440 function. ISR safe FreeRTOS API functions must *only* be called
441 from interrupts that have been assigned a priority at or below
442 configMAX_SYSCALL_INTERRUPT_PRIORITY.
443
444 Numerically low interrupt priority numbers represent logically high
445 interrupt priorities, therefore the priority of the interrupt must
446 be set to a value equal to or numerically *higher* than
447 configMAX_SYSCALL_INTERRUPT_PRIORITY.
448
449 FreeRTOS maintains separate thread and ISR API functions to ensure
450 interrupt entry is as fast and simple as possible. */
451 uint64_t ullRunningInterruptPriority;
452 /* s3_0_c12_c11_3 is ICC_RPR_EL1. */
453 __asm volatile ( "MRS %0, s3_0_c12_c11_3" : "=r" ( ullRunningInterruptPriority ) );
454 configASSERT( ullRunningInterruptPriority >= ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) );
455 }
456
457 #endif /* configASSERT_DEFINED */
458 /*-----------------------------------------------------------*/
459