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