TrapFrame and System Registers
Relevant source files
This document details the core data structures used for preserving CPU state during context switches in the arm_vcpu hypervisor. The system uses two primary structures: Aarch64ContextFrame for saving general-purpose registers and basic execution state, and GuestSystemRegisters for preserving system control registers and hypervisor-specific state.
For information about the broader context switching mechanism and how these structures are used during VM exits, see Context Switching and State Management. For details on the assembly-level context switching code that manipulates these structures, see Assembly Exception Vectors.
TrapFrame Structure Overview
The Aarch64ContextFrame serves as the primary trap frame for saving guest execution context when exceptions occur. This structure captures the minimal set of registers needed to preserve the guest's execution state across VM exits.
flowchart TD
subgraph subGraph2["Special Handling"]
ZERO["Register x31Always returns 0Cannot be modified(AArch64 Zero Register)"]
end
subgraph subGraph1["Access Methods"]
GET["gpr(index) -> usizeset_gpr(index, val)exception_pc() -> usizeset_exception_pc(pc)set_argument(arg)"]
end
subgraph subGraph0["Aarch64ContextFrame Memory Layout"]
GPR["gpr[31]: u64General Purpose Registersx0 through x30"]
SP["sp_el0: u64EL0 Stack Pointer"]
ELR["elr: u64Exception Link Register(Return Address)"]
SPSR["spsr: u64Saved Program Status Register(CPU Flags & State)"]
end
ELR --> GET
GPR --> GET
GPR --> ZERO
SP --> GET
SPSR --> GET
The structure uses #[repr(C)] layout to ensure predictable memory organization for hardware and assembly code interaction. The default state configures SPSR to mask all exceptions and set EL1h mode for safe guest initialization.
Sources: src/context_frame.rs(L17 - L63)
System Registers Structure
The GuestSystemRegisters structure manages the complete system-level state of a guest VM, including control registers, memory management settings, timer state, and hypervisor configuration.
flowchart TD
subgraph Operations["Operations"]
STORE["store() - Save hardware to structUses MRS instructions"]
RESTORE["restore() - Load struct to hardwareUses MSR instructions"]
RESET["reset() - Zero all fields"]
end
subgraph subGraph0["GuestSystemRegisters Categories"]
TIMER["Timer Registerscntvoff_el2, cntp_cval_el0cntv_cval_el0, cntkctl_el1cntvct_el0, cntp_ctl_el0cntv_ctl_el0, cntp_tval_el0cntv_tval_el0"]
PROC["Processor IDvpidr_el2vmpidr_el2"]
EL1["EL1/EL0 System Statesp_el0, sp_el1, elr_el1spsr_el1, sctlr_el1actlr_el1, cpacr_el1"]
MMU["Memory Managementttbr0_el1, ttbr1_el1tcr_el1, mair_el1amair_el1, par_el1"]
EXC["Exception Handlingesr_el1, far_el1vbar_el1, far_el2hpfar_el2"]
THREAD["Thread Pointerstpidr_el0, tpidr_el1tpidrro_el0contextidr_el1"]
HYP["Hypervisor Contexthcr_el2, vttbr_el2cptr_el2, hstr_el2pmcr_el0, vtcr_el2"]
end
EL1 --> STORE
EXC --> STORE
HYP --> STORE
MMU --> STORE
PROC --> STORE
RESTORE --> RESET
STORE --> RESTORE
THREAD --> STORE
TIMER --> STORE
The structure is aligned to 16 bytes (#[repr(align(16))]) for optimal memory access performance during context switches.
Sources: src/context_frame.rs(L138 - L300)
Register State Preservation
The system employs a two-tier approach to state preservation, with different registers handled by different mechanisms based on their access patterns and importance.
TrapFrame Register Operations
flowchart TD
subgraph subGraph2["Default State"]
DEF["SPSR ConfigurationM: EL1h modeI,F,A,D: All maskedELR: 0, SP_EL0: 0"]
end
subgraph subGraph1["Special Operations"]
PC["exception_pc()set_exception_pc(pc)Maps to ELR field"]
ARG["set_argument(arg)Sets x0 registerFor return values"]
end
subgraph subGraph0["GPR Access Pattern"]
INDEX["Register Index0-30: Normal GPRs31: Zero Register"]
GET["gpr(index)Returns register valueIndex 31 -> 0"]
SET["set_gpr(index, val)Sets register valueIndex 31 -> Warning"]
end
ARG --> SET
INDEX --> GET
INDEX --> SET
PC --> GET
SET --> DEF
Sources: src/context_frame.rs(L65 - L136)
System Register Save/Restore Cycle
sequenceDiagram
participant GuestVM as "Guest VM"
participant HardwareRegisters as "Hardware Registers"
participant GuestSystemRegisters as "GuestSystemRegisters"
participant HostHypervisor as "Host/Hypervisor"
Note over GuestVM,HostHypervisor: VM Exit Sequence
GuestVM ->> HardwareRegisters: Exception/Trap occurs
HardwareRegisters ->> GuestSystemRegisters: store() - MRS instructions
Note over GuestSystemRegisters: All system state captured
GuestSystemRegisters ->> HostHypervisor: Context available for inspection
Note over GuestVM,HostHypervisor: VM Entry Sequence
HostHypervisor ->> GuestSystemRegisters: Configure guest state
GuestSystemRegisters ->> HardwareRegisters: restore() - MSR instructions
Note over HardwareRegisters: Guest context loaded
HardwareRegisters ->> GuestVM: ERET to guest execution
The store() and restore() methods use inline assembly with MRS (Move Register from System) and MSR (Move System Register) instructions to efficiently transfer register state between hardware and memory.
Sources: src/context_frame.rs(L213 - L299)
Integration with Context Switching
These structures integrate closely with the assembly-level context switching mechanism to provide complete state preservation during VM transitions.
flowchart TD
subgraph subGraph2["State Structures"]
TF["Aarch64ContextFrameGPRs, SP_EL0, ELR, SPSR"]
GSR["GuestSystemRegistersSystem control state"]
end
subgraph subGraph1["VM Entry Flow"]
SETUP["Host configuresguest state"]
SYS_RESTORE["guest_system_regs.restore()Struct to hardware"]
ASM_RESTORE["context_vm_entryAssembly restores from TrapFrame"]
GUEST["Guest executionresumes"]
end
subgraph subGraph0["VM Exit Flow"]
TRAP["Exception OccursHardware trap"]
ASM_SAVE["SAVE_REGS_FROM_EL1Assembly saves to TrapFrame"]
SYS_SAVE["guest_system_regs.store()System registers to struct"]
HANDLER["Exception handlerProcesses exit reason"]
end
ASM_RESTORE --> GUEST
ASM_RESTORE --> TF
ASM_SAVE --> SYS_SAVE
ASM_SAVE --> TF
SETUP --> SYS_RESTORE
SYS_RESTORE --> ASM_RESTORE
SYS_RESTORE --> GSR
SYS_SAVE --> GSR
SYS_SAVE --> HANDLER
TRAP --> ASM_SAVE
The TrapFrame is manipulated directly by assembly code during the low-level context switch, while the GuestSystemRegisters structure is managed by Rust code using the store() and restore() methods.
Sources: src/context_frame.rs(L1 - L301)
Register Categories and Usage
| Category | Registers | Purpose | Access Pattern |
|---|---|---|---|
| General Purpose | x0-x30, SP_EL0 | Function arguments, local variables, stack management | Every VM exit/entry via assembly |
| Execution State | ELR, SPSR | Program counter and processor status | Every VM exit/entry via assembly |
| Memory Management | TTBR0_EL1, TTBR1_EL1, TCR_EL1, MAIR_EL1 | Page table configuration, memory attributes | Restored on VM entry |
| System Control | SCTLR_EL1, CPACR_EL1, ACTLR_EL1 | Processor features, access control | Restored on VM entry |
| Exception Handling | VBAR_EL1, ESR_EL1, FAR_EL1 | Vector table, exception information | Preserved across exits |
| Timer Management | CNTVOFF_EL2, CNTKCTL_EL1, timer control/value registers | Virtual timer state | Managed by hypervisor |
| Hypervisor Control | HCR_EL2, VTTBR_EL2, VTCR_EL2 | Virtualization configuration, stage-2 MMU | Set during VCPU setup |
The register categories reflect the layered nature of AArch64 virtualization, with some registers requiring preservation on every context switch while others are configured once during VCPU initialization.
Sources: src/context_frame.rs(L148 - L197)