Why can't your app just reformat the disk or read another app's memory? Because the CPU runs code at different privilege levels. Today we learn how ARM separates "trusted system code" from "ordinary apps" — the foundation of every OS, hypervisor and security feature.
Imagine if any app could reprogram the interrupt controller, disable memory protection, or peek at your banking app's memory. Chaos. So the CPU runs code at two broad trust levels:
An app that wants something powerful (open a file, allocate memory) must ask the OS via a system call, which traps up into privileged code. The CPU enforces this in hardware — an app literally cannot execute privileged operations.
A hotel. Guests (apps) can enter their own room. Staff (the OS) have master keys to every room and the control systems. A guest who needs something restricted calls the front desk (a system call) — they don't get a master key.
Modern 64-bit ARM organises privilege into four clean Exception Levels. Higher number = more privilege. Code "traps up" to a higher level to handle exceptions:
A typical phone uses EL0 (apps) → EL1 (the OS kernel) → and EL3 for secure-world switching. EL2 appears when virtualization is in play. Not every system uses all four — but the ladder is always there.
Older 32-bit ARM expresses privilege as named modes instead of numbered levels. The CPU switches mode automatically when an exception happens:
| Mode | Entered when… | Privilege |
|---|---|---|
| User (USR) | normal application code | Unprivileged |
| Supervisor (SVC) | a system call (SVC) / on reset | Privileged |
| IRQ | a normal interrupt fires | Privileged |
| FIQ | a fast interrupt fires | Privileged |
| Abort (ABT) | a memory access fault | Privileged |
| Undefined (UND) | an illegal instruction | Privileged |
| System (SYS) | privileged code sharing User registers | Privileged |
Banked registers: several modes get their own private copies of some registers (notably SP and LR, and FIQ gets extra). That's why an interrupt handler has its own stack pointer without clobbering the app's — the hardware swaps in the banked copy on entry. (Remember the CPSR mode bits from Day 3? They record which mode you're in.)
Microcontrollers don't need that whole ladder, so Cortex-M (M-profile) drops it for just two modes:
Code can additionally be privileged or unprivileged, and there are two stack pointers (MSP/PSP). That's it — simple, fast and deterministic, exactly what a microcontroller wants. (The interrupt machinery, the NVIC, is Day 18.)
Privilege is a ladder of trust. Apps sit at the bottom (EL0 / User / Thread). The OS and handlers sit higher. The CPU automatically climbs the ladder when an exception or system call happens, runs the trusted handler, then climbs back down. This single mechanism powers multitasking, security and virtualization.
AArch64 exception levels: EL0 apps, EL1 OS kernel, EL2 hypervisor, EL3 secure monitor. Higher = more privileged.
Unprivileged (apps) can only touch their own resources; privileged (kernel/handlers) can configure the whole machine.
It uses just Thread and Handler modes (plus privileged/unprivileged), not the classic AArch32 mode set.