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/* Including FreeRTOSConfig.h here will cause build errors if the header file
29contains code not understood by the assembler - for example the 'extern' keyword.
30To avoid errors place any such code inside a #ifdef __ICCARM__/#endif block so
31the code is included in C files but excluded by the preprocessor in assembly
32files (__ICCARM__ is defined by the IAR C compiler but not by the IAR assembler. */
33#include "FreeRTOSConfig.h"
34
35/* System call numbers includes. */
36#include "mpu_syscall_numbers.h"
37
38#ifndef configUSE_MPU_WRAPPERS_V1
39    #define configUSE_MPU_WRAPPERS_V1 0
40#endif
41
42#ifndef configRUN_FREERTOS_SECURE_ONLY
43    #define configRUN_FREERTOS_SECURE_ONLY 0
44#endif
45
46    EXTERN pxCurrentTCB
47    EXTERN vTaskSwitchContext
48    EXTERN vPortSVCHandler_C
49#if ( ( configENABLE_MPU == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
50    EXTERN vSystemCallEnter
51    EXTERN vSystemCallExit
52#endif
53
54    PUBLIC xIsPrivileged
55    PUBLIC vResetPrivilege
56    PUBLIC vRestoreContextOfFirstTask
57    PUBLIC vRaisePrivilege
58    PUBLIC vStartFirstTask
59    PUBLIC ulSetInterruptMask
60    PUBLIC vClearInterruptMask
61    PUBLIC PendSV_Handler
62    PUBLIC SVC_Handler
63
64#if ( configENABLE_FPU == 1 )
65    #error Cortex-M23 does not have a Floating Point Unit (FPU) and therefore configENABLE_FPU must be set to 0.
66#endif
67/*-----------------------------------------------------------*/
68
69/*---------------- Unprivileged Functions -------------------*/
70
71/*-----------------------------------------------------------*/
72
73    SECTION .text:CODE:NOROOT(2)
74    THUMB
75/*-----------------------------------------------------------*/
76
77xIsPrivileged:
78    mrs r0, control                         /* r0 = CONTROL. */
79    movs r1, #1                             /* r1 = 1. */
80    tst r0, r1                              /* Perform r0 & r1 (bitwise AND) and update the conditions flag. */
81    beq running_privileged                  /* If the result of previous AND operation was 0, branch. */
82    movs r0, #0                             /* CONTROL[0]!=0. Return false to indicate that the processor is not privileged. */
83    bx lr                                   /* Return. */
84    running_privileged:
85        movs r0, #1                         /* CONTROL[0]==0. Return true to indicate that the processor is privileged. */
86        bx lr                               /* Return. */
87
88/*-----------------------------------------------------------*/
89
90vResetPrivilege:
91    mrs r0, control                         /* r0 = CONTROL. */
92    movs r1, #1                             /* r1 = 1. */
93    orrs r0, r1                             /* r0 = r0 | r1. */
94    msr control, r0                         /* CONTROL = r0. */
95    bx lr                                   /* Return to the caller. */
96/*-----------------------------------------------------------*/
97
98/*----------------- Privileged Functions --------------------*/
99
100/*-----------------------------------------------------------*/
101
102    SECTION privileged_functions:CODE:NOROOT(2)
103    THUMB
104/*-----------------------------------------------------------*/
105
106#if ( configENABLE_MPU == 1 )
107
108vRestoreContextOfFirstTask:
109    program_mpu_first_task:
110        ldr r3, =pxCurrentTCB               /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
111        ldr r0, [r3]                        /* r0 = pxCurrentTCB.*/
112
113        dmb                                 /* Complete outstanding transfers before disabling MPU. */
114        ldr r1, =0xe000ed94                 /* r1 = 0xe000ed94 [Location of MPU_CTRL]. */
115        ldr r2, [r1]                        /* Read the value of MPU_CTRL. */
116        movs r3, #1                         /* r3 = 1. */
117        bics r2, r3                         /* r2 = r2 & ~r3 i.e. Clear the bit 0 in r2. */
118        str r2, [r1]                        /* Disable MPU. */
119
120        adds r0, #4                         /* r0 = r0 + 4. r0 now points to MAIR0 in TCB. */
121        ldr r1, [r0]                        /* r1 = *r0 i.e. r1 = MAIR0. */
122        ldr r2, =0xe000edc0                 /* r2 = 0xe000edc0 [Location of MAIR0]. */
123        str r1, [r2]                        /* Program MAIR0. */
124
125        adds r0, #4                         /* r0 = r0 + 4. r0 now points to first RBAR in TCB. */
126        ldr r1, =0xe000ed98                 /* r1 = 0xe000ed98 [Location of RNR]. */
127
128        movs r3, #4                         /* r3 = 4. */
129        str r3, [r1]                        /* Program RNR = 4. */
130        ldmia r0!, {r4-r5}                  /* Read first set of RBAR/RLAR registers from TCB. */
131        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
132        stmia r2!, {r4-r5}                  /* Write first set of RBAR/RLAR registers. */
133        movs r3, #5                         /* r3 = 5. */
134        str r3, [r1]                        /* Program RNR = 5. */
135        ldmia r0!, {r4-r5}                  /* Read second set of RBAR/RLAR registers from TCB. */
136        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
137        stmia r2!, {r4-r5}                  /* Write second set of RBAR/RLAR registers. */
138        movs r3, #6                         /* r3 = 6. */
139        str r3, [r1]                        /* Program RNR = 6. */
140        ldmia r0!, {r4-r5}                  /* Read third set of RBAR/RLAR registers from TCB. */
141        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
142        stmia r2!, {r4-r5}                  /* Write third set of RBAR/RLAR registers. */
143        movs r3, #7                         /* r3 = 6. */
144        str r3, [r1]                        /* Program RNR = 7. */
145        ldmia r0!, {r4-r5}                  /* Read fourth set of RBAR/RLAR registers from TCB. */
146        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
147        stmia r2!, {r4-r5}                  /* Write fourth set of RBAR/RLAR registers. */
148
149        ldr r1, =0xe000ed94                 /* r1 = 0xe000ed94 [Location of MPU_CTRL]. */
150        ldr r2, [r1]                        /* Read the value of MPU_CTRL. */
151        movs r3, #1                         /* r3 = 1. */
152        orrs r2, r3                         /* r2 = r2 | r3 i.e. Set the bit 0 in r2. */
153        str r2, [r1]                        /* Enable MPU. */
154        dsb                                 /* Force memory writes before continuing. */
155
156    restore_context_first_task:
157        ldr r2, =pxCurrentTCB               /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
158        ldr r0, [r2]                        /* r0 = pxCurrentTCB.*/
159        ldr r1, [r0]                        /* r1 = Location of saved context in TCB. */
160
161    restore_special_regs_first_task:
162        subs r1, #16
163        ldmia r1!, {r2-r5}                  /* r2 = original PSP, r3 = PSPLIM, r4 = CONTROL, r5 = LR. */
164        subs r1, #16
165        msr psp, r2
166    #if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
167        msr psplim, r3
168    #endif
169        msr control, r4
170        mov lr, r5
171
172    restore_general_regs_first_task:
173        subs r1, #32
174        ldmia r1!, {r4-r7}                  /* r4-r7 contain half of the hardware saved context. */
175        stmia r2!, {r4-r7}                  /* Copy half of the the hardware saved context on the task stack. */
176        ldmia r1!, {r4-r7}                  /* r4-r7 contain rest half of the hardware saved context. */
177        stmia r2!, {r4-r7}                  /* Copy rest half of the the hardware saved context on the task stack. */
178        subs r1, #48
179        ldmia r1!, {r4-r7}                  /* Restore r8-r11. */
180        mov r8, r4                          /* r8 = r4. */
181        mov r9, r5                          /* r9 = r5. */
182        mov r10, r6                         /* r10 = r6. */
183        mov r11, r7                         /* r11 = r7. */
184        subs r1, #32
185        ldmia r1!, {r4-r7}                  /* Restore r4-r7. */
186        subs r1, #16
187
188    restore_context_done_first_task:
189        str r1, [r0]                        /* Save the location where the context should be saved next as the first member of TCB. */
190        bx lr
191
192#else /* configENABLE_MPU */
193
194vRestoreContextOfFirstTask:
195    ldr  r2, =pxCurrentTCB                  /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
196    ldr  r1, [r2]                           /* Read pxCurrentTCB. */
197    ldr  r0, [r1]                           /* Read top of stack from TCB - The first item in pxCurrentTCB is the task top of stack. */
198
199    ldm  r0!, {r1-r2}                       /* Read from stack - r1 = PSPLIM and r2 = EXC_RETURN. */
200#if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
201    msr  psplim, r1                         /* Set this task's PSPLIM value. */
202#endif
203    movs r1, #2                             /* r1 = 2. */
204    msr  CONTROL, r1                        /* Switch to use PSP in the thread mode. */
205    adds r0, #32                            /* Discard everything up to r0. */
206    msr  psp, r0                            /* This is now the new top of stack to use in the task. */
207    isb
208    bx   r2                                 /* Finally, branch to EXC_RETURN. */
209
210#endif /* configENABLE_MPU */
211/*-----------------------------------------------------------*/
212
213vRaisePrivilege:
214    mrs  r0, control                        /* Read the CONTROL register. */
215    movs r1, #1                             /* r1 = 1. */
216    bics r0, r1                             /* Clear the bit 0. */
217    msr  control, r0                        /* Write back the new CONTROL value. */
218    bx lr                                   /* Return to the caller. */
219/*-----------------------------------------------------------*/
220
221vStartFirstTask:
222    ldr r0, =0xe000ed08                     /* Use the NVIC offset register to locate the stack. */
223    ldr r0, [r0]                            /* Read the VTOR register which gives the address of vector table. */
224    ldr r0, [r0]                            /* The first entry in vector table is stack pointer. */
225    msr msp, r0                             /* Set the MSP back to the start of the stack. */
226    cpsie i                                 /* Globally enable interrupts. */
227    dsb
228    isb
229    svc 102                                 /* System call to start the first task. portSVC_START_SCHEDULER = 102. */
230    nop
231/*-----------------------------------------------------------*/
232
233ulSetInterruptMask:
234    mrs r0, PRIMASK
235    cpsid i
236    bx lr
237/*-----------------------------------------------------------*/
238
239vClearInterruptMask:
240    msr PRIMASK, r0
241    bx lr
242/*-----------------------------------------------------------*/
243
244#if ( configENABLE_MPU == 1 )
245
246PendSV_Handler:
247    ldr r2, =pxCurrentTCB                   /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
248    ldr r0, [r2]                            /* r0 = pxCurrentTCB. */
249    ldr r1, [r0]                            /* r1 = Location in TCB where the context should be saved. */
250    mrs r2, psp                             /* r2 = PSP. */
251
252    save_general_regs:
253        stmia r1!, {r4-r7}                  /* Store r4-r7. */
254        mov r4, r8                          /* r4 = r8. */
255        mov r5, r9                          /* r5 = r9. */
256        mov r6, r10                         /* r6 = r10. */
257        mov r7, r11                         /* r7 = r11. */
258        stmia r1!, {r4-r7}                  /* Store r8-r11. */
259        ldmia r2!, {r4-r7}                  /* Copy half of the  hardware saved context into r4-r7. */
260        stmia r1!, {r4-r7}                  /* Store the hardware saved context. */
261        ldmia r2!, {r4-r7}                  /* Copy rest half of the  hardware saved context into r4-r7. */
262        stmia r1!, {r4-r7}                  /* Store the hardware saved context. */
263
264    save_special_regs:
265        mrs r2, psp                         /* r2 = PSP. */
266    #if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
267        mrs r3, psplim                      /* r3 = PSPLIM. */
268    #else
269        movs r3, #0                         /* r3 = 0. 0 is stored in the PSPLIM slot. */
270    #endif
271        mrs r4, control                     /* r4 = CONTROL. */
272        mov r5, lr                          /* r5 = LR. */
273        stmia r1!, {r2-r5}                  /* Store original PSP (after hardware has saved context), PSPLIM, CONTROL and LR. */
274        str r1, [r0]                        /* Save the location from where the context should be restored as the first member of TCB. */
275
276    select_next_task:
277        cpsid i
278        bl vTaskSwitchContext
279        cpsie i
280
281    program_mpu:
282        ldr r3, =pxCurrentTCB               /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
283        ldr r0, [r3]                        /* r0 = pxCurrentTCB.*/
284
285        dmb                                 /* Complete outstanding transfers before disabling MPU. */
286        ldr r1, =0xe000ed94                 /* r1 = 0xe000ed94 [Location of MPU_CTRL]. */
287        ldr r2, [r1]                        /* Read the value of MPU_CTRL. */
288        movs r3, #1                         /* r3 = 1. */
289        bics r2, r3                         /* r2 = r2 & ~r3 i.e. Clear the bit 0 in r2. */
290        str r2, [r1]                        /* Disable MPU. */
291
292        adds r0, #4                         /* r0 = r0 + 4. r0 now points to MAIR0 in TCB. */
293        ldr r1, [r0]                        /* r1 = *r0 i.e. r1 = MAIR0. */
294        ldr r2, =0xe000edc0                 /* r2 = 0xe000edc0 [Location of MAIR0]. */
295        str r1, [r2]                        /* Program MAIR0. */
296
297        adds r0, #4                         /* r0 = r0 + 4. r0 now points to first RBAR in TCB. */
298        ldr r1, =0xe000ed98                 /* r1 = 0xe000ed98 [Location of RNR]. */
299
300        movs r3, #4                         /* r3 = 4. */
301        str r3, [r1]                        /* Program RNR = 4. */
302        ldmia r0!, {r4-r5}                  /* Read first set of RBAR/RLAR registers from TCB. */
303        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
304        stmia r2!, {r4-r5}                  /* Write first set of RBAR/RLAR registers. */
305        movs r3, #5                         /* r3 = 5. */
306        str r3, [r1]                        /* Program RNR = 5. */
307        ldmia r0!, {r4-r5}                  /* Read second set of RBAR/RLAR registers from TCB. */
308        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
309        stmia r2!, {r4-r5}                  /* Write second set of RBAR/RLAR registers. */
310        movs r3, #6                         /* r3 = 6. */
311        str r3, [r1]                        /* Program RNR = 6. */
312        ldmia r0!, {r4-r5}                  /* Read third set of RBAR/RLAR registers from TCB. */
313        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
314        stmia r2!, {r4-r5}                  /* Write third set of RBAR/RLAR registers. */
315        movs r3, #7                         /* r3 = 6. */
316        str r3, [r1]                        /* Program RNR = 7. */
317        ldmia r0!, {r4-r5}                  /* Read fourth set of RBAR/RLAR registers from TCB. */
318        ldr r2, =0xe000ed9c                 /* r2 = 0xe000ed9c [Location of RBAR]. */
319        stmia r2!, {r4-r5}                  /* Write fourth set of RBAR/RLAR registers. */
320
321        ldr r1, =0xe000ed94                 /* r1 = 0xe000ed94 [Location of MPU_CTRL]. */
322        ldr r2, [r1]                        /* Read the value of MPU_CTRL. */
323        movs r3, #1                         /* r3 = 1. */
324        orrs r2, r3                         /* r2 = r2 | r3 i.e. Set the bit 0 in r2. */
325        str r2, [r1]                        /* Enable MPU. */
326        dsb                                 /* Force memory writes before continuing. */
327
328    restore_context:
329        ldr r2, =pxCurrentTCB               /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
330        ldr r0, [r2]                        /* r0 = pxCurrentTCB.*/
331        ldr r1, [r0]                        /* r1 = Location of saved context in TCB. */
332
333    restore_special_regs:
334        subs r1, #16
335        ldmia r1!, {r2-r5}                  /* r2 = original PSP, r3 = PSPLIM, r4 = CONTROL, r5 = LR. */
336        subs r1, #16
337        msr psp, r2
338    #if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
339        msr psplim, r3
340    #endif
341        msr control, r4
342        mov lr, r5
343
344    restore_general_regs:
345        subs r1, #32
346        ldmia r1!, {r4-r7}                  /* r4-r7 contain half of the hardware saved context. */
347        stmia r2!, {r4-r7}                  /* Copy half of the the hardware saved context on the task stack. */
348        ldmia r1!, {r4-r7}                  /* r4-r7 contain rest half of the hardware saved context. */
349        stmia r2!, {r4-r7}                  /* Copy rest half of the the hardware saved context on the task stack. */
350        subs r1, #48
351        ldmia r1!, {r4-r7}                  /* Restore r8-r11. */
352        mov r8, r4                          /* r8 = r4. */
353        mov r9, r5                          /* r9 = r5. */
354        mov r10, r6                         /* r10 = r6. */
355        mov r11, r7                         /* r11 = r7. */
356        subs r1, #32
357        ldmia r1!, {r4-r7}                  /* Restore r4-r7. */
358        subs r1, #16
359
360    restore_context_done:
361        str r1, [r0]                        /* Save the location where the context should be saved next as the first member of TCB. */
362        bx lr
363
364#else /* configENABLE_MPU */
365
366PendSV_Handler:
367    mrs r0, psp                             /* Read PSP in r0. */
368    ldr r2, =pxCurrentTCB                   /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
369    ldr r1, [r2]                            /* Read pxCurrentTCB. */
370
371    subs r0, r0, #40                        /* Make space for PSPLIM, LR and the remaining registers on the stack. */
372    str r0, [r1]                            /* Save the new top of stack in TCB. */
373#if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
374    mrs r2, psplim                          /* r2 = PSPLIM. */
375#else
376    movs r2, #0                             /* r0 = 0. 0 is stored in the PSPLIM slot. */
377#endif
378    mov r3, lr                              /* r3 = LR/EXC_RETURN. */
379    stmia r0!, {r2-r7}                      /* Store on the stack - PSPLIM, LR and low registers that are not automatically saved. */
380    mov r4, r8                              /* r4 = r8. */
381    mov r5, r9                              /* r5 = r9. */
382    mov r6, r10                             /* r6 = r10. */
383    mov r7, r11                             /* r7 = r11. */
384    stmia r0!, {r4-r7}                      /* Store the high registers that are not saved automatically. */
385
386    cpsid i
387    bl vTaskSwitchContext
388    cpsie i
389
390    ldr r2, =pxCurrentTCB                   /* Read the location of pxCurrentTCB i.e. &( pxCurrentTCB ). */
391    ldr r1, [r2]                            /* Read pxCurrentTCB. */
392    ldr r0, [r1]                            /* The first item in pxCurrentTCB is the task top of stack. r0 now points to the top of stack. */
393
394    adds r0, r0, #24                        /* Move to the high registers. */
395    ldmia r0!, {r4-r7}                      /* Restore the high registers that are not automatically restored. */
396    mov r8, r4                              /* r8 = r4. */
397    mov r9, r5                              /* r9 = r5. */
398    mov r10, r6                             /* r10 = r6. */
399    mov r11, r7                             /* r11 = r7. */
400    msr psp, r0                             /* Remember the new top of stack for the task. */
401    subs r0, r0, #40                        /* Move to the starting of the saved context. */
402    ldmia r0!, {r2-r7}                      /* Read from stack - r2 = PSPLIM, r3 = LR and r4-r7 restored. */
403#if ( configRUN_FREERTOS_SECURE_ONLY == 1 )
404    msr psplim, r2                          /* Restore the PSPLIM register value for the task. */
405#endif
406    bx r3
407
408#endif /* configENABLE_MPU */
409/*-----------------------------------------------------------*/
410
411#if ( ( configENABLE_MPU == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) )
412
413SVC_Handler:
414    movs r0, #4
415    mov r1, lr
416    tst r0, r1
417    beq stack_on_msp
418    stack_on_psp:
419        mrs r0, psp
420        b route_svc
421    stack_on_msp:
422        mrs r0, msp
423        b route_svc
424
425    route_svc:
426        ldr r3, [r0, #24]
427        subs r3, #2
428        ldrb r2, [r3, #0]
429        cmp r2, #NUM_SYSTEM_CALLS
430        blt system_call_enter
431        cmp r2, #104        /* portSVC_SYSTEM_CALL_EXIT. */
432        beq system_call_exit
433        b vPortSVCHandler_C
434
435    system_call_enter:
436        b vSystemCallEnter
437    system_call_exit:
438        b vSystemCallExit
439
440#else /* ( configENABLE_MPU == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
441
442SVC_Handler:
443    movs r0, #4
444    mov r1, lr
445    tst r0, r1
446    beq stacking_used_msp
447    mrs r0, psp
448    b vPortSVCHandler_C
449    stacking_used_msp:
450        mrs r0, msp
451        b vPortSVCHandler_C
452
453#endif /* ( configENABLE_MPU == 1 ) && ( configUSE_MPU_WRAPPERS_V1 == 0 ) */
454/*-----------------------------------------------------------*/
455
456    END
457