Changes to Execution state and Exception level caused by exceptions
When an exception is taken, the processor can change Execution state (from AArch64 to AArch32) or stay in the same Execution state. For example, an external source can generate an IRQ exception while executing an application in AArch32 and then execute the IRQ handler within the OS Kernel in AArch64.
Consider, for example, an application running in EL0, which is interrupted by an IRQ as in the figure below.
The Kernel IRQ handler runs at EL1. The processor determines which Execution state to set when it takes the IRQ exception. It determines the Execution state by reading the RW bit of the control register for the Exception level above the one where the exception is being handled. For example, if the exception is taken in EL1, the Execution state for the handler is controlled by HCR_EL2.RW.
Now consider what Exception level an exception is taken at.
When an exception is taken, the Exception level can stay the same, or it can get higher.
Exceptions are never taken to EL0.
Exceptions are typically taken in EL1 by default. When implemented, many classes of exception can instead be routed to EL2 (Hypervisor) using HCR_EL2 or EL3 (Secure Monitor) using SCR_EL3.
In both cases, there are separate bits to control routing of IRQ, FIQ, and SError. The Exception level can never go down by taking an exception. Interrupts are always masked at the Exception level where the interrupt is taken.
When taking an exception from AArch32 to AArch64, there are some special considerations. AArch64 handler code can require access to AArch32 registers and the architecture therefore defines mappings to allow access to AArch32 registers.
Bits [63:32] of the X registers are not available in AArch32 state and contain either 0 or the last value that is written in AArch64. There is no architectural guarantee on which value it is. It is therefore usual to access AArch32 registers as W registers.