Technical Reference
Relevant source files
This document provides detailed technical information about the internal implementation of AxVisor, the ArceOS hypervisor. It covers core components, execution models, and internal data structures used by the hypervisor. For information about building and running AxVisor, see Building and Running, and for configuration details, see Configuration.
1. VMM Implementation
The Virtual Machine Manager (VMM) is the core component of AxVisor responsible for managing virtual machines throughout their lifecycle. It handles VM initialization, booting, execution, and shutdown.
flowchart TD
subgraph subGraph1["VM States"]
vminit["VM::new()"]
vmboot["VM::boot()"]
vmrun["VM running"]
vmshutdown["VM::shutdown()"]
end
subgraph subGraph0["VMM Lifecycle"]
init["VMM::init()"]
config["config::init_guest_vms()"]
setup["vcpus::setup_vm_primary_vcpu()"]
start["VMM::start()"]
boot["vm.boot() for each VM"]
notify["vcpus::notify_primary_vcpu()"]
inc["Increment RUNNING_VM_COUNT"]
wait["Wait until all VMs stopped"]
dec["RUNNING_VM_COUNT == 0"]
end
boot --> inc
boot --> notify
config --> setup
init --> config
init --> vminit
notify --> vmrun
start --> boot
start --> vmboot
vmshutdown --> dec
wait --> dec
Sources: src/vmm/mod.rs(L28 - L65)
1.1 VM Initialization Process
The VMM initializes VMs through the following steps:
- Loads VM configuration from TOML files via
config::init_guest_vms() - Creates VM instances using
VM::new() - Loads VM images according to configuration
- Sets up the primary VCPU for each VM using
vcpus::setup_vm_primary_vcpu()
The RUNNING_VM_COUNT atomic counter tracks active VMs, allowing the VMM to determine when all VMs have stopped.
sequenceDiagram
participant VMM as VMM
participant Configuration as Configuration
participant VirtualMachine as Virtual Machine
participant VirtualCPU as Virtual CPU
VMM ->> Configuration: init_guest_vms()
Configuration ->> Configuration: static_vm_configs()
Configuration ->> Configuration: AxVMCrateConfig::from_toml()
Configuration ->> VirtualMachine: VM::new(vm_config)
Configuration ->> VirtualMachine: load_vm_images()
VMM ->> VirtualCPU: setup_vm_primary_vcpu()
VMM ->> VirtualMachine: vm.boot()
VMM ->> VirtualCPU: notify_primary_vcpu()
VMM ->> VMM: RUNNING_VM_COUNT.fetch_add(1)
Note over VMM: Wait for all VMs to stop
Sources: src/vmm/mod.rs(L28 - L65) src/vmm/config.rs(L25 - L43)
2. VCPU Management System
The VCPU management system handles the creation, execution, and synchronization of virtual CPUs. Each VCPU runs as a separate task in the ArceOS task scheduler.
2.1 VCPU Task Structure
Each VCPU is associated with a task that contains:
- Reference to the parent VM
- Reference to the VCPU itself
- Stack space for execution (256 KiB)
- Processor affinity settings (if configured)
classDiagram
class TaskExt {
VMRef vm
VCpuRef vcpu
+new(vm, vcpu)
}
class VMVCpus {
_vm_id: usize
wait_queue: WaitQueue
vcpu_task_list: Vec~AxTaskRef~
running_halting_vcpu_count: AtomicUsize
+new(vm)
+add_vcpu_task(vcpu_task)
+wait()
+wait_until(condition)
+notify_one()
+mark_vcpu_running()
+mark_vcpu_exiting() -~ bool
}
class VMRef {
}
class VCpuRef {
}
class AxTaskRef {
}
TaskExt "1" --> "1" VMRef : references
TaskExt "1" --> "1" VCpuRef : references
VMVCpus "1" --> "*" AxTaskRef : manages
Sources: src/task.rs(L1 - L19) src/vmm/vcpus.rs(L27 - L107)
2.2 VCPU Execution Flow
The VCPU execution flow follows these steps:
- Task is created for the VCPU with
alloc_vcpu_task() - VCPU task waits until the VM is in running state
- VCPU enters the execution loop via
vcpu_run() - VM executes guest code with
vm.run_vcpu(vcpu_id) - VCPU handles various exit reasons (hypercalls, interrupts, etc.)
- If the VM is shutting down, VCPU exits the loop
flowchart TD
subgraph subGraph1["Exit Reasons"]
hypercall["Hypercall"]
interrupt["External Interrupt"]
halt["Halt"]
cpuup["CPU Up"]
cpudown["CPU Down"]
sysdown["System Down"]
end
subgraph subGraph0["VCPU Task Lifecycle"]
alloc["alloc_vcpu_task()"]
task["Create task with vcpu_run() entry"]
wait["Wait for VM running state"]
mark["mark_vcpu_running()"]
loop["Enter execution loop"]
run["vm.run_vcpu()"]
handle["Handle exit reason"]
check["Check VM shutdown state"]
exit["Exit loop"]
dec["Decrement RUNNING_VM_COUNT if last VCPU"]
end
waitq["Wait on queue"]
create["Create new VCPU task"]
shutdown["vm.shutdown()"]
alloc --> task
check --> exit
check --> loop
cpuup --> create
create --> loop
exit --> dec
halt --> waitq
handle --> check
handle --> cpudown
handle --> cpuup
handle --> halt
handle --> hypercall
handle --> interrupt
handle --> sysdown
loop --> run
mark --> loop
run --> handle
shutdown --> check
sysdown --> shutdown
task --> wait
wait --> mark
waitq --> loop
Sources: src/vmm/vcpus.rs(L169 - L367)
2.3 Synchronization Mechanism
VCPUs are synchronized using a wait queue system:
- Each VM has a
VMVCpusstructure containing a wait queue - VCPUs can wait on the queue using
wait()orwait_until() - Other VCPUs can wake waiting VCPUs using
notify_one() - The primary VCPU is notified when the VM boots
The system also tracks running VCPUs with an atomic counter to determine when all VCPUs in a VM have exited.
sequenceDiagram
participant VMM as VMM
participant VirtualMachine as Virtual Machine
participant PrimaryVCPU as Primary VCPU
participant SecondaryVCPU as Secondary VCPU
participant WaitQueue as Wait Queue
VMM ->> VirtualMachine: boot()
VMM ->> WaitQueue: notify_primary_vcpu()
WaitQueue ->> PrimaryVCPU: wake up
PrimaryVCPU ->> VirtualMachine: run_vcpu()
PrimaryVCPU ->> SecondaryVCPU: vcpu_on() (CPU Up)
SecondaryVCPU ->> VirtualMachine: run_vcpu()
SecondaryVCPU ->> WaitQueue: wait() (Halt)
PrimaryVCPU ->> WaitQueue: notify_one()
WaitQueue ->> SecondaryVCPU: wake up
PrimaryVCPU ->> VirtualMachine: shutdown()
VirtualMachine ->> VirtualMachine: shutting_down = true
SecondaryVCPU ->> SecondaryVCPU: check VM state
SecondaryVCPU ->> VMM: mark_vcpu_exiting()
PrimaryVCPU ->> PrimaryVCPU: check VM state
PrimaryVCPU ->> VMM: mark_vcpu_exiting()
PrimaryVCPU ->> VMM: RUNNING_VM_COUNT.fetch_sub(1)
Sources: src/vmm/vcpus.rs(L110 - L167) src/vmm/mod.rs(L22 - L65)
3. Internal Data Structures
The hypervisor uses several key data structures to manage VMs and VCPUs.
3.1 VM and VCPU Types
VM = axvm::AxVM<AxVMHalImpl, AxVCpuHalImpl>
VMRef = axvm::AxVMRef<AxVMHalImpl, AxVCpuHalImpl>
VCpuRef = axvm::AxVCpuRef<AxVCpuHalImpl>
These types abstract the architecture-specific implementations behind common interfaces.
Sources: src/vmm/mod.rs(L16 - L20)
3.2 Global State Management
| Structure | Purpose | Implementation |
|---|---|---|
| RUNNING_VM_COUNT | Tracks number of running VMs | static AtomicUsize |
| VMM | Wait queue for VMM synchronization | static AxWaitQueueHandle |
| VM_VCPU_TASK_WAIT_QUEUE | Maps VM IDs to their VCPU wait queues | static mut BTreeMap<usize, VMVCpus> |
Sources: src/vmm/mod.rs(L22 - L25) src/vmm/vcpus.rs(L23)
3.3 Task Extension
The TaskExt structure associates an ArceOS task with VM and VCPU references:
pub struct TaskExt {
pub vm: VMRef,
pub vcpu: VCpuRef,
}
This allows the task scheduler to properly manage VCPU execution.
Sources: src/task.rs(L6 - L11)
4. VM Exit Handling
VM exits occur when the guest execution needs to be intercepted by the hypervisor. The hypervisor handles various exit reasons through the VCPU execution loop.
4.1 Exit Reason Processing
The following table shows the main exit reasons and their handling:
| Exit Reason | Description | Handling |
|---|---|---|
| Hypercall | Guest executed a hypercall | Log hypercall details and arguments |
| ExternalInterrupt | Interrupt received | Handle interrupt and continue execution |
| Halt | Guest halted CPU | VCPU waits on wait queue |
| CpuUp | Guest wants to start another CPU | Create new VCPU task for target CPU |
| CpuDown | Guest wants to stop a CPU | VCPU waits on wait queue |
| SystemDown | Guest wants to shutdown | Shutdown VM and exit VCPU task |
| FailEntry | Failed to enter VM | Log error and continue |
stateDiagram-v2 [*] --> Running Running --> Hypercall : Guest makes hypercall Hypercall --> Running : Process and continue Running --> Interrupt : External interrupt Interrupt --> Running : Handle interrupt Running --> Halted : Guest halts CPU Halted --> Running : Notified by other VCPU Running --> Creating : CPU Up request Creating --> Running : New VCPU created Running --> Waiting : CPU Down request Waiting --> Running : Notified Running --> Exiting : VM shutting down Exiting --> [*] : Last VCPU updates RUNNING_VM_COUNT
Sources: src/vmm/vcpus.rs(L290 - L363)
4.2 VCPU State Transitions
VCPUs transition through the following states during their lifecycle:
- Free: Initial state when VCPU is created
- Running: VCPU is executing guest code
- Halted: VCPU is waiting on a wait queue
- Exiting: VCPU is exiting due to VM shutdown
The mark_vcpu_running() and mark_vcpu_exiting() functions track these transitions.
Sources: src/vmm/vcpus.rs(L92 - L107) src/vmm/vcpus.rs(L155 - L167)
5. Architecture-Specific Components
AxVisor supports multiple CPU architectures through hardware abstraction layers.
5.1 HAL Implementation
The hypervisor uses architecture-specific implementations behind common interfaces:
classDiagram
class AxVMHalImpl {
<<HAL for VM operations>>
}
class AxVCpuHalImpl {
<<HAL for VCPU operations>>
}
class VM {
<<axvm::AxVM>>
+boot()
+shutdown()
+run_vcpu()
}
class VCpu {
<<axvm::AxVCpu>>
+run()
+set_entry()
+set_gpr()
}
VM *-- AxVMHalImpl
VCpu *-- AxVCpuHalImpl
The actual implementations vary by architecture (x86_64, ARM/aarch64, RISC-V) but present a unified interface.
Sources: src/vmm/mod.rs(L12 - L20)
5.2 Architecture-Specific Behaviors
Some behaviors vary by architecture:
#[cfg(target_arch = "riscv64")]
{
debug!(
"vcpu_on: vcpu[{}] entry={:x} opaque={:x}",
vcpu_id, entry_point, arg
);
vcpu.set_gpr(0, vcpu_id);
vcpu.set_gpr(1, arg);
}
This example shows RISC-V specific register setup for secondary VCPUs.
Sources: src/vmm/vcpus.rs(L193 - L201)
6. System Configuration
VM configurations are loaded from TOML files and used to initialize VMs.
6.1 Configuration Loading
Static VM configurations are included at compile time and parsed during VM initialization:
pub fn init_guest_vms() {
let gvm_raw_configs = config::static_vm_configs();
for raw_cfg_str in gvm_raw_configs {
let vm_create_config =
AxVMCrateConfig::from_toml(raw_cfg_str).expect("Failed to resolve VM config");
let vm_config = AxVMConfig::from(vm_create_config.clone());
// Create VM and load images
// ...
}
}
Architecture-specific configurations are selected based on the target architecture.
Sources: src/vmm/config.rs(L10 - L22) src/vmm/config.rs(L25 - L43)
6.2 Configuration Structure
VM configurations include:
- Basic properties (ID, name, CPU count)
- Memory regions
- Image location and loading parameters
- Device configurations
For more detailed information on configuration options, see VM Configuration.