xref: /Kernel-v10.6.2/portable/GCC/ARM_CA53_64_BIT_SRE/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
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