ARM64 Register Reference
Quick reference for the ARM64 registers we use in our kernel. Refer to this when writing assembly code, exception handlers, or context switching routines.
General-Purpose Registers
ARM64 has 31 general-purpose 64-bit registers (x0-x30). The lower 32 bits are accessible as w0-w30. Writing to wN zero-extends to the upper 32 bits of xN.
| Register | Role (AAPCS64) | Who saves it? |
|---|---|---|
| x0 | Argument 1 / return value | Caller |
| x1-x7 | Arguments 2-8 | Caller |
| x8-x15 | Temporary (scratch) | Caller |
| x16-x17 | Intra-procedure (IP0, IP1) | Caller |
| x18 | Platform register (do not use) | N/A |
| x19-x28 | Callee-saved temporaries | Callee must save/restore |
| x29 | Frame Pointer (FP) | Callee must save/restore |
| x30 | Link Register (LR) / return address | Callee must save (if it calls other functions) |
Special-Purpose Registers
| Register | Name | Notes |
|---|---|---|
| SP | Stack Pointer | Shares register number 31 with XZR. Context determines whether it refers to SP or XZR. |
| PC | Program Counter | Cannot be written directly. Changed by branches, exceptions, and ERET. |
| XZR | Zero Register | Always reads as 0. Writes are ignored. Register number 31. |
Exception System Registers
| Register | Access | Purpose |
|---|---|---|
CurrentEL | EL0+ | Read current exception level (bits 3:2). 0b00=EL0, 0b01=EL1, 0b10=EL2, 0b11=EL3. |
SPSR_EL1 | EL1+ | Saved PSTATE when an exception occurred. The ERET instruction restores from here. |
ELR_EL1 | EL1+ | Exception return address. The ERET instruction branches to this address. |
ESR_EL1 | EL1+ | Exception Syndrome Register. Describes why the exception happened. |
Memory Management Registers
| Register | Access | Purpose |
|---|---|---|
TTBR0_EL1 | EL1+ | Page table base for user space (EL0) addresses. Points to level-0 table. |
TTBR1_EL1 | EL1+ | Page table base for kernel space addresses. Points to level-0 table. |
TCR_EL1 | EL1+ | Translation Control Register. Page size, address size, cache policy for table walks. |
MAIR_EL1 | EL1+ | Memory Attribute Indirection Register. Maps 8 attribute indices to memory types (normal, device, etc.). |
SCTLR_EL1 | EL1+ | System Control Register. Enables MMU (bit 0), data cache (bit 2), instruction cache (bit 12), etc. |
Stack Pointer Selection
ARM64 has two stack pointers: SP_EL0 and SP_EL1. The SPSel
register (bit 0 of PSTATE) selects which one is active:
SPSel = 0: UseSP_EL0(even when running at EL1)SPSel = 1: UseSP_EL1(each exception level has its own SP)
Our kernel uses SPSel = 1 at EL1 (kernel uses SP_EL1) and SPSel = 0
at EL0 (user uses SP_EL0). This gives each mode a separate stack.
/* Set SP_EL1 as the current stack pointer */
msr SPSel, #1
/* Now 'mov sp, x0' modifies SP_EL1 */
PSTATE (Processor State) Fields
| Field | Description |
|---|---|
| N | Negative condition flag (result was negative) |
| Z | Zero condition flag (result was zero) |
| C | Carry condition flag (unsigned overflow) |
| V | Overflow condition flag (signed overflow) |
| D | Debug mask (0 = debug exceptions enabled) |
| A | SError mask (0 = SError interrupts enabled) |
| I | IRQ mask (0 = IRQ interrupts enabled) |
| F | FIQ mask (0 = FIQ interrupts enabled) |
| M[3:0] | Current exception level and execution state |
The DAIF bits (D, A, I, F) control which interrupts are masked. Our kernel uses MSR DAIFSet, #2
to mask IRQs (set bit I) during critical sections.
Calling Convention Quick Rules
- Arguments 1-8: x0-x7
- Arguments 9+: on the stack (growing downward)
- Return value: x0
- Callee must save x19-x29 and x30 (if it modifies them)
- Stack must be 16-byte aligned at function call boundary
- SP must never be misaligned
/* Function prologue (if using frame pointer) */
my_function:
stp x29, x30, [sp, #-16]! /* save FP and LR */
mov x29, sp /* set new frame pointer */
...
ldp x29, x30, [sp], #16 /* restore FP and LR */
ret
/* Function prologue (no frame pointer, just save what we use) */
my_function:
stp x19, x20, [sp, #-16]! /* save callee-saved regs we modify */
...
ldp x19, x20, [sp], #16 /* restore */
ret
Reading System Registers
/* Read */
mrs x0, CurrentEL
mrs x0, SCTLR_EL1
mrs x0, TTBR0_EL1
/* Write */
msr SCTLR_EL1, x0
msr TTBR0_EL1, x0
/* After modifying system registers, synchronize */
isb
MRS and MSR are only available at the appropriate exception
level. Accessing an EL1 register at EL0 causes an exception.
Instruction Quick Reference
| Instruction | Example | Meaning |
|---|---|---|
ADD | add x0, x1, x2 | x0 = x1 + x2 |
SUB | sub x0, x1, #16 | x0 = x1 - 16 |
MUL | mul x0, x1, x2 | x0 = x1 * x2 |
LDR | ldr x0, [x1] | x0 = memory[x1] |
STR | str x0, [x1] | memory[x1] = x0 |
STP | stp x0, x1, [sp, #-16]! | Push x0, x1 to stack |
LDP | ldp x0, x1, [sp], #16 | Pop x0, x1 from stack |
BL | bl func | Call func; save return address in x30 |
RET | ret | Return to x30 |
CBZ | cbz x0, label | Branch to label if x0 == 0 |
CBNZ | cbnz x0, label | Branch to label if x0 != 0 |
SVC | svc #0 | Trigger system call (EL0 -> EL1) |
ERET | eret | Return from exception (EL1 -> EL0) |
WFI | wfi | Wait for interrupt (low power) |
NOP | nop | No operation |
See the Assembly Guide chapter for detailed instruction explanations and patterns.
Further Reading
- ARM Architecture Reference Manual ARMv8-A, sections C5 (A64 instruction set) and D1 (system registers)
- Procedure Call Standard for the ARM 64-bit Architecture (AAPCS64)
aarch64-none-elf-objdump -d kernel.elfto disassemble and verify your code