VCPU Lifecycle and Management
Relevant source files
This document covers the complete lifecycle management of RISC-V virtual CPUs in the hypervisor system, from initial creation through execution and cleanup. It details the RISCVVCpu
implementation, register state management, and the coordination between guest and hypervisor execution contexts.
For information about SBI interface handling during VCPU execution, see SBI Interface and Hypercalls. For details about VM exit processing, see VM Exit Processing. For per-CPU system initialization, see Per-CPU Management.
VCPU Creation and Initialization
The VCPU lifecycle begins with the creation of a RISCVVCpu<H>
instance through the new()
method. This process establishes the fundamental guest execution environment and initializes the register state required for virtual machine operation.
flowchart TD CreateConfig["RISCVVCpuCreateConfig"] NewMethod["RISCVVCpu::new()"] RegInit["VmCpuRegisters::default()"] SetHartId["Set A0 register (hart_id)"] SetDTB["Set A1 register (dtb_addr)"] SBIInit["RISCVVCpuSbi::default()"] GuestRegs["GuestCpuState"] HypRegs["HypervisorCpuState"] VsCSRs["GuestVsCsrs"] VirtualHsCSRs["GuestVirtualHsCsrs"] TrapCSRs["VmCpuTrapState"] VCPUCreated["RISCVVCpu Instance"] CreateConfig --> NewMethod GuestRegs --> VCPUCreated HypRegs --> VCPUCreated NewMethod --> RegInit NewMethod --> SBIInit NewMethod --> SetDTB NewMethod --> SetHartId RegInit --> GuestRegs RegInit --> HypRegs RegInit --> TrapCSRs RegInit --> VirtualHsCSRs RegInit --> VsCSRs SBIInit --> VCPUCreated SetDTB --> VCPUCreated SetHartId --> VCPUCreated TrapCSRs --> VCPUCreated VirtualHsCSRs --> VCPUCreated VsCSRs --> VCPUCreated
The creation process initializes several critical components:
Component | Purpose | Configuration |
---|---|---|
regs.guest_regs.gprs | General purpose registers | A0 = hart_id, A1 = dtb_addr |
sbi | SBI interface handler | Forward-only RustSBI implementation |
regs | Complete register state | Default-initialized structures |
Sources: src/vcpu.rs(L47 - L62) src/regs.rs(L179 - L196)
VCPU Setup and Configuration
After creation, the VCPU undergoes setup to establish the proper execution environment. This involves configuring supervisor-level and hypervisor-level control and status registers.
flowchart TD SetupCall["setup() method"] ReadSstatus["sstatus::read()"] ConfigSstatus["Set SPP = Supervisor"] WriteSstatus["Store in guest_regs.sstatus"] ReadHstatus["hstatus::read()"] ConfigHstatus["Set SPV = true, SPVP = true"] WriteHstatus["hstatus.write() & store"] SetupComplete["Setup Complete"] SetEntry["set_entry(entry_point)"] SetEPT["set_ept_root(ept_root)"] ConfigSEPC["guest_regs.sepc = entry"] ConfigHGATP["virtual_hs_csrs.hgatp = ept_config"] ConfigHstatus --> WriteHstatus ConfigSstatus --> WriteSstatus ReadHstatus --> ConfigHstatus ReadSstatus --> ConfigSstatus SetEPT --> ConfigHGATP SetEntry --> ConfigSEPC SetupCall --> ReadHstatus SetupCall --> ReadSstatus SetupComplete --> SetEPT SetupComplete --> SetEntry WriteHstatus --> SetupComplete WriteSstatus --> SetupComplete
The setup process configures two critical status registers:
- sstatus Configuration: Sets the previous privilege level to Supervisor mode, ensuring proper guest execution context
- hstatus Configuration: Enables virtualization (
SPV=true
) and VS-mode memory access from HS-mode (SPVP=true
)
Additional configuration methods complete the VCPU preparation:
Method | Purpose | Register Modified |
---|---|---|
set_entry() | Sets guest entry point | guest_regs.sepc |
set_ept_root() | Configures page table root | virtual_hs_csrs.hgatp |
Sources: src/vcpu.rs(L64 - L90)
Runtime State Management
The VCPU maintains extensive state information through the VmCpuRegisters
structure, which encompasses all register contexts needed for virtualization.
Register State Architecture
flowchart TD VmCpuRegisters["VmCpuRegisters"] HypRegs["hyp_regs: HypervisorCpuState"] GuestRegs["guest_regs: GuestCpuState"] VsCSRs["vs_csrs: GuestVsCsrs"] VirtualHsCSRs["virtual_hs_csrs: GuestVirtualHsCsrs"] TrapCSRs["trap_csrs: VmCpuTrapState"] HypGPRs["gprs: GeneralPurposeRegisters"] HypStatus["sstatus, hstatus, scounteren"] HypVec["stvec, sscratch"] GuestGPRs["gprs: GeneralPurposeRegisters"] GuestStatus["sstatus, hstatus, scounteren"] GuestPC["sepc"] VsState["vsstatus, vsie, vstvec"] VsTrap["vsepc, vscause, vstval"] VsTime["htimedelta, vstimecmp"] VsMMU["vsatp"] VirtualInt["hie, hgeie"] VirtualMMU["hgatp"] TrapInfo["scause, stval, htval, htinst"] GuestRegs --> GuestGPRs GuestRegs --> GuestPC GuestRegs --> GuestStatus HypRegs --> HypGPRs HypRegs --> HypStatus HypRegs --> HypVec TrapCSRs --> TrapInfo VirtualHsCSRs --> VirtualInt VirtualHsCSRs --> VirtualMMU VmCpuRegisters --> GuestRegs VmCpuRegisters --> HypRegs VmCpuRegisters --> TrapCSRs VmCpuRegisters --> VirtualHsCSRs VmCpuRegisters --> VsCSRs VsCSRs --> VsMMU VsCSRs --> VsState VsCSRs --> VsTime VsCSRs --> VsTrap
Register Access Interface
The VCPU provides controlled access to register state through dedicated methods:
Method | Purpose | Register Type |
---|---|---|
get_gpr() | Read general purpose register | Guest GPRs |
set_gpr_from_gpr_index() | Write general purpose register | Guest GPRs |
advance_pc() | Increment program counter | Guest SEPC |
regs() | Access complete register state | All registers |
Sources: src/vcpu.rs(L144 - L163) src/regs.rs(L1 - L196)
Execution Lifecycle
The VCPU execution cycle involves binding to a physical CPU, running guest code, and handling exits. This process manages the transition between hypervisor and guest execution contexts.
Execution Flow
flowchart TD BindCall["bind()"] SetHGATP["csrw hgatp, virtual_hs_csrs.hgatp"] FlushTLB["hfence_gvma_all()"] Bound["VCPU Bound to CPU"] RunCall["run()"] DisableInts["sstatus::clear_sie()"] EnableExts["Enable external/software/timer interrupts"] RunGuest["_run_guest(&mut regs)"] VMExit["Guest exits to hypervisor"] DisableExts["Disable guest interrupts"] EnableInts["sstatus::set_sie()"] VMExitHandler["vmexit_handler()"] ExitReason["AxVCpuExitReason"] CheckContinue["Continue execution?"] UnbindCall["unbind()"] Unbound["VCPU Unbound"] BindCall --> SetHGATP Bound --> RunCall CheckContinue --> RunCall CheckContinue --> UnbindCall DisableExts --> EnableInts DisableInts --> EnableExts EnableExts --> RunGuest EnableInts --> VMExitHandler ExitReason --> CheckContinue FlushTLB --> Bound RunCall --> DisableInts RunGuest --> VMExit SetHGATP --> FlushTLB UnbindCall --> Unbound VMExit --> DisableExts VMExitHandler --> ExitReason
Interrupt Management During Execution
The execution cycle carefully manages interrupt state to ensure proper isolation between guest and hypervisor:
Pre-execution Setup src/vcpu.rs(L93 - L98) :
- Disable supervisor interrupts (
sstatus::clear_sie()
) - Enable external, software, and timer interrupt delegation
- Prepare for guest entry
Post-execution Cleanup src/vcpu.rs(L104 - L109) :
- Disable interrupt delegation
- Re-enable supervisor interrupts
- Return control to hypervisor
Sources: src/vcpu.rs(L92 - L126)
Memory and Address Space Management
The VCPU manages guest physical address translation through the hypervisor's extended page table mechanism and maintains proper memory isolation.
Memory Management Components
flowchart TD GuestVA["Guest Virtual Address"] GuestPT["Guest Page Table (vsatp)"] GuestPA["Guest Physical Address"] HGATP["Hypervisor Gate Page Table (hgatp)"] HostPA["Host Physical Address"] SetEPTRoot["set_ept_root()"] ConfigHGATP["Configure virtual_hs_csrs.hgatp"] TwoStageTranslation["Two-stage Address Translation"] BindVCPU["bind()"] LoadHGATP["Load hgatp into hardware"] FlushTLB["Flush guest TLB (hfence_gvma_all)"] BindVCPU --> LoadHGATP ConfigHGATP --> TwoStageTranslation GuestPA --> HGATP GuestPT --> GuestPA GuestVA --> GuestPT HGATP --> HostPA LoadHGATP --> FlushTLB SetEPTRoot --> ConfigHGATP
The memory management system implements two-stage address translation:
- First Stage: Guest virtual to guest physical using
vsatp
- Second Stage: Guest physical to host physical using
hgatp
The hgatp
register configuration src/vcpu.rs(L88) uses:
- Mode field (bits 63:60) = 8 (Sv39x4 format)
- Physical page number derived from EPT root address
Sources: src/vcpu.rs(L87 - L89) src/vcpu.rs(L113 - L122)
Error Handling and State Validation
The VCPU implementation includes comprehensive error handling for invalid operations and state inconsistencies.
Register Access Validation
General purpose register access includes bounds checking src/vcpu.rs(L129 - L141) :
flowchart TD SetGPR["set_gpr(index, val)"] CheckIndex["index in range 0..=7?"] MapIndex["Map to GprIndex::A(index+10)"] LogWarning["Log unsupported register warning"] SetRegister["set_gpr_from_gpr_index()"] UpdateState["Update guest_regs.gprs"] NoOperation["No state change"] CheckIndex --> LogWarning CheckIndex --> MapIndex LogWarning --> NoOperation MapIndex --> SetRegister SetGPR --> CheckIndex SetRegister --> UpdateState
Special Register Handling
The zero register (GprIndex::Zero
) receives special treatment src/regs.rs(L97 - L99) :
- Write attempts are silently ignored
- Always reads as zero value
- Maintains RISC-V architectural compliance