ARM commonly uses interrupt to mean interrupt signal. On ARM A-profile and R-profile processors, that means an external IRQ or FIQ interrupt signal. The architecture does not specify how these signals are used. FIQ is often reserved for secure interrupt sources.
Both A-profile and R-profile processors cores have two physical interrupt pins, IRQ, and FIQ. An FIQ can mask IRQs, but an IRQ cannot mask FIQs. Historically, the architecture had features to permit lower-latency processing of FIQs.
When the processor takes an exception to AArch64 Execution state, all the PSTATE.DAIF interrupt masks are set automatically. This means that further interrupts are disabled. If software is to support nested interrupts, for example, to allow a higher priority interrupt to interrupt the handling of a lower priority source, then software must explicitly re-enable interrupts using the following instruction:
The immediate value is in fact a 4-bit field. There are also masks for:
- A (for SError)
- D (for Debug)
An assembly language IRQ handler might look like this:
IRQ_Handler // Stack all corruptible registers and save PCS corruptible interrupts STP X0, X1, [SP, #-16]! // SP = SP -16 STP X2, X3, [SP, #-16]! // SP = SP – 16 ... // PUSH the rest of the corruptible registers BL read_irq_source // a function to work out why we took an // interrupt and clear the request BL C_irq_handler // the C interrupt handler // restore from stack the corruptible // registers LDP X2, X3, [SP], #16 // S = SP + 16 LDP X0, X1, [SP], #16 // S = SP + 16 … ERET
However, from a performance point of view, the following sequence can be used:
IRQ_Handler SUB SP, SP, #<frame_size> // SP = SP - <frame_size> STP X0, X1, [SP], +16 // Store X0 and X1 at the base of the frame STP X2, X3, [SP], +16 // Store X2 and X3 at the base of the frame // + 16 bytes ... // more register storing ... // Interrupt handling BL read_irq_source // a function to work out why we took an // interrupt and clear the request BL C_irq_handler // the C interrupt handler // restore from stack the corruptible // registers LDP X0, X1, [SP, #-16]! // Load X0 and X1 at the base of the frame LDP X2, X3, [SP, #-16]! // Load X2 and X3 at the base of the frame + // 16 bytes ... // more register loading ADD SP, SP, #<frame_size> // Restore SP at its original value … ERET
The nested handler requires a little extra code. It must preserve on the stack the contents of SPSR_EL1 and ELR_EL1. IRQs must be re-enabled after determining (and clearing) the interrupt source. However, as the link register for subroutine calls is different to the link register for exceptions, it is possible to avoid having to do anything special with LR or modes.