The ARM trap instruction allows developers to trigger exceptions intentionally in ARM-based systems. It provides a mechanism to switch from unprivileged mode to privileged mode in order to run exception handlers or operating system code. The trap instruction is an important part of implementing protected operating systems and hypervisors on ARM processors.
What is the ARM Trap Instruction?
The ARM trap instruction is implemented differently across ARM architecture versions, but fundamentally it causes the processor to trigger an exception synchronously. This means the trap takes effect immediately after the instruction is executed, before the next instruction is run.
In ARMv6 and earlier architectures, the trap instruction was called SWI (Software Interrupt). It caused the processor to switch to Supervisor mode and jump to the SWI vector to handle the exception.
In ARMv7, this was replaced with the HVC (Hypervisor Call) instruction which traps to the hypervisor if present. HVC was intended for use by hypervisors on ARM systems.
The ARMv8 architecture introduced the ERET (Exception Return) instruction for returning from exceptions, and two new trap instructions:
- SVC (Supervisor Call) – for trapping to privileged software like the OS kernel
- BRK (Breakpoint) – for triggering debug exceptions
These trap instructions all cause the processor to switch modes, save context, and branch to an exception handler routine. The handler code can then perform privileged operations before returning to the original unprivileged code.
Why Use the Trap Instruction?
There are several reasons why a developer would intentionally trigger a trap/exception using these instructions:
- To implement system calls – traps allow switching to privileged kernel mode
- For hypercalls in a hypervisor environment
- Debugging and instrumentation using breakpoints
- Emulating privileged instructions in unprivileged mode
- Accessing protected system resources like the Memory Management Unit (MMU)
The trap instructions provide a controlled way for unprivileged user processes to request privileged operations through the operating system kernel or hypervisor. This is essential for implementing protected multitasking systems.
SVC Instruction on ARMv8
The SVC (Supervisor Call) instruction is the most commonly used trap instruction in ARMv8 systems. It is used to make kernel calls to request operating system services.
When SVC is executed, the processor enters Supervisor mode, saves register context to the Supervisor Stack Pointer (SP_EL1), disables interrupts, and branches to the SVC vector table to call the handler routine.
The SVC handler does any privilege checking, performs the requested operation, and returns to the instruction after the SVC using the ERET instruction. This process allows unprivileged user code to safely invoke privileged OS services.
To make an SVC call, user code places the SVC opcode (0x010000XX) into a register, masks it to the low 8 bits, and executes it. An immediate number from 0-15 is passed in the low 5 bits to indicate the service being requested.
For example: MOV x0, #1 // syscall number 1 SVC #0 // make SVC call
This triggers an exception, causing the kernel’s SVC handler to be invoked. The handler can then check x0 to determine which system call is being requested.
Usage in Operating System Kernels
OS kernels implement system call interfaces using the SVC instruction. During application development, a user-space library wraps the raw SVC invocations to provide a cleaner API.
For example, to open a file, an application would call fopen() in libc. The library code then uses SVC to trap into the kernel and call the sys_open() system call handler. The handler opens the file and returns the file descriptor.
Common operations like read(), write(), ioctl(), mmap() are all typically implemented as system calls. The OS kernel leverages SVC to mediate access to underlying hardware resources.
In Linux, the eabi_svr4.h header defines convenience macros like __ARM_NR_SYSCALL_BASE to help generate SVC numbers for each system call.
Usage in Hypervisors
Hypervisors can also implement hypercall interfaces using the SVC instruction or HVC in ARMv8. This allows virtual machines to call into the hypervisor to request privileged operations.
For example, a VM may use SVC to trap into the hypervisor’s vmcall() handler. This handler performs operations like toggling virtual interrupts, mapping pages to the VM, and device emulation.
Hypercall interfaces allow efficient para-virtualization, reducing the complexity of emulating privileged instructions in software. Hypervisors leverage trap instructions like SVC to retain ultimate control over platform hardware resources.
Debugging with the BRK Instruction
The BRK instruction is used to intentionally trigger a debug exception in ARMv8. When executed, it causes the processor to enter Debug mode and branch to the debug exception vector.
A debugger can set breakpoints in user code using BRK. For example, GDB inserts the BRK opcode at addresses where code breakpoints are set. When reached, the debug handler in the OS kernel gets invoked.
BRK is also useful for inserting tracepoints and logging instrumentation. A developer can insert BRK instructions, then collect additional context in the exception handler like register dumps before returning.
However, extra care must be taken to single-step over the BRK in the debugger. Otherwise repeatedly hitting the BRK will end up flooding the trace logs with duplicate data.
Arm Trap Return – The ERET Instruction
The ERET (Exception Return) instruction is used to return from trap exceptions in ARMv8. It restores context and returns to the instruction where the exception was triggered.
ERET specifies which exception level to return to using a numeric operand. For example, to return to EL1 from EL3: ERET #0x1E
This loads the saved program counter and processor state from the stack linked to that exception level, before resuming execution.
ERET must only be executed at an exception level higher than the target level being returned to. Otherwise, it will be treated as an illegal exception return.
The ERET instruction is essential for all trap handlers to re-enter application or OS code cleanly after handling the trap. It ensures the original context is fully restored.
Privileged Instruction Emulation
Some applications need to use privileged instructions which are not available from user mode code. The kernel can emulate these by trapping the instruction execution.
For example, user processes cannot directly access the hypervisor configuration registers. But the OS kernel can define a hypercall using SVC to transparently emulate access.
The kernel hypercall handler runs in EL1 mode, reads the register on behalf of the user, returns the result via register arguments, and uses ERET to resume user execution. This provides safe emulated access.
Similar techniques apply for other privileged operations like MMU configuration, cache maintenance, TLB maintenance, power management, performance monitoring, and debugging registers.
The ARM trap instructions – SVC, HVC, and BRK – provide fundamental building blocks for implementing protected high-level OS capabilities. They enable clean transitions between different privilege modes during execution.
OS and hypervisor developers leverage these trap instructions to safely virtualize and share underlying hardware resources on ARM systems. They allow user applications to request privileged operations without compromising stability and security.