May 17 / Sebastian Helmut

ARMv8-A Exception Levels Explained: From EL0 to EL3

Don't hesitate

Get the Professional Embedded Starter Kit: Production-ready templates and architectural cheat sheets for your firmware projects.

How ARMv8-A Exception Levels Work: Kernel, Hypervisor, and Secure Monitor


1. Introduction

Every instruction a processor executes runs at a specific privilege level. On ARMv8-A, these levels are called Exception Levels, or ELs. They control what the code running at that level is allowed to do: which registers it can access, which memory it can touch, and which system configuration it can modify.

Understanding exception levels is fundamental to understanding how ARMv8-A systems are structured. The separation between user applications, the operating system kernel, a hypervisor, and a secure monitor is not a software convention. It is enforced in hardware by the exception level mechanism. Code running at a lower level simply cannot access the resources of a higher level, regardless of what it tries to do.


2. What Are Exception Levels

2.1 The Four Levels: EL0, EL1, EL2, EL3

ARMv8-A defines four exception levels numbered EL0 through EL3. The number indicates the privilege level. EL0 is the least privileged and EL3 is the most privileged.

- EL0: Unprivileged execution. User space applications run here.

- EL1: Privileged execution. The operating system kernel runs here.

- EL2: Hypervisor execution. A virtual machine monitor runs here when virtualization is in use.

- EL3: Secure monitor execution. The firmware or TrustZone monitor runs here.

Not all exception levels need to be implemented or active in a given system. EL2 is optional and only meaningful when virtualization is used. EL3 is optional and only meaningful when TrustZone security extensions are used.

2.2 Privilege and Trust Model

The exception level model encodes two independent axes: privilege and trust.

Privilege controls what system resources are accessible. Code at EL0 cannot configure the MMU, cannot mask interrupts system-wide, and cannot access most system registers. Code at EL1 can do all of these things for the normal world. Code at EL3 has unrestricted access and can configure the entire system including the security state.

Trust is orthogonal to privilege. ARMv8-A defines two security states: Secure and Non-Secure. EL3 always executes in the Secure state. EL0 and EL1 can execute in either state depending on how EL3 configures the system. EL2 exists only in the Non-Secure state in most configurations.

Fig 1. The four exception levels and their typical software assignments.


Struggling to implement this for a professional project? If you need to master full-scale firmware architecture, security, and build automation, join our 1-D or 4-Day Live Implemnetation Workshops. I'll show you the exact direct path to production-ready firmware without the trial and error.

3. What Runs at Each Level

3.1 EL0: User Space Applications

EL0 is where user space code runs. Applications, libraries, and user space daemons all execute at EL0. At this level the processor has the minimum privilege. The code cannot directly configure the MMU, cannot access system control registers, and cannot mask exceptions globally.

When a user space application needs a service from the operating system, it issues a supervisor call instruction, SVC. This instruction triggers a synchronous exception that causes the processor to switch to EL1 where the kernel handles the request.

3.2 EL1: Operating System Kernel

EL1 is where the operating system kernel runs. At this level the kernel has access to the full set of EL1 system registers including the translation table base registers TTBR0_EL1 and TTBR1_EL1, the system control register SCTLR_EL1, and the exception link register ELR_EL1.

The kernel configures the MMU, manages page tables, handles exceptions from EL0, and services system calls. It runs at higher privilege than user space but below the hypervisor and secure monitor.

3.3 EL2: Hypervisor

EL2 is where a hypervisor runs when the system uses hardware-assisted virtualization. The hypervisor manages multiple virtual machines, each of which typically runs an operating system at EL1.

At EL2, the processor has access to the virtualization control registers including HCR_EL2, the Hypervisor Configuration Register, which controls trapping of EL1 operations to EL2. The hypervisor uses this to intercept and emulate operations that would otherwise affect hardware directly.

EL2 is optional. On systems that do not use virtualization, EL2 may not be active and execution goes directly between EL1 and EL3.

3.4 EL3: Secure Monitor

EL3 is the highest privilege level and is always in the Secure state. It hosts the secure monitor firmware, which is responsible for managing the transition between the Secure and Non-Secure worlds.

On platforms using TrustZone, the secure monitor initializes the system, configures the security policy, and handles Secure Monitor Calls (SMC) from lower exception levels. It controls the SCR_EL3 register which among other things determines the security state of EL1 and EL0.

#define SCR_EL3_NS (1U << 0) // Non-Secure bit: 1 = EL1/EL0 in Non-Secure state

#define SCR_EL3_IRQ (1U << 1) // IRQ routed to EL3 when set

#define SCR_EL3_FIQ (1U << 2) // FIQ routed to EL3 when set

#define SCR_EL3_RW (1U << 10) // EL1 execution state: 1 = AArch64, 0 = AArch32


4. Switching Between Exception Levels

4.1 Taking an Exception: Moving to a Higher EL

The processor moves to a higher exception level when an exception is taken. Exceptions include synchronous exceptions such as system calls and data aborts, and asynchronous exceptions such as IRQ, FIQ, and SError.

When an exception is taken from EL0 to EL1, the processor automatically saves the current program counter into ELR_EL1, saves the current processor state into SPSR_EL1, switches the stack pointer to SP_EL1, and jumps to the vector table entry for that exception type at EL1.

The transition is hardware-driven. Software does not explicitly request it. The processor detects the exception condition and performs the level switch as part of the exception entry sequence.

4.2 Returning from an Exception: Moving to a Lower EL

The processor returns to a lower exception level using the ERET instruction. ERET restores the program counter from ELR_ELn and restores the processor state from SPSR_ELn, effectively undoing the exception entry. The processor returns to the level and state that were saved when the exception was taken.

// minimal exception return sequence at EL1

mrs x0, elr_el1 // read saved return address

mrs x1, spsr_el1 //read saved processor state

// handle exception

eret // restore ELR_EL1 and SPSR_EL1

4.3 Moving to a Lower EL at Boot

During boot, firmware starts at EL3. To hand off execution to a lower level, the firmware sets up the exception link register and saved processor state register for the target level, then executes ERET. This causes the processor to drop to the configured level as if returning from an exception.

// drop from EL3 to EL1 at boot

adr x0, el1_entry

msr elr_el3, x0 //set return address to EL1 entry point

mov x0, 0x3C5

msr spsr_el3, x0 // set target state: EL1h, AArch64

eret //jump to EL1

Fig 2. Exception level transitions: taking an exception moves up, ERET moves down.


5. Secure and Non-Secure Worlds

5.1 The Security State

ARMv8-A partitions the system into two worlds: the Normal world and the Secure world. The Normal world runs untrusted software such as a general purpose operating system and its applications. The Secure world runs trusted software such as a trusted OS and secure services.

EL3 always runs in the Secure state. The security state of EL1 and EL0 is controlled by the NS bit in SCR_EL3. When NS=1, EL1 and EL0 execute in the Non-Secure state. When NS=0, they execute in the Secure state.

5.2 How EL3 Controls the World Switch

Transitioning between Normal and Secure worlds always passes through EL3. When Normal world software needs a secure service, it issues an SMC instruction. This triggers an exception that routes to EL3. The secure monitor at EL3 saves the Normal world context, sets NS=0, and returns to a Secure world OS at EL1. When the secure service completes, the secure monitor sets NS=1 and returns control to the Normal world.

Fig 3. Secure and Non-Secure worlds separated by EL3 secure monitor.


6. Registers That Change With Exception Level

6.1 SP_ELn, ELR_ELn, SPSR_ELn

Each exception level has its own dedicated registers. This is what makes exception level switching safe and efficient without requiring software to manually save and restore everything.

SP_EL0, SP_EL1, SP_EL2, and SP_EL3 are the stack pointers for each level. When the processor takes an exception from EL0 to EL1, it switches to SP_EL1 automatically. The EL0 stack pointer SP_EL0 is preserved and restored on return.

ELR_EL1, ELR_EL2, and ELR_EL3 are the exception link registers. When an exception is taken to ELn, the return address is saved into ELR_ELn. The ERET instruction reads this register to return to the interrupted code.

SPSR_EL1, SPSR_EL2, and SPSR_EL3 are the saved program status registers. They capture the processor state at the moment the exception was taken, including the exception level, execution state, and condition flags.

6.2 Why Each Level Has Its Own Stack Pointer

Having a dedicated stack pointer per exception level is critical for security. If EL0 and EL1 shared a stack pointer, a malicious user space program could corrupt the kernel stack by manipulating the shared pointer before triggering an exception. With separate stack pointers, the kernel's SP_EL1 is completely invisible and inaccessible to EL0 code.

/* EL1 exception entry saves general purpose registers on SP_EL1 stack */

/* EL0 stack pointer SP_EL0 is untouched during EL1 handling */

/* on ERET, SP_EL0 is restored and kernel stack SP_EL1 is left intact */


7. Conclusion

ARMv8-A exception levels provide a hardware-enforced privilege hierarchy that partitions software into four distinct layers: user space at EL0, the kernel at EL1, the hypervisor at EL2, and the secure monitor at EL3. Each level has its own stack pointer, exception link register, and saved program status register, making context switches between levels safe and efficient.

The Secure and Non-Secure worlds add a second dimension to this hierarchy. EL3 acts as the gatekeeper between the two worlds, and every world switch passes through it.


Want to master this? Here are your next steps:



Created with