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:

ComponentPurposeConfiguration
regs.guest_regs.gprsGeneral purpose registersA0 = hart_id, A1 = dtb_addr
sbiSBI interface handlerForward-only RustSBI implementation
regsComplete register stateDefault-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:

MethodPurposeRegister Modified
set_entry()Sets guest entry pointguest_regs.sepc
set_ept_root()Configures page table rootvirtual_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:

MethodPurposeRegister Type
get_gpr()Read general purpose registerGuest GPRs
set_gpr_from_gpr_index()Write general purpose registerGuest GPRs
advance_pc()Increment program counterGuest SEPC
regs()Access complete register stateAll 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:

  1. First Stage: Guest virtual to guest physical using vsatp
  2. 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

Sources: src/vcpu.rs(L129 - L141)  src/regs.rs(L95 - L102)