Exception levels
Arm names four privilege bands, EL0 through EL3. A conventional hypervisor can virtualise the bottom two cleanly. It cannot hand the guest the top two as real hardware. The host hypervisor already occupies EL2. Host EL3 belongs to the silicon vendor and is never yielded.
This is the constraint Fusion Engine is built around. The levels themselves are not MX terminology. They are the Arm architecture.
The stack
- EL0 is userspace. Applications, JIT compilers, games. Unprivileged.
- EL1 is the operating system kernel, and a lot of firmware once it has dropped out of a higher level. Page tables, syscalls, drivers.
- EL2 is the hypervisor. Stage-2 translation, virtual timers, the controls that trap a guest.
- EL3 is the secure monitor. Vendor ROM, TrustZone, PSCI, the switch between the normal world and the secure world.
AArch64 moves between them with architected instructions. SVC takes EL0 to EL1. HVC takes a less-privileged caller to EL2. SMC is the call into EL3. ERET returns down the stack, restoring the saved program status.
On a real board the silicon vendor owns EL3 from reset. The boot ROM, the secure monitor, and often the first-stage firmware live there. A general-purpose OS then either stays at EL1 or, if it is itself a hypervisor, takes EL2.
Why they cannot all be virtualised
Hardware virtualisation was designed to run a guest kernel at EL1 and guest userspace at EL0, while the host hypervisor sits at EL2 and intercepts the rest. That is the only combination that is both architected and widely implemented.
EL0 and EL1 virtualise. The guest's applications and kernel run as guest EL0 and guest EL1. Stage-2 translation gives them a fake physical address space. System-register traps send privileged accesses back to the host. This is what every mainstream hypervisor backend does.
EL2 is already occupied. The host hypervisor owns real EL2. It cannot move out of the way and still remain a hypervisor. Giving a guest its own EL2 means nested virtualisation: the guest thinks it is at EL2, but the real EL2 still belongs to the host. Newer Arm features (FEAT_NV, FEAT_NV2) help, and MX uses guest EL2 when the selected backend can actually host it. It is still a nested view, not a gift of the physical level.
Even when guest EL2 is on, ownership flips. HCR_EL2 becomes the guest's register. The emulator cannot commandeer those trap bits as its own interception mechanism without breaking the guest contract. Not every EL2 bank round-trips through the host API, and VHE (HCR_EL2.E2H) is not always writable.
EL3 is not a userspace hypervisor API. Host EL3 is the silicon vendor's secure world. A third-party process cannot enter it, and host virtualisation APIs do not offer a "run this guest at EL3" switch. Advertising EL3 to a guest means emulating the monitor: catch SMC, run monitor firmware in software, then ERET back. There is no native EL3 for a guest on these hosts.
A hypervisor that lives at EL2 can virtualise EL0 and EL1. It can give a guest EL2 when the selected backend can host that nested view. It cannot virtualise EL3 at all. Full levels are a Fusion problem, not a missing host checkbox.
What guests actually need
An AArch64 Linux guest often boots through UEFI at EL2 and talks to a monitor at EL3 for PSCI: CPU_ON, CPU_OFF, system reset, suspend. Firmware that looks for a board with EL3 will not treat an EL1-only VM as real hardware.
Some machine models keep their own platform monitor on the EL3 boundary and expose EL2 to guest execution. The advertised cap can still be EL3. The shared monitor is for guests that need a generic AArch64 secure world, not a substitute for every board's firmware.
A hypervisor that only ever offers EL1 cannot boot firmware that assumes the stack above it. That is why MX can advertise EL3, and why Fusion Engine exists.