Register Management
Relevant source files
This document covers the register data structures and management systems that maintain CPU state for virtual machines in the RISC-V hypervisor. This includes general-purpose registers, control and status registers (CSRs), and the mechanisms for saving, restoring, and accessing register state during VM execution and context switches.
For information about trap handling that uses these registers, see Low-Level Implementation. For details about the VCPU implementation that manages these registers, see Core VCPU Implementation.
Register Data Structures
The register management system is built around a hierarchy of data structures that organize different categories of CPU state. The main container is VmCpuRegisters
, which holds all register state needed for virtualization.
Core Register Structure Hierarchy
flowchart TD VmCpuRegisters["VmCpuRegistersMain register container"] HypervisorCpuState["HypervisorCpuStatehyp_regs"] GuestCpuState["GuestCpuStateguest_regs"] GuestVsCsrs["GuestVsCsrsvs_csrs"] GuestVirtualHsCsrs["GuestVirtualHsCsrsvirtual_hs_csrs"] VmCpuTrapState["VmCpuTrapStatetrap_csrs"] HypGprs["GeneralPurposeRegistersgprs"] HypCsrs["CSRs:sstatus, hstatusscounteren, stvec, sscratch"] GuestGprs["GeneralPurposeRegistersgprs"] GuestCsrs["CSRs:sstatus, hstatusscounteren, sepc"] VsCsrList["VS-level CSRs:htimedelta, vsstatus, vsievstvec, vsscratch, vsepcvscause, vstval, vsatp, vstimecmp"] VirtHsCsrList["Virtual HS CSRs:hie, hgeie, hgatp"] TrapCsrList["Trap CSRs:scause, stvalhtval, htinst"] GprIndex["GprIndex enumZero, RA, SP, GP, TPT0-T6, S0-S11, A0-A7"] GuestCpuState --> GuestCsrs GuestCpuState --> GuestGprs GuestGprs --> GprIndex GuestVirtualHsCsrs --> VirtHsCsrList GuestVsCsrs --> VsCsrList HypGprs --> GprIndex HypervisorCpuState --> HypCsrs HypervisorCpuState --> HypGprs VmCpuRegisters --> GuestCpuState VmCpuRegisters --> GuestVirtualHsCsrs VmCpuRegisters --> GuestVsCsrs VmCpuRegisters --> HypervisorCpuState VmCpuRegisters --> VmCpuTrapState VmCpuTrapState --> TrapCsrList
Sources: src/regs.rs(L1 - L197)
General Purpose Register Management
The GeneralPurposeRegisters
structure provides a type-safe wrapper around the 32 RISC-V general-purpose registers, using the GprIndex
enum for meaningful register names.
Register Category | Registers | Purpose |
---|---|---|
Argument registers | A0-A7 | Function arguments and return values |
Saved registers | S0-S11 | Callee-saved registers |
Temporary registers | T0-T6 | Caller-saved temporary values |
Special registers | Zero, RA, SP, GP, TP | Zero, return address, stack pointer, global pointer, thread pointer |
The GeneralPurposeRegisters
implementation enforces RISC-V semantics, particularly that writes to the zero register (GprIndex::Zero
) are ignored.
flowchart TD GprAccess["Register Access Methods"] reg["reg(GprIndex) -> usizeRead register value"] set_reg["set_reg(GprIndex, usize)Write register value(ignores Zero writes)"] a_regs["a_regs() -> &[usize]Get A0-A7 slice"] a_regs_mut["a_regs_mut() -> &mut [usize]Get mutable A0-A7 slice"] GprIndex["GprIndex"] Zero["Zero (x0)"] RA["RA (x1)"] SP["SP (x2)"] A0A7["A0-A7 (x10-x17)"] T0T6["T0-T6 (x5-x7, x28-x31)"] S0S11["S0-S11 (x8-x9, x18-x27)"] GprAccess --> a_regs GprAccess --> a_regs_mut GprAccess --> reg GprAccess --> set_reg GprIndex --> A0A7 GprIndex --> RA GprIndex --> S0S11 GprIndex --> SP GprIndex --> T0T6 GprIndex --> Zero
Sources: src/regs.rs(L1 - L114)
Register Categories and Organization
The register management system organizes CPU state into five distinct categories, each serving different purposes in the virtualization lifecycle.
Hypervisor vs Guest Register State
flowchart TD subgraph subGraph2["Virtualization-Specific State"] VsCsrs["GuestVsCsrsActive when V=1"] VirtHsCsrs["GuestVirtualHsCsrsEmulated hypervisor state"] TrapCsrs["VmCpuTrapStateSet on VM exit"] end subgraph subGraph1["Guest State"] GuestRegs["GuestCpuStateRestored when entering VM"] GuestGprs["GPRs: Guest register values"] GuestSstatus["sstatus: Guest supervisor status"] GuestHstatus["hstatus: Guest hypervisor status"] GuestSepc["sepc: Guest program counter"] end subgraph subGraph0["Hypervisor State"] HypRegs["HypervisorCpuStateSaved when entering VM"] HypGprs["GPRs: Host register values"] HypSstatus["sstatus: Host supervisor status"] HypHstatus["hstatus: Host hypervisor status"] HypOther["scounteren, stvec, sscratch"] end VMEntry["VM Entry"] VMExit["VM Exit"] GuestRegs --> GuestGprs GuestRegs --> GuestHstatus GuestRegs --> GuestSepc GuestRegs --> GuestSstatus HypRegs --> HypGprs HypRegs --> HypHstatus HypRegs --> HypOther HypRegs --> HypSstatus VMEntry --> GuestRegs VMEntry --> HypRegs VMEntry --> VsCsrs VMExit --> HypRegs VMExit --> TrapCsrs
Sources: src/regs.rs(L116 - L196)
Privilege Level and Usage Context
Structure | Usage Context | Save/Restore Timing |
---|---|---|
HypervisorCpuState | Host execution state | Saved on VM entry, restored on VM exit |
GuestCpuState | Guest execution state | Restored on VM entry, saved on VM exit |
GuestVsCsrs | VS-mode virtualization | Active when V=1, context switched between VMs |
GuestVirtualHsCsrs | Emulated hypervisor | Maintained per-VM for nested virtualization |
VmCpuTrapState | Trap information | Written by hardware on VM exit |
Register Access Methods
The VCPU provides several interfaces for accessing and manipulating register state, with different methods for different use cases.
VCPU Register Access Interface
flowchart TD subgraph subGraph1["Internal Register State"] VmCpuRegs["VmCpuRegistersself.regs"] GuestGprs["guest_regs.gprs"] GuestSepc["guest_regs.sepc"] end subgraph subGraph0["Public VCPU Methods"] GetGpr["get_gpr(GprIndex) -> usizeRead guest GPR"] SetGpr["set_gpr_from_gpr_index(GprIndex, usize)Write guest GPR"] SetGprIndex["set_gpr(index: usize, val: usize)Write GPR by numeric index"] AdvancePc["advance_pc(instr_len: usize)Increment guest PC"] RegsAccess["regs() -> &mut VmCpuRegistersDirect register access"] end AdvancePc --> GuestSepc GetGpr --> GuestGprs RegsAccess --> VmCpuRegs SetGpr --> GuestGprs SetGprIndex --> GuestGprs VmCpuRegs --> GuestGprs VmCpuRegs --> GuestSepc
Sources: src/vcpu.rs(L129 - L163)
Register Initialization During VCPU Creation
The VCPU constructor initializes guest registers with boot parameters, setting up the initial execution environment for the guest VM.
flowchart TD subgraph subGraph0["Boot Parameters"] HartId["A0 = config.hart_idHardware thread ID"] DtbAddr["A1 = config.dtb_addrDevice tree blob address"] end VCpuNew["RISCVVCpu::new(config)"] CreateRegs["VmCpuRegisters::default()"] SetA0["regs.guest_regs.gprs.set_reg(A0, hart_id)"] SetA1["regs.guest_regs.gprs.set_reg(A1, dtb_addr)"] VCpuReady["VCPU ready with initialized registers"] CreateRegs --> SetA0 SetA0 --> HartId SetA0 --> SetA1 SetA1 --> DtbAddr SetA1 --> VCpuReady VCpuNew --> CreateRegs
Sources: src/vcpu.rs(L47 - L62)
Register Lifecycle and Context Switching
Register state undergoes several transitions during VM execution, with different register categories being saved and restored at different points in the virtualization lifecycle.
VM Entry and Exit Register Flow
sequenceDiagram participant HostExecution as "Host Execution" participant RISCVVCpu as "RISCVVCpu" participant _run_guest as "_run_guest" participant GuestExecution as "Guest Execution" HostExecution ->> RISCVVCpu: run() RISCVVCpu ->> RISCVVCpu: Setup interrupt state (sie, sstatus) RISCVVCpu ->> _run_guest: _run_guest(&mut self.regs) Note over _run_guest: Save hypervisor state to hyp_regs<br>Restore guest state from guest_regs<br>Load VS CSRs, virtual HS CSRs _run_guest ->> GuestExecution: Enter guest mode GuestExecution ->> GuestExecution: Execute guest code GuestExecution ->> _run_guest: VM Exit (trap, interrupt, ecall) Note over _run_guest: Save guest state to guest_regs<br>Restore hypervisor state from hyp_regs<br>Update trap_csrs with exit info _run_guest ->> RISCVVCpu: Return to hypervisor RISCVVCpu ->> RISCVVCpu: vmexit_handler() RISCVVCpu ->> RISCVVCpu: Read trap CSRs (scause, stval, htval, htinst) RISCVVCpu ->> HostExecution: Return AxVCpuExitReason
Sources: src/vcpu.rs(L92 - L111) src/vcpu.rs(L167 - L181)
Register State During SBI Call Processing
When the guest makes an SBI call, the VCPU accesses argument registers and modifies return value registers according to the SBI specification.
flowchart TD SbiCall["Guest SBI Call (ecall)"] ReadArgs["Read arguments from A0-A7a_regs() -> [a0..a7]"] ExtractIds["Extract extension_id (A7)Extract function_id (A6)"] ProcessCall["Process SBI call"] LegacyTimer["Legacy Timerset_timer(a0)"] LegacyConsole["Legacy Consoleputchar/getchar"] HsmCalls["HSM Extensionhart_start/stop/suspend"] Hypercall["HypercallCustom extension"] ForwardSbi["Forward to RustSBI"] SetA0Zero["set_gpr_from_gpr_index(A0, 0)"] SetA0Result["set_gpr_from_gpr_index(A0, result)"] SetRetVals["set_gpr_from_gpr_index(A0, error)set_gpr_from_gpr_index(A1, value)"] AdvancePC["advance_pc(4)"] ContinueGuest["Continue guest execution"] AdvancePC --> ContinueGuest ExtractIds --> ProcessCall ForwardSbi --> SetRetVals LegacyConsole --> SetA0Result LegacyTimer --> SetA0Zero ProcessCall --> ForwardSbi ProcessCall --> HsmCalls ProcessCall --> Hypercall ProcessCall --> LegacyConsole ProcessCall --> LegacyTimer ReadArgs --> ExtractIds SbiCall --> ReadArgs SetA0Result --> AdvancePC SetA0Zero --> AdvancePC SetRetVals --> AdvancePC
Sources: src/vcpu.rs(L184 - L277)
The register management system provides the foundation for all VCPU operations, maintaining the complete CPU state needed for virtualizing RISC-V guests. The structured approach to organizing different categories of register state enables efficient context switching and proper isolation between hypervisor and guest execution environments.