This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:multiasm:paarm:chapter_5_7 [2025/12/04 10:19] – [Conditional Branches] eriks.klavins | en:multiasm:paarm:chapter_5_7 [2025/12/04 12:48] (current) – [Interrupts] eriks.klavins | ||
|---|---|---|---|
| Line 141: | Line 141: | ||
| ** Stack Pointer in Exception Levels ** | ** Stack Pointer in Exception Levels ** | ||
| + | |||
| + | AArch64 supports multiple exception levels (EL0 to EL3). Each level can have its own stack pointer. The processor provides two stack pointers for EL1 and above: | ||
| + | * SP_EL0 – stack pointer used when running code at EL0 (user mode) | ||
| + | * SP_ELx – stack pointer used for each higher exception level (EL1, EL2, EL3) | ||
| + | When an exception or interrupt occurs, the CPU automatically switches to the appropriate stack pointer for the new level. This prevents user-level code from corrupting kernel or hypervisor data and keeps the stacks for each privilege level separate. | ||
| + | It can be manually accessed or configured by using the system registers: | ||
| + | ''< | ||
| + | ''< | ||
| + | |||
| + | ** Using the Stack for Parameter Passing ** | ||
| + | |||
| + | In AArch64, the first eight function arguments are passed in registers ''< | ||
| + | ''< | ||
| + | ''< | ||
| + | ''< | ||
| + | This pushes the ninth argument onto the stack before calling // | ||
| + | |||
| + | ===== Interrupts ===== | ||
| + | |||
| + | An interrupt is a special signal that may cause the processor to temporarily stop normal program execution to handle an event that requires immediate attention. Interrupts are part of the previously described exception system or exception layer. | ||
| + | Interrupts on the processors are handled similarly, regardless of the architecture – the processor saves the current program state, program counter and status register. AArch64 then switches to privileged exception levels and finally jumps to the exception vector – a specific address that tells the processor where to find the instructions to handle the current event. The exception vector contains the address of a specific function. In the ARMv8 documentations, | ||
| + | There are four types of exceptions. | ||
| + | |||
| + | Synchronous exception – the exceptions of this type are always caused by the currently executed instruction. For example, the use of the str instruction to store some data at a memory location that does not exist or for which write operations are not available. In this case, a synchronous exception is generated. Synchronous exceptions can also be used to create a “software interrupt”. A software interrupt is a synchronous exception generated by the svc instruction. | ||
| + | |||
| + | IRQ (Interrupt Request) – these are normal interrupts. They are always asynchronous, | ||
| + | |||
| + | FIQ (Fast Interrupt Request) – this type of exception is called “fast interrupts” and exists solely for prioritising exceptions. It is possible to configure some interrupts as “normal” and others as “fast”. Fast interrupts will be signalled first and will be handled by a separate exception handler. | ||
| + | |||
| + | SError (System Error) – like IRQ and FIQ, SError exceptions are asynchronous and are generated by external hardware. Unlike IRQ and FIQ, SError always indicates some error condition. | ||
| + | |||
| + | Each exception type needs its own handler, the special function that handles an exact event. Also, separate handlers should be defined for each different exception level at which an exception is generated. If the current code is working on EL1, those states can be defined as follows: the EL1t Exception is taken from EL1, while the stack pointer is shared with EL0. This happens when the SPSel register holds the value 0. EL1h Exception is taken from EL1 at the time when the dedicated stack pointer was allocated for EL1. This means that SPSel holds the value 1. EL0_64 Exception is taken from EL0 executing in 64-bit mode, and EL0_32 Exception is taken from EL0 executing in 32-bit mode. | ||
| + | In total, 16 exception handlers must be defined (four exception levels multiplied by four execution states). A special structure that holds addresses of all exception handlers is called the exception vector table, or just the vector table. The [[https:// | ||
| + | {{: | ||
| + | |||
| + | There is no fixed number of interrupts available for the processor. The total number of available interrupts is defined by the Generic Interrupt Controller (GIC) implemented in the system. The Raspberry Pi 5 have a GIC-500 interrupt controller, and according to [[https:// | ||
| + | * ID0..ID15 is used for Software Generated Interrupts (system calls) | ||
| + | * The next 16 IDs are used for Private Peripheral Interrupts for a single core | ||
| + | * The rest of the IDs are for Shared Peripheral interrupts | ||
| + | |||
| + | Practically, | ||
| + | |||
| + | The interrupts can be disabled and enabled. For example:\\ | ||
| + | ''< | ||
| + | ''< | ||
| + | ''< | ||
| + | |||
| + | |||
| + | ** The Stack Pointer and Interrupt Handling ** | ||
| + | |||
| + | When an interrupt or exception occurs, the processor automatically saves the minimal state. It then switches to the stack pointer associated with the current exception level. The interrupt handler can safely use the stack at that level without overwriting user or kernel data. For example, when an IRQ occurs at EL1, the CPU switches from the user’s stack (SP_EL0) to the kernel’s stack (SP_EL1). This change is invisible to user code and helps isolate privilege levels. | ||
| + | Inside an interrupt handler, the code must save and restore any registers it modifies. A minimal handler might look like this:\\ | ||
| + | '' | ||
| + | ''< | ||
| + | ''< | ||
| + | ''< | ||
| + | ''< | ||
| + | |||
| + | Here, the stack pointer ensures the handler has a private area to store data safely, even if multiple interrupts occur. | ||
| + | |||
| + | < | ||
| + | < | ||
| + | < | ||
| + | irq_el1_handler: | ||
| + | @ Save registers | ||
| + | STP X0, X1, [SP, #-16]! | ||
| + | STP X2, X3, [SP, #-16]! | ||
| + | |||
| + | @ Acknowledge interrupt (example for GIC) | ||
| + | MRS X0, ICC_IAR1_EL1 | ||
| + | CMP X0, #1020 @ Spurious? | ||
| + | BEQ irq_done | ||
| + | |||
| + | @ Handle interrupt (custom code here) | ||
| + | BL handle_device_irq | ||
| + | |||
| + | @ Signal end of interrupt | ||
| + | MSR ICC_EOIR1_EL1, | ||
| + | |||
| + | irq_done: | ||
| + | @ Restore registers | ||
| + | LDP X2, X3, [SP], #16 | ||
| + | LDP X0, X1, [SP], #16 | ||
| + | ERET @ Return from exception | ||
| + | </ | ||
| + | </ | ||
| + | |||