/* | |
* 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 | |
* | |
*/ | |
.text | |
.arm | |
.set SYS_MODE, 0x1f | |
.set SVC_MODE, 0x13 | |
.set IRQ_MODE, 0x12 | |
/* Variables and functions. */ | |
.extern ulMaxAPIPriorityMask | |
.extern _freertos_vector_table | |
.extern pxCurrentTCB | |
.extern vTaskSwitchContext | |
.extern vApplicationIRQHandler | |
.extern ulPortInterruptNesting | |
.extern ulPortTaskHasFPUContext | |
.extern ulICCEOIR | |
.extern ulPortYieldRequired | |
.global FreeRTOS_IRQ_Handler | |
.global FreeRTOS_SVC_Handler | |
.global vPortRestoreTaskContext | |
.macro portSAVE_CONTEXT | |
/* Save the LR and SPSR onto the system mode stack before switching to | |
system mode to save the remaining system mode registers. */ | |
SRSDB sp!, #SYS_MODE | |
CPS #SYS_MODE | |
PUSH {R0-R12, R14} | |
/* Push the critical nesting count. */ | |
LDR R2, ulCriticalNestingConst | |
LDR R1, [R2] | |
PUSH {R1} | |
/* Does the task have a floating point context that needs saving? If | |
ulPortTaskHasFPUContext is 0 then no. */ | |
LDR R2, ulPortTaskHasFPUContextConst | |
LDR R3, [R2] | |
CMP R3, #0 | |
/* Save the floating point context, if any. */ | |
FMRXNE R1, FPSCR | |
VPUSHNE {D0-D15} | |
#if configFPU_D32 == 1 | |
VPUSHNE {D16-D31} | |
#endif /* configFPU_D32 */ | |
PUSHNE {R1} | |
/* Save ulPortTaskHasFPUContext itself. */ | |
PUSH {R3} | |
/* Save the stack pointer in the TCB. */ | |
LDR R0, pxCurrentTCBConst | |
LDR R1, [R0] | |
STR SP, [R1] | |
.endm | |
; /**********************************************************************/ | |
.macro portRESTORE_CONTEXT | |
/* Set the SP to point to the stack of the task being restored. */ | |
LDR R0, pxCurrentTCBConst | |
LDR R1, [R0] | |
LDR SP, [R1] | |
/* Is there a floating point context to restore? If the restored | |
ulPortTaskHasFPUContext is zero then no. */ | |
LDR R0, ulPortTaskHasFPUContextConst | |
POP {R1} | |
STR R1, [R0] | |
CMP R1, #0 | |
/* Restore the floating point context, if any. */ | |
POPNE {R0} | |
#if configFPU_D32 == 1 | |
VPOPNE {D16-D31} | |
#endif /* configFPU_D32 */ | |
VPOPNE {D0-D15} | |
VMSRNE FPSCR, R0 | |
/* Restore the critical section nesting depth. */ | |
LDR R0, ulCriticalNestingConst | |
POP {R1} | |
STR R1, [R0] | |
/* Restore all system mode registers other than the SP (which is already | |
being used). */ | |
POP {R0-R12, R14} | |
/* Return to the task code, loading CPSR on the way. */ | |
RFEIA sp! | |
.endm | |
/****************************************************************************** | |
* SVC handler is used to yield. | |
*****************************************************************************/ | |
.align 4 | |
.type FreeRTOS_SVC_Handler, %function | |
FreeRTOS_SVC_Handler: | |
/* Save the context of the current task and select a new task to run. */ | |
portSAVE_CONTEXT | |
LDR R0, vTaskSwitchContextConst | |
BLX R0 | |
portRESTORE_CONTEXT | |
/****************************************************************************** | |
* vPortRestoreTaskContext is used to start the scheduler. | |
*****************************************************************************/ | |
.align 4 | |
.type vPortRestoreTaskContext, %function | |
vPortRestoreTaskContext: | |
/* Switch to system mode. */ | |
CPS #SYS_MODE | |
portRESTORE_CONTEXT | |
.align 4 | |
.type FreeRTOS_IRQ_Handler, %function | |
FreeRTOS_IRQ_Handler: | |
/* Return to the interrupted instruction. */ | |
SUB lr, lr, #4 | |
/* Push the return address and SPSR. */ | |
PUSH {lr} | |
MRS lr, SPSR | |
PUSH {lr} | |
/* Change to supervisor mode to allow reentry. */ | |
CPS #0x13 | |
/* Push used registers. */ | |
PUSH {r0-r3, r12} | |
/* Increment nesting count. r3 holds the address of ulPortInterruptNesting | |
for future use. r1 holds the original ulPortInterruptNesting value for | |
future use. */ | |
LDR r3, ulPortInterruptNestingConst | |
LDR r1, [r3] | |
ADD r0, r1, #1 | |
STR r0, [r3] | |
/* Ensure bit 2 of the stack pointer is clear. r2 holds the bit 2 value for | |
future use. */ | |
MOV r0, sp | |
AND r2, r0, #4 | |
SUB sp, sp, r2 | |
/* Call the interrupt handler. */ | |
PUSH {r0-r3, lr} | |
LDR r1, vApplicationIRQHandlerConst | |
BLX r1 | |
POP {r0-r3, lr} | |
ADD sp, sp, r2 | |
CPSID i | |
DSB | |
ISB | |
/* Write to the EOI register. */ | |
LDR r0, ulICCEOIRConst | |
LDR r2, [r0] | |
STR r0, [r2] | |
/* Restore the old nesting count. */ | |
STR r1, [r3] | |
/* A context switch is never performed if the nesting count is not 0. */ | |
CMP r1, #0 | |
BNE exit_without_switch | |
/* Did the interrupt request a context switch? r1 holds the address of | |
ulPortYieldRequired and r0 the value of ulPortYieldRequired for future | |
use. */ | |
LDR r1, ulPortYieldRequiredConst | |
LDR r0, [r1] | |
CMP r0, #0 | |
BNE switch_before_exit | |
exit_without_switch: | |
/* No context switch. Restore used registers, LR_irq and SPSR before | |
returning. */ | |
POP {r0-r3, r12} | |
CPS #IRQ_MODE | |
POP {LR} | |
MSR SPSR_cxsf, LR | |
POP {LR} | |
MOVS PC, LR | |
switch_before_exit: | |
/* A context swtich is to be performed. Clear the context switch pending | |
flag. */ | |
MOV r0, #0 | |
STR r0, [r1] | |
/* Restore used registers, LR-irq and SPSR before saving the context | |
to the task stack. */ | |
POP {r0-r3, r12} | |
CPS #IRQ_MODE | |
POP {LR} | |
MSR SPSR_cxsf, LR | |
POP {LR} | |
portSAVE_CONTEXT | |
/* Call the function that selects the new task to execute. | |
vTaskSwitchContext() if vTaskSwitchContext() uses LDRD or STRD | |
instructions, or 8 byte aligned stack allocated data. LR does not need | |
saving as a new LR will be loaded by portRESTORE_CONTEXT anyway. */ | |
LDR R0, vTaskSwitchContextConst | |
BLX R0 | |
/* Restore the context of, and branch to, the task selected to execute | |
next. */ | |
portRESTORE_CONTEXT | |
ulICCEOIRConst: .word ulICCEOIR | |
pxCurrentTCBConst: .word pxCurrentTCB | |
ulCriticalNestingConst: .word ulCriticalNesting | |
ulPortTaskHasFPUContextConst: .word ulPortTaskHasFPUContext | |
vTaskSwitchContextConst: .word vTaskSwitchContext | |
vApplicationIRQHandlerConst: .word vApplicationIRQHandler | |
ulPortInterruptNestingConst: .word ulPortInterruptNesting | |
ulPortYieldRequiredConst: .word ulPortYieldRequired | |
.end | |