Embedded systems typically monitor inputs waiting for an event, which then triggers a response by the system. You need to write code that listens for these events and acts on them. With Armv8-A, enable asynchronous exceptions (IRQs
, FIQs
, and SErrors
) which are taken when the processor needs to handle an event outside to the current flow of execution.
For example, a thermostat might monitor room temperature until it drops below a specified threshold. When the threshold is reached, the system must turn on the heating system.
This example will implement an FIQ. When this occurs, it will go to the highest exception level, EL3
.
Modify startup_el3.s
to extend the boot
code to perform the following, before branching to __main
.
Configure the
SCR_EL3, Secure Configuration Register
to route FIQ exceptions to EL3
.
// Configure SCR_EL3
MOV w1, #0
ORR w1, w1, #(1 << 2) // Set FIQ bit (FIQs routed to EL3)
MSR SCR_EL3, x1
Set the
VBAR_EL3, Vector Based Address Register
to the location of the exception vector table. You will create vectors
in the next section.
// Install vector table
.global vectors
LDR x0, =vectors
MSR VBAR_EL3, x0
ISB
Clear appropriate bits within DAIF, Interrupt Mask Bits .
// Clear interrupt masks
MSR DAIFClr, #0xF
Save your startup_el3.s
file.
The exception vector table tells the processor what code to run in the event of an exception. The format of this table is fixed and architecturally defined.
Create vectors.s
containing the following code.
You will implement only the necessary FIQ exception for this example. A real system would need to implement all handlers.
.section VECTORS,"ax"
.global vectors
.balign 0x800
vectors:
.space 0x100, 0x0 // Current EL with SP0
curr_el_sp0_fiq:
B fiqFirstLevelHandler
.balign 0x80
.space 0x80, 0x0
.space 0x100, 0x0 // Current EL with SPx
curr_el_spx_fiq:
B fiqFirstLevelHandler
.balign 0x80
.space 0x80, 0x0
.space 0x100, 0x0 // Lower EL using AArch64
lower_el_aarch64_fiq:
B fiqFirstLevelHandler
.balign 0x80
.space 0x80, 0x0
.space 0x200, 0x0 // Lower EL using AArch32
Each exception has a window of 0x80 bytes in the vector table area to use for its code. In this case you will simply branch to fiqFirstLevelHandler
, which preserves all registers, before calling fiqHandler()
(which shall be implemented later).
When fiqHandler()
returns, undo the register preservation, before returning to where the code was before the exception occurred, using the ERET
instruction.
Add the following to your vectors.s
:
.global fiqHandler
fiqFirstLevelHandler:
STP x29, x30, [sp, #-16]!
STP x18, x19, [sp, #-16]!
STP x16, x17, [sp, #-16]!
STP x14, x15, [sp, #-16]!
STP x12, x13, [sp, #-16]!
STP x10, x11, [sp, #-16]!
STP x8, x9, [sp, #-16]!
STP x6, x7, [sp, #-16]!
STP x4, x5, [sp, #-16]!
STP x2, x3, [sp, #-16]!
STP x0, x1, [sp, #-16]!
BL fiqHandler
LDP x0, x1, [sp], #16
LDP x2, x3, [sp], #16
LDP x4, x5, [sp], #16
LDP x6, x7, [sp], #16
LDP x8, x9, [sp], #16
LDP x10, x11, [sp], #16
LDP x12, x13, [sp], #16
LDP x14, x15, [sp], #16
LDP x16, x17, [sp], #16
LDP x18, x19, [sp], #16
LDP x29, x30, [sp], #16
ERET