1/* 2 * FreeRTOS Kernel V11.1.0 3 * Copyright (C) 2024 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 .arm 30 .syntax unified 31 .section privileged_functions 32 33#define FREERTOS_ASSEMBLY 34 #include "portmacro_asm.h" 35 #include "mpu_syscall_numbers.h" 36#undef FREERTOS_ASSEMBLY 37 38 /* External FreeRTOS-Kernel variables. */ 39 .extern pxCurrentTCB 40 .extern uxSystemCallImplementations 41 .extern ulPortInterruptNesting 42 .extern ulPortYieldRequired 43 44 /* External Llnker script variables. */ 45 .extern __syscalls_flash_start__ 46 .extern __syscalls_flash_end__ 47 48 /* External FreeRTOS-Kernel functions. */ 49 .extern vTaskSwitchContext 50 .extern vApplicationIRQHandler 51 52/* ----------------------------------------------------------------------------------- */ 53 54/* Save the context of a FreeRTOS Task. */ 55.macro portSAVE_CONTEXT 56 DSB 57 ISB 58 /* Push R0 and LR to the stack for current mode. */ 59 PUSH { R0, LR } 60 61 LDR LR, =pxCurrentTCB /* LR = &( pxCurrentTCB ). */ 62 LDR LR, [LR] /* LR = pxCurrentTCB. */ 63 LDR LR, [LR] /* LR = pxTopOfStack i.e. the address where to store the task context. */ 64 65 LDR R0, =ulCriticalNesting /* R0 = &( ulCriticalNesting ). */ 66 LDR R0, [R0] /* R0 = ulCriticalNesting. */ 67 STM LR!, { R0 } /* Store ulCriticalNesting. ! increments LR after storing. */ 68 69#if ( portENABLE_FPU == 1 ) 70 VMRS R0, FPSCR /* R0 = FPSCR. */ 71 STM LR!, { R0 } /* Store FPSCR. */ 72 VSTM LR!, { D0-D15 } /* Store D0-D15. */ 73#endif /* ( portENABLE_FPU == 1 ) */ 74 75 POP { R0 } /* Restore R0 to pre-exception value. */ 76 /* STM (user registers) - In a PL1 mode other than System mode, STM (user 77 * registers) instruction stores multiple User mode registers to 78 * consecutive memory locations using an address from a base register. The 79 * processor reads the base register value normally, using the current mode 80 * to determine the correct Banked version of the register. This instruction 81 * cannot writeback to the base register. 82 * 83 * The following can be derived from the above description: 84 * - The macro portSAVE_CONTEXT MUST be called from a PL1 mode other than 85 * the System mode. 86 * - Base register LR of the current mode will be used which contains the 87 * location to store the context. 88 * - It will store R0-R14 of User mode i.e. pre-exception SP(R13) and LR(R14) 89 * will be stored. */ 90 STM LR, { R0-R14 }^ 91 ADD LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */ 92 93 POP { R0 } /* Pre-exception PC is in R0. */ 94 MRS R1, SPSR /* R1 = Pre-exception CPSR. */ 95 STM LR!, { R0-R1 } /* Store pre-exception PC and CPSR. */ 96 97.endm 98 99/* ----------------------------------------------------------------------------------- */ 100 101/* Restore the context of a FreeRTOS Task. */ 102.macro portRESTORE_CONTEXT 103 /* Load the pointer to the current task's Task Control Block (TCB). */ 104 LDR LR, =pxCurrentTCB /* LR = &( pxCurrentTCB ). */ 105 LDR LR, [LR] /* LR = pxCurrentTCB. */ 106 ADD R1, LR, #0x4 /* R1 now points to the xMPUSettings in TCB. */ 107 LDR LR, [LR] /* LR = pxTopOfStack i.e. the address where to restore the task context from. */ 108 109 /* When creating a loop label in a macro it has to be a numeric label. 110 * for( R5 = portFIRST_CONFIGURABLE_REGION ; R5 <= portNUM_CONFIGURABLE_REGIONS ; R5++ ) */ 111 MOV R5, #portFIRST_CONFIGURABLE_REGION 112 123: 113 LDMIA R1!, { R2-R4 } /* R2 = ulRegionSize, R3 = ulRegionAttribute, R4 = ulRegionBaseAddress. */ 114 115 MCR p15, #0, R5, c6, c2, #0 /* MPU Region Number Register. */ 116 MCR p15, #0, R4, c6, c1, #0 /* MPU Region Base Address Register. */ 117 MCR p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */ 118 MCR p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */ 119 120 ADD R5, R5, #1 121 CMP R5, #portNUM_CONFIGURABLE_REGIONS 122 BLE 123b 123 124 LDR R1, =ulCriticalNesting /* R1 = &( ulCriticalNesting ). */ 125 LDM LR!, { R2 } /* R2 = Stored ulCriticalNesting. */ 126 STR R2, [R1] /* Restore ulCriticalNesting. */ 127 128#if ( portENABLE_FPU == 1 ) 129 LDM LR!, { R1 } /* R1 = Stored FPSCR. */ 130 VMSR FPSCR, R1 /* Restore FPSCR. */ 131 VLDM LR!, { D0-D15 } /* Restore D0-D15. */ 132#endif /* portENABLE_FPU*/ 133 134 /* LDM (User registers) - In a PL1 mode other than System mode, LDM (User 135 * registers) loads multiple User mode registers from consecutive memory 136 * locations using an address from a base register. The registers loaded 137 * cannot include the PC. The processor reads the base register value 138 * normally, using the current mode to determine the correct Banked version 139 * of the register. This instruction cannot writeback to the base register. 140 * 141 * The following can be derived from the above description: 142 * - The macro portRESTORE_CONTEXT MUST be called from a PL1 mode other than 143 * the System mode. 144 * - Base register LR of the current mode will be used which contains the 145 * location to restore the context from. 146 * - It will restore R0-R14 of User mode i.e. SP(R13) and LR(R14) of User 147 * mode will be restored. 148 */ 149 LDM LR, { R0-R14 }^ 150 ADD LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */ 151 152 RFE LR /* Restore PC and CPSR from the context. */ 153 154.endm 155 156/* ----------------------------------------------------------------------------------- */ 157 158/* 159 * void vPortStartFirstTask( void ); 160 */ 161.align 4 162.global vPortStartFirstTask 163.type vPortStartFirstTask, %function 164vPortStartFirstTask: 165 /* This function is called from System Mode to start the FreeRTOS-Kernel. 166 * As described in the portRESTORE_CONTEXT macro, portRESTORE_CONTEXT cannot 167 * be called from the System mode. We, therefore, switch to the Supervisor 168 * mode before calling portRESTORE_CONTEXT. */ 169 CPS #SVC_MODE 170 portRESTORE_CONTEXT 171 172/* ----------------------------------------------------------------------------------- */ 173 174.align 4 175.global FreeRTOS_SVC_Handler 176.type FreeRTOS_SVC_Handler, %function 177FreeRTOS_SVC_Handler: 178 PUSH { R11-R12 } 179 180 /* ------------------------- Caller Flash Location Check ------------------------- */ 181 182 LDR R11, =__syscalls_flash_start__ 183 LDR R12, =__syscalls_flash_end__ 184 CMP LR, R11 /* If SVC instruction address is less than __syscalls_flash_start__, exit. */ 185 BLT svcHandlerExit 186 CMP LR, R12 /* If SVC instruction address is greater than __syscalls_flash_end__, exit. */ 187 BGT svcHandlerExit 188 189 /* ---------------------------- Get Caller SVC Number ---------------------------- */ 190 191 MRS R11, SPSR /* LR = CPSR at the time of SVC. */ 192 TST R11, #0x20 /* Check Thumb bit (5) in CPSR. */ 193 LDRHNE R11, [LR, #-0x2] /* If Thumb, load halfword. */ 194 BICNE R11, R11, #0xFF00 /* And extract immidiate field (i.e. SVC number). */ 195 LDREQ R11, [LR, #-0x4] /* If ARM, load word. */ 196 BICEQ R11, R11, #0xFF000000 /* And extract immidiate field (i.e. SVC number). */ 197 198 /* --------------------------------- SVC Routing --------------------------------- */ 199 200 /* If SVC Number < #NUM_SYSTEM_CALLS, go to svcSystemCallEnter. */ 201 CMP R11, #NUM_SYSTEM_CALLS 202 BLT svcSystemCallEnter 203 204 /* If SVC Number == #portSVC_SYSTEM_CALL_EXIT, go to svcSystemCallExit. */ 205 CMP R11, #portSVC_SYSTEM_CALL_EXIT 206 BEQ svcSystemCallExit 207 208 /* If SVC Number == #portSVC_YIELD, go to svcPortYield. */ 209 CMP R11, #portSVC_YIELD 210 BEQ svcPortYield 211 212svcHandlerExit: 213 POP { R11-R12 } 214 MOVS PC, LR /* Copies the SPSR into the CPSR, performing the mode swap. */ 215 216svcPortYield: 217 POP { R11-R12 } 218 portSAVE_CONTEXT 219 BL vTaskSwitchContext 220 portRESTORE_CONTEXT 221 222svcSystemCallExit: 223 LDR R11, =pxCurrentTCB /* R11 = &( pxCurrentTCB ). */ 224 LDR R11, [R11] /* R11 = pxCurrentTCB. */ 225 ADD R11, R11, #portSYSTEM_CALL_INFO_OFFSET /* R11 now points to xSystemCallStackInfo in TCB. */ 226 227 /* Restore the user mode SP and LR. */ 228 LDM R11, { R13-R14 }^ 229 230 AND R12, R12, #0x0 /* R12 = 0. */ 231 STR R12, [R11] /* xSystemCallStackInfo.pulTaskStackPointer = NULL. */ 232 STR R12, [R11, #0x4] /* xSystemCallStackInfo.pulLinkRegisterAtSystemCallEntry = NULL. */ 233 234 LDMDB R11, { R12 } /* R12 = ulTaskFlags. */ 235 236 TST R12, #portTASK_IS_PRIVILEGED_FLAG 237 /* If the task is privileged, we can exit now. */ 238 BNE svcHandlerExit 239 /* Otherwise, we need to switch back to User mode. */ 240 MRS R12, SPSR 241 BIC R12, R12, #0x0F 242 MSR SPSR_cxsf, R12 243 244 B svcHandlerExit 245 246svcSystemCallEnter: 247 LDR R12, =uxSystemCallImplementations /* R12 = uxSystemCallImplementations. */ 248 /* R12 = uxSystemCallImplementations[ R12 + ( R11 << 2 ) ]. 249 * R12 now contains the address of the system call impl function. */ 250 LDR R12, [R12, R11, lsl #2] 251 252 /* If R12 == NULL, exit. */ 253 CMP R12, #0x0 254 BEQ svcHandlerExit 255 256 /* It is okay to clobber LR here because we do not need to return to the 257 * SVC enter location anymore. LR now contains the address of the system 258 * call impl function. */ 259 MOV LR, R12 260 261 LDR R11, =pxCurrentTCB /* R11 = &( pxCurrentTCB ). */ 262 LDR R11, [R11] /* R11 = pxCurrentTCB. */ 263 ADD R11, R11, #portSYSTEM_CALL_INFO_OFFSET /* R11 now points to xSystemCallStackInfo in TCB. */ 264 265 /* Store User mode SP and LR in xSystemCallStackInfo.pulTaskStackPointer and 266 * xSystemCallStackInfo.pulLinkRegisterAtSystemCallEntry. */ 267 STM R11, { R13-R14 }^ 268 ADD R11, R11, 0x8 269 270 /* Load User mode SP an LR with xSystemCallStackInfo.pulSystemCallStackPointer 271 * and xSystemCallStackInfo.pulSystemCallExitAddress. */ 272 LDM R11, { R13-R14 }^ 273 274 /* Change to SYS_MODE for the System Call. */ 275 MRS R12, SPSR 276 ORR R12, R12, #SYS_MODE 277 MSR SPSR_cxsf, R12 278 279 B svcHandlerExit 280 281/* ----------------------------------------------------------------------------------- */ 282 283/* 284 * void vPortDisableInterrupts( void ); 285 */ 286.align 4 287.global vPortDisableInterrupts 288.type vPortDisableInterrupts, %function 289vPortDisableInterrupts: 290 CPSID I 291 BX LR 292 293/* ----------------------------------------------------------------------------------- */ 294 295/* 296 * void vPortEnableInterrupts( void ); 297 */ 298.align 4 299.global vPortEnableInterrupts 300.type vPortEnableInterrupts, %function 301vPortEnableInterrupts: 302 CPSIE I 303 BX LR 304 305/* ----------------------------------------------------------------------------------- */ 306 307/* 308 * void vMPUSetRegion( uint32_t ulRegionNumber, 309 * uint32_t ulBaseAddress, 310 * uint32_t ulRegionSize, 311 * uint32_t ulRegionPermissions ); 312 * 313 * According to the Procedure Call Standard for the ARM Architecture (AAPCS), 314 * paramters are passed in the following registers: 315 * R0 = ulRegionNumber. 316 * R1 = ulBaseAddress. 317 * R2 = ulRegionSize. 318 * R3 = ulRegionPermissions. 319 */ 320.align 4 321.global vMPUSetRegion 322.type vMPUSetRegion, %function 323vMPUSetRegion: 324 AND R0, R0, #0x0F /* R0 = R0 & 0x0F. Max possible region number is 15. */ 325 326 MCR p15, #0, R0, c6, c2, #0 /* MPU Region Number Register. */ 327 MCR p15, #0, R1, c6, c1, #0 /* MPU Region Base Address Register. */ 328 MCR p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */ 329 MCR p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */ 330 331 BX LR 332 333/* ----------------------------------------------------------------------------------- */ 334 335/* 336 * void vMPUEnable( void ); 337 */ 338.align 4 339.global vMPUEnable 340.type vMPUEnable, %function 341vMPUEnable: 342 PUSH { R0 } 343 344 MRC p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */ 345 ORR R0, R0, #0x1 /* R0 = R0 | 0x1. Set the M bit in SCTLR. */ 346 DSB 347 MCR p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */ 348 ISB 349 350 POP { R0 } 351 BX LR 352 353/* ----------------------------------------------------------------------------------- */ 354 355/* 356 * void vMPUDisable( void ); 357 */ 358.align 4 359.global vMPUDisable 360.type vMPUDisable, %function 361vMPUDisable: 362 PUSH { R0 } 363 364 MRC p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */ 365 BIC R0, R0, #1 /* R0 = R0 & ~0x1. Clear the M bit in SCTLR. */ 366 /* Wait for all pending data accesses to complete. */ 367 DSB 368 MCR p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */ 369 /* Flush the pipeline and prefetch buffer(s) in the processor to ensure that 370 * all following instructions are fetched from cache or memory. */ 371 ISB 372 373 POP { R0 } 374 BX LR 375 376/* ----------------------------------------------------------------------------------- */ 377 378/* 379 * void vMPUEnableBackgroundRegion( void ); 380 */ 381.align 4 382.global vMPUEnableBackgroundRegion 383.type vMPUEnableBackgroundRegion, %function 384vMPUEnableBackgroundRegion: 385 PUSH { R0 } 386 387 MRC p15, #0, R0, c1, c0, #0 /* R0 = System Control Register (SCTLR). */ 388 ORR R0, R0, #0x20000 /* R0 = R0 | 0x20000. Set the BR bit in SCTLR. */ 389 MCR p15, #0, R0, c1, c0, #0 /* SCTLR = R0. */ 390 391 POP { R0 } 392 BX LR 393 394/* ----------------------------------------------------------------------------------- */ 395 396/* 397 * void vMPUDisableBackgroundRegion( void ); 398 */ 399.align 4 400.global vMPUDisableBackgroundRegion 401.type vMPUDisableBackgroundRegion, %function 402vMPUDisableBackgroundRegion: 403 PUSH { R0 } 404 405 MRC p15, 0, R0, c1, c0, 0 /* R0 = System Control Register (SCTLR). */ 406 BIC R0, R0, #0x20000 /* R0 = R0 & ~0x20000. Clear the BR bit in SCTLR. */ 407 MCR p15, 0, R0, c1, c0, 0 /* SCTLR = R0. */ 408 409 POP { R0 } 410 BX LR 411 412/* ----------------------------------------------------------------------------------- */ 413 414.align 4 415.global FreeRTOS_IRQ_Handler 416.type FreeRTOS_IRQ_Handler, %function 417FreeRTOS_IRQ_Handler: 418 SUB LR, LR, #4 /* Return to the interrupted instruction. */ 419 SRSDB SP!, #IRQ_MODE /* Save return state (i.e. SPSR_irq and LR_irq) to the IRQ stack. */ 420 421 /* Change to supervisor mode to allow reentry. It is necessary to ensure 422 * that a BL instruction within the interrupt handler code does not 423 * overwrite LR_irq. */ 424 CPS #SVC_MODE 425 426 PUSH { R0-R3, R12 } /* Push AAPCS callee saved registers. */ 427 428 /* Update interrupt nesting count. */ 429 LDR R0, =ulPortInterruptNesting /* R0 = &( ulPortInterruptNesting ). */ 430 LDR R1, [R0] /* R1 = ulPortInterruptNesting. */ 431 ADD R2, R1, #1 /* R2 = R1 + 1. */ 432 STR R2, [R0] /* Store the updated nesting count. */ 433 434 /* Call the application provided IRQ handler. */ 435 PUSH { R0-R3, LR } 436 BL vApplicationIRQHandler 437 POP { R0-R3, LR } 438 439 /* Disable IRQs incase vApplicationIRQHandler enabled them for re-entry. */ 440 CPSID I 441 DSB 442 ISB 443 444 /* Restore the old interrupt nesting count. R0 holds the address of 445 * ulPortInterruptNesting and R1 holds original value of 446 * ulPortInterruptNesting. */ 447 STR R1, [R0] 448 449 /* Context switch is only performed when interrupt nesting count is 0. */ 450 CMP R1, #0 451 BNE exit_without_switch 452 453 /* Check ulPortInterruptNesting to see if the interrupt requested a context 454 * switch. */ 455 LDR R1, =ulPortYieldRequired /* R1 = &( ulPortYieldRequired ). */ 456 LDR R0, [R1] /* R0 = ulPortYieldRequired. */ 457 /* If ulPortYieldRequired != 0, goto switch_before_exit. */ 458 CMP R0, #0 459 BNE switch_before_exit 460 461exit_without_switch: 462 POP { R0-R3, R12 } /* Restore AAPCS callee saved registers. */ 463 CPS #IRQ_MODE 464 RFE SP! 465 466switch_before_exit: 467 /* A context switch is to be performed. Clear ulPortYieldRequired. R1 holds 468 * the address of ulPortYieldRequired. */ 469 MOV R0, #0 470 STR R0, [R1] 471 472 /* Restore AAPCS callee saved registers, SPSR_irq and LR_irq before saving 473 * the task context. */ 474 POP { R0-R3, R12 } 475 CPS #IRQ_MODE 476 /* The contents of the IRQ stack at this point is the following: 477 * +----------+ 478 * SP+4 | SPSR_irq | 479 * +----------+ 480 * SP | LR_irq | 481 * +----------+ 482 */ 483 LDMIB SP!, { LR } 484 MSR SPSR_cxsf, LR 485 LDMDB SP, { LR } 486 ADD SP, SP, 0x4 487 portSAVE_CONTEXT 488 489 /* Call the function that selects the new task to execute. */ 490 BLX vTaskSwitchContext 491 492 /* Restore the context of, and branch to, the task selected to execute 493 * next. */ 494 portRESTORE_CONTEXT 495 496/* ----------------------------------------------------------------------------------- */ 497 498.end 499