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