1/*
2 * FreeRTOS Kernel V11.1.0
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/* Including FreeRTOSConfig.h here will cause build errors if the header file
30contains code not understood by the assembler - for example the 'extern' keyword.
31To avoid errors place any such code inside a #ifdef __ICCARM__/#endif block so
32the code is included in C files but excluded by the preprocessor in assembly
33files (__ICCARM__ is defined by the IAR C compiler but not by the IAR assembler. */
34#include <FreeRTOSConfig.h>
35#include <mpu_syscall_numbers.h>
36
37    RSEG    CODE:CODE(2)
38    thumb
39
40    EXTERN pxCurrentTCB
41    EXTERN vTaskSwitchContext
42    EXTERN vPortSVCHandler_C
43    EXTERN vSystemCallEnter
44    EXTERN vSystemCallExit
45
46    PUBLIC xPortPendSVHandler
47    PUBLIC vPortSVCHandler
48    PUBLIC vPortStartFirstTask
49    PUBLIC vPortEnableVFP
50    PUBLIC vPortRestoreContextOfFirstTask
51    PUBLIC xIsPrivileged
52    PUBLIC vResetPrivilege
53
54/*-----------------------------------------------------------*/
55
56#ifndef configUSE_MPU_WRAPPERS_V1
57    #define configUSE_MPU_WRAPPERS_V1 0
58#endif
59
60/* Errata 837070 workaround must be enabled on Cortex-M7 r0p0
61 * and r0p1 cores. */
62#ifndef configENABLE_ERRATA_837070_WORKAROUND
63    #define configENABLE_ERRATA_837070_WORKAROUND 0
64#endif
65
66/* These must be in sync with portmacro.h. */
67#define portSVC_START_SCHEDULER        100
68#define portSVC_SYSTEM_CALL_EXIT       103
69/*-----------------------------------------------------------*/
70
71xPortPendSVHandler:
72
73    ldr r3, =pxCurrentTCB
74    ldr r2, [r3]                           /* r2 = pxCurrentTCB. */
75    ldr r1, [r2]                           /* r1 = Location where the context should be saved. */
76
77    /*------------ Save Context. ----------- */
78    mrs r3, control
79    mrs r0, psp
80    isb
81
82    add r0, r0, #0x20                      /* Move r0 to location where s0 is saved. */
83    tst lr, #0x10
84    ittt eq
85    vstmiaeq r1!, {s16-s31}                /* Store s16-s31. */
86    vldmiaeq r0, {s0-s16}                  /* Copy hardware saved FP context into s0-s16. */
87    vstmiaeq r1!, {s0-s16}                 /* Store hardware saved FP context. */
88    sub r0, r0, #0x20                      /* Set r0 back to the location of hardware saved context. */
89
90    stmia r1!, {r3-r11, lr}                /* Store CONTROL register, r4-r11 and LR. */
91    ldmia r0, {r4-r11}                     /* Copy hardware saved context into r4-r11. */
92    stmia r1!, {r0, r4-r11}                /* Store original PSP (after hardware has saved context) and the hardware saved context. */
93    str r1, [r2]                           /* Save the location from where the context should be restored as the first member of TCB. */
94
95    /*---------- Select next task. --------- */
96    mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
97#if ( configENABLE_ERRATA_837070_WORKAROUND == 1 )
98    cpsid i                                /* ARM Cortex-M7 r0p1 Errata 837070 workaround. */
99#endif
100    msr basepri, r0
101    dsb
102    isb
103#if ( configENABLE_ERRATA_837070_WORKAROUND == 1 )
104    cpsie i                                /* ARM Cortex-M7 r0p1 Errata 837070 workaround. */
105#endif
106    bl vTaskSwitchContext
107    mov r0, #0
108    msr basepri, r0
109
110    /*------------ Program MPU. ------------ */
111    ldr r3, =pxCurrentTCB
112    ldr r2, [r3]                           /* r2 = pxCurrentTCB. */
113    add r2, r2, #4                         /* r2 = Second item in the TCB which is xMPUSettings. */
114
115    dmb                                    /* Complete outstanding transfers before disabling MPU. */
116    ldr r0, =0xe000ed94                    /* MPU_CTRL register. */
117    ldr r3, [r0]                           /* Read the value of MPU_CTRL. */
118    bic r3, #1                             /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
119    str r3, [r0]                           /* Disable MPU. */
120
121    ldr r0, =0xe000ed9c                    /* Region Base Address register. */
122    ldmia r2!, {r4-r11}                    /* Read 4 sets of MPU registers [MPU Region # 0 - 3]. */
123    stmia r0, {r4-r11}                     /* Write 4 sets of MPU registers [MPU Region # 0 - 3]. */
124
125#ifdef configTOTAL_MPU_REGIONS
126    #if ( configTOTAL_MPU_REGIONS == 16 )
127        ldmia r2!, {r4-r11}                 /* Read 4 sets of MPU registers [MPU Region # 4 - 7]. */
128        stmia r0, {r4-r11}                  /* Write 4 sets of MPU registers. [MPU Region # 4 - 7]. */
129        ldmia r2!, {r4-r11}                 /* Read 4 sets of MPU registers [MPU Region # 8 - 11]. */
130        stmia r0, {r4-r11}                  /* Write 4 sets of MPU registers. [MPU Region # 8 - 11]. */
131    #endif /* configTOTAL_MPU_REGIONS == 16. */
132#endif
133
134    ldr r0, =0xe000ed94                    /* MPU_CTRL register. */
135    ldr r3, [r0]                           /* Read the value of MPU_CTRL. */
136    orr r3, #1                             /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
137    str r3, [r0]                           /* Enable MPU. */
138    dsb                                    /* Force memory writes before continuing. */
139
140    /*---------- Restore Context. ---------- */
141    ldr r3, =pxCurrentTCB
142    ldr r2, [r3]                           /* r2 = pxCurrentTCB. */
143    ldr r1, [r2]                           /* r1 = Location of saved context in TCB. */
144
145    ldmdb r1!, {r0, r4-r11}                /* r0 contains PSP after the hardware had saved context. r4-r11 contain hardware saved context. */
146    msr psp, r0
147    stmia r0!, {r4-r11}                    /* Copy the hardware saved context on the task stack. */
148    ldmdb r1!, {r3-r11, lr}                /* r3 contains CONTROL register. r4-r11 and LR restored. */
149    msr control, r3
150
151    tst lr, #0x10
152    ittt eq
153    vldmdbeq r1!, {s0-s16}                 /* s0-s16 contain hardware saved FP context. */
154    vstmiaeq r0!, {s0-s16}                 /* Copy hardware saved FP context on the task stack. */
155    vldmdbeq r1!, {s16-s31}                /* Restore s16-s31. */
156
157    str r1, [r2]                           /* Save the location where the context should be saved next as the first member of TCB. */
158    bx lr
159
160/*-----------------------------------------------------------*/
161
162#if ( configUSE_MPU_WRAPPERS_V1 == 0 )
163
164vPortSVCHandler:
165    tst lr, #4
166    ite eq
167    mrseq r0, msp
168    mrsne r0, psp
169
170    ldr r1, [r0, #24]
171    ldrb r2, [r1, #-2]
172    cmp r2, #NUM_SYSTEM_CALLS
173    blt syscall_enter
174    cmp r2, #portSVC_SYSTEM_CALL_EXIT
175    beq syscall_exit
176    b vPortSVCHandler_C
177
178    syscall_enter:
179        mov r1, lr
180        b vSystemCallEnter
181
182    syscall_exit:
183        mov r1, lr
184        b vSystemCallExit
185
186#else /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
187
188vPortSVCHandler:
189    #ifndef USE_PROCESS_STACK
190        tst lr, #4
191        ite eq
192        mrseq r0, msp
193        mrsne r0, psp
194    #else
195        mrs r0, psp
196    #endif
197        b vPortSVCHandler_C
198
199#endif /* #if ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
200/*-----------------------------------------------------------*/
201
202vPortStartFirstTask:
203    /* Use the NVIC offset register to locate the stack. */
204    ldr r0, =0xE000ED08
205    ldr r0, [r0]
206    ldr r0, [r0]
207    /* Set the msp back to the start of the stack. */
208    msr msp, r0
209    /* Clear the bit that indicates the FPU is in use in case the FPU was used
210    before the scheduler was started - which would otherwise result in the
211    unnecessary leaving of space in the SVC stack for lazy saving of FPU
212    registers. */
213    mov r0, #0
214    msr control, r0
215    /* Call SVC to start the first task. */
216    cpsie i
217    cpsie f
218    dsb
219    isb
220    svc #portSVC_START_SCHEDULER
221
222/*-----------------------------------------------------------*/
223
224vPortRestoreContextOfFirstTask:
225    ldr r0, =0xE000ED08                    /* Use the NVIC offset register to locate the stack. */
226    ldr r0, [r0]
227    ldr r0, [r0]
228    msr msp, r0                            /* Set the msp back to the start of the stack. */
229
230    /*------------ Program MPU. ------------ */
231    ldr r3, =pxCurrentTCB
232    ldr r2, [r3]                           /* r2 = pxCurrentTCB. */
233    add r2, r2, #4                         /* r2 = Second item in the TCB which is xMPUSettings. */
234
235    dmb                                    /* Complete outstanding transfers before disabling MPU. */
236    ldr r0, =0xe000ed94                    /* MPU_CTRL register. */
237    ldr r3, [r0]                           /* Read the value of MPU_CTRL. */
238    bic r3, #1                             /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */
239    str r3, [r0]                           /* Disable MPU. */
240
241    ldr r0, =0xe000ed9c                    /* Region Base Address register. */
242    ldmia r2!, {r4-r11}                    /* Read 4 sets of MPU registers [MPU Region # 0 - 3]. */
243    stmia r0, {r4-r11}                     /* Write 4 sets of MPU registers [MPU Region # 0 - 3]. */
244
245#ifdef configTOTAL_MPU_REGIONS
246    #if ( configTOTAL_MPU_REGIONS == 16 )
247        ldmia r2!, {r4-r11}                /* Read 4 sets of MPU registers [MPU Region # 4 - 7]. */
248        stmia r0, {r4-r11}                 /* Write 4 sets of MPU registers. [MPU Region # 4 - 7]. */
249        ldmia r2!, {r4-r11}                /* Read 4 sets of MPU registers [MPU Region # 8 - 11]. */
250        stmia r0, {r4-r11}                 /* Write 4 sets of MPU registers. [MPU Region # 8 - 11]. */
251    #endif /* configTOTAL_MPU_REGIONS == 16. */
252#endif
253
254    ldr r0, =0xe000ed94                    /* MPU_CTRL register. */
255    ldr r3, [r0]                           /* Read the value of MPU_CTRL. */
256    orr r3, #1                             /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */
257    str r3, [r0]                           /* Enable MPU. */
258    dsb                                    /* Force memory writes before continuing. */
259
260    /*---------- Restore Context. ---------- */
261    ldr r3, =pxCurrentTCB
262    ldr r2, [r3]                           /* r2 = pxCurrentTCB. */
263    ldr r1, [r2]                           /* r1 = Location of saved context in TCB. */
264
265    ldmdb r1!, {r0, r4-r11}                /* r0 contains PSP after the hardware had saved context. r4-r11 contain hardware saved context. */
266    msr psp, r0
267    stmia r0, {r4-r11}                     /* Copy the hardware saved context on the task stack. */
268    ldmdb r1!, {r3-r11, lr}                /* r3 contains CONTROL register. r4-r11 and LR restored. */
269    msr control, r3
270    str r1, [r2]                           /* Save the location where the context should be saved next as the first member of TCB. */
271
272    mov r0, #0
273    msr basepri, r0
274    bx lr
275
276/*-----------------------------------------------------------*/
277
278vPortEnableVFP:
279    /* The FPU enable bits are in the CPACR. */
280    ldr.w r0, =0xE000ED88
281    ldr r1, [r0]
282
283    /* Enable CP10 and CP11 coprocessors, then save back. */
284    orr r1, r1, #( 0xf << 20 )
285    str r1, [r0]
286    bx  r14
287
288/*-----------------------------------------------------------*/
289
290xIsPrivileged:
291    mrs r0, control     /* r0 = CONTROL. */
292    tst r0, #1          /* Perform r0 & 1 (bitwise AND) and update the conditions flag. */
293    ite ne
294    movne r0, #0        /* CONTROL[0]!=0. Return false to indicate that the processor is not privileged. */
295    moveq r0, #1        /* CONTROL[0]==0. Return true to indicate that the processor is privileged. */
296    bx lr               /* Return. */
297/*-----------------------------------------------------------*/
298
299vResetPrivilege:
300    mrs r0, control     /* r0 = CONTROL. */
301    orr r0, r0, #1      /* r0 = r0 | 1. */
302    msr control, r0     /* CONTROL = r0. */
303    bx lr               /* Return to the caller. */
304/*-----------------------------------------------------------*/
305
306    END
307