1/* 2 * FreeRTOS Kernel <DEVELOPMENT BRANCH> 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 .text 30 .arm 31 .syntax unified 32 33 .set SYS_MODE, 0x1f 34 .set SVC_MODE, 0x13 35 .set IRQ_MODE, 0x12 36 .set CPSR_I_BIT, 0x80 37 38 /* Variables and functions. */ 39 .extern pxCurrentTCB 40 .extern vTaskSwitchContext 41 .extern vApplicationIRQHandler 42 .extern vApplicationFPUSafeIRQHandler 43 .extern ulPortInterruptNesting 44 .extern ulPortTaskHasFPUContext 45 .extern ulICCEOIR 46 .extern ulPortYieldRequired 47 48 .global FreeRTOS_IRQ_Handler 49 .global FreeRTOS_SVC_Handler 50 .global vPortRestoreTaskContext 51 .global vPortInitialiseFPSCR 52 .global ulReadAPSR 53 .global vPortYield 54 .global vPortEnableInterrupts 55 .global vPortDisableInterrupts 56 .global ulPortSetInterruptMaskFromISR 57 .global ulPortCountLeadingZeros 58 59 .weak vApplicationSVCHandler 60/*-----------------------------------------------------------*/ 61 62.macro portSAVE_CONTEXT 63 64 /* Save the LR and SPSR onto the system mode stack before switching to 65 * system mode to save the remaining system mode registers. */ 66 SRSDB SP!, #SYS_MODE 67 CPS #SYS_MODE 68 PUSH {R0-R12, R14} 69 70 /* Push the critical nesting count. */ 71 LDR R2, =ulCriticalNesting 72 LDR R1, [R2] 73 PUSH {R1} 74 75 /* Does the task have a floating point context that needs saving? If 76 * ulPortTaskHasFPUContext is 0 then no. */ 77 LDR R2, =ulPortTaskHasFPUContext 78 LDR R3, [R2] 79 CMP R3, #0 80 81 /* Save the floating point context, if any. */ 82 VMRSNE R1, FPSCR 83 VPUSHNE {D0-D15} 84#if configFPU_D32 == 1 85 VPUSHNE {D16-D31} 86#endif /* configFPU_D32 */ 87 PUSHNE {R1} 88 89 /* Save ulPortTaskHasFPUContext itself. */ 90 PUSH {R3} 91 92 /* Save the stack pointer in the TCB. */ 93 LDR R0, =pxCurrentTCB 94 LDR R1, [R0] 95 STR SP, [R1] 96 97 .endm 98 99/*-----------------------------------------------------------*/ 100 101.macro portRESTORE_CONTEXT 102 103 /* Set the SP to point to the stack of the task being restored. */ 104 LDR R0, =pxCurrentTCB 105 LDR R1, [R0] 106 LDR SP, [R1] 107 108 /* Is there a floating point context to restore? If the restored 109 * ulPortTaskHasFPUContext is zero then no. */ 110 LDR R0, =ulPortTaskHasFPUContext 111 POP {R1} 112 STR R1, [R0] 113 CMP R1, #0 114 115 /* Restore the floating point context, if any. */ 116 POPNE {R0} 117#if configFPU_D32 == 1 118 VPOPNE {D16-D31} 119#endif /* configFPU_D32 */ 120 VPOPNE {D0-D15} 121 VMSRNE FPSCR, R0 122 123 /* Restore the critical section nesting depth. */ 124 LDR R0, =ulCriticalNesting 125 POP {R1} 126 STR R1, [R0] 127 128 /* Restore all system mode registers other than the SP (which is already 129 being used). */ 130 POP {R0-R12, R14} 131 132 /* Return to the task code, loading CPSR on the way. */ 133 RFEIA SP! 134 135 .endm 136 137/*-----------------------------------------------------------*/ 138 139/* 140 * void vPortRestoreTaskContext( void ); 141 * 142 * vPortRestoreTaskContext is used to start the scheduler. 143 */ 144.align 4 145.type vPortRestoreTaskContext, %function 146vPortRestoreTaskContext: 147 /* Switch to system mode. */ 148 CPS #SYS_MODE 149 portRESTORE_CONTEXT 150 151/*-----------------------------------------------------------*/ 152 153/* 154 * void vPortInitialiseFPSCR( void ); 155 * 156 * vPortInitialiseFPSCR is used to initialize the FPSCR register. 157 */ 158.align 4 159.type vPortInitialiseFPSCR, %function 160vPortInitialiseFPSCR: 161 MOV R0, #0 162 VMSR FPSCR, R0 163 BX LR 164 165/*-----------------------------------------------------------*/ 166 167/* 168 * uint32_t ulReadAPSR( void ); 169 * 170 * ulReadAPSR is used to read the value of APSR context. 171 */ 172.align 4 173.type ulReadAPSR, %function 174ulReadAPSR: 175 MRS R0, APSR 176 BX LR 177 178/*-----------------------------------------------------------*/ 179 180/* 181 * void vPortYield( void ); 182 */ 183.align 4 184.type vPortYield, %function 185vPortYield: 186 SVC 0 187 ISB 188 BX LR 189 190/*-----------------------------------------------------------*/ 191 192/* 193 * void vPortEnableInterrupts( void ); 194 */ 195.align 4 196.type vPortEnableInterrupts, %function 197vPortEnableInterrupts: 198 CPSIE I 199 BX LR 200 201/*-----------------------------------------------------------*/ 202 203/* 204 * void vPortDisableInterrupts( void ); 205 */ 206.align 4 207.type vPortDisableInterrupts, %function 208vPortDisableInterrupts: 209 CPSID I 210 DSB 211 ISB 212 BX LR 213 214/*-----------------------------------------------------------*/ 215 216/* 217 * uint32_t ulPortSetInterruptMaskFromISR( void ); 218 */ 219.align 4 220.type ulPortSetInterruptMaskFromISR, %function 221ulPortSetInterruptMaskFromISR: 222 MRS R0, CPSR 223 AND R0, R0, #CPSR_I_BIT 224 CPSID I 225 DSB 226 ISB 227 BX LR 228 229/*-----------------------------------------------------------*/ 230 231/* 232 * void vApplicationSVCHandler( uint32_t ulSvcNumber ); 233 */ 234.align 4 235.type vApplicationSVCHandler, %function 236vApplicationSVCHandler: 237 B vApplicationSVCHandler 238 239/*-----------------------------------------------------------*/ 240 241/* If the application provides an implementation of vApplicationIRQHandler(), 242 * then it will get called directly without saving the FPU registers on 243 * interrupt entry, and this weak implementation of vApplicationIRQHandler() 244 * will not get called. 245 * 246 * If the application provides its own implementation of 247 * vApplicationFPUSafeIRQHandler() then this implementation of 248 * vApplicationIRQHandler() will be called, save the FPU registers, and then 249 * call vApplicationFPUSafeIRQHandler(). 250 * 251 * Therefore, if the application writer wants FPU registers to be saved on 252 * interrupt entry, their IRQ handler must be called 253 * vApplicationFPUSafeIRQHandler(), and if the application writer does not want 254 * FPU registers to be saved on interrupt entry their IRQ handler must be 255 * called vApplicationIRQHandler(). 256 */ 257.align 4 258.weak vApplicationIRQHandler 259.type vApplicationIRQHandler, %function 260vApplicationIRQHandler: 261 PUSH {LR} 262 263 VMRS R1, FPSCR 264 VPUSH {D0-D7} 265 PUSH {R1} 266 267 BLX vApplicationFPUSafeIRQHandler 268 269 POP {R0} 270 VPOP {D0-D7} 271 VMSR FPSCR, R0 272 273 POP {PC} 274 275/*-----------------------------------------------------------*/ 276 277.align 4 278.weak vApplicationFPUSafeIRQHandler 279.type vApplicationFPUSafeIRQHandler, %function 280vApplicationFPUSafeIRQHandler: 281 B vApplicationFPUSafeIRQHandler 282 283/*-----------------------------------------------------------*/ 284 285/* 286 * UBaseType_t ulPortCountLeadingZeros( UBaseType_t ulBitmap ); 287 * 288 * According to the Procedure Call Standard for the ARM Architecture (AAPCS): 289 * - Parameter ulBitmap is passed in R0. 290 * - Return value must be in R0. 291 */ 292.align 4 293.type ulPortCountLeadingZeros, %function 294ulPortCountLeadingZeros: 295 CLZ R0, R0 296 BX LR 297 298/*-----------------------------------------------------------*/ 299 300/* 301 * SVC handler is used to yield. 302 */ 303.align 4 304.type FreeRTOS_SVC_Handler, %function 305FreeRTOS_SVC_Handler: 306 PUSH { R0-R1 } 307 308 /* ---------------------------- Get Caller SVC Number ---------------------------- */ 309 MRS R0, SPSR /* R0 = CPSR at the time of SVC. */ 310 TST R0, #0x20 /* Check Thumb bit (5) in CPSR. */ 311 LDRHNE R0, [LR, #-0x2] /* If Thumb, load halfword. */ 312 BICNE R0, R0, #0xFF00 /* And extract immidiate field (i.e. SVC number). */ 313 LDREQ R0, [LR, #-0x4] /* If ARM, load word. */ 314 BICEQ R0, R0, #0xFF000000 /* And extract immidiate field (i.e. SVC number). */ 315 316 /* --------------------------------- SVC Routing --------------------------------- */ 317 CMP R0, #0 318 BEQ svcPortYield 319 BNE svcApplicationCall 320 321svcPortYield: 322 POP { R0-R1 } 323 portSAVE_CONTEXT 324 BLX vTaskSwitchContext 325 portRESTORE_CONTEXT 326 327svcApplicationCall: 328 POP { R0-R1 } 329 portSAVE_CONTEXT 330 BLX vApplicationSVCHandler 331 portRESTORE_CONTEXT 332 333/*-----------------------------------------------------------*/ 334 335.align 4 336.type FreeRTOS_IRQ_Handler, %function 337FreeRTOS_IRQ_Handler: 338 /* Return to the interrupted instruction. */ 339 SUB LR, LR, #4 340 341 /* Push the return address and SPSR. */ 342 PUSH {LR} 343 MRS LR, SPSR 344 PUSH {LR} 345 346 /* Change to supervisor mode to allow reentry. */ 347 CPS #SVC_MODE 348 349 /* Push used registers. */ 350 PUSH {R0-R3, R12} 351 352 /* Increment nesting count. r3 holds the address of ulPortInterruptNesting 353 * for future use. r1 holds the original ulPortInterruptNesting value for 354 * future use. */ 355 LDR R3, =ulPortInterruptNesting 356 LDR R1, [R3] 357 ADD R0, R1, #1 358 STR R0, [R3] 359 360 /* Ensure bit 2 of the stack pointer is clear. r2 holds the bit 2 value for 361 * future use. */ 362 MOV R0, SP 363 AND R2, R0, #4 364 SUB SP, SP, R2 365 366 /* Call the interrupt handler. */ 367 PUSH {R0-R3, LR} 368 BLX vApplicationIRQHandler 369 POP {R0-R3, LR} 370 ADD SP, SP, R2 371 372 /* Disable IRQs incase vApplicationIRQHandler enabled them for re-entry. */ 373 CPSID i 374 DSB 375 ISB 376 377 /* Write to the EOI register. */ 378 LDR R0, =ulICCEOIR 379 LDR R2, [R0] 380 STR R0, [R2] 381 382 /* Restore the old nesting count. */ 383 STR R1, [R3] 384 385 /* A context switch is never performed if the nesting count is not 0. */ 386 CMP R1, #0 387 BNE exit_without_switch 388 389 /* Did the interrupt request a context switch? r1 holds the address of 390 * ulPortYieldRequired and r0 the value of ulPortYieldRequired for future 391 * use. */ 392 LDR R1, =ulPortYieldRequired 393 LDR R0, [R1] 394 CMP R0, #0 395 BNE switch_before_exit 396 397exit_without_switch: 398 /* No context switch. Restore used registers, LR_irq and SPSR before 399 * returning. */ 400 POP {R0-R3, R12} 401 CPS #IRQ_MODE 402 POP {LR} 403 MSR SPSR_cxsf, LR 404 POP {LR} 405 MOVS PC, LR 406 407switch_before_exit: 408 /* A context switch is to be performed. Clear the context switch pending 409 * flag. */ 410 MOV R0, #0 411 STR R0, [R1] 412 413 /* Restore used registers, LR-irq and SPSR before saving the context 414 * to the task stack. */ 415 POP {R0-R3, R12} 416 CPS #IRQ_MODE 417 POP {LR} 418 MSR SPSR_cxsf, LR 419 POP {LR} 420 portSAVE_CONTEXT 421 422 /* Call the function that selects the new task to execute. 423 * vTaskSwitchContext() if vTaskSwitchContext() uses LDRD or STRD 424 * instructions, or 8 byte aligned stack allocated data. LR does not need 425 * saving as a new LR will be loaded by portRESTORE_CONTEXT anyway. */ 426 BLX vTaskSwitchContext 427 428 /* Restore the context of, and branch to, the task selected to execute 429 * next. */ 430 portRESTORE_CONTEXT 431 432/*-----------------------------------------------------------*/ 433 434.end 435