Returning from an exception
The processor has to be told when to return from an exception by software. This is done
in code using the
ERET instruction. This restores the pre-exception PSTATE
from SPSR_ELn and returns program execution back to the original location by
restoring the PC from ELR_ELn.
The architecture provides separate link registers for subroutines and exception returns.
In the A64 instruction set, register X30 is used (with the RET instruction) to return from subroutines. Its value is updated with the address of the instruction to return to whenever a branch with link instruction (BL or BLR) is executed.
The ELR_ELn register is used to store the return address from an exception. The value in this register is automatically written on entry to an exception and is written to the PC as one of the effects of executing the ERET instruction that is used to return from exceptions.
When returning from an exception, you see an error if the value in the SPSR conflicts with the settings in the System Registers.
ELR_ELn contains the return address, which depends upon the specific exception type. Typically, this is the address of the instruction after the one that generated the exception.
For example, when an SVC (system call) instruction is executed, you want to return to the following instruction in the application. In other cases, however, you might want to re-execute the instruction that generated the exception.
For asynchronous exceptions, the ELR_ELn points to the address of the first instruction that has not been executed, or executed fully, because of taking the interrupt. Handler code is permitted to modify the ELR_En if, for example, it was necessary to return to the instruction after aborting a synchronous exception. The ARMv8-A AArch64 model is simpler than that used in AArch32 or ARMv7-A, where for backward compatibility reasons, it was necessary to subtract 4 or 8 from the Link register value when returning from certain types of exception.
In addition to the SPSR and ELR registers, each Exception level has its own dedicated stack pointer register. These are SP_EL0, SP_EL1, SP_EL2 and SP_EL3. These registers are used to point to a dedicated stack. The stack can, for example, be used to store registers that are corrupted by the exception handler, so that they can be restored to their original value, before returning to the original code.
Handler code may switch from using SP_ELn to SP_EL0. For example, SP_EL1 might point to a piece of memory that holds a small stack that the kernel can always guarantee to be valid. SP_EL0 might point to a kernel task stack that is larger, but not guaranteed to be safe from overflow. This switching is controlled by writing to the SPSel register.