| /* |
| * FreeRTOS Kernel <DEVELOPMENT BRANCH> |
| * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| * |
| * SPDX-License-Identifier: MIT |
| * |
| * 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 |
| * |
| */ |
| |
| /* Standard includes. */ |
| #include <stdlib.h> |
| |
| /* Scheduler includes. */ |
| #include "FreeRTOS.h" |
| #include "task.h" |
| |
| #ifndef configUNIQUE_INTERRUPT_PRIORITIES |
| #error configUNIQUE_INTERRUPT_PRIORITIES must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html |
| #endif |
| |
| #ifndef configSETUP_TICK_INTERRUPT |
| #error configSETUP_TICK_INTERRUPT() must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html |
| #endif /* configSETUP_TICK_INTERRUPT */ |
| |
| #ifndef configMAX_API_CALL_INTERRUPT_PRIORITY |
| #error configMAX_API_CALL_INTERRUPT_PRIORITY must be defined. See https://www.FreeRTOS.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html |
| #endif |
| |
| #if configMAX_API_CALL_INTERRUPT_PRIORITY == 0 |
| #error configMAX_API_CALL_INTERRUPT_PRIORITY must not be set to 0 |
| #endif |
| |
| #if configMAX_API_CALL_INTERRUPT_PRIORITY > configUNIQUE_INTERRUPT_PRIORITIES |
| #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 |
| #endif |
| |
| #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 |
| /* Check the configuration. */ |
| #if( configMAX_PRIORITIES > 32 ) |
| #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. |
| #endif |
| #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ |
| |
| /* In case security extensions are implemented. */ |
| #if configMAX_API_CALL_INTERRUPT_PRIORITY <= ( configUNIQUE_INTERRUPT_PRIORITIES / 2 ) |
| #error configMAX_API_CALL_INTERRUPT_PRIORITY must be greater than ( configUNIQUE_INTERRUPT_PRIORITIES / 2 ) |
| #endif |
| |
| /* Some vendor specific files default configCLEAR_TICK_INTERRUPT() in |
| portmacro.h. */ |
| #ifndef configCLEAR_TICK_INTERRUPT |
| #define configCLEAR_TICK_INTERRUPT() |
| #endif |
| |
| /* A critical section is exited when the critical section nesting count reaches |
| this value. */ |
| #define portNO_CRITICAL_NESTING ( ( size_t ) 0 ) |
| |
| /* In all GICs 255 can be written to the priority mask register to unmask all |
| (but the lowest) interrupt priority. */ |
| #define portUNMASK_VALUE ( 0xFFUL ) |
| |
| /* Tasks are not created with a floating point context, but can be given a |
| floating point context after they have been created. A variable is stored as |
| part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task |
| does not have an FPU context, or any other value if the task does have an FPU |
| context. */ |
| #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 ) |
| |
| /* Constants required to setup the initial task context. */ |
| #define portSP_ELx ( ( StackType_t ) 0x01 ) |
| #define portSP_EL0 ( ( StackType_t ) 0x00 ) |
| |
| #if defined( GUEST ) |
| #define portEL1 ( ( StackType_t ) 0x04 ) |
| #define portINITIAL_PSTATE ( portEL1 | portSP_EL0 ) |
| #else |
| #define portEL3 ( ( StackType_t ) 0x0c ) |
| /* At the time of writing, the BSP only supports EL3. */ |
| #define portINITIAL_PSTATE ( portEL3 | portSP_EL0 ) |
| #endif |
| |
| /* Masks all bits in the APSR other than the mode bits. */ |
| #define portAPSR_MODE_BITS_MASK ( 0x0C ) |
| |
| /* The I bit in the DAIF bits. */ |
| #define portDAIF_I ( 0x80 ) |
| |
| /* Macro to unmask all interrupt priorities. */ |
| /* s3_0_c4_c6_0 is ICC_PMR_EL1. */ |
| #define portCLEAR_INTERRUPT_MASK() \ |
| { \ |
| __asm volatile ( "MSR DAIFSET, #2 \n" \ |
| "DSB SY \n" \ |
| "ISB SY \n" \ |
| "MSR s3_0_c4_c6_0, %0 \n" \ |
| "DSB SY \n" \ |
| "ISB SY \n" \ |
| "MSR DAIFCLR, #2 \n" \ |
| "DSB SY \n" \ |
| "ISB SY \n" \ |
| ::"r"( portUNMASK_VALUE ) ); \ |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * Starts the first task executing. This function is necessarily written in |
| * assembly code so is implemented in portASM.s. |
| */ |
| extern void vPortRestoreTaskContext( void ); |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* A variable is used to keep track of the critical section nesting. This |
| variable has to be stored as part of the task context and must be initialised to |
| a non zero value to ensure interrupts don't inadvertently become unmasked before |
| the scheduler starts. As it is stored as part of the task context it will |
| automatically be set to 0 when the first task is started. */ |
| volatile uint64_t ullCriticalNesting = 9999ULL; |
| |
| /* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero |
| then floating point context must be saved and restored for the task. */ |
| uint64_t ullPortTaskHasFPUContext = pdFALSE; |
| |
| /* Set to 1 to pend a context switch from an ISR. */ |
| uint64_t ullPortYieldRequired = pdFALSE; |
| |
| /* Counts the interrupt nesting depth. A context switch is only performed if |
| if the nesting depth is 0. */ |
| uint64_t ullPortInterruptNesting = 0; |
| |
| /* Used in the ASM code. */ |
| __attribute__(( used )) const uint64_t ullMaxAPIPriorityMask = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ); |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * See header file for description. |
| */ |
| StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) |
| { |
| /* Setup the initial stack of the task. The stack is set exactly as |
| expected by the portRESTORE_CONTEXT() macro. */ |
| |
| /* First all the general purpose registers. */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0101010101010101ULL; /* R1 */ |
| pxTopOfStack--; |
| *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0303030303030303ULL; /* R3 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0202020202020202ULL; /* R2 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0505050505050505ULL; /* R5 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0404040404040404ULL; /* R4 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0707070707070707ULL; /* R7 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0606060606060606ULL; /* R6 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0909090909090909ULL; /* R9 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x0808080808080808ULL; /* R8 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1111111111111111ULL; /* R11 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1010101010101010ULL; /* R10 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1313131313131313ULL; /* R13 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1212121212121212ULL; /* R12 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1515151515151515ULL; /* R15 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1414141414141414ULL; /* R14 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1717171717171717ULL; /* R17 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1616161616161616ULL; /* R16 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1919191919191919ULL; /* R19 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x1818181818181818ULL; /* R18 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2121212121212121ULL; /* R21 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2020202020202020ULL; /* R20 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2323232323232323ULL; /* R23 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2222222222222222ULL; /* R22 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2525252525252525ULL; /* R25 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2424242424242424ULL; /* R24 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2727272727272727ULL; /* R27 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2626262626262626ULL; /* R26 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2929292929292929ULL; /* R29 */ |
| pxTopOfStack--; |
| *pxTopOfStack = 0x2828282828282828ULL; /* R28 */ |
| pxTopOfStack--; |
| *pxTopOfStack = ( StackType_t ) 0x00; /* XZR - has no effect, used so there are an even number of registers. */ |
| pxTopOfStack--; |
| *pxTopOfStack = ( StackType_t ) 0x00; /* R30 - procedure call link register. */ |
| pxTopOfStack--; |
| |
| *pxTopOfStack = portINITIAL_PSTATE; |
| pxTopOfStack--; |
| |
| *pxTopOfStack = ( StackType_t ) pxCode; /* Exception return address. */ |
| pxTopOfStack--; |
| |
| /* The task will start with a critical nesting count of 0 as interrupts are |
| enabled. */ |
| *pxTopOfStack = portNO_CRITICAL_NESTING; |
| pxTopOfStack--; |
| |
| /* The task will start without a floating point context. A task that uses |
| the floating point hardware must call vPortTaskUsesFPU() before executing |
| any floating point instructions. */ |
| *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT; |
| |
| return pxTopOfStack; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| BaseType_t xPortStartScheduler( void ) |
| { |
| uint32_t ulAPSR; |
| |
| __asm volatile ( "MRS %0, CurrentEL" : "=r" ( ulAPSR ) ); |
| ulAPSR &= portAPSR_MODE_BITS_MASK; |
| |
| #if defined( GUEST ) |
| configASSERT( ulAPSR == portEL1 ); |
| if( ulAPSR == portEL1 ) |
| #else |
| configASSERT( ulAPSR == portEL3 ); |
| if( ulAPSR == portEL3 ) |
| #endif |
| { |
| /* Interrupts are turned off in the CPU itself to ensure a tick does |
| not execute while the scheduler is being started. Interrupts are |
| automatically turned back on in the CPU when the first task starts |
| executing. */ |
| portDISABLE_INTERRUPTS(); |
| |
| /* Start the timer that generates the tick ISR. */ |
| configSETUP_TICK_INTERRUPT(); |
| |
| /* Start the first task executing. */ |
| vPortRestoreTaskContext(); |
| } |
| |
| return 0; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vPortEndScheduler( void ) |
| { |
| /* Not implemented in ports where there is nothing to return to. |
| Artificially force an assert. */ |
| configASSERT( ullCriticalNesting == 1000ULL ); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vPortEnterCritical( void ) |
| { |
| /* Mask interrupts up to the max syscall interrupt priority. */ |
| uxPortSetInterruptMask(); |
| |
| /* Now interrupts are disabled ullCriticalNesting can be accessed |
| directly. Increment ullCriticalNesting to keep a count of how many times |
| portENTER_CRITICAL() has been called. */ |
| ullCriticalNesting++; |
| |
| /* This is not the interrupt safe version of the enter critical function so |
| assert() if it is being called from an interrupt context. Only API |
| functions that end in "FromISR" can be used in an interrupt. Only assert if |
| the critical nesting count is 1 to protect against recursive calls if the |
| assert function also uses a critical section. */ |
| if( ullCriticalNesting == 1ULL ) |
| { |
| configASSERT( ullPortInterruptNesting == 0 ); |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vPortExitCritical( void ) |
| { |
| if( ullCriticalNesting > portNO_CRITICAL_NESTING ) |
| { |
| /* Decrement the nesting count as the critical section is being |
| exited. */ |
| ullCriticalNesting--; |
| |
| /* If the nesting level has reached zero then all interrupt |
| priorities must be re-enabled. */ |
| if( ullCriticalNesting == portNO_CRITICAL_NESTING ) |
| { |
| /* Critical nesting has reached zero so all interrupt priorities |
| should be unmasked. */ |
| portCLEAR_INTERRUPT_MASK(); |
| } |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void FreeRTOS_Tick_Handler( void ) |
| { |
| /* Must be the lowest possible priority. */ |
| #if !defined( QEMU ) |
| { |
| uint64_t ullRunningInterruptPriority; |
| /* s3_0_c12_c11_3 is ICC_RPR_EL1. */ |
| __asm volatile ( "MRS %0, s3_0_c12_c11_3" : "=r" ( ullRunningInterruptPriority ) ); |
| configASSERT( ullRunningInterruptPriority == ( portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); |
| } |
| #endif |
| |
| /* Interrupts should not be enabled before this point. */ |
| #if( configASSERT_DEFINED == 1 ) |
| { |
| uint32_t ulMaskBits; |
| |
| __asm volatile( "MRS %0, DAIF" : "=r"( ulMaskBits ) :: "memory" ); |
| configASSERT( ( ulMaskBits & portDAIF_I ) != 0 ); |
| } |
| #endif /* configASSERT_DEFINED */ |
| |
| /* Set interrupt mask before altering scheduler structures. The tick |
| handler runs at the lowest priority, so interrupts cannot already be masked, |
| so there is no need to save and restore the current mask value. It is |
| necessary to turn off interrupts in the CPU itself while the ICCPMR is being |
| updated. */ |
| /* s3_0_c4_c6_0 is ICC_PMR_EL1. */ |
| __asm volatile ( "MSR s3_0_c4_c6_0, %0 \n" |
| "DSB SY \n" |
| "ISB SY \n" |
| :: "r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" ); |
| |
| /* Ok to enable interrupts after the interrupt source has been cleared. */ |
| configCLEAR_TICK_INTERRUPT(); |
| portENABLE_INTERRUPTS(); |
| |
| /* Increment the RTOS tick. */ |
| if( xTaskIncrementTick() != pdFALSE ) |
| { |
| ullPortYieldRequired = pdTRUE; |
| } |
| |
| /* Ensure all interrupt priorities are active again. */ |
| portCLEAR_INTERRUPT_MASK(); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vPortTaskUsesFPU( void ) |
| { |
| /* A task is registering the fact that it needs an FPU context. Set the |
| FPU flag (which is saved as part of the task context). */ |
| ullPortTaskHasFPUContext = pdTRUE; |
| |
| /* Consider initialising the FPSR here - but probably not necessary in |
| AArch64. */ |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vPortClearInterruptMask( UBaseType_t uxNewMaskValue ) |
| { |
| if( uxNewMaskValue == pdFALSE ) |
| { |
| portCLEAR_INTERRUPT_MASK(); |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| UBaseType_t uxPortSetInterruptMask( void ) |
| { |
| uint32_t ulReturn; |
| uint64_t ullPMRValue; |
| |
| /* Interrupt in the CPU must be turned off while the ICCPMR is being |
| updated. */ |
| portDISABLE_INTERRUPTS(); |
| /* s3_0_c4_c6_0 is ICC_PMR_EL1. */ |
| __asm volatile ( "MRS %0, s3_0_c4_c6_0" : "=r" ( ullPMRValue ) ); |
| if( ullPMRValue == ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ) |
| { |
| /* Interrupts were already masked. */ |
| ulReturn = pdTRUE; |
| } |
| else |
| { |
| ulReturn = pdFALSE; |
| /* s3_0_c4_c6_0 is ICC_PMR_EL1. */ |
| __asm volatile ( "MSR s3_0_c4_c6_0, %0 \n" |
| "DSB SY \n" |
| "ISB SY \n" |
| :: "r" ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) : "memory" ); |
| } |
| |
| portENABLE_INTERRUPTS(); |
| |
| return ulReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| #if( configASSERT_DEFINED == 1 ) |
| |
| void vPortValidateInterruptPriority( void ) |
| { |
| /* The following assertion will fail if a service routine (ISR) for |
| an interrupt that has been assigned a priority above |
| configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API |
| function. ISR safe FreeRTOS API functions must *only* be called |
| from interrupts that have been assigned a priority at or below |
| configMAX_SYSCALL_INTERRUPT_PRIORITY. |
| |
| Numerically low interrupt priority numbers represent logically high |
| interrupt priorities, therefore the priority of the interrupt must |
| be set to a value equal to or numerically *higher* than |
| configMAX_SYSCALL_INTERRUPT_PRIORITY. |
| |
| FreeRTOS maintains separate thread and ISR API functions to ensure |
| interrupt entry is as fast and simple as possible. */ |
| uint64_t ullRunningInterruptPriority; |
| /* s3_0_c12_c11_3 is ICC_RPR_EL1. */ |
| __asm volatile ( "MRS %0, s3_0_c12_c11_3" : "=r" ( ullRunningInterruptPriority ) ); |
| configASSERT( ullRunningInterruptPriority >= ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); |
| } |
| |
| #endif /* configASSERT_DEFINED */ |
| /*-----------------------------------------------------------*/ |
| |