Overview

Relevant source files

Purpose and Scope

The axvm crate provides a minimal virtual machine monitor (VMM) for the ArceOS hypervisor ecosystem. It implements core virtualization capabilities including virtual CPU management, memory virtualization, and device emulation across multiple hardware architectures. This document covers the fundamental architecture and capabilities of the axvm hypervisor.

For detailed information about the VM configuration system, see Configuration System. For build and development processes, see Development Guide.

High-Level Architecture

The axvm hypervisor is structured around four core modules that provide a clean abstraction layer for virtualization:

AxVM Core Components

flowchart TD
subgraph subGraph0["Main Exports"]
    AxVM_Export["AxVM"]
    AxVMHal_Export["AxVMHal"]
    AxVCpuRef_Export["AxVCpuRef"]
    AxVMRef_Export["AxVMRef"]
end
LibRS["lib.rsMain Library Interface"]
VM["vm.rsAxVM Core Implementation"]
VCPU["vcpu.rsMulti-Architecture VCPU"]
HAL["hal.rsHardware Abstraction Layer"]
CONFIG["config.rsVM Configuration"]

LibRS --> AxVCpuRef_Export
LibRS --> AxVMHal_Export
LibRS --> AxVMRef_Export
LibRS --> AxVM_Export
LibRS --> CONFIG
LibRS --> HAL
LibRS --> VCPU
LibRS --> VM
VM --> CONFIG
VM --> HAL
VM --> VCPU

The main library interface exports the AxVM struct as the primary virtualization abstraction, along with supporting types for hardware abstraction (AxVMHal) and CPU references (AxVCpuRef, AxVMRef). The has_hardware_support() function provides runtime detection of virtualization capabilities.

Sources: src/lib.rs(L6 - L33)  Cargo.toml(L1 - L8) 

Multi-Architecture Support

The axvm hypervisor provides unified virtualization across x86_64, RISC-V, and AArch64 architectures through architecture-specific backend implementations:

Architecture-Specific Backend Structure


The multi-architecture support is implemented through conditional compilation using cfg-if directives. Each architecture provides its own VCPU implementation while conforming to the common AxArchVCpuImpl interface. The AxVMPerCpu type is defined as axvcpu::AxPerCpu<vcpu::AxVMArchPerCpuImpl<U>> to provide per-CPU state management across all supported architectures.

Sources: Cargo.toml(L29 - L36)  src/lib.rs(L26 - L32)  Cargo.toml(L19 - L21) 

Core Capabilities

The axvm hypervisor provides three primary virtualization capabilities through its modular architecture:

CapabilityImplementationKey Components
Virtual CPU ManagementMulti-architecture VCPU abstractionAxArchVCpuImpl, architecture-specific backends
Memory VirtualizationAddress space and page table managementaxaddrspace,page_table_multiarch
Device EmulationHardware device virtualizationaxdevice, MMIO handling

VM Lifecycle and State Management


The VM lifecycle is managed through the core AxVM struct, which coordinates virtual CPU execution, handles VM exits for MMIO and I/O operations, and manages the overall VM state transitions.

Sources: src/lib.rs(L9 - L10)  src/lib.rs(L22 - L24) 

ArceOS Ecosystem Integration

The axvm hypervisor integrates deeply with the ArceOS ecosystem through a set of specialized crates that provide system-level abstractions:

Dependency Architecture

flowchart TD
subgraph subGraph3["External Dependencies"]
    log_crate["logLogging framework"]
    cfg_if["cfg-ifConditional compilation"]
    spin["spinSynchronization primitives"]
end
subgraph subGraph2["ArceOS System Crates"]
    axerrno["axerrnoError handling"]
    memory_addr["memory_addrAddress types"]
    page_table_multiarch["page_table_multiarchMulti-arch page tables"]
    page_table_entry["page_table_entryARM EL2 support"]
    percpu["percpuPer-CPU data structures"]
end
subgraph subGraph1["ArceOS Hypervisor Modules"]
    axvcpu["axvcpuVirtual CPU abstraction"]
    axaddrspace["axaddrspaceAddress space management"]
    axdevice["axdeviceDevice emulation framework"]
    axvmconfig["axvmconfigConfiguration management"]
end
subgraph subGraph0["AxVM Core"]
    axvm["axvm crate"]
end

axvm --> axaddrspace
axvm --> axdevice
axvm --> axerrno
axvm --> axvcpu
axvm --> axvmconfig
axvm --> cfg_if
axvm --> log_crate
axvm --> memory_addr
axvm --> page_table_entry
axvm --> page_table_multiarch
axvm --> percpu
axvm --> spin

The crate uses the no_std environment for embedded and kernel-space deployment. All ArceOS hypervisor modules are sourced from the arceos-hypervisor GitHub organization, providing a cohesive ecosystem for virtualization components.

Sources: Cargo.toml(L10 - L27)  src/lib.rs(L1 - L13) 

The AxVMHal trait provides the hardware abstraction interface that must be implemented by the hosting environment, enabling axvm to run on different underlying platforms while maintaining a consistent virtualization interface.

Sources: src/lib.rs(L21) 

Core Architecture

Relevant source files

This document provides a detailed examination of axvm's fundamental architecture, focusing on the core components that enable virtual machine creation, management, and execution. It covers the main AxVM struct, its internal organization, lifecycle management, and integration with hardware abstraction layers.

For information about specific vCPU architecture implementations, see Virtual CPU Architecture. For details about hardware abstraction interfaces, see Hardware Abstraction Layer. For VM configuration mechanisms, see Configuration System.

Architecture Overview

The axvm hypervisor is built around a central AxVM struct that coordinates virtual machine execution through a clean separation of concerns. The architecture employs generic type parameters to abstract hardware and vCPU implementations while maintaining compile-time optimization.

flowchart TD
subgraph subGraph4["Generic Type Parameters"]
    HAL["H: AxVMHal"]
    VCPUHAL["U: AxVCpuHal"]
end
subgraph subGraph3["VM Lifecycle Flags"]
    Running["running: AtomicBool"]
    ShuttingDown["shutting_down: AtomicBool"]
end
subgraph subGraph2["Mutable VM State"]
    AddrSpace["address_space: Mutex<AddrSpace<H::PagingHandler>>"]
    PhantomData["_marker: PhantomData<H>"]
end
subgraph subGraph1["Immutable VM State"]
    VMId["id: usize"]
    VMConfig["config: AxVMConfig"]
    VCpuList["vcpu_list: Box[AxVCpuRef<U>]"]
    VMDevices["devices: AxVmDevices"]
end
subgraph subGraph0["Core VM Structure"]
    AxVM["AxVM<H, U>"]
    AxVMInnerConst["AxVMInnerConst<U>"]
    AxVMInnerMut["AxVMInnerMut<H>"]
end

AxVM --> AxVMInnerConst
AxVM --> AxVMInnerMut
AxVM --> HAL
AxVM --> Running
AxVM --> ShuttingDown
AxVM --> VCPUHAL
AxVMInnerConst --> VCpuList
AxVMInnerConst --> VMConfig
AxVMInnerConst --> VMDevices
AxVMInnerConst --> VMId
AxVMInnerMut --> AddrSpace
AxVMInnerMut --> PhantomData

Sources: src/vm.rs(L47 - L53)  src/vm.rs(L31 - L39)  src/vm.rs(L41 - L45) 

Core VM Structure

The AxVM struct is organized into distinct sections that separate immutable configuration data from mutable runtime state. This design ensures thread safety while minimizing lock contention during VM execution.

Data Organization

ComponentTypePurposeThread Safety
runningAtomicBoolVM execution stateLock-free atomic operations
shutting_downAtomicBoolVM shutdown stateLock-free atomic operations
inner_constAxVMInnerConstImmutable VM configurationUnsafe Send/Sync implementation
inner_mutAxVMInnerMutMutable runtime stateMutex-protected access

The immutable section (AxVMInnerConst) contains the VM identifier, configuration, vCPU list, and device registry. The mutable section (AxVMInnerMut) contains the address space with its associated page table handler.

Sources: src/vm.rs(L47 - L53)  src/vm.rs(L31 - L39)  src/vm.rs(L41 - L45) 

VM Creation and Initialization

VM creation follows a multi-stage initialization process that sets up vCPUs, memory regions, and devices before the VM can be executed.

sequenceDiagram
    participant AxVMnew as "AxVM::new()"
    participant vCPUCreation as "vCPU Creation"
    participant MemorySetup as "Memory Setup"
    participant DeviceSetup as "Device Setup"
    participant AddrSpace as "AddrSpace"

    AxVMnew ->> vCPUCreation: "Create VCpus from config"
    Note over vCPUCreation: "Architecture-specific AxVCpuCreateConfig"
    vCPUCreation ->> vCPUCreation: "Arc::new(VCpu::new())"
    AxVMnew ->> MemorySetup: "Process memory_regions()"
    MemorySetup ->> AddrSpace: "AddrSpace::new_empty()"
    loop "For each memory region"
        MemorySetup ->> MemorySetup: "Validate MappingFlags"
    alt "VmMemMappingType::MapIentical"
        MemorySetup ->> AddrSpace: "map_linear()"
    else "VmMemMappingType::MapAlloc"
        MemorySetup ->> AddrSpace: "map_alloc()"
    end
    end
    loop "For each pass_through_device"
        MemorySetup ->> AddrSpace: "map_linear() with DEVICE flags"
    end
    AxVMnew ->> DeviceSetup: "AxVmDevices::new()"
    DeviceSetup ->> DeviceSetup: "Initialize from emu_configs"
    AxVMnew ->> AxVMnew: "Create AxVM struct"
    loop "For each vCPU"
        AxVMnew ->> vCPUCreation: "vcpu.setup(entry, ept_root, config)"
    end

Sources: src/vm.rs(L59 - L219)  src/vm.rs(L95 - L163)  src/vm.rs(L182 - L184)  src/vm.rs(L204 - L217) 

Memory Region Setup

The VM initialization process handles two distinct memory mapping types:

  • VmMemMappingType::MapIentical: Creates identity mappings where guest physical addresses map directly to host physical addresses
  • VmMemMappingType::MapAlloc: Allocates new physical memory pages that may be non-contiguous in host physical memory

The address space spans from VM_ASPACE_BASE (0x0) to VM_ASPACE_SIZE (0x7fff_ffff_f000), providing a large virtual address space for guest execution.

Sources: src/vm.rs(L18 - L19)  src/vm.rs(L122 - L162) 

VM Lifecycle Management

The VM lifecycle is controlled through atomic boolean flags that coordinate state transitions across multiple threads without requiring locks.


State Validation

VM operations include state validation to prevent invalid transitions:

  • boot() checks hardware virtualization support and prevents double-booting
  • shutdown() prevents multiple shutdown attempts
  • run_vcpu() validates vCPU existence before execution

Sources: src/vm.rs(L277 - L288)  src/vm.rs(L299 - L310)  src/vm.rs(L328 - L376) 

vCPU Execution Loop

The core VM execution occurs in the run_vcpu() method, which implements a continuous loop that handles various exit reasons from hardware virtualization.

flowchart TD
Start["run_vcpu(vcpu_id)"]
ValidateVCpu["vcpu(vcpu_id)?"]
BindVCpu["vcpu.bind()"]
RunLoop["vcpu.run()"]
ExitReason["Match exit_reason"]
MmioRead["MmioRead"]
MmioWrite["MmioWrite"]
PageFault["NestedPageFault"]
IoOps["IoRead/IoWrite"]
Other["Other reasons"]
DeviceRead["devices.handle_mmio_read()"]
SetGPR["vcpu.set_gpr()"]
Continue["Continue loop"]
DeviceWrite["devices.handle_mmio_write()"]
HandleFault["address_space.handle_page_fault()"]
Unbind["vcpu.unbind()"]
Return["Return exit_reason"]

BindVCpu --> RunLoop
Continue --> RunLoop
DeviceRead --> SetGPR
DeviceWrite --> Continue
ExitReason --> IoOps
ExitReason --> MmioRead
ExitReason --> MmioWrite
ExitReason --> Other
ExitReason --> PageFault
HandleFault --> Continue
IoOps --> Continue
MmioRead --> DeviceRead
MmioWrite --> DeviceWrite
Other --> Unbind
PageFault --> HandleFault
RunLoop --> ExitReason
SetGPR --> Continue
Start --> ValidateVCpu
Unbind --> Return
ValidateVCpu --> BindVCpu

The execution loop handles several categories of VM exits:

  • MMIO Operations: Delegated to the device emulation layer via AxVmDevices
  • Page Faults: Handled by the address space's page fault handler
  • I/O Operations: Currently handled as no-ops
  • Unhandled Exits: Return control to the caller

Sources: src/vm.rs(L328 - L376)  src/vm.rs(L335 - L372) 

Component Integration

The AxVM struct integrates with several key subsystems through well-defined interfaces:

Address Space Integration

flowchart TD
AxVM["AxVM"]
AddrSpaceMutex["Mutex<AddrSpace<H::PagingHandler>>"]
AddrSpace["AddrSpace"]
PageTableRoot["page_table_root()"]
HandlePageFault["handle_page_fault()"]
TranslateBuffer["translated_byte_buffer()"]
EPTRoot["ept_root() for vCPU setup"]
ImageLoad["get_image_load_region()"]

AddrSpace --> HandlePageFault
AddrSpace --> PageTableRoot
AddrSpace --> TranslateBuffer
AddrSpaceMutex --> AddrSpace
AxVM --> AddrSpaceMutex
PageTableRoot --> EPTRoot
TranslateBuffer --> ImageLoad

Device Integration

flowchart TD
AxVM["AxVM"]
AxVmDevices["AxVmDevices"]
MmioRead["handle_mmio_read()"]
MmioWrite["handle_mmio_write()"]
DeviceConfig["AxVmDeviceConfig"]
EmuConfigs["emu_configs: Vec"]

AxVM --> AxVmDevices
AxVmDevices --> DeviceConfig
AxVmDevices --> MmioRead
AxVmDevices --> MmioWrite
DeviceConfig --> EmuConfigs

vCPU Management

The VM maintains references to vCPUs through Arc<VCpu<AxArchVCpuImpl<U>>> allowing shared ownership across threads while providing architecture-independent access through the AxArchVCpu trait.

Sources: src/vm.rs(L21 - L26)  src/vm.rs(L242 - L250)  src/vm.rs(L260 - L270)  src/vm.rs(L315 - L318) 

Type System and Generics

The architecture leverages Rust's type system to provide compile-time guarantees about hardware compatibility and vCPU implementations:

  • H: AxVMHal: Hardware abstraction layer interface
  • U: AxVCpuHal: vCPU hardware abstraction layer interface
  • Type Aliases: AxVMRef<H, U> and AxVCpuRef<U> provide ergonomic reference types

This design enables architecture-specific optimizations while maintaining a unified API across different hardware platforms.

Sources: src/vm.rs(L21 - L29)  src/lib.rs(L21 - L27) 

Virtual Machine Implementation

Relevant source files

This document covers the core virtual machine implementation in AxVM, focusing on the AxVM struct and its lifecycle management. This includes VM creation, booting, vCPU execution coordination, memory management, and device emulation integration. For details about the underlying vCPU architecture abstraction, see Virtual CPU Architecture. For hardware abstraction interfaces, see Hardware Abstraction Layer.

AxVM Structure Overview

The AxVM struct serves as the central coordinator for all virtual machine operations. It uses a generic design with two type parameters to support multiple architectures and hardware abstraction layers.

classDiagram
class AxVM {
    +running: AtomicBool
    +shutting_down: AtomicBool
    +inner_const: AxVMInnerConst
    +inner_mut: AxVMInnerMut
    +new(config: AxVMConfig) AxResult~AxVMRef~
    +boot() AxResult
    +shutdown() AxResult
    +run_vcpu(vcpu_id: usize) AxResult~AxVCpuExitReason~
    +vcpu(vcpu_id: usize) Option~AxVCpuRef~
    +get_devices() &AxVmDevices
}

class AxVMInnerConst {
    +id: usize
    +config: AxVMConfig
    +vcpu_list: Box[AxVCpuRef]
    +devices: AxVmDevices
    
}

class AxVMInnerMut {
    +address_space: Mutex~AddrSpace~
    +_marker: PhantomData
    
}

AxVM  *--  AxVMInnerConst : "contains"
AxVM  *--  AxVMInnerMut : "contains"

Sources: src/vm.rs(L47 - L53)  src/vm.rs(L31 - L40)  src/vm.rs(L41 - L45) 

The structure separates immutable data (AxVMInnerConst) from mutable data (AxVMInnerMut) to optimize concurrent access patterns. The type aliases provide convenient references for shared ownership.

Type AliasDefinitionPurpose
VCpuAxVCpu<AxArchVCpuImpl>Architecture-independent vCPU interface
AxVCpuRefArc<VCpu>Shared reference to a vCPU
AxVMRef<H, U>Arc<AxVM<H, U>>Shared reference to a VM

Sources: src/vm.rs(L21 - L29) 

VM Creation Process

VM creation follows a multi-stage initialization process that sets up all necessary components before the VM can be booted.

sequenceDiagram
    participant Client as Client
    participant AxVMnew as AxVM::new
    participant VCpuCreation as VCpu Creation
    participant AddressSpace as Address Space
    participant DeviceSetup as Device Setup
    participant VCpuSetup as VCpu Setup

    Client ->> AxVMnew: "AxVMConfig"
    Note over AxVMnew: "Parse vcpu affinities"
    AxVMnew ->> VCpuCreation: "for each vcpu_id, phys_cpu_set"
    VCpuCreation ->> VCpuCreation: "AxVCpuCreateConfig (arch-specific)"
    VCpuCreation ->> AxVMnew: "Arc<VCpu>"
    Note over AxVMnew: "Setup memory regions"
    AxVMnew ->> AddressSpace: "AddrSpace::new_empty()"
    AxVMnew ->> AddressSpace: "map_linear() / map_alloc()"
    Note over AxVMnew: "Setup passthrough devices"
    AxVMnew ->> AddressSpace: "map_linear() with DEVICE flags"
    AxVMnew ->> DeviceSetup: "AxVmDevices::new()"
    Note over AxVMnew: "Create AxVM instance"
    AxVMnew ->> VCpuSetup: "vcpu.setup(entry, ept_root, config)"
    VCpuSetup ->> AxVMnew: "Result"
    AxVMnew ->> Client: "Arc<AxVM>"

Sources: src/vm.rs(L59 - L220) 

vCPU Creation and Configuration

The VM creates vCPUs based on the configuration's CPU affinity settings. Each architecture requires specific configuration parameters:

ArchitectureConfiguration FieldsSource Location
AArch64mpidr_el1,dtb_addrsrc/vm.rs67-75
RISC-Vhart_id,dtb_addrsrc/vm.rs76-83
x86_64Default (empty)src/vm.rs84-85

Sources: src/vm.rs(L61 - L93) 

Memory Region Setup

The VM supports two mapping types for memory regions:

  • MapIentical: Direct mapping where guest physical addresses map to identical host physical addresses
  • MapAlloc: Dynamic allocation where the hypervisor allocates physical memory for the guest
flowchart TD
config["Memory Region Config"]
check_flags["Check MappingFlags validity"]
map_type["Mapping Type?"]
alloc_at["H::alloc_memory_region_at()"]
map_linear1["address_space.map_linear()"]
map_alloc["address_space.map_alloc()"]
complete["Memory region mapped"]

alloc_at --> map_linear1
check_flags --> map_type
config --> check_flags
map_alloc --> complete
map_linear1 --> complete
map_type --> alloc_at
map_type --> map_alloc

Sources: src/vm.rs(L95 - L163) 

For passthrough devices, the VM creates device mappings with the DEVICE flag to ensure proper memory attributes for hardware access.

Sources: src/vm.rs(L165 - L180) 

VM Lifecycle Management

The VM follows a simple state machine with atomic boolean flags for coordination between multiple threads.


Sources: src/vm.rs(L49 - L50)  src/vm.rs(L278 - L310) 

Boot Process

The boot() method performs hardware support validation before allowing VM execution:

  1. Check hardware virtualization support via has_hardware_support()
  2. Verify VM is not already running
  3. Set running flag to true atomically

Sources: src/vm.rs(L278 - L288) 

Shutdown Process

The shutdown() method initiates graceful VM termination:

  1. Check VM is not already shutting down
  2. Set shutting_down flag to true
  3. Note: VM re-initialization is not currently supported

Sources: src/vm.rs(L299 - L310) 

vCPU Execution and Exit Handling

The run_vcpu() method implements the main VM execution loop, handling various exit reasons from the hardware virtualization layer.

flowchart TD
start["run_vcpu(vcpu_id)"]
bind["vcpu.bind()"]
run_loop["vcpu.run()"]
check_exit["Exit Reason?"]
mmio_read["devices.handle_mmio_read()"]
set_reg["vcpu.set_gpr(reg, val)"]
continue_loop["continue_loop"]
mmio_write["devices.handle_mmio_write()"]
io_handled["Handle I/O (placeholder)"]
page_fault["address_space.handle_page_fault()"]
fault_handled["Handled?"]
exit_loop["Break loop"]
unbind["vcpu.unbind()"]
return_exit["Return AxVCpuExitReason"]

bind --> run_loop
check_exit --> exit_loop
check_exit --> io_handled
check_exit --> mmio_read
check_exit --> mmio_write
check_exit --> page_fault
continue_loop --> run_loop
exit_loop --> unbind
fault_handled --> continue_loop
fault_handled --> exit_loop
io_handled --> continue_loop
mmio_read --> set_reg
mmio_write --> continue_loop
page_fault --> fault_handled
run_loop --> check_exit
set_reg --> continue_loop
start --> bind
unbind --> return_exit

Sources: src/vm.rs(L328 - L376) 

Exit Reason Handling

The VM handles several categories of VM exits:

Exit TypeHandlerAction
MmioReaddevices.handle_mmio_read()Read from emulated device, set guest register
MmioWritedevices.handle_mmio_write()Write to emulated device
IoRead/IoWritePlaceholderCurrently returns true (no-op)
NestedPageFaultaddress_space.handle_page_fault()Handle EPT/NPT page fault

Sources: src/vm.rs(L338 - L368) 

Address Space and Memory Management

The VM uses a two-stage address translation system with an AddrSpace that manages guest-to-host physical address mappings.

Address Space Configuration

The VM address space uses fixed bounds to limit guest memory access:

ConstantValuePurpose
VM_ASPACE_BASE0x0Base guest physical address
VM_ASPACE_SIZE0x7fff_ffff_f000Maximum guest address space size

Sources: src/vm.rs(L18 - L19) 

EPT Root Access

The ept_root() method provides access to the page table root for hardware virtualization:

#![allow(unused)]
fn main() {
pub fn ept_root(&self) -> HostPhysAddr {
    self.inner_mut.address_space.lock().page_table_root()
}
}

Sources: src/vm.rs(L248 - L250) 

Image Loading Support

The VM provides functionality to obtain host virtual addresses for loading guest images, handling potentially non-contiguous physical memory:

Sources: src/vm.rs(L260 - L270) 

Device Integration

The VM integrates with the AxVM device emulation framework through the AxVmDevices component, which handles MMIO operations for emulated devices.

Device initialization occurs during VM creation, using the configuration's emulated device list:

Sources: src/vm.rs(L182 - L184) 

Device access is coordinated through the exit handling mechanism, where MMIO reads and writes are forwarded to the appropriate device handlers.

Sources: src/vm.rs(L345 - L355) 

Error Handling and Safety

The VM implementation uses Rust's type system and AxResult for comprehensive error handling:

  • Hardware Support: Validates virtualization capabilities before boot
  • State Validation: Prevents invalid state transitions (e.g., double boot)
  • Resource Management: Uses Arc for safe sharing across threads
  • Memory Safety: Leverages Rust's ownership system for memory region management

Sources: src/vm.rs(L7)  src/vm.rs(L279 - L287)  src/vm.rs(L300 - L310) 

Virtual CPU Architecture

Relevant source files

Purpose and Scope

The Virtual CPU Architecture in axvm provides a unified abstraction layer for virtual CPU management across multiple hardware architectures. This system enables axvm to support x86_64, RISC-V, and AArch64 platforms through architecture-specific backends while maintaining a consistent interface for the core hypervisor components.

This document covers the multi-architecture vCPU abstraction, architecture-specific implementations, configuration mechanisms, and hardware support detection. For information about the broader VM lifecycle and management, see Virtual Machine Implementation. For hardware abstraction interfaces beyond vCPUs, see Hardware Abstraction Layer.

Multi-Architecture Abstraction Layer

The vCPU architecture uses compile-time conditional compilation to select the appropriate backend for each target architecture. This design provides zero-cost abstraction while maintaining type safety across different hardware platforms.

Architecture Selection and Type Aliases

The core abstraction is implemented through type aliases that map to architecture-specific implementations based on the compilation target:

flowchart TD
subgraph subGraph3["AArch64 Backend"]
    Aarch64VCpu["Aarch64VCpu"]
    Aarch64PerCpu["Aarch64PerCpu"]
    Aarch64VCpuCreateConfig["Aarch64VCpuCreateConfig"]
    Aarch64HWSupport["ARM EL2 Support Check"]
end
subgraph subGraph2["RISC-V Backend"]
    RISCVVCpu["RISCVVCpu"]
    RISCVPerCpu["RISCVPerCpu"]
    RISCVVCpuCreateConfig["RISCVVCpuCreateConfig"]
    RISCVHWSupport["RISC-V H-Extension Check"]
end
subgraph subGraph1["x86_64 Backend"]
    VmxArchVCpu["VmxArchVCpu"]
    VmxArchPerCpuState["VmxArchPerCpuState"]
    UnitConfig["() Unit Type"]
    VmxHWSupport["VMX Hardware Check"]
end
subgraph subGraph0["Common Interface Types"]
    AxArchVCpuImpl["AxArchVCpuImplMain vCPU Interface"]
    AxVMArchPerCpuImpl["AxVMArchPerCpuImplPer-CPU State"]
    AxVCpuCreateConfig["AxVCpuCreateConfigCreation Configuration"]
    HasHWSupport["has_hardware_supportHardware Detection"]
end

AxArchVCpuImpl --> Aarch64VCpu
AxArchVCpuImpl --> RISCVVCpu
AxArchVCpuImpl --> VmxArchVCpu
AxVCpuCreateConfig --> Aarch64VCpuCreateConfig
AxVCpuCreateConfig --> RISCVVCpuCreateConfig
AxVCpuCreateConfig --> UnitConfig
AxVMArchPerCpuImpl --> Aarch64PerCpu
AxVMArchPerCpuImpl --> RISCVPerCpu
AxVMArchPerCpuImpl --> VmxArchPerCpuState
HasHWSupport --> Aarch64HWSupport
HasHWSupport --> RISCVHWSupport
HasHWSupport --> VmxHWSupport

Sources: src/vcpu.rs(L3 - L27) 

Architecture-Specific Implementations

Each supported architecture provides its own implementation of the vCPU interface, tailored to the specific virtualization capabilities and requirements of that platform.

x86_64 Implementation

The x86_64 backend uses Intel VT-x and AMD-V virtualization extensions through the x86_vcpu crate. This implementation provides VMX-based virtualization with EPT (Extended Page Tables) support.

ComponentTypePurpose
AxArchVCpuImplVmxArchVCpuMain vCPU implementation using VMX
AxVMArchPerCpuImplVmxArchPerCpuStatePer-CPU VMX state management
AxVCpuCreateConfig()No additional configuration needed
Hardware Detectionhas_hardware_supportVMX capability detection

Sources: src/vcpu.rs(L4 - L8) 

RISC-V Implementation

The RISC-V backend leverages the RISC-V hypervisor extension (H-extension) through the riscv_vcpu crate, providing virtualization support for RISC-V processors.

ComponentTypePurpose
AxArchVCpuImplRISCVVCpuH-extension based vCPU
AxVMArchPerCpuImplRISCVPerCpuRISC-V per-CPU state
AxVCpuCreateConfigRISCVVCpuCreateConfigRISC-V specific configuration
Hardware Detectionhas_hardware_supportH-extension capability check

Sources: src/vcpu.rs(L16 - L20) 

AArch64 Implementation

The AArch64 backend utilizes ARM virtualization extensions (EL2) through the arm_vcpu crate, enabling hypervisor functionality on ARM processors.

ComponentTypePurpose
AxArchVCpuImplAarch64VCpuARM EL2 based vCPU
AxVMArchPerCpuImplAarch64PerCpuARM per-CPU state management
AxVCpuCreateConfigAarch64VCpuCreateConfigARM specific configuration
Hardware Detectionhas_hardware_supportARM virtualization support check

Sources: src/vcpu.rs(L21 - L26) 

vCPU Lifecycle and Integration

The vCPU architecture integrates with the broader axvm system through well-defined interfaces that handle creation, configuration, and execution of virtual CPUs.

vCPU Creation and Configuration Flow

sequenceDiagram
    participant AxVM as "AxVM"
    participant AxVCpuCreateConfig as "AxVCpuCreateConfig"
    participant has_hardware_support as "has_hardware_support"
    participant AxArchVCpuImpl as "AxArchVCpuImpl"
    participant AxVMArchPerCpuImpl as "AxVMArchPerCpuImpl"

    AxVM ->> has_hardware_support: "Check virtualization support"
    has_hardware_support -->> AxVM: "Hardware capability result"
    alt Hardware Supported
        AxVM ->> AxVCpuCreateConfig: "Create vCPU configuration"
        AxVM ->> AxArchVCpuImpl: "new(config)"
        AxArchVCpuImpl ->> AxVMArchPerCpuImpl: "Initialize per-CPU state"
        AxVMArchPerCpuImpl -->> AxArchVCpuImpl: "Per-CPU state ready"
        AxArchVCpuImpl -->> AxVM: "vCPU instance created"
    else Hardware Not Supported
        AxVM ->> AxVM: "Return error or fallback"
    end

Sources: src/vcpu.rs(L1 - L27) 

Hardware Support Detection

Each architecture backend provides a has_hardware_support function that performs runtime detection of virtualization capabilities. This enables axvm to gracefully handle systems that may not have the necessary hardware extensions.

The hardware support detection is critical for:

  • Determining if the host system can run the hypervisor
  • Selecting appropriate virtualization features
  • Providing meaningful error messages when virtualization is unavailable

Sources: src/vcpu.rs(L7 - L25) 

Physical Frame Interface Requirements

The x86_64 implementation has a unique requirement where the PhysFrameIf trait must be implemented by the application layer. This design decision aligns with axvm's architecture principle of separating hypervisor functionality from OS resource management.

The PhysFrameIf implementation is handled by the vmm_app component rather than within axvm itself, allowing for flexible memory management strategies while maintaining clear separation of concerns.

Sources: src/vcpu.rs(L10 - L15) 

Integration with VM Core

The vCPU architecture seamlessly integrates with the main AxVM implementation through the standardized interfaces, enabling the VM to manage virtual CPUs without needing architecture-specific knowledge. This abstraction allows the core VM logic to remain architecture-agnostic while leveraging the full capabilities of each supported platform.

Sources: src/vcpu.rs(L1 - L27) 

Hardware Abstraction Layer

Relevant source files

The Hardware Abstraction Layer (HAL) provides a standardized interface for platform-specific operations that the axvm hypervisor requires from the underlying host system. This abstraction enables axvm to run on different host operating systems and hypervisors by delegating low-level hardware operations to implementations of the AxVMHal trait.

For information about virtual machine lifecycle management, see Virtual Machine Implementation. For details about multi-architecture CPU support, see Virtual CPU Architecture.

AxVMHal Trait Interface

The core of the hardware abstraction layer is the AxVMHal trait, which defines the contract between axvm and the underlying host system. This trait must be implemented by any host system that wants to run axvm virtual machines.

AxVMHal Trait Structure

classDiagram
class AxVMHal {
    <<trait>>
    +PagingHandler: page_table_multiarch::PagingHandler
    +alloc_memory_region_at(base: HostPhysAddr, size: usize) bool
    +dealloc_memory_region_at(base: HostPhysAddr, size: usize)
    +virt_to_phys(vaddr: HostVirtAddr) HostPhysAddr
    +current_time_nanos() u64
}

class PagingHandler {
    <<interface>>
    +page_table_multiarch::PagingHandler
    
}

class HostPhysAddr {
    +axaddrspace::HostPhysAddr
    
}

class HostVirtAddr {
    +axaddrspace::HostVirtAddr
    
}

AxVMHal  -->  PagingHandler : "associated type"
AxVMHal  -->  HostPhysAddr : "uses"
AxVMHal  -->  HostVirtAddr : "uses"

Sources: src/hal.rs(L1 - L22) 

Memory Management Abstraction

The HAL provides two primary memory management operations that abstract the underlying host's memory allocation mechanisms:

MethodPurposeReturn Value
alloc_memory_region_atAllocates memory at a specific physical addressboolindicating success
dealloc_memory_region_atDeallocates a previously allocated memory region()

These methods enable axvm to request specific physical memory regions from the host, which is essential for guest physical memory layout and device memory mapping.

Memory Management Flow

sequenceDiagram
    participant AxVM as "AxVM"
    participant AxVMHalImplementation as "AxVMHal Implementation"
    participant HostSystem as "Host System"

    AxVM ->> AxVMHalImplementation: "alloc_memory_region_at(base, size)"
    AxVMHalImplementation ->> HostSystem: "Request physical memory allocation"
    HostSystem -->> AxVMHalImplementation: "Success/failure"
    AxVMHalImplementation -->> AxVM: "bool result"
    Note over AxVM,HostSystem: "VM operation continues..."
    AxVM ->> AxVMHalImplementation: "dealloc_memory_region_at(base, size)"
    AxVMHalImplementation ->> HostSystem: "Release physical memory"
    HostSystem -->> AxVMHalImplementation: "Memory freed"
    AxVMHalImplementation -->> AxVM: "() completion"

Sources: src/hal.rs(L8 - L14) 

Address Translation Interface

The virt_to_phys method provides virtual-to-physical address translation, enabling axvm to convert host virtual addresses to their corresponding physical addresses. This is crucial for setting up guest physical memory mappings and ensuring proper memory coherency.

Address Translation Architecture

flowchart TD
subgraph subGraph2["VM Memory Management"]
    GuestPhys["Guest Physical Memory"]
    HostMapping["Host Memory Mapping"]
end
subgraph subGraph1["Page Table Integration"]
    PagingHandler["PagingHandler(page_table_multiarch)"]
    PageTable["Multi-arch Page Tables"]
end
subgraph subGraph0["Address Translation Layer"]
    VirtAddr["HostVirtAddr(axaddrspace)"]
    VirtToPhys["virt_to_phys()"]
    PhysAddr["HostPhysAddr(axaddrspace)"]
end

PagingHandler --> PageTable
PhysAddr --> GuestPhys
VirtAddr --> HostMapping
VirtAddr --> VirtToPhys
VirtToPhys --> PagingHandler
VirtToPhys --> PhysAddr

Sources: src/hal.rs(L16 - L17)  src/hal.rs(L6) 

Time Management

The HAL provides time services through the current_time_nanos method, which returns the current time in nanoseconds. This enables axvm to implement time-based features such as:

  • Performance monitoring and profiling
  • Timeout mechanisms for VM operations
  • Guest time synchronization
  • Event scheduling and timing

Sources: src/hal.rs(L19 - L20) 

Integration with ArceOS Ecosystem

The HAL interfaces directly with several components from the ArceOS ecosystem:

HAL Ecosystem Integration

flowchart TD
subgraph subGraph3["Host Implementation"]
    HostOS["Host OS/Hypervisor"]
    MemMgmt["Memory Management"]
    TimeService["Time Services"]
end
subgraph subGraph2["Page Table Infrastructure"]
    PageTableMultiarch["page_table_multiarch"]
    PagingHandlerImpl["PagingHandler impl"]
end
subgraph subGraph1["ArceOS Address Space"]
    HostPhysAddr["HostPhysAddr"]
    HostVirtAddr["HostVirtAddr"]
    AddrSpace["axaddrspace"]
end
subgraph subGraph0["AxVM HAL Layer"]
    AxVMHal["AxVMHal trait"]
    PagingType["PagingHandler type"]
end

AddrSpace --> HostPhysAddr
AddrSpace --> HostVirtAddr
AxVMHal --> HostOS
AxVMHal --> HostPhysAddr
AxVMHal --> HostVirtAddr
MemMgmt --> AxVMHal
PagingHandlerImpl --> PagingType
PagingType --> PageTableMultiarch
TimeService --> AxVMHal

The HAL serves as the critical bridge between axvm's hardware-agnostic virtualization logic and the host system's specific capabilities, enabling portability across different host environments while maintaining performance and functionality.

Sources: src/hal.rs(L1 - L6) 

Configuration System

Relevant source files

This document covers the AxVM hypervisor's configuration system, which manages VM setup parameters including CPU allocation, memory regions, device configurations, and image load addresses. The configuration system transforms TOML configuration files into structured data that drives VM creation and initialization.

For information about the VM creation process that uses these configurations, see Virtual Machine Implementation. For details about hardware abstraction interfaces, see Hardware Abstraction Layer.

Configuration Flow Overview

The configuration system follows a two-stage transformation process: external TOML configuration files are first parsed into raw configuration structures, then converted to internal VM configuration objects used during VM creation.

flowchart TD
subgraph subGraph1["Processed Configuration (axvm)"]
    VMConfig["AxVMConfig"]
    CPUConfig["cpu_config: AxVCpuConfig"]
    ImageConfig["image_config: VMImageConfig"]
    MemConfig["memory_regions: Vec"]
    EmuDevices["emu_devices: Vec"]
    PassDevices["pass_through_devices: Vec"]
end
subgraph subGraph0["Raw Configuration (axvmconfig crate)"]
    CrateConfig["AxVMCrateConfig"]
    BaseConfig["base: VM metadata"]
    KernelConfig["kernel: Image addresses"]
    DeviceConfig["devices: Device setup"]
end
TOML["TOML Configuration File"]
VMCreation["AxVM::new(config)"]
AddressSpace["Address Space Setup"]
VCPUSetup["vCPU Creation"]
DeviceSetup["Device Initialization"]

CrateConfig --> BaseConfig
CrateConfig --> DeviceConfig
CrateConfig --> KernelConfig
CrateConfig --> VMConfig
TOML --> CrateConfig
VMConfig --> CPUConfig
VMConfig --> EmuDevices
VMConfig --> ImageConfig
VMConfig --> MemConfig
VMConfig --> PassDevices
VMConfig --> VMCreation
VMCreation --> AddressSpace
VMCreation --> DeviceSetup
VMCreation --> VCPUSetup

Sources: src/config.rs(L1 - L12)  src/config.rs(L63 - L87) 

Configuration Structure Hierarchy

The configuration system defines several interconnected structures that represent different aspects of VM setup. The main configuration object AxVMConfig aggregates specialized configuration components.

classDiagram
class AxVMConfig {
    -id: usize
    -name: String
    -vm_type: VMType
    -cpu_num: usize
    -phys_cpu_ids: Option~Vec~usize~~
    -phys_cpu_sets: Option~Vec~usize~~
    -cpu_config: AxVCpuConfig
    -image_config: VMImageConfig
    -memory_regions: Vec~VmMemConfig~
    -emu_devices: Vec~EmulatedDeviceConfig~
    -pass_through_devices: Vec~PassThroughDeviceConfig~
    +id() usize
    +name() String
    +get_vcpu_affinities_pcpu_ids() Vec~(usize, Option~usize~, usize) ~
    +bsp_entry() GuestPhysAddr
    +ap_entry() GuestPhysAddr
    +memory_regions() Vec~VmMemConfig~
    +emu_devices() Vec~EmulatedDeviceConfig~
    +pass_through_devices() Vec~PassThroughDeviceConfig~
}

class AxVCpuConfig {
    +bsp_entry: GuestPhysAddr
    +ap_entry: GuestPhysAddr
    
}

class VMImageConfig {
    +kernel_load_gpa: GuestPhysAddr
    +bios_load_gpa: Option~GuestPhysAddr~
    +dtb_load_gpa: Option~GuestPhysAddr~
    +ramdisk_load_gpa: Option~GuestPhysAddr~
    
}

class AxVMCrateConfig {
    +base: BaseConfig
    +kernel: KernelConfig
    +devices: DeviceConfig
    
}

AxVMConfig  *--  AxVCpuConfig
AxVMConfig  *--  VMImageConfig
AxVMConfig  ..>  AxVMCrateConfig : "From trait"

Sources: src/config.rs(L24 - L31)  src/config.rs(L34 - L44)  src/config.rs(L47 - L61) 

Configuration Transformation Process

The From<AxVMCrateConfig> trait implementation handles the conversion from raw TOML-derived configuration to the internal VM configuration format. This transformation maps external configuration fields to internal structures and converts address types to GuestPhysAddr.

Key Transformation Steps

Source FieldTarget FieldTransformation
cfg.base.ididDirect assignment
cfg.base.namenameDirect assignment
cfg.base.cpu_numcpu_numDirect assignment
cfg.kernel.entry_pointcpu_config.bsp_entryGuestPhysAddr::from()
cfg.kernel.kernel_load_addrimage_config.kernel_load_gpaGuestPhysAddr::from()
cfg.kernel.bios_load_addrimage_config.bios_load_gpaOption::map(GuestPhysAddr::from)
cfg.devices.emu_devicesemu_devicesDirect assignment

Sources: src/config.rs(L63 - L87) 

CPU Configuration

The CPU configuration system manages virtual CPU allocation, entry points, and physical CPU affinity mapping. The AxVCpuConfig structure defines entry addresses for both Bootstrap Processor (BSP) and Application Processor (AP) initialization.

vCPU Affinity Management

The get_vcpu_affinities_pcpu_ids method returns CPU mapping information as tuples containing:

  • vCPU ID (0 to cpu_num-1)
  • Physical CPU affinity mask (optional bitmap)
  • Physical CPU ID (defaults to vCPU ID if not specified)
flowchart TD
subgraph subGraph0["CPU Configuration"]
    CPUNum["cpu_num: usize"]
    VCPUIDs["vCPU IDs: 0..cpu_num"]
    PhysCPUIDs["phys_cpu_ids: Option>"]
    PhysIDs["Physical IDs mapping"]
    PhysCPUSets["phys_cpu_sets: Option>"]
    AffinityMask["Affinity bitmasks"]
    TupleGen["get_vcpu_affinities_pcpu_ids()"]
    Result["Vec<(vcpu_id, affinity_mask, phys_id)>"]
end

AffinityMask --> TupleGen
CPUNum --> VCPUIDs
PhysCPUIDs --> PhysIDs
PhysCPUSets --> AffinityMask
PhysIDs --> TupleGen
TupleGen --> Result
VCPUIDs --> TupleGen

Sources: src/config.rs(L24 - L31)  src/config.rs(L108 - L124) 

Memory Configuration

Memory configuration defines the guest physical memory layout through a vector of VmMemConfig structures. Each memory region specifies mapping type, guest physical address ranges, and access permissions.

The VMImageConfig structure manages load addresses for different VM image components:

Image ComponentConfiguration FieldPurpose
Kernelkernel_load_gpaMain kernel image load address
BIOSbios_load_gpaOptional BIOS/firmware load address
Device Treedtb_load_gpaOptional device tree blob address
Ramdiskramdisk_load_gpaOptional initial ramdisk address

Sources: src/config.rs(L34 - L44)  src/config.rs(L144 - L146) 

Device Configuration

The device configuration system supports two types of devices: emulated devices and passthrough devices. Device configurations are stored in separate vectors and accessed through dedicated getter methods.

flowchart TD
subgraph subGraph1["External Types (axvmconfig)"]
    EmulatedDeviceConfig["EmulatedDeviceConfig"]
    PassThroughDeviceConfig["PassThroughDeviceConfig"]
end
subgraph subGraph0["Device Configuration"]
    DeviceConfig["devices section (TOML)"]
    EmuDevices["emu_devices: Vec"]
    PassDevices["passthrough_devices: Vec"]
    EmuGetter["emu_devices() getter"]
    PassGetter["pass_through_devices() getter"]
    VMSetup["VM Device Setup"]
end

DeviceConfig --> EmuDevices
DeviceConfig --> PassDevices
EmuDevices --> EmuGetter
EmuDevices --> EmulatedDeviceConfig
EmuGetter --> VMSetup
PassDevices --> PassGetter
PassDevices --> PassThroughDeviceConfig
PassGetter --> VMSetup

Sources: src/config.rs(L9 - L12)  src/config.rs(L149 - L156) 

Configuration Usage in VM Creation

The AxVMConfig object serves as the primary input to VM creation, providing all necessary parameters for initializing VM components. The configuration system exposes specific getter methods that VM creation logic uses to access different configuration aspects.

Configuration Access Methods

The configuration provides specialized accessor methods for different VM subsystems:

  • bsp_entry() / ap_entry(): CPU entry points for processor initialization
  • image_config(): Image load addresses for kernel and auxiliary components
  • memory_regions(): Memory layout and mapping configuration
  • emu_devices() / pass_through_devices(): Device configuration for emulation and passthrough
  • get_vcpu_affinities_pcpu_ids(): CPU affinity and physical mapping information

Sources: src/config.rs(L126 - L156) 

Dependencies and Integration

Relevant source files

This document explains how the AxVM hypervisor integrates with the ArceOS ecosystem and manages its external dependencies. It covers the modular architecture that enables multi-platform virtualization support and the specific crate dependencies that provide core functionality.

For information about the core VM implementation details, see Virtual Machine Implementation. For architecture-specific virtualization backends, see Virtual CPU Architecture.

ArceOS Ecosystem Integration

AxVM is designed as a core component within the ArceOS hypervisor ecosystem, leveraging specialized crates for different aspects of virtualization. The integration follows a modular design where each major subsystem is provided by a dedicated crate.

Core Hypervisor Dependencies

The primary integration occurs through four key ArceOS-Hypervisor modules that provide the foundational virtualization capabilities:

flowchart TD
AXVM["axvmCore Hypervisor"]
AXVCPU["axvcpuVirtual CPU Management"]
AXADDR["axaddrspaceAddress Space Management"]
AXDEV["axdeviceDevice Emulation"]
AXCONF["axvmconfigConfiguration Framework"]
ARCH_VCPU["Architecture-SpecificvCPU Backends"]
PAGETBL["page_table_multiarchMemory Translation"]
MMIO["MMIO DeviceImplementations"]
TOML["TOML ConfigurationParsing"]

AXADDR --> PAGETBL
AXCONF --> TOML
AXDEV --> MMIO
AXVCPU --> ARCH_VCPU
AXVM --> AXADDR
AXVM --> AXCONF
AXVM --> AXDEV
AXVM --> AXVCPU

Sources: Cargo.toml(L24 - L27) 

Each ArceOS-Hypervisor module serves a specific role in the virtualization stack:

CrateRepositoryPurpose
axvcpugithub.com/arceos-hypervisor/axvcpu.gitProvides architecture-agnostic vCPU abstraction and management
axaddrspacegithub.com/arceos-hypervisor/axaddrspace.gitHandles guest address space management and memory translation
axdevicegithub.com/arceos-hypervisor/axdevice.gitImplements device emulation and MMIO handling
axvmconfiggithub.com/arceos-hypervisor/axvmconfig.gitProvides VM configuration parsing and validation

Sources: Cargo.toml(L24 - L27) 

Architecture-Specific Dependencies

AxVM supports multiple hardware architectures through conditional compilation and architecture-specific vCPU backends. This design allows the same codebase to provide native virtualization support across different platforms.

Multi-Architecture Dependency Resolution

flowchart TD
subgraph subGraph1["Architecture Backends"]
    X86_VCPU["x86_vcpuIntel VT-x/AMD-V"]
    RISCV_VCPU["riscv_vcpuRISC-V H-Extension"]
    ARM_VCPU["arm_vcpuARM EL2"]
end
subgraph subGraph0["Target Architecture Selection"]
    X86["target_arch = x86_64"]
    RISCV["target_arch = riscv64"]
    ARM["target_arch = aarch64"]
end
CONFIG["cfg-ifConditional Compilation"]
VTX["VT-x/SVMHardware Features"]
HEXT["HypervisorExtension"]
EL2["Exception Level 2Virtualization"]

ARM --> ARM_VCPU
ARM_VCPU --> EL2
CONFIG --> ARM
CONFIG --> RISCV
CONFIG --> X86
RISCV --> RISCV_VCPU
RISCV_VCPU --> HEXT
X86 --> X86_VCPU
X86_VCPU --> VTX

Sources: Cargo.toml(L29 - L36)  Cargo.toml(L12) 

The architecture-specific dependencies are resolved at compile time:

Target ArchitectureCrateHardware Support
x86_64x86_vcpuIntel VT-x, AMD-V
riscv64riscv_vcpuRISC-V Hypervisor Extension
aarch64arm_vcpuARM Virtualization Extensions (EL2)

Sources: Cargo.toml(L29 - L36) 

System Infrastructure Dependencies

AxVM relies on several ArceOS system-independent crates that provide low-level infrastructure for hypervisor operations.

Memory Management Infrastructure

flowchart TD
MEMORY_MGMT["Memory ManagementInfrastructure"]
MEMORY_ADDR["memory_addrAddress Abstractions"]
PAGE_ENTRY["page_table_entryPage Table Entries"]
PAGE_MULTI["page_table_multiarchMulti-arch Page Tables"]
PERCPU["percpuPer-CPU Data Storage"]
PHYS_ADDR["PhysAddrGuestPhysAddr"]
VIRT_ADDR["VirtAddrGuestVirtAddr"]
ARM_EL2["ARM EL2Page Entries"]
EPT["Extended Page Tables"]
NPT["Nested Page Tables"]
CPU_STATE["Per-CPU VM State"]
VCPU_DATA["vCPU Local Data"]

MEMORY_ADDR --> PHYS_ADDR
MEMORY_ADDR --> VIRT_ADDR
MEMORY_MGMT --> MEMORY_ADDR
MEMORY_MGMT --> PAGE_ENTRY
MEMORY_MGMT --> PAGE_MULTI
MEMORY_MGMT --> PERCPU
PAGE_ENTRY --> ARM_EL2
PAGE_MULTI --> EPT
PAGE_MULTI --> NPT
PERCPU --> CPU_STATE
PERCPU --> VCPU_DATA

Sources: Cargo.toml(L18 - L21) 

Memory Management Crate Details

CrateVersionFeaturesPurpose
memory_addr0.3.1-ProvidesPhysAddr,VirtAddr, and guest address types
page_table_entry0.5arm-el2ARM EL2-specific page table entry implementations
page_table_multiarch0.5-Cross-architecture page table management
percpu0.2.0arm-el2Per-CPU data storage with ARM EL2 support

Sources: Cargo.toml(L18 - L21) 

Utility and System Dependencies

AxVM uses several external utility crates for core system functionality including error handling, logging, and synchronization.

External Utility Integration

flowchart TD
UTILITIES["System Utilities"]
LOG["log v0.4Logging Framework"]
SPIN["spin v0.9Synchronization"]
CFGIF["cfg-if v1.0Conditional Compilation"]
AXERRNO["axerrno v0.1.0Error Handling"]
LOG_IMPL["VM Event LoggingDebug Information"]
LOCKS["SpinlocksMutex Primitives"]
ARCH_SELECT["Architecture SelectionFeature Gates"]
ERROR_CODES["Hypervisor Error CodesResult Types"]

AXERRNO --> ERROR_CODES
CFGIF --> ARCH_SELECT
LOG --> LOG_IMPL
SPIN --> LOCKS
UTILITIES --> AXERRNO
UTILITIES --> CFGIF
UTILITIES --> LOG
UTILITIES --> SPIN

Sources: Cargo.toml(L11 - L16) 

Utility Dependency Functions

CrateVersionUsage in AxVM
log0.4VM event logging, debug output, error reporting
spin0.9Spinlock-based synchronization for VM state
cfg-if1.0Conditional compilation for architecture support
axerrno0.1.0ArceOS-compatible error handling and result types

Sources: Cargo.toml(L11 - L16) 

Feature Configuration

AxVM uses Cargo features to enable optional functionality and architecture-specific optimizations.

Default Feature Set

The hypervisor defines a minimal set of default features focused on x86_64 virtualization:

[features]
default = ["vmx"]
vmx = []

Sources: Cargo.toml(L6 - L8) 

The vmx feature enables Intel VT-x and AMD-V support on x86_64 platforms, representing the most common virtualization use case.

Integration Patterns

AxVM follows specific integration patterns that enable clean separation of concerns while maintaining performance and flexibility.

Repository Integration Model

flowchart TD
GITHUB["GitHub Organizationarceos-hypervisor"]
AXVM_REPO["axvmCore Hypervisor"]
AXVCPU_REPO["axvcpuvCPU Abstraction"]
AXADDR_REPO["axaddrspaceMemory Management"]
AXDEV_REPO["axdeviceDevice Emulation"]
AXCONF_REPO["axvmconfigConfiguration"]
X86_REPO["x86_vcpux86 Backend"]
RISCV_REPO["riscv_vcpuRISC-V Backend"]
ARM_REPO["arm_vcpuARM Backend"]

AXVCPU_REPO --> ARM_REPO
AXVCPU_REPO --> RISCV_REPO
AXVCPU_REPO --> X86_REPO
AXVM_REPO --> AXADDR_REPO
AXVM_REPO --> AXCONF_REPO
AXVM_REPO --> AXDEV_REPO
AXVM_REPO --> AXVCPU_REPO
GITHUB --> ARM_REPO
GITHUB --> AXADDR_REPO
GITHUB --> AXCONF_REPO
GITHUB --> AXDEV_REPO
GITHUB --> AXVCPU_REPO
GITHUB --> AXVM_REPO
GITHUB --> RISCV_REPO
GITHUB --> X86_REPO

Sources: Cargo.toml(L24 - L36) 

All ArceOS-Hypervisor components are developed in separate repositories under the arceos-hypervisor GitHub organization, enabling independent development cycles while maintaining API compatibility through semantic versioning.

Development Guide

Relevant source files

This document provides an overview of development practices and guidelines for contributors to the AxVM hypervisor project. It covers the build system, testing procedures, contribution workflow, and project governance aspects needed for effective development and collaboration.

For detailed information about specific development processes, see Build and Testing. For legal requirements and contribution guidelines, see License and Legal.

Development Workflow Overview

The AxVM project follows a standard GitHub-based development workflow with continuous integration and automated testing across multiple architectures. The development process emphasizes code quality, documentation completeness, and cross-platform compatibility.

Development Process Flow

flowchart TD
Developer["Developer"]
Fork["Fork Repository"]
Clone["Clone Fork"]
Branch["Create Feature Branch"]
Code["Write Code"]
LocalTest["Local Testing"]
Commit["Commit Changes"]
Push["Push to Fork"]
PR["Create Pull Request"]
CITrigger["CI Pipeline Triggered"]
FormatCheck["Format Check (cargo fmt)"]
ClippyCheck["Clippy Linting"]
BuildMatrix["Multi-Architecture Build"]
UnitTests["Unit Tests (x86_64 only)"]
DocBuild["Documentation Build"]
CIResult["CI Results"]
CodeReview["Code Review"]
FixIssues["Fix Issues"]
Approved["Approved?"]
Merge["Merge to Main"]
AddressComments["Address Comments"]
DocDeploy["Deploy Documentation"]

AddressComments --> Code
Approved --> AddressComments
Approved --> Merge
Branch --> Code
BuildMatrix --> CIResult
CIResult --> CodeReview
CIResult --> FixIssues
CITrigger --> BuildMatrix
CITrigger --> ClippyCheck
CITrigger --> DocBuild
CITrigger --> FormatCheck
CITrigger --> UnitTests
ClippyCheck --> CIResult
Clone --> Branch
Code --> LocalTest
CodeReview --> Approved
Commit --> Push
Developer --> Fork
DocBuild --> CIResult
FixIssues --> Code
Fork --> Clone
FormatCheck --> CIResult
LocalTest --> Commit
Merge --> DocDeploy
PR --> CITrigger
Push --> PR
UnitTests --> CIResult

Sources: .github/workflows/ci.yml(L1 - L61) 

Build System and Architecture Support

The project uses a comprehensive build matrix to ensure compatibility across multiple Rust toolchains and target architectures. This approach guarantees that AxVM functions correctly on all supported platforms.

Supported Build Targets

ArchitectureTarget TripleUse Case
x86_64x86_64-unknown-linux-gnuDevelopment and testing
x86_64x86_64-unknown-noneBare-metal hypervisor
RISC-Vriscv64gc-unknown-none-elfRISC-V bare-metal
AArch64aarch64-unknown-none-softfloatARM bare-metal

Build Matrix Configuration

flowchart TD
CI["CI Pipeline"]
Matrix["Build Matrix"]
ToolchainA["nightly-2024-12-25"]
ToolchainB["nightly"]
TargetA1["x86_64-unknown-linux-gnu"]
TargetA2["x86_64-unknown-none"]
TargetA3["riscv64gc-unknown-none-elf"]
TargetA4["aarch64-unknown-none-softfloat"]
TargetB1["x86_64-unknown-linux-gnu"]
TargetB2["x86_64-unknown-none"]
TargetB3["riscv64gc-unknown-none-elf"]
TargetB4["aarch64-unknown-none-softfloat"]
BuildStepsA1["Format → Clippy → Build → Test"]
BuildStepsA2["Format → Clippy → Build"]
BuildStepsA3["Format → Clippy → Build"]
BuildStepsA4["Format → Clippy → Build"]
BuildStepsB1["Format → Clippy → Build → Test"]
BuildStepsB2["Format → Clippy → Build"]
BuildStepsB3["Format → Clippy → Build"]
BuildStepsB4["Format → Clippy → Build"]

CI --> Matrix
Matrix --> ToolchainA
Matrix --> ToolchainB
TargetA1 --> BuildStepsA1
TargetA2 --> BuildStepsA2
TargetA3 --> BuildStepsA3
TargetA4 --> BuildStepsA4
TargetB1 --> BuildStepsB1
TargetB2 --> BuildStepsB2
TargetB3 --> BuildStepsB3
TargetB4 --> BuildStepsB4
ToolchainA --> TargetA1
ToolchainA --> TargetA2
ToolchainA --> TargetA3
ToolchainA --> TargetA4
ToolchainB --> TargetB1
ToolchainB --> TargetB2
ToolchainB --> TargetB3
ToolchainB --> TargetB4

Sources: .github/workflows/ci.yml(L8 - L33) 

Continuous Integration Pipeline

The CI system performs comprehensive validation of all changes through automated checks and builds. The pipeline includes code quality enforcement, multi-architecture compilation, and automated documentation generation.

CI Job Structure

flowchart TD
subgraph subGraph2["Doc Job Steps"]
    DocCheckout["Checkout Code"]
    DocRustSetup["Setup Rust Toolchain"]
    DocBuild["cargo doc --no-deps"]
    IndexGen["Generate Index HTML"]
    PagesDeploy["Deploy to GitHub Pages"]
end
subgraph subGraph1["CI Job Steps"]
    Checkout["Checkout Code"]
    RustSetup["Setup Rust Toolchain"]
    VersionCheck["Check Rust Version"]
    FormatCheck["cargo fmt --check"]
    ClippyRun["cargo clippy"]
    BuildRun["cargo build"]
    TestRun["cargo test (x86_64 only)"]
end
subgraph subGraph0["CI Jobs"]
    CIJob["ci job"]
    DocJob["doc job"]
end

BuildRun --> TestRun
CIJob --> Checkout
Checkout --> RustSetup
ClippyRun --> BuildRun
DocBuild --> IndexGen
DocCheckout --> DocRustSetup
DocJob --> DocCheckout
DocRustSetup --> DocBuild
FormatCheck --> ClippyRun
IndexGen --> PagesDeploy
RustSetup --> VersionCheck
VersionCheck --> FormatCheck

Sources: .github/workflows/ci.yml(L6 - L34)  .github/workflows/ci.yml(L35 - L61) 

Code Quality Standards

The project enforces strict code quality standards through automated tooling and manual review processes.

Automated Quality Checks

Quality Enforcement Configuration

ToolConfigurationPurpose
clippy-A clippy::new_without_defaultAllow structs without Default trait
rustdoc-D rustdoc::broken_intra_doc_linksFail on broken documentation links
rustdoc-D missing-docsRequire documentation for public items

Sources: .github/workflows/ci.yml(L26)  .github/workflows/ci.yml(L43) 

Documentation System

AxVM maintains comprehensive documentation that is automatically built and deployed through the CI system. The documentation is generated using Rust's built-in documentation tools and hosted on GitHub Pages.

Documentation Build Process

sequenceDiagram
    participant Developer as "Developer"
    participant PullRequest as "Pull Request"
    participant CISystem as "CI System"
    participant rustdoc as "rustdoc"
    participant GitHubPages as "GitHub Pages"

    Developer ->> PullRequest: Submit Changes
    PullRequest ->> CISystem: Trigger Doc Job
    CISystem ->> rustdoc: cargo doc --no-deps --all-features
    rustdoc ->> CISystem: Generate Documentation
    CISystem ->> CISystem: Create Index Redirect
    Note over CISystem: Generate redirect to main crate
    CISystem ->> GitHubPages: Deploy Documentation
    Note over GitHubPages: Only on main branch

The documentation system generates API documentation for all public interfaces and automatically redirects to the main crate documentation. Documentation deployment only occurs for changes merged to the default branch.

Sources: .github/workflows/ci.yml(L49 - L60) 

Testing Strategy

The project implements a multi-layered testing approach with unit tests executed in the CI pipeline. Testing is currently limited to the x86_64-unknown-linux-gnu target due to the nature of bare-metal hypervisor code.

Test Execution Framework

Sources: .github/workflows/ci.yml(L30 - L33) 

Contributing Guidelines

Contributions to AxVM are governed by the Apache 2.0 license terms. All contributors must ensure their submissions comply with the project's quality standards and legal requirements.

Contribution Requirements

  1. Code Quality: All submissions must pass automated format, lint, and build checks
  2. Documentation: Public APIs must include comprehensive documentation
  3. Testing: Changes should include appropriate test coverage where applicable
  4. Legal Compliance: Contributions must be compatible with Apache 2.0 licensing

For detailed legal information and license terms, see License and Legal.

Sources: LICENSE.Apache2(L1 - L202) 

Build and Testing

Relevant source files

This document covers the automated build system, continuous integration pipeline, testing procedures, and documentation generation for the AxVM hypervisor project. It explains the multi-architecture build matrix, CI/CD workflow stages, and quality assurance processes that ensure code reliability across supported platforms.

For information about development dependencies and integration with the ArceOS ecosystem, see Dependencies and Integration.

Build System Overview

The AxVM project uses a GitHub Actions-based continuous integration system that validates code quality, builds across multiple architectures, and generates documentation. The build system supports three primary target architectures (x86_64, RISC-V, and AArch64) with different execution environments ranging from hosted Linux to bare-metal embedded targets.

CI Pipeline Architecture

flowchart TD
subgraph subGraph3["Documentation Pipeline"]
    doc_build["cargo doc --no-deps"]
    index_redirect["index.html redirect"]
    gh_pages_deploy["GitHub Pages deploy"]
end
subgraph subGraph2["Build Stages"]
    format_check["cargo fmt --check"]
    clippy_lint["cargo clippy"]
    cargo_build["cargo build"]
    unit_tests["cargo test"]
end
subgraph subGraph1["CI Job Matrix"]
    matrix_strategy["strategy.matrix"]
    toolchain_variants["rust-toolchain variants"]
    target_variants["targets variants"]
    nightly_2024["nightly-2024-12-25"]
    nightly_latest["nightly"]
    x86_linux["x86_64-unknown-linux-gnu"]
    x86_none["x86_64-unknown-none"]
    riscv_none["riscv64gc-unknown-none-elf"]
    aarch64_none["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["GitHub Actions Workflow"]
    trigger["push/pull_request triggers"]
    ci_job["ci job"]
    doc_job["doc job"]
end

cargo_build --> unit_tests
ci_job --> format_check
ci_job --> matrix_strategy
clippy_lint --> cargo_build
doc_build --> index_redirect
doc_job --> doc_build
format_check --> clippy_lint
index_redirect --> gh_pages_deploy
matrix_strategy --> target_variants
matrix_strategy --> toolchain_variants
target_variants --> aarch64_none
target_variants --> riscv_none
target_variants --> x86_linux
target_variants --> x86_none
toolchain_variants --> nightly_2024
toolchain_variants --> nightly_latest
trigger --> ci_job
trigger --> doc_job

Sources: .github/workflows/ci.yml(L1 - L61) 

Supported Architecture Targets

The build system validates AxVM across multiple target architectures that align with the hypervisor's multi-architecture support design. Each target represents a different execution environment and use case.

Target TripleArchitectureEnvironmentUse Case
x86_64-unknown-linux-gnux86_64Hosted LinuxDevelopment, unit testing
x86_64-unknown-nonex86_64Bare metalHypervisor deployment
riscv64gc-unknown-none-elfRISC-V 64-bitBare metal ELFRISC-V hypervisor
aarch64-unknown-none-softfloatAArch64Bare metal, soft FPARM hypervisor

Build Matrix Configuration

flowchart TD
subgraph Components["Components"]
    rust_src["rust-src"]
    clippy_comp["clippy"]
    rustfmt_comp["rustfmt"]
end
subgraph subGraph1["Target Matrix"]
    x86_linux["x86_64-unknown-linux-gnu"]
    x86_bare["x86_64-unknown-none"]
    riscv_bare["riscv64gc-unknown-none-elf"]
    arm_bare["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["Toolchain Matrix"]
    stable_toolchain["nightly-2024-12-25"]
    latest_toolchain["nightly"]
end

latest_toolchain --> arm_bare
latest_toolchain --> riscv_bare
latest_toolchain --> x86_bare
latest_toolchain --> x86_linux
stable_toolchain --> arm_bare
stable_toolchain --> clippy_comp
stable_toolchain --> riscv_bare
stable_toolchain --> rust_src
stable_toolchain --> rustfmt_comp
stable_toolchain --> x86_bare
stable_toolchain --> x86_linux

Sources: .github/workflows/ci.yml(L10 - L19) 

CI Pipeline Stages

The continuous integration pipeline consists of multiple validation stages that run sequentially for each target in the build matrix. The pipeline uses a fail-fast: false strategy to ensure all combinations are tested even if some fail.

Code Quality Validation

The first stage validates code formatting and style consistency:

sequenceDiagram
    participant GitHubActions as "GitHub Actions"
    participant RustToolchain as "Rust Toolchain"
    participant cargofmt as "cargo fmt"
    participant cargoclippy as "cargo clippy"

    GitHubActions ->> RustToolchain: "Setup nightly toolchain"
    GitHubActions ->> RustToolchain: "Install components: rust-src, clippy, rustfmt"
    GitHubActions ->> RustToolchain: "Add target: ${{ matrix.targets }}"
    GitHubActions ->> cargofmt: "cargo fmt --all -- --check"
    cargofmt ->> GitHubActions: "Format validation result"
    GitHubActions ->> cargoclippy: "cargo clippy --target ${{ matrix.targets }} --all-features"
    Note over cargoclippy: "Allows clippy::new_without_default"
    cargoclippy ->> GitHubActions: "Lint validation result"

The clippy stage includes specific configuration to suppress the new_without_default warning, allowing constructors without Default implementation.

Sources: .github/workflows/ci.yml(L22 - L26) 

Build Validation

Each target architecture undergoes compilation validation with full feature enablement:

cargo build --target ${{ matrix.targets }} --all-features

The build stage uses continue-on-error for the latest nightly toolchain to prevent pipeline failures from upstream Rust changes while maintaining stability with the pinned nightly version.

Sources: .github/workflows/ci.yml(L27 - L29) 

Unit Testing

Unit tests execute only on the x86_64-unknown-linux-gnu target, which provides the hosted environment necessary for test execution:

cargo test --target x86_64-unknown-linux-gnu -- --nocapture

This restriction reflects the practical limitation that bare-metal targets cannot execute standard Rust test harnesses without additional infrastructure.

Sources: .github/workflows/ci.yml(L30 - L33) 

Documentation Generation

The documentation pipeline operates independently from the main CI job and includes automated deployment to GitHub Pages for the default branch.

Documentation Build Process

flowchart TD
subgraph subGraph2["Deployment Conditions"]
    default_branch_check["github.ref == default-branch"]
    single_commit["single-commit: true"]
    target_folder["folder: target/doc"]
end
subgraph subGraph1["RUSTDOCFLAGS Environment"]
    broken_links["-D rustdoc::broken_intra_doc_links"]
    missing_docs["-D missing-docs"]
end
subgraph subGraph0["Documentation Job"]
    doc_trigger["GitHub trigger"]
    doc_checkout["Checkout repository"]
    doc_toolchain["Setup nightly-2024-12-25"]
    doc_build["cargo doc --no-deps --all-features"]
    doc_index["Generate index.html redirect"]
    doc_deploy["Deploy to gh-pages branch"]
end

broken_links --> doc_build
default_branch_check --> doc_deploy
doc_build --> doc_index
doc_checkout --> doc_toolchain
doc_index --> doc_deploy
doc_toolchain --> doc_build
doc_trigger --> doc_checkout
missing_docs --> doc_build
single_commit --> doc_deploy
target_folder --> doc_deploy

The documentation build enforces strict documentation standards through RUSTDOCFLAGS that treat broken intra-doc links and missing documentation as compilation errors.

Sources: .github/workflows/ci.yml(L35 - L61) 

Index Page Generation

The pipeline automatically generates a redirect index page that points to the main crate documentation:

printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html

This command extracts the crate name from cargo tree output and creates an HTML redirect to the appropriate documentation entry point.

Sources: .github/workflows/ci.yml(L52 - L53) 

Error Handling and Resilience

The CI configuration includes several resilience mechanisms to handle different failure scenarios:

ConditionBehaviorPurpose
continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}Allow latest nightly failuresPrevent upstream breakage
fail-fast: falseContinue other matrix jobs on failureComplete validation coverage
Documentation error handlingAllow non-default branch doc failuresPrevent blocking on doc issues

The pipeline distinguishes between the stable nightly toolchain (nightly-2024-12-25) and the latest nightly, allowing experimentation with newer compiler versions while maintaining build stability.

Sources: .github/workflows/ci.yml(L8 - L9)  .github/workflows/ci.yml(L25)  .github/workflows/ci.yml(L28)  .github/workflows/ci.yml(L32)  .github/workflows/ci.yml(L50) 

Quality Assurance Standards

The CI pipeline enforces several quality standards across the codebase:

  • Code Formatting: Uniform formatting via rustfmt with --check flag
  • Linting: Comprehensive linting through clippy with minimal suppressions
  • Documentation: Complete documentation coverage with broken link detection
  • Multi-architecture Compatibility: Build validation across all supported targets
  • Feature Completeness: All builds use --all-features to ensure complete validation

These standards ensure that AxVM maintains high code quality and reliability across its supported platforms and use cases.

Sources: .github/workflows/ci.yml(L22 - L33)  .github/workflows/ci.yml(L43)  .github/workflows/ci.yml(L49 - L53) 

License and Legal

Relevant source files

This document provides comprehensive legal information for the AxVM hypervisor project, including licensing terms, contribution guidelines, and intellectual property considerations. This information is essential for developers, contributors, and users who need to understand the legal framework governing the use, modification, and distribution of the AxVM codebase.

For build and testing procedures, see Build and Testing. For general development information, see Development Guide.

License Overview

The AxVM hypervisor is licensed under the Apache License 2.0, a permissive open-source license that allows for both commercial and non-commercial use while providing patent protection and clear contribution terms.

License Structure and Components

flowchart TD
subgraph subGraph3["Protection Mechanisms"]
    Warranty["Warranty DisclaimerSection 7"]
    Liability["Liability LimitationSection 8"]
    Trademark["Trademark ProtectionSection 6"]
    PatentTerm["Patent TerminationSection 3"]
end
subgraph subGraph2["Key Obligations"]
    Attribution["Attribution RequirementsSection 4(c)"]
    Notice["License Notice InclusionSection 4(a)"]
    Changes["Change DocumentationSection 4(b)"]
    NoticeTxt["NOTICE File HandlingSection 4(d)"]
end
subgraph subGraph1["Key Rights Granted"]
    Copyright["Copyright LicenseSection 2"]
    Patent["Patent LicenseSection 3"]
    Redistribute["Redistribution RightsSection 4"]
    Modify["Modification RightsSection 4"]
end
subgraph subGraph0["Apache License 2.0 Framework"]
    License["LICENSE.Apache2"]
    Terms["License Terms"]
    Grants["Rights Granted"]
    Conditions["Conditions & Obligations"]
    Limitations["Limitations & Disclaimers"]
end

Conditions --> Attribution
Conditions --> Changes
Conditions --> Notice
Conditions --> NoticeTxt
Grants --> Copyright
Grants --> Modify
Grants --> Patent
Grants --> Redistribute
License --> Terms
Limitations --> Liability
Limitations --> PatentTerm
Limitations --> Trademark
Limitations --> Warranty
Terms --> Conditions
Terms --> Grants
Terms --> Limitations

Sources: LICENSE.Apache2(L1 - L202) 

License Terms and Conditions

The Apache License 2.0 provides comprehensive rights to users of the AxVM hypervisor:

Right TypeScopeSection Reference
Copyright LicensePerpetual, worldwide, non-exclusive, royalty-freeLICENSE.Apache266-71
Patent LicenseCovers patent claims necessarily infringed by contributionsLICENSE.Apache274-87
DistributionSource and Object form distribution permittedLICENSE.Apache289-128
ModificationRight to create and distribute derivative worksLICENSE.Apache240-46

Key Definitions

The license establishes several critical definitions that govern how the AxVM codebase can be used:

flowchart TD
subgraph subGraph2["Legal Relationships"]
    OwnershipChain["Copyright Ownership"]
    SubmissionFlow["Contribution Submission"]
    LicenseGrant["License Grant Flow"]
end
subgraph subGraph1["Work Classifications"]
    Source["Source Form(.rs files, configs)"]
    Object["Object Form(compiled binaries)"]
    Derivative["Derivative Works(based on AxVM)"]
    Contribution["Contribution(submitted changes)"]
end
subgraph subGraph0["Core Legal Entities"]
    Work["Work(AxVM Source Code)"]
    Contributor["Contributor(Code Authors)"]
    Licensee["You(License Recipients)"]
    Licensor["Licensor(Copyright Holders)"]
end

Contribution --> Work
Contributor --> Contribution
Contributor --> OwnershipChain
LicenseGrant --> Licensee
Licensor --> LicenseGrant
OwnershipChain --> Licensor
Work --> Derivative
Work --> Object
Work --> Source

Sources: LICENSE.Apache2(L8 - L64) 

Redistribution Requirements

When redistributing AxVM or derivative works, the following obligations must be met:

Mandatory Inclusions

  1. License Copy: Include a copy of the Apache License 2.0 with any distribution
  2. Attribution Notices: Retain all copyright, patent, trademark, and attribution notices
  3. Change Documentation: Mark any modified files with prominent notices
  4. NOTICE File: If present, include readable copy of attribution notices

Source Code Distribution

flowchart TD
subgraph subGraph0["Required Components"]
    LicenseCopy["LICENSE.Apache2Full license text"]
    CopyrightNotices["Copyright NoticesIn source headers"]
    ChangeMarkers["Change MarkersModified file notices"]
    NoticeFile["NOTICE FileAttribution notices"]
end
OrigSource["Original AxVM Source"]
ModSource["Modified Source"]
Distribution["Source Distribution"]

Distribution --> CopyrightNotices
Distribution --> LicenseCopy
Distribution --> NoticeFile
ModSource --> ChangeMarkers
ModSource --> Distribution
OrigSource --> Distribution

Sources: LICENSE.Apache2(L89 - L121) 

Contribution Guidelines

Contribution License Terms

All contributions to AxVM are subject to the Apache License 2.0 terms:

AspectRequirementLegal Basis
License AgreementContributions under Apache 2.0 termsLICENSE.Apache2130-136
Copyright AssignmentNo explicit assignment requiredImplied by license grant
Patent GrantAutomatic patent license for contributionsLICENSE.Apache274-87
Warranty DisclaimerNo warranties provided by contributorsLICENSE.Apache2143-151

Submission Process

sequenceDiagram
    participant Developer as Developer
    participant AxVMRepository as "AxVM Repository"
    participant Maintainer as Maintainer
    participant Apache20Framework as "Apache 2.0 Framework"

    Developer ->> Developer: "Create contribution"
    Developer ->> Apache20Framework: "Implicit agreement to license terms"
    Developer ->> AxVMRepository: "Submit pull request/patch"
    Note over AxVMRepository: "Contribution becomes subject to<br>Apache 2.0 Section 5"
    AxVMRepository ->> Maintainer: "Review contribution"
    Maintainer ->> AxVMRepository: "Accept/merge contribution"
    AxVMRepository ->> Apache20Framework: "Contribution incorporated under Apache 2.0"
    Note over Apache20Framework: "Patent license automatically granted<br>Copyright license automatically granted"

Sources: LICENSE.Apache2(L48 - L60)  LICENSE.Apache2(L130 - L136) 

Intellectual Property Considerations

Patent Protection

The Apache License 2.0 provides specific patent protections:

  1. Automatic Patent License: Contributors automatically grant patent licenses for their contributions
  2. Patent Termination Clause: Patent licenses terminate if litigation is filed against the project
  3. Defensive Termination: Protects against patent trolling and frivolous litigation

Trademark Restrictions

The license explicitly does not grant trademark rights:

flowchart TD
subgraph subGraph1["Permitted Uses"]
    ReasonableUse["Reasonable and Customary UseOrigin description"]
    NoticeRepro["NOTICE File Reproduction"]
end
subgraph subGraph0["Trademark Scope"]
    ProjectName["AxVM Project Name"]
    ArceOSBrand["ArceOS Branding"]
    LogosMarks["Logos and Marks"]
    ServiceMarks["Service Marks"]
end
subgraph subGraph2["Prohibited Uses"]
    CommercialBrand["Commercial Branding"]
    Endorsement["Implied Endorsement"]
    Confusion["Likelihood of Confusion"]
end
Prohibited["Prohibited"]

ArceOSBrand --> NoticeRepro
LogosMarks --> Prohibited
ProjectName --> ReasonableUse
ServiceMarks --> Prohibited

Sources: LICENSE.Apache2(L138 - L141) 

Compliance and Attribution

License Compliance Checklist

For users and distributors of AxVM:

RequirementBinary DistributionSource DistributionDerivative Works
Include License✓ Required✓ Required✓ Required
Copyright Notices✓ Required✓ Required✓ Required
NOTICE File✓ If exists✓ If exists✓ If exists
Change Documentation✗ Not required✓ Required✓ Required
Patent License✓ Automatic✓ Automatic✓ Automatic

Attribution Requirements

flowchart TD
subgraph subGraph1["Distribution Attribution"]
    LicenseFile["LICENSE.Apache2Full license text"]
    NoticeInclude["NOTICE FileThird-party attributions"]
end
subgraph subGraph0["Source Attribution"]
    SourceHeaders["Source File HeadersCopyright notices"]
    LicenseRef["License ReferencesSPDX identifiers"]
end
subgraph subGraph2["Modification Attribution"]
    ChangeLog["Change DocumentationModified file markers"]
    AuthorInfo["Author InformationContributor attribution"]
end

ChangeLog --> AuthorInfo
LicenseRef --> NoticeInclude
SourceHeaders --> LicenseFile

Sources: LICENSE.Apache2(L94 - L120) 

Disclaimer and Limitation of Liability

Warranty Disclaimer

The AxVM hypervisor is provided "AS IS" without warranties of any kind. This includes:

  • No warranty of merchantability or fitness for a particular purpose
  • No warranty of title or non-infringement
  • No express or implied warranties beyond those required by applicable law

Liability Limitations

Contributors and distributors have limited liability exposure:

Damage TypeLiabilityCoverage
Direct DamagesLimitedOnly for deliberate/grossly negligent acts
Indirect DamagesExcludedIncluding loss of goodwill, work stoppage
Special/IncidentalExcludedAny character arising from license use
Commercial LossesExcludedUnless required by applicable law

Sources: LICENSE.Apache2(L143 - L174) 

This legal framework ensures that AxVM can be freely used, modified, and distributed while providing appropriate protections for contributors and clear obligations for users.

Overview

Relevant source files

This document provides a comprehensive introduction to AxVisor, a unified modular hypervisor built on the ArceOS unikernel framework. It covers the core concepts, architecture, and key components of AxVisor. For more detailed information about specific subsystems, refer to the respective wiki pages linked throughout this document.

What is AxVisor?

AxVisor is a hypervisor implemented based on the ArceOS unikernel framework. Its goal is to leverage the foundational operating system features provided by ArceOS to implement a unified modular hypervisor that supports multiple hardware architectures with a single codebase.

Key attributes that define AxVisor:

  • Unified: AxVisor uses the same codebase to simultaneously support x86_64, ARM (aarch64), and RISC-V architectures. This maximizes the reuse of architecture-independent code and simplifies development and maintenance.
  • Modular: The hypervisor's functionality is decomposed into multiple modules, each implementing a specific function. These modules communicate with each other through standard interfaces to achieve decoupling and reuse of functionality.

Sources: README.md(L21 - L28) 

Architecture Overview

AxVisor follows a layered architecture approach with clearly defined components that have specific responsibilities:

flowchart TD
subgraph subGraph3["ArceOS Foundation"]
    axtask["axtask - Task Management"]
    axfs["axfs - Virtual File System"]
    axalloc["axalloc - Memory Allocator"]
    axconfig["axconfig - Configuration Management"]
end
subgraph subGraph2["Hardware Abstraction"]
    axhal["axhal - Hardware Abstraction Layer"]
end
subgraph subGraph1["Virtual Machine Management"]
    axvm["axvm - VM Management"]
    axvcpu["axvcpu - Virtual CPU Management"]
    axaddrspace["axaddrspace - Address Space Management"]
    axdevice["axdevice - Device Emulation"]
end
subgraph subGraph0["Top Level"]
    axvisor["axvisor - Top-level Hypervisor"]
end

axhal --> axalloc
axhal --> axconfig
axhal --> axfs
axhal --> axtask
axvcpu --> axaddrspace
axvisor --> axaddrspace
axvisor --> axconfig
axvisor --> axhal
axvisor --> axvcpu
axvisor --> axvm
axvm --> axaddrspace
axvm --> axdevice
axvm --> axvcpu

Sources: README.md(L29 - L35) 

Key Components

  1. axvisor: The top-level hypervisor component that coordinates all subsystems.
  2. axvm: Manages virtual machines, their lifecycle, and interactions with the host system.
  3. axvcpu: Handles virtual CPU operations, including context switching, instruction emulation, and exit handling.
  4. axaddrspace: Manages guest memory address spaces, including page tables and memory region mapping.
  5. axhal: Hardware Abstraction Layer that provides unified interfaces to hardware across different architectures.
  6. axdevice: Emulates hardware devices for guests or manages device passthrough.
  7. axconfig: Handles configuration parsing and management for the hypervisor and virtual machines.

Sources: README.md(L29 - L35) 

Virtual Machine Execution Flow

The following diagram illustrates the typical flow of VM initialization, boot, and execution in AxVisor:

sequenceDiagram
    participant VMConfigTOML as "VMConfig (TOML)"
    participant VMMinitrun as "VMM::init/run"
    participant VirtualMachine as "Virtual Machine"
    participant VirtualCPU as "Virtual CPU"
    participant GuestOS as "Guest OS"

    VMConfigTOML ->> VMMinitrun: Load VM Configuration
    VMMinitrun ->> VirtualMachine: Initialize VM (id, name, vcpus)
    VMMinitrun ->> VirtualMachine: Setup memory regions
    VMMinitrun ->> VirtualMachine: Load guest image
    alt Load from
    alt filesystem
        VirtualMachine ->> VirtualMachine: Load from FAT32
    else Load from
    else Load from
        VirtualMachine ->> VirtualMachine: Load compiled image
    end
    end
    VMMinitrun ->> VirtualCPU: Initialize VCPUs
    VMMinitrun ->> VirtualCPU: Create VCPU tasks
    VMMinitrun ->> VirtualMachine: Boot VM
    VirtualCPU ->> VirtualCPU: Run VCPU execution loop
    VirtualCPU ->> GuestOS: Execute guest code
    loop VCPU execution
        GuestOS ->> VirtualCPU: VM Exit (hypercall/interrupt/etc.)
        VirtualCPU ->> VirtualCPU: Handle exit reason
        VirtualCPU ->> GuestOS: Resume execution
    end
    GuestOS ->> VirtualCPU: Shutdown request
    VirtualCPU ->> VirtualMachine: Signal VM shutdown
    VirtualMachine ->> VMMinitrun: Decrement running VM count

Sources: README.md(L57 - L59)  README.md(L116 - L202) 

Cross-Platform Support

AxVisor achieves its cross-platform capability through architecture-specific implementations behind common interfaces:

flowchart TD
subgraph subGraph1["Architecture-Specific Implementations"]
    x86_vcpu["x86_vcpu - x86 Implementation"]
    arm_vcpu["arm_vcpu - ARM Implementation"]
    riscv_vcpu["riscv_vcpu - RISC-V Implementation"]
    x86_hal["x86_64 HAL Implementation"]
    arm_hal["ARM/aarch64 HAL Implementation"]
    riscv_hal["RISC-V HAL Implementation"]
end
subgraph subGraph0["AxVisor Common Interface"]
    axvcpu_common["axvcpu - Common Interface"]
    axhal_common["axhal - Common Interface"]
end

axhal_common --> arm_hal
axhal_common --> riscv_hal
axhal_common --> x86_hal
axvcpu_common --> arm_vcpu
axvcpu_common --> riscv_vcpu
axvcpu_common --> x86_vcpu

Sources: README.md(L38 - L47) 

Configuration System

AxVisor uses TOML configuration files to manage virtual machine settings. The configuration system handles:

  • VM identification (ID, name)
  • Hardware allocation (CPU cores, memory size)
  • Guest image loading method and parameters
  • Virtual and passthrough devices
  • Architecture-specific settings

Configuration and Image Loading

flowchart TD
subgraph subGraph1["Image Loading Methods"]
    fs_load["Load from Filesystem"]
    mem_load["Load from Memory"]
end
subgraph subGraph0["Configuration Flow"]
    toml["TOML Config Files"]
    vmconfig["VM Configuration Object"]
    memory["Memory Configuration"]
    devices["Device Configuration"]
    image["Image Configuration"]
end
fs_image["FAT32 Filesystem Image"]
compiled_image["Statically Compiled Image"]

fs_load --> fs_image
image --> fs_load
image --> mem_load
mem_load --> compiled_image
toml --> vmconfig
vmconfig --> devices
vmconfig --> image
vmconfig --> memory

Sources: README.md(L61 - L75)  README.md(L76 - L113) 

Supported Guest Operating Systems

AxVisor supports multiple guest operating systems:

Guest OSDescriptionStatus
ArceOSThe foundational unikernel frameworkSupported
NimbOSA simple operating systemSupported
LinuxFull-featured OSSupported (mainly on aarch64)
Starry-OSEducational OSSupported

Sources: README.md(L46 - L56) 

Hardware Platform Support

AxVisor has been verified on the following platforms:

PlatformArchitectureStatus
QEMU ARM64 virtaarch64Verified
Rockchip RK3568/RK3588aarch64Verified
黑芝麻华山 A1000aarch64Verified
QEMU x86_64x86_64Supported
QEMU RISC-Vriscv64Supported

Sources: README.md(L38 - L43) 

Code Organization

AxVisor's codebase is organized around a modular architecture with the following key crates:

  • axvisor: Top-level hypervisor implementation
  • axvm: Virtual machine management
  • axvcpu: Common virtual CPU interface
  • arm_vcpu/x86_vcpu/riscv_vcpu: Architecture-specific VCPU implementations
  • axaddrspace: Address space and memory virtualization
  • axdevice: Device virtualization and management
  • axvmconfig: Configuration management

For more detailed information about the system components, refer to System Components.

For information about VM management, see VM Management.

Sources: Cargo.lock(L118 - L121)  Cargo.lock(L574 - L593)  Cargo.lock(L536 - L545)  Cargo.lock(L154 - L168)  Cargo.lock(L1248 - L1269)  Cargo.lock(L177 - L192)  Cargo.lock(L239 - L261)  Cargo.lock(L596 - L605) 

Development Environment

AxVisor provides tools to simplify the development environment setup:

  • dev_env.py: Script to localize relevant crates for easier development and debugging
  • Makefile: Handles build configurations and targets

For more information on setting up a development environment, see Development Environment.

Sources: README.md(L210 - L212) 

License

AxVisor is open-source software and uses the following licenses:

  • Apache-2.0
  • MulanPubL-2.0
  • MulanPSL-2.0
  • GPL-3.0-or-later

Sources: README.md(L222 - L229)  LICENSE.Apache2 LICENSE.MulanPSL2 LICENSE.MulanPubL2 LICENSE.GPLv3

Architecture

Relevant source files

This page describes the core architectural design and principles of AxVisor, a unified modular hypervisor based on the ArceOS unikernel framework. It explains the high-level system structure, key components, and the relationships between them. For specific details on VM management, see VM Management, and for hardware abstraction details, see Hardware Abstraction Layer.

Architectural Overview

AxVisor is designed as a modular hypervisor with a layered architecture that achieves two primary goals:

  1. Unification: Using a single codebase to support multiple architectures (x86_64, ARM/aarch64, and RISC-V)
  2. Modularity: Decomposing hypervisor functionality into distinct modules that communicate through well-defined interfaces

The hypervisor is built on top of the ArceOS unikernel framework, which provides essential OS functionality like task scheduling, memory management, and interrupt handling.

flowchart TD
subgraph Implementation["Implementation"]
    A1["ArceOS Components (axruntime, axalloc, axtask, etc.)"]
    A2["Hardware Abstraction (axhal, architecture-specific modules)"]
    A3["VM Management (axvm, axvcpu)"]
    A4["Device Support (axdevice)"]
    A5["Configuration (axvmconfig)"]
end
subgraph subGraph0["Architecture Layers"]
    L1["Host OS Layer"]
    L2["Hardware Abstraction Layer"]
    L3["Virtual Machine Management Layer"]
    L4["Device Virtualization Layer"]
    L5["Configuration Layer"]
end

A2 --> A1
A3 --> A2
A4 --> A3
A5 --> A4
L1 --> A1
L2 --> A2
L2 --> L1
L3 --> A3
L3 --> L2
L4 --> A4
L4 --> L3
L5 --> A5
L5 --> L4

Sources: README.md(L29 - L36) 

Core Modules and Their Relationships

AxVisor is composed of several key modules, each with specific responsibilities:

flowchart TD
subgraph subGraph0["System Modules"]
    axvisor["axvisor - Top-level Hypervisor"]
    axvm["axvm - Virtual Machine Management"]
    axhal["axhal - Hardware Abstraction Layer"]
    axvcpu["axvcpu - Virtual CPU Management"]
    axaddrspace["axaddrspace - Address Space Management"]
    axconfig["axconfig - Configuration Management"]
    axstd["axstd - Standard Library"]
    axdevice["axdevice - Device Emulation"]
    axalloc["axalloc - Memory Allocator"]
end

axhal --> axalloc
axhal --> axconfig
axvisor --> axaddrspace
axvisor --> axconfig
axvisor --> axhal
axvisor --> axstd
axvisor --> axvcpu
axvisor --> axvm
axvm --> axaddrspace
axvm --> axdevice
axvm --> axvcpu

The key relationships between these modules:

ModulePrimary ResponsibilityDependencies
axvisorTop-level hypervisor implementationaxvm,axhal,axvcpu,axaddrspace,axconfig
axvmVirtual machine lifecycle managementaxvcpu,axaddrspace,axdevice
axhalHardware abstraction for different architecturesaxalloc,axconfig
axvcpuVirtual CPU creation and executionaxaddrspace
axaddrspaceMemory virtualization and address space managementmemory_addr,page_table_entry,page_table_multiarch
axdeviceDevice emulation and passthroughaxaddrspace

Sources: Cargo.toml(L15 - L46) 

Cross-Architecture Implementation

One of AxVisor's key features is its unified architecture approach. The system uses a common interface for architecture-independent code, with architecture-specific implementations provided through separate modules.

flowchart TD
subgraph subGraph1["Hardware Features"]
    x86_feat["x86 Features:- VT-x- x2APIC"]
    arm_feat["ARM Features:- EL2 Mode- GICv2"]
    riscv_feat["RISC-V Features:- SBI Runtime"]
end
subgraph subGraph0["Architecture Support"]
    common["Common Interface Layer (axvcpu, axaddrspace)"]
    x86["x86_64 Implementation (x86_vcpu)"]
    arm["ARM Implementation (arm_vcpu)"]
    riscv["RISC-V Implementation (riscv_vcpu)"]
end

arm --> arm_feat
common --> arm
common --> riscv
common --> x86
riscv --> riscv_feat
x86 --> x86_feat

This architecture allows AxVisor to:

  • Share common virtualization logic across architectures
  • Isolate architecture-specific implementation details
  • Add new architecture support without changing core functionality

Sources: Cargo.toml(L35 - L37)  Cargo.toml(L41 - L46) 

Virtualization Flow

The core virtualization process in AxVisor follows these key steps:

sequenceDiagram
    participant axvisorvmm as "axvisor::vmm"
    participant axvisorvmmconfig as "axvisor::vmm::config"
    participant axvmvm as "axvm::vm"
    participant axvisorvmmvcpus as "axvisor::vmm::vcpus"
    participant ArchitecturespecificVCPU as "Architecture-specific VCPU"

    axvisorvmm ->> axvisorvmmconfig: Load VM configuration
    axvisorvmmconfig ->> axvmvm: Create VM with configuration
    axvmvm ->> axvmvm: Setup memory regions
    axvisorvmmconfig ->> axvisorvmmconfig: Load guest image
    axvisorvmm ->> axvisorvmmvcpus: Initialize VCPUs
    axvisorvmmvcpus ->> axvisorvmmvcpus: Create VCPU tasks
    axvisorvmm ->> axvmvm: Boot VM
    axvmvm ->> axvisorvmmvcpus: Notify VCPUs to start
    loop VCPU Execution
        axvisorvmmvcpus ->> ArchitecturespecificVCPU: vm.run_vcpu(vcpu_id)
        ArchitecturespecificVCPU ->> ArchitecturespecificVCPU: Handle VM exits
    alt VM Exit: Hypercall
        ArchitecturespecificVCPU ->> ArchitecturespecificVCPU: Handle hypercall
    else VM Exit: External Interrupt
        ArchitecturespecificVCPU ->> ArchitecturespecificVCPU: Handle interrupt
    else VM Exit: System Shutdown
        ArchitecturespecificVCPU ->> axvmvm: Notify VM shutdown
        axvmvm ->> axvisorvmm: Decrement running VM count
    end
    end

This execution model enables AxVisor to:

  1. Load guest operating systems from either filesystem or memory
  2. Manage VM execution through architecture-specific VCPU implementations
  3. Handle various virtualization events through the VM exit mechanism

Sources: README.md(L99 - L112)  README.md(L159 - L201) 

Memory Management Architecture

Memory virtualization in AxVisor is built around the axaddrspace module, which provides memory region management and address translation services.

flowchart TD
subgraph subGraph3["Memory Virtualization"]
    ma["memory_addr"]
    subgraph subGraph2["Address Translation"]
        addr_trans["Address Translation"]
        gva_gpa["GVA → GPA"]
        gpa_hpa["GPA → HPA"]
    end
    subgraph subGraph1["Memory Region Management"]
        as["axaddrspace"]
        mem_regions["Memory Regions"]
        guest_phys["Guest Physical Address Space"]
        host_phys["Host Physical Address Space"]
    end
    subgraph subGraph0["Page Table Management"]
        pte["page_table_entry"]
        ptm["page_table_multiarch"]
        arch_pt["Architecture-specificpage table implementations"]
    end
end

addr_trans --> gpa_hpa
addr_trans --> gva_gpa
as --> addr_trans
as --> ma
as --> mem_regions
as --> pte
as --> ptm
mem_regions --> guest_phys
mem_regions --> host_phys
pte --> ptm
ptm --> arch_pt

Key features of the memory management architecture:

  • Separation of address spaces between host and guest
  • Architecture-independent memory region management
  • Architecture-specific page table implementations
  • Two-stage address translation (Guest Virtual → Guest Physical → Host Physical)

Sources: Cargo.toml(L37)  Cargo.toml(L42 - L44) 

Configuration System

AxVisor uses a robust configuration system based on TOML files to define VM properties and behavior:

flowchart TD
subgraph subGraph1["Configuration Components"]
    vm_id["VM ID"]
    vm_name["VM Name"]
    cpu_num["CPU Count"]
    memory["Memory Configuration"]
    devices["Device Configuration"]
    images["Image Configuration"]
    img_loc["image_location(fs or memory)"]
    kernel_path["kernel_path"]
    entry_point["entry_point"]
    load_addr["kernel_load_addr"]
    regions["Memory Regions"]
    mem_attrs["Memory Attributes(READ|WRITE|EXECUTE)"]
end
subgraph subGraph0["Configuration Flow"]
    toml["VM TOML Configuration Files"]
    parser["axvmconfig Parser"]
    vmconfig["VM Configuration Object"]
    axvisor["axvisor Hypervisor"]
end

images --> entry_point
images --> img_loc
images --> kernel_path
images --> load_addr
memory --> regions
parser --> vmconfig
regions --> mem_attrs
toml --> parser
vmconfig --> axvisor
vmconfig --> cpu_num
vmconfig --> devices
vmconfig --> images
vmconfig --> memory
vmconfig --> vm_id
vmconfig --> vm_name

The configuration system enables:

  • Declarative definition of VM properties
  • Flexible image loading from filesystem or memory
  • Consistent VM configuration across different architectures
  • Separation of configuration from implementation logic

Sources: README.md(L72 - L75)  Cargo.toml(L48 - L52) 

Multi-Guest Support

AxVisor is designed to support multiple guest operating systems simultaneously:

Guest OSSupported ArchitecturesFeatures
ArceOSx86_64, ARM, RISC-VFull virtualization support
NimbOSx86_64, ARM, RISC-VFull virtualization support
LinuxARM (aarch64)Single-core and SMP with passthrough devices
Starry-OSx86_64Full virtualization support

The multi-guest architecture enables:

  • Testing of different operating systems on the same hypervisor
  • Comparison of performance across different guest types
  • Development and debugging of guest-specific virtualization features

Sources: README.md(L45 - L56) 

Conclusion

AxVisor's architecture follows key design principles that enable it to provide a unified hypervisor solution across multiple hardware architectures:

  1. Layered Design: Clear separation of concerns through a well-defined layering pattern
  2. Modular Structure: Functionality divided into well-defined modules with clear interfaces
  3. Architecture Independence: Separation of architecture-specific code from common functionality
  4. Configuration-Driven: Extensive use of configuration files for flexible deployment
  5. Flexible Guest Support: Support for multiple guest operating systems and image loading methods

This design enables AxVisor to maintain a single codebase while supporting diverse hardware platforms and guest operating systems, making it an adaptable and maintainable hypervisor solution.

Sources: README.md(L21 - L28)  README.md(L29 - L36) 

System Components

Relevant source files

This document details the main crates and their relationships within the AxVisor hypervisor system. It provides an overview of the modular architecture of AxVisor, explaining how various components interact to create a unified hypervisor framework that supports multiple architectures. For information about VM configuration, see VM Configuration. For implementation details of the VMM (Virtual Machine Manager), see VMM Implementation.

Component Architecture Overview

AxVisor follows a modular design approach with clearly defined components that have specific responsibilities. This architecture enables:

  1. Cross-architecture support (x86_64, ARM/aarch64, RISC-V)
  2. Clear separation of concerns
  3. Hardware abstraction
  4. Simplified development and testing

The system is organized into several categories of components that work together to provide hypervisor functionality.

flowchart TD
subgraph subGraph2["ArceOS Foundation"]
    axstd["axstd - Standard Library"]
    axhal["axhal - Hardware Abstraction Layer"]
    axtask["axtask - Task Management"]
    axalloc["axalloc - Memory Allocation"]
    axconfig["axconfig - System Configuration"]
    axfs["axfs - Virtual Filesystem"]
end
subgraph subGraph1["Architecture-Specific Components"]
    x86_vcpu["x86_vcpu - x86 VCPU Implementation"]
    arm_vcpu["arm_vcpu - ARM VCPU Implementation"]
    riscv_vcpu["riscv_vcpu - RISC-V VCPU Implementation"]
end
subgraph subGraph0["Core Hypervisor Components"]
    axvisor["axvisor - Top-level Hypervisor"]
    axvm["axvm - VM Management"]
    axvcpu["axvcpu - Virtual CPU Interface"]
    axaddrspace["axaddrspace - Memory Virtualization"]
    axdevice["axdevice - Device Emulation"]
    axvmconfig["axvmconfig - VM Configuration"]
end

axstd --> axalloc
axstd --> axconfig
axstd --> axfs
axstd --> axhal
axstd --> axtask
axvcpu --> arm_vcpu
axvcpu --> riscv_vcpu
axvcpu --> x86_vcpu
axvisor --> axaddrspace
axvisor --> axstd
axvisor --> axvcpu
axvisor --> axvm
axvm --> axaddrspace
axvm --> axdevice
axvm --> axvcpu
axvm --> axvmconfig

Sources: Cargo.toml(L14 - L45) 

Core Hypervisor Components

The main components of AxVisor can be categorized into core hypervisor functionality crates that are independent of the underlying architecture.

axvisor

The top-level hypervisor crate that ties all components together and provides the main entry point for the hypervisor. It coordinates the virtual machine lifecycle and manages the hypervisor state.

axvm

The Virtual Machine Manager responsible for creating, running, and managing virtual machines. It handles VM lifecycle operations such as creation, initialization, execution, and shutdown.

Key responsibilities:

  • VM resource allocation and management
  • Guest image loading
  • Coordinating interactions between VCPUs, memory, and devices
  • VM state management

axvcpu

Defines the common Virtual CPU interface used across all architectures. It provides an abstraction layer for architecture-specific VCPU implementations.

Key responsibilities:

  • Common VCPU interface definition
  • VCPU state management
  • Architecture-agnostic VCPU operations

axaddrspace

Handles memory virtualization and address space management for virtual machines.

Key responsibilities:

  • Memory region allocation and management
  • Second-stage page table management
  • Memory protection mechanisms
  • Address translation services

axdevice

Provides device emulation capabilities for virtual machines, allowing guest operating systems to interact with virtualized hardware.

Key responsibilities:

  • Device model implementation
  • I/O handling
  • Device state management

axvmconfig

Manages VM configurations through TOML-based configuration files, defining VM properties such as memory size, CPU count, and device settings.

Sources: Cargo.toml(L34 - L37) 

Architecture-Specific Components

AxVisor supports multiple architectures through specialized implementation crates that implement the common interfaces defined by the core components.

flowchart TD
subgraph subGraph3["Architecture-Specific Implementations"]
    axvcpu["axvcpu - Common VCPU Interface"]
    x86_vcpu["x86_vcpu - x86 Implementation"]
    arm_vcpu["arm_vcpu - ARM Implementation"]
    riscv_vcpu["riscv_vcpu - RISC-V Implementation"]
    subgraph subGraph2["RISC-V Specifics"]
        hs_mode["HS Mode"]
        sbi_rt["SBI Runtime"]
        trap_handler["Trap Handler"]
    end
    subgraph subGraph1["ARM Specifics"]
        el2["EL2 Mode"]
        gicv2["GICv2 Interrupt Controller"]
        hvc["HVC Exception Handler"]
    end
    subgraph subGraph0["x86_64 Specifics"]
        vt_x["VT-x Virtualization"]
        x2apic["x2APIC Support"]
        vmexit_handler["VM Exit Handler"]
    end
end

arm_vcpu --> el2
arm_vcpu --> gicv2
arm_vcpu --> hvc
axvcpu --> arm_vcpu
axvcpu --> riscv_vcpu
axvcpu --> x86_vcpu
riscv_vcpu --> hs_mode
riscv_vcpu --> sbi_rt
riscv_vcpu --> trap_handler
x86_vcpu --> vmexit_handler
x86_vcpu --> vt_x
x86_vcpu --> x2apic

Sources: Cargo.lock(L138 - L148)  Cargo.lock(L154 - L168)  Cargo.lock(L1247 - L1269)  Cargo.lock(L1721 - L1739) 

x86_vcpu

Implements the VCPU interface for x86_64 architecture using Intel VT-x or AMD SVM technologies.

Key features:

  • VM entry/exit handling
  • Extended Page Tables (EPT) support
  • Interrupt virtualization
  • MSR virtualization

arm_vcpu

Implements the VCPU interface for ARM/aarch64 architecture using ARM Virtualization Extensions.

Key features:

  • EL2 (Hypervisor mode) operations
  • Stage-2 translation
  • GICv2 interrupt controller virtualization
  • Hypervisor calls (HVC) handling

riscv_vcpu

Implements the VCPU interface for RISC-V architecture using the RISC-V Hypervisor Extension.

Key features:

  • Hypervisor-Supervisor (HS) mode operations
  • Guest-page-table handling
  • SBI services virtualization
  • Trap handling

Sources: Cargo.lock(L1721 - L1739)  Cargo.lock(L154 - L168)  Cargo.lock(L1247 - L1269) 

ArceOS Foundation

AxVisor is built on top of the ArceOS (Architecture-centric OS) unikernel framework, which provides essential OS services and hardware abstractions.

axstd

A minimal standard library for ArceOS that provides common functionality and interfaces to the underlying OS services.

axhal

The Hardware Abstraction Layer that provides a uniform interface to hardware across different architectures.

Key responsibilities:

  • CPU and interrupt management
  • Timer services
  • Memory management primitives
  • Platform-specific initialization

axtask

Task management services including task creation, scheduling, and synchronization.

Key responsibilities:

  • Task creation and lifecycle management
  • Scheduling
  • Inter-task synchronization

axalloc

Memory allocation services and memory management.

Key responsibilities:

  • Physical memory allocation
  • Heap management
  • Memory pools

axconfig

System configuration management.

Key responsibilities:

  • System parameters
  • Feature flags
  • Runtime configuration

axfs

Virtual filesystem support.

Key responsibilities:

  • File operations
  • Filesystem implementations (FAT32, etc.)
  • Device filesystem

Sources: Cargo.toml(L24 - L32)  Cargo.lock(L118 - L134) 

Component Relationships and Dependencies

The components in AxVisor have well-defined relationships and dependencies that enable the modular architecture to function as a cohesive system.

flowchart TD
subgraph subGraph6["AxVisor System"]
    axvisor["axvisor - Entry Point"]
    subgraph subGraph5["OS Services"]
        axstd["axstd - Standard Library"]
        axhal["axhal - Hardware Abstraction Layer"]
        axtask["axtask - Task Management"]
        axalloc["axalloc - Memory Allocator"]
        axconfig["axconfig - System Configuration"]
        axfs["axfs - Filesystem"]
    end
    subgraph subGraph4["Device Emulation"]
        axdevice["axdevice - Device Emulation"]
        axdevice_base["axdevice_base - Base Device Interface"]
    end
    subgraph subGraph3["Memory Management"]
        axaddrspace["axaddrspace - Address Space Management"]
        page_tables["Page Tables"]
    end
    subgraph subGraph2["CPU Virtualization"]
        axvcpu["axvcpu - VCPU Interface"]
        arch_vcpu["Architecture-specific VCPU Implementations"]
    end
    subgraph subGraph1["VM Management"]
        axvm["axvm - VM Management"]
        axvmconfig["axvmconfig - VM Configuration"]
    end
end
subgraph subGraph0["User Applications"]
    vm_configs["VM Configuration Files"]
end

axaddrspace --> page_tables
axalloc --> axhal
axdevice --> axaddrspace
axdevice --> axdevice_base
axstd --> axalloc
axstd --> axconfig
axstd --> axfs
axstd --> axhal
axstd --> axtask
axtask --> axhal
axvcpu --> arch_vcpu
axvcpu --> axaddrspace
axvisor --> axstd
axvisor --> axvm
axvm --> axaddrspace
axvm --> axdevice
axvm --> axvcpu
axvm --> axvmconfig
vm_configs --> axvmconfig

Sources: Cargo.toml(L24 - L45) 

Component Interaction Flow

The following diagram illustrates how the components interact during VM creation and execution:

sequenceDiagram
    participant VMConfiguration as VM Configuration
    participant axvisor as axvisor
    participant axvm as axvm
    participant axvcpu as axvcpu
    participant ArchspecificVCPU as Arch-specific VCPU
    participant axaddrspace as axaddrspace
    participant axdevice as axdevice

    VMConfiguration ->> axvisor: Load configuration
    axvisor ->> axvm: Create VM(config)
    axvm ->> axaddrspace: Create address space
    axaddrspace -->> axvm: Address space created
    axvm ->> axdevice: Initialize devices
    axdevice -->> axvm: Devices initialized
    axvm ->> axvcpu: Create VCPUs
    axvcpu ->> ArchspecificVCPU: Create architecture-specific VCPU
    ArchspecificVCPU -->> axvcpu: VCPU created
    axvcpu -->> axvm: VCPUs created
    axvm ->> axvm: Load guest image
    axvm ->> axvcpu: Run VCPUs
    loop VCPU Execution
        axvcpu ->> ArchspecificVCPU: Run VCPU
    alt VM Exit: I/O Operation
        ArchspecificVCPU ->> axdevice: Handle I/O
        axdevice -->> ArchspecificVCPU: I/O handled
    else VM Exit: Memory Access
        ArchspecificVCPU ->> axaddrspace: Handle memory access
        axaddrspace -->> ArchspecificVCPU: Memory access handled
    else VM Exit: Hypercall
        ArchspecificVCPU ->> axvm: Handle hypercall
        axvm -->> ArchspecificVCPU: Hypercall handled
    end
    ArchspecificVCPU -->> axvcpu: VCPU exited
    end
    axvcpu ->> axvm: VM stopped
    axvm ->> axvisor: VM execution completed

Sources: Cargo.lock(L549 - L571)  Cargo.lock(L576 - L593)  Cargo.lock(L536 - L545)  Cargo.lock(L177 - L191) 

Component Key Features and Capabilities

Below is a table summarizing the key features and capabilities of the main components:

ComponentPrimary RoleKey FeaturesArchitecture Support
axvisorEntry point and coordinatorVM lifecycle management, System initializationAll
axvmVM managementVM creation, resource management, guest image loadingAll
axvcpuVCPU interfaceCommon VCPU operations, architecture-agnostic interfaceAll
x86_vcpux86 VCPU implementationVT-x/SVM, EPT, VMCS managementx86_64
arm_vcpuARM VCPU implementationEL2 mode, GICv2, Stage-2 translationARM/aarch64
riscv_vcpuRISC-V VCPU implementationHS mode, SBI services virtualizationRISC-V
axaddrspaceMemory virtualizationMemory regions, second-stage page tables, address translationAll
axdeviceDevice emulationDevice models, I/O handlingAll
axvmconfigVM configurationTOML-based VM configurationAll
axhalHardware abstractionPlatform initialization, interrupts, timersAll (platform-specific)
axtaskTask managementScheduling, synchronizationAll
axallocMemory allocationPhysical memory, heap managementAll

Sources: Cargo.toml(L14 - L45)  Cargo.lock(L549 - L571) 

Crate Dependency Tree

The core hypervisor crates have specific dependencies that demonstrate their relationships:

  1. axvisor depends on:
  • axvm - For VM management
  • axvcpu - For VCPU operations
  • axaddrspace - For memory management
  • axstd - For OS services
  • Various utility crates (log, bitflags, spin, etc.)
  1. axvm depends on:
  • axvcpu - For VCPU operations
  • axaddrspace - For memory management
  • axdevice - For device emulation
  • axvmconfig - For configuration management
  1. axvcpu provides common interfaces implemented by:
  • x86_vcpu - For x86_64
  • arm_vcpu - For ARM/aarch64
  • riscv_vcpu - For RISC-V

This modular structure allows AxVisor to maintain a single codebase while supporting multiple architectures through well-defined interfaces and architecture-specific implementations.

Sources: Cargo.toml(L34 - L45)  Cargo.lock(L549 - L571)  Cargo.lock(L576 - L593) 

VM Management

Relevant source files

This document describes how virtual machines (VMs) are created, configured, and managed within the AxVisor hypervisor system. It explains the VM lifecycle from initialization through execution to shutdown, as well as the management of virtual CPUs (VCPUs) within each VM. For information about the underlying hardware abstraction layer, see Hardware Abstraction Layer.

VM Architecture Overview

AxVisor implements a modular VM management system that separates VM configuration, execution, and resource management concerns. The core components work together to create, execute, and monitor virtual machines.

flowchart TD
subgraph subGraph1["Task Management"]
    TaskScheduler["ArceOS Task Scheduler"]
    TaskExt["Task Extensions"]
end
subgraph subGraph0["VM Management System"]
    VMM["VMM Module"]
    VMConfig["VM Configuration"]
    VMList["VM List"]
    VCPUManager["VCPU Manager"]
    ImageLoader["Image Loader"]
    VMs["VMs"]
    VCPUs["VCPUs"]
end

ImageLoader --> VMs
TaskExt --> VCPUs
TaskExt --> VMs
VCPUManager --> TaskScheduler
VCPUManager --> VCPUs
VMConfig --> VMs
VMList --> VMs
VMM --> VMConfig
VMM --> VMs
VMs --> VCPUs

Sources: src/vmm/mod.rs src/task.rs

VM Types and References

The VM management system uses specific types to represent VMs and VCPUs:

TypeDescriptionSource
VMInstantiated VM type usingaxvm::AxVM<AxVMHalImpl, AxVCpuHalImpl>src/vmm/mod.rs16
VMRefVM reference type (shared viaArc)src/vmm/mod.rs18
VCpuRefVCPU reference type (shared viaArc)src/vmm/mod.rs20

These types provide the foundation for VM management operations, with reference types allowing shared access to VM and VCPU resources across different components and tasks.

Sources: src/vmm/mod.rs(L16 - L20) 

VM Lifecycle

The VM lifecycle in AxVisor follows a defined sequence of operations, from initialization through execution to shutdown.


Sources: src/vmm/mod.rs(L28 - L65)  src/vmm/vcpus.rs(L275 - L367) 

VM Initialization

The VMM initializes VMs through the following process:

  1. Call VMM::init() to initialize guest VMs and set up primary VCPUs
  2. Load VM configurations from TOML files
  3. Create VM instances using VM::new()
  4. Load VM images based on configuration
  5. Set up primary VCPUs for each VM
sequenceDiagram
    participant VMM as VMM
    participant ConfigModule as Config Module
    participant VMInstance as VM Instance
    participant VCPUManager as VCPU Manager

    VMM ->> ConfigModule: init_guest_vms()
    ConfigModule ->> ConfigModule: Parse TOML configs
    ConfigModule ->> VMInstance: Create VM with AxVMConfig
    VMInstance -->> ConfigModule: Return VM instance
    ConfigModule ->> ConfigModule: load_vm_images()
    ConfigModule ->> VMM: Return created VMs
    VMM ->> VCPUManager: setup_vm_primary_vcpu() for each VM
    VCPUManager ->> VCPUManager: Create primary VCPU task

Sources: src/vmm/mod.rs(L28 - L39)  src/vmm/config.rs(L25 - L43) 

VM Execution

The VMM starts VM execution:

  1. Call VMM::start() to boot all VMs
  2. For each VM, call vm.boot() to start execution
  3. Notify the primary VCPU to begin execution
  4. Increment RUNNING_VM_COUNT for each successfully started VM
  5. Wait until all VMs are stopped (when RUNNING_VM_COUNT reaches 0)

VCPUs execute in their own tasks, handling various exit reasons such as hypercalls, interrupts, halts, and system shutdown events.

Sources: src/vmm/mod.rs(L42 - L65)  src/vmm/vcpus.rs(L275 - L367) 

VM Configuration

AxVisor uses TOML-based configuration files to define VM properties:

  1. Configuration files are stored in configs/vms/ directory
  2. Default configurations are provided for different architectures (x86_64, aarch64, riscv64)
  3. Configurations define VM properties like ID, name, CPU count, memory regions, etc.
  4. Configuration processing involves:
  • Parsing TOML into AxVMCrateConfig
  • Converting to AxVMConfig for VM creation
  • Using configuration to load appropriate VM images

Sources: src/vmm/config.rs(L1 - L43) 

VCPU Management

VCPUs are managed for each VM, with special handling for the primary VCPU:

classDiagram
class VMVCpus {
    -usize _vm_id
    -WaitQueue wait_queue
    -Vec~AxTaskRef~ vcpu_task_list
    -AtomicUsize running_halting_vcpu_count
    +new(VMRef) VMVCpus
    +add_vcpu_task(AxTaskRef)
    +wait()
    +wait_until(F)
    +notify_one()
    +mark_vcpu_running()
    +mark_vcpu_exiting() bool
}

class VCPU_Task {
    +stack_size KERNEL_STACK_SIZE
    +TaskExt ext
    +run() vcpu_run
}

class TaskExt {
    +VMRef vm
    +VCpuRef vcpu
    +new(VMRef, VCpuRef) TaskExt
}

VCPU_Task "1" *-- "1" TaskExt : contains
VMVCpus "1" *-- "*" VCPU_Task : manages

Sources: src/vmm/vcpus.rs(L27 - L40)  src/task.rs(L6 - L11) 

VCPU Initialization

For each VM, VCPUs are initialized:

  1. setup_vm_primary_vcpu() is called for each VM
  2. A VMVCpus structure is created to manage VCPUs for the VM
  3. The primary VCPU (ID 0) is set up first
  4. A task is created for the primary VCPU with the vcpu_run entry point
  5. The VCPU task is added to the VM's VCPU task list
  6. The VM's VCPU structure is stored in a global mapping

Sources: src/vmm/vcpus.rs(L218 - L231) 

VCPU Task Creation

Each VCPU runs in its own ArceOS task:

  1. alloc_vcpu_task() creates a task for a VCPU
  2. Tasks are created with a 256 KiB kernel stack
  3. The task's entry point is set to vcpu_run()
  4. If configured, the VCPU is assigned to specific physical CPUs
  5. The task is initialized with TaskExt containing references to the VM and VCPU
  6. The task is spawned using axtask::spawn_task()

Sources: src/vmm/vcpus.rs(L249 - L268)  src/task.rs(L1 - L19) 

VCPU Execution Loop

The main VCPU execution flow is:

  1. Wait for the VM to be in running state
  2. Mark the VCPU as running, incrementing the running count
  3. Enter execution loop:
  • Run the VCPU using vm.run_vcpu(vcpu_id)
  • Handle various exit reasons (hypercalls, interrupts, halts, etc.)
  • Check if the VM is shutting down
  1. When the VM is shutting down:
  • Mark the VCPU as exiting
  • If this was the last VCPU, decrement RUNNING_VM_COUNT
  • Wake the VMM wait queue if necessary
  • Exit the execution loop

Sources: src/vmm/vcpus.rs(L275 - L367) 

Secondary VCPU Management

AxVisor supports dynamic secondary VCPU initialization:

  1. When a primary VCPU requests to start a secondary VCPU (via CpuUp exit):
  • vcpu_on() is called with the target VCPU ID, entry point, and argument
  • The target VCPU's entry point and registers are set
  • A new task is created for the secondary VCPU
  • The task is added to the VM's VCPU task list
  1. Secondary VCPUs follow the same execution loop as the primary VCPU

Sources: src/vmm/vcpus.rs(L179 - L208)  src/vmm/vcpus.rs(L319 - L330) 

VM Coordination and Shutdown

VMs and VCPUs coordinate through wait queues:

  1. VMVCpus maintains a wait queue for each VM
  2. VCPUs can wait on the queue (e.g., when halted)
  3. Other VCPUs can notify waiting VCPUs (e.g., for interrupt handling)
  4. When a VM is shutting down, all VCPUs detect this condition
  5. Each VCPU marks itself as exiting
  6. The last exiting VCPU decrements RUNNING_VM_COUNT
  7. When RUNNING_VM_COUNT reaches 0, the VMM itself is notified to exit

This coordination ensures proper cleanup when VMs are shut down.

Sources: src/vmm/vcpus.rs(L42 - L108)  src/vmm/mod.rs(L55 - L65) 

VM Image Loading

VM images are loaded according to configuration:

  1. Images can be loaded from a FAT32 filesystem or from memory
  2. Configuration specifies the kernel path, entry point, and load address
  3. The image loading process occurs after VM creation but before VCPU setup

This completes the VM preparation before execution begins.

Sources: src/vmm/config.rs(L39 - L41) 

Hardware Abstraction Layer

Relevant source files

The Hardware Abstraction Layer (HAL) in AxVisor provides a uniform interface to access hardware features across multiple architectures. This component enables architecture-independent code to interact with hardware resources without needing to handle architecture-specific details directly. The HAL is a critical foundation that allows AxVisor to support x86_64, ARM/aarch64, and RISC-V architectures from a single codebase.

For detailed information about memory virtualization specifically, see Memory Management.

HAL Architecture Overview

AxVisor's HAL is implemented in the axhal crate, which provides platform-agnostic interfaces that delegate to architecture-specific implementations. This design enables higher-level components of AxVisor to operate independently of the underlying hardware.

flowchart TD
subgraph subGraph0["Hardware Abstraction Layer Structure"]
    hal["axhal - Hardware Abstraction Layer"]
    common["Common Interface"]
    arch_spec["Architecture-Specific Implementations"]
    mem_mgmt["Memory Management"]
    int_mgmt["Interrupt Management"]
    time_mgmt["Time Management"]
    cpu_mgmt["CPU Features"]
    x86_impl["x86_64 Implementation"]
    arm_impl["ARM/aarch64 Implementation"]
    riscv_impl["RISC-V Implementation"]
    x86_paging["x86_64 Paging"]
    x86_int["x86_64 Interrupts(x2APIC)"]
    x86_cpu["x86_64 CPU Features(VT-x)"]
    arm_paging["ARM Paging"]
    arm_int["ARM Interrupts(GICv2)"]
    arm_cpu["ARM CPU Features(EL2 mode)"]
    riscv_paging["RISC-V Paging"]
    riscv_int["RISC-V Interrupts"]
    riscv_cpu["RISC-V CPU Features(SBI runtime)"]
end

arch_spec --> arm_impl
arch_spec --> riscv_impl
arch_spec --> x86_impl
arm_impl --> arm_cpu
arm_impl --> arm_int
arm_impl --> arm_paging
common --> cpu_mgmt
common --> int_mgmt
common --> mem_mgmt
common --> time_mgmt
hal --> arch_spec
hal --> common
riscv_impl --> riscv_cpu
riscv_impl --> riscv_int
riscv_impl --> riscv_paging
x86_impl --> x86_cpu
x86_impl --> x86_int
x86_impl --> x86_paging

Sources:

  • Cargo.lock
  • Cargo.toml

HAL Core Components

The HAL provides several key functionality areas that are essential for hypervisor operation:

1. Architecture-Independent Interfaces

The HAL defines common interfaces for hardware operations that higher-level components can use regardless of architecture:

flowchart TD
subgraph subGraph1["HAL Common Interfaces"]
    hi["High-Level Components"]
    hal_if["HAL Interface Functions"]
    mem_if["Memory Interface- Page tables- Memory mapping"]
    int_if["Interrupt Interface- Register handlers- Enable/disable"]
    time_if["Time Interface- Current time- Timers"]
    cpu_if["CPU Interface- Core features- Virtualization"]
    subgraph Implementations["Implementations"]
        arch_impl["Architecture-Specific Implementations"]
        x86["x86_64"]
        arm["ARM/aarch64"]
        riscv["RISC-V"]
    end
end

arch_impl --> arm
arch_impl --> riscv
arch_impl --> x86
hal_if --> arch_impl
hal_if --> cpu_if
hal_if --> int_if
hal_if --> mem_if
hal_if --> time_if
hi --> hal_if

Sources:

  • Cargo.lock (lines 391-427)

2. Memory Management

The HAL provides memory management facilities including page table manipulation, address space management, and physical memory allocation:

ComponentFunctionArchitecture-Specific Aspects
Page TablesVirtual-to-physical translationx86_64: 4-level pagingARM: Stage 2 translationRISC-V: Sv39/Sv48 paging
Memory MappingMap/unmap regions of memoryHardware-specific protection bits
Physical MemoryAllocate and track physical memoryPlatform-specific memory layouts
Memory BarriersEnforce memory access orderingDifferent barrier instructions per architecture

Sources:

  • Cargo.lock (lines 1019-1032) - memory_addr and memory_set packages
  • Cargo.lock (lines 1062-1084) - page_table_entry and page_table_multiarch packages

3. Interrupt Handling

The HAL manages hardware interrupts through an architecture-independent interface:

flowchart TD
subgraph subGraph1["Interrupt Handling Architecture"]
    int_if["Interrupt Interface"]
    subgraph Architecture-Specific["Architecture-Specific"]
        int_reg["Register Handler"]
        int_en["Enable/Disable"]
        int_ack["Acknowledge"]
        x86_int["x86_64 (x2APIC)"]
        arm_int["ARM (GICv2)"]
        riscv_int["RISC-V (PLIC)"]
    end
end

int_ack --> arm_int
int_ack --> riscv_int
int_ack --> x86_int
int_en --> arm_int
int_en --> riscv_int
int_en --> x86_int
int_if --> int_ack
int_if --> int_en
int_if --> int_reg
int_reg --> arm_int
int_reg --> riscv_int
int_reg --> x86_int

Sources:

  • Cargo.lock (lines 137-142) - arm_gicv2 package
  • Cargo.lock (lines 1673-1683) - x2apic package

4. Timer and Time Management

The HAL provides interfaces for accessing system timers and managing time-related events:

Time FeatureFunctionArchitecture Implementation
System TimerProvides current system timex86_64: TSC or HPETARM: Generic TimerRISC-V: TIME CSR
Timer EventsSchedule timer interruptsPlatform-specific timer devices
Time SynchronizationSync time between host and guestsArchitecture-specific counters

Sources:

  • Cargo.lock (lines 1460-1463) - timer_list package

Architecture-Specific Implementations

The HAL implements architecture-specific features for the supported platforms:

x86_64 Implementation

The x86_64 implementation provides:

  • Memory management through 4-level paging
  • Interrupt handling using x2APIC
  • CPU virtualization using VT-x technology
  • I/O ports and MMIO access
  • MSR (Model Specific Registers) access

Sources:

  • Cargo.lock (lines 1686-1694) - x86 package
  • Cargo.lock (lines 1709-1718) - x86_64 package

ARM/aarch64 Implementation

The ARM implementation offers:

  • Memory management through stage 2 translation tables
  • Interrupt handling using GICv2
  • CPU virtualization using EL2 execution level
  • System register access
  • Virtualization extensions for memory and interrupts

Sources:

  • Cargo.lock (lines 6-12) - aarch64-cpu package
  • Cargo.lock (lines 137-142) - arm_gicv2 package
  • Cargo.lock (lines 145-151) - arm_pl011 package (UART)

RISC-V Implementation

The RISC-V implementation provides:

  • Memory management through Sv39/Sv48 paging
  • Interrupt handling using platform-specific interrupt controllers
  • SBI (Supervisor Binary Interface) runtime for hypervisor services
  • H-extension support for virtualization (where available)

Sources:

  • Cargo.lock (lines 1203-1213) - riscv package
  • Cargo.lock (lines 1320-1333) - sbi-rt and sbi-spec packages

Integration with Virtual CPUs

The HAL provides the foundation for virtual CPU (VCPU) implementations by exposing hardware virtualization features through a common interface:

flowchart TD
subgraph subGraph1["HAL Integration with VCPUs"]
    axhal["axhal (HAL)"]
    axvcpu["axvcpu (Virtual CPU Interface)"]
    x86_vcpu["x86_vcpu Implementation"]
    arm_vcpu["arm_vcpu Implementation"]
    riscv_vcpu["riscv_vcpu Implementation"]
    subgraph subGraph0["Architecture-Specific HAL Features Used by VCPUs"]
        x86_vt["x86 VT-x Features- VMCS- EPT- VM Entry/Exit"]
        arm_el2["ARM EL2 Features- Stage 2 MMU- vGIC- HVC"]
        riscv_h["RISC-V H-Extension- Guest mode- Two-stage translation"]
    end
end

arm_vcpu --> arm_el2
axhal --> axvcpu
axvcpu --> arm_vcpu
axvcpu --> riscv_vcpu
axvcpu --> x86_vcpu
riscv_vcpu --> riscv_h
x86_vcpu --> x86_vt

Sources:

  • Cargo.lock (lines 538-544) - axvcpu package
  • Cargo.lock (lines 154-168) - arm_vcpu package
  • Cargo.lock (lines 1248-1269) - riscv_vcpu package
  • Cargo.lock (lines 1721-1739) - x86_vcpu package

Hardware Resource Management

Memory Allocation

The HAL works with the memory allocator (axalloc) to provide physical memory management:

FeatureDescription
Physical Memory AllocationAllocates physical memory pages for guest VMs
Memory Region ManagementManages contiguous regions of physical memory
Device Memory MappingMaps device MMIO regions into address spaces
DMA OperationsManages DMA-accessible memory regions

Sources:

  • Cargo.lock (lines 196-205) - axalloc package

Device Access

The HAL provides access to physical devices and exposes platform-specific devices:

Device TypeFunctionImplementation
UARTSerial communicationx86_64: 8250/16550 UARTARM: PL011 UARTRISC-V: SBI console
Interrupt ControllerInterrupt managementx86_64: APIC/x2APICARM: GICv2RISC-V: PLIC
TimerTime-keepingPlatform-specific timer devices
Platform-specificBoard-specific functionalityCustom device drivers

Sources:

  • Cargo.lock (lines 145-151) - arm_pl011 package
  • Cargo.lock (lines 1035-1038) - ns16550a package
  • Cargo.lock (lines 137-142) - arm_gicv2 package

HAL Initialization

During system boot, the HAL is initialized with architecture-specific procedures:

  1. Early platform detection and initialization
  2. Memory map discovery and setup
  3. Architecture-specific hardware initialization
  4. Interrupt controller setup
  5. Timer initialization
  6. Per-CPU state initialization

This initialization sequence ensures that the hardware is properly configured before higher-level hypervisor components begin execution.

Sources:

  • Cargo.lock (lines 391-427) - axhal package

Summary

The Hardware Abstraction Layer in AxVisor provides a unified interface to access hardware features across multiple architectures. It implements architecture-specific features while presenting a common API to higher-level components. This design enables AxVisor to support multiple guest architectures (x86_64, ARM/aarch64, and RISC-V) from a single codebase, allowing for efficient hypervisor operation across diverse hardware platforms.

The HAL is tightly integrated with other components of AxVisor, particularly with memory management, virtual CPU implementations, and device emulation, forming the foundation upon which the entire hypervisor is built.

Memory Management

Relevant source files

This page documents the memory management subsystem in AxVisor, focusing on how memory virtualization and address space management are implemented in the hypervisor. For information about the hardware abstraction layer, see Hardware Abstraction Layer.

Overview

AxVisor's memory management system is responsible for:

  • Virtualizing physical memory for guest VMs
  • Managing address spaces for both hypervisor and VMs
  • Handling stage-2 page tables for memory virtualization
  • Performing address translations between different memory spaces
  • Allocating and mapping memory regions for guest VMs

The system utilizes the axaddrspace crate to provide architecture-independent abstractions for memory management while supporting x86_64, ARM/aarch64, and RISC-V architectures.

flowchart TD
subgraph subGraph2["Hardware Abstraction"]
    axhal["axhal"]
    paging["Paging Subsystem"]
end
subgraph subGraph1["Virtual Machine"]
    vm["VM"]
    vcpu["VCPU"]
    guest_memory["Guest Memory"]
end
subgraph subGraph0["Memory Management System"]
    axaddrspace["axaddrspace"]
    addrspace["AddressSpace"]
    memory_regions["MemoryRegion Management"]
    page_tables["Page Table Handling"]
    addr_translation["Address Translation"]
end

addrspace --> guest_memory
addrspace --> page_tables
axaddrspace --> addr_translation
axaddrspace --> addrspace
axaddrspace --> memory_regions
axaddrspace --> page_tables
axhal --> paging
page_tables --> paging
vcpu --> guest_memory
vm --> guest_memory
vm --> memory_regions
vm --> vcpu

Sources: Cargo.toml(L35 - L37) 

Address Spaces and Memory Virtualization

In AxVisor, memory virtualization involves three distinct address spaces:

  1. Guest Virtual Address (GVA) - Virtual addresses used by the guest OS
  2. Guest Physical Address (GPA) - Physical addresses from the guest OS perspective
  3. Host Physical Address (HPA) - Actual physical addresses in the host system

Memory virtualization is achieved through two levels of translation:

  • First-stage translation (managed by the guest): GVA → GPA
  • Second-stage translation (managed by the hypervisor): GPA → HPA
flowchart TD
subgraph subGraph1["Hypervisor Control"]
    HPA["Host PhysicalAddress (HPA)"]
end
subgraph subGraph0["Guest OS Control"]
    GVA["Guest VirtualAddress (GVA)"]
    GPA["Guest PhysicalAddress (GPA)"]
end

GPA --> HPA
GVA --> GPA
GVA --> HPA

Sources: configs/platforms/x86_64-qemu-q35.toml(L15 - L30) 

Address Space Management

The axaddrspace crate provides a unified interface for managing address spaces across different architectures. It handles the creation, manipulation, and destruction of second-stage page tables used for GPA to HPA translations.

Key components include:

  • AddressSpace: Represents a virtual address space for a guest VM
  • MemoryRegion: Defines a contiguous region of memory with specific permissions
  • PageTable: Manages the page tables for address translation
classDiagram
class AddressSpace {
    +root_page_table
    +memory_regions
    +create()
    +map()
    +unmap()
    +query()
    +contains()
}

class MemoryRegion {
    +start
    +size
    +flags
    +map_type
    
}

class PageTable {
    +arch_specific_implementation
    +map_page()
    +unmap_page()
    +translate()
}

AddressSpace "1" --> "many" MemoryRegion : contains
AddressSpace "1" --> "1" PageTable : uses

Sources: Cargo.toml(L37) 

Memory Region Types

AxVisor supports different types of memory regions that can be mapped into a guest VM's address space:

Region TypeDescriptionUse Case
MAP_ALLOCMemory allocated from hypervisor and mapped to guestRegular guest memory
MAP_IDENTICALIdentity-mapped memory from host to guestDevice pass-through, shared memory
MMIOMemory-mapped I/O regionsDevice access
ROMRead-only memoryBIOS, firmware

Memory regions are configured with specific access permissions (read, write, execute) and mapping types.

Sources: configs/vms/arceos-x86_64.toml(L40 - L44)  configs/platforms/x86_64-qemu-q35.toml(L37 - L45) 

VM Memory Configuration

Virtual machines in AxVisor are configured with memory regions specified in their configuration files. Here's how memory regions are defined:

memory_regions = [
    [base_paddr, size, flags, map_type],
]

Where:

  • base_paddr: Base guest physical address
  • size: Size of the memory region in bytes
  • flags: Access permissions (bit 0: read, bit 1: write, bit 2: execute)
  • map_type: 0 for MAP_ALLOC, 1 for MAP_IDENTICAL

The hypervisor creates and manages these memory regions when a VM is initialized.

flowchart TD
subgraph subGraph0["Configuration Example"]
    config["memory_regions = [[0x0000_0000, 0x100_0000, 0x7, 0],]"]
end
vmconfig["VM Configuration"]
mm_region["Memory Region Specification"]
vmm["VMM"]
addrspace["Address Space"]
map_operation["Memory Mapping"]

addrspace --> map_operation
config --> mm_region
mm_region --> vmm
vmconfig --> mm_region
vmm --> addrspace

Sources: configs/vms/arceos-x86_64.toml(L40 - L44)  configs/vms/nimbos-x86_64.toml(L40 - L44) 

Page Table Management

AxVisor uses the page_table_multiarch and page_table_entry crates to provide architecture-independent page table management. These crates abstract the architecture-specific details of page tables for x86_64, ARM/aarch64, and RISC-V.

Key functionalities include:

  • Creating and managing page tables
  • Mapping guest physical addresses to host physical addresses
  • Handling page faults and TLB operations
  • Supporting different page sizes for efficient memory mapping

The page tables implement the second-stage translation (GPA → HPA) required for memory virtualization.

Sources: Cargo.toml(L44 - L45) 

Address Translation

Address translation in AxVisor involves converting between different address spaces:

  1. GPA to HPA Translation: Used when a guest VM accesses physical memory
  2. HPA to GPA Translation: Used for handling page faults and device emulation
  3. Linear Address Translation: Used for quick conversions between host virtual and physical addresses

The physical-virtual offset defined in the platform configuration allows for efficient linear mapping between host virtual and physical addresses.

flowchart TD
subgraph subGraph1["Translation Components"]
    stage2_pt["Stage-2 Page Tables"]
    tlb["TLB"]
    page_fault["Page Fault Handler"]
end
subgraph subGraph0["Translation Process"]
    gpa["Guest Physical Address (GPA)"]
    hpa["Host Physical Address (HPA)"]
end

gpa --> hpa
gpa --> stage2_pt
hpa --> gpa
page_fault --> stage2_pt
stage2_pt --> hpa
stage2_pt --> tlb

Sources: configs/platforms/x86_64-qemu-q35.toml(L22 - L26) 

Memory Isolation and Protection

AxVisor ensures memory isolation between guest VMs and between guests and the hypervisor through several mechanisms:

  1. Separate Address Spaces: Each VM has its own address space with second-stage page tables
  2. Permission Enforcement: Memory regions have specific access permissions enforced by hardware
  3. MMIO Protection: Special handling for memory-mapped I/O regions
  4. EPT/NPT/Stage-2 MMU: Hardware virtualization extensions enforce memory isolation

This ensures that one VM cannot access the memory of another VM or the hypervisor, providing strong security isolation.

Sources: Cargo.toml(L37) 

Kernel Memory Layout

The hypervisor kernel itself has a specific memory layout defined by the linker script. This layout organizes different sections of the kernel in memory:

SectionDescription
.textExecutable code
.rodataRead-only data
.dataInitialized data
.tdata/.tbssThread-local storage
.percpuPer-CPU data
.bssUninitialized data

The layout ensures proper memory alignment and access protection for each section.

Sources: scripts/lds/linker.lds.S(L11 - L76) 

Timer Subsystem and Memory

The VMM timer subsystem interacts with memory management through the allocation and management of timer events. Each timer event is stored in memory and scheduled based on its deadline.

flowchart TD
subgraph subGraph1["Memory Management"]
    allocation["Memory Allocation"]
    percpu["Per-CPU Storage"]
end
subgraph subGraph0["Timer Subsystem"]
    timer_list["TimerList"]
    timer_event["VmmTimerEvent"]
    register["register_timer()"]
    check["check_events()"]
end
time_value["TimeValue"]

allocation --> timer_event
check --> timer_list
percpu --> timer_list
register --> timer_list
time_value --> timer_list
timer_list --> timer_event

Sources: src/vmm/timer.rs(L18 - L23)  src/vmm/timer.rs(L44) 

Architecture-Specific Considerations

While AxVisor provides architecture-independent abstractions for memory management, there are important architecture-specific considerations:

x86_64

  • Uses Extended Page Tables (EPT) for second-stage translation
  • Supports large pages (2MB, 1GB) for efficient mapping
  • Handles specific x86 memory types and caching attributes

ARM/aarch64

  • Uses Stage-2 translation tables
  • Supports different EL2 translation regimes
  • Handles ARM-specific memory attributes

RISC-V

  • Uses Sv39/Sv48 page tables with appropriate extensions
  • Implements RISC-V specific memory permission bits
  • Handles RISC-V address translation modes

Each architecture implementation conforms to the common interfaces provided by axaddrspace, page_table_entry, and page_table_multiarch.

Sources: Cargo.toml(L44 - L45) 

Conclusion

AxVisor's memory management system provides a robust, architecture-independent framework for virtualizing memory across different processor architectures. By abstracting the details of address space management and page table handling, it enables efficient memory virtualization while ensuring strong isolation between guest VMs.

The system leverages hardware virtualization features to minimize overhead while providing the flexibility needed to support diverse guest operating systems and configurations.

Configuration

Relevant source files

This document describes the configuration system used by AxVisor. Configuration files define essential parameters for both the hypervisor and the virtual machines it manages. This page covers the structure and format of configuration files, how they are processed, and the available configuration options. For specific VM configuration details, see VM Configuration.

Overview of Configuration System

AxVisor uses TOML files for configuration, which provide a clear and human-readable format for defining hypervisor and virtual machine parameters. Two primary types of configuration files exist:

  1. Platform Configurations - Define hardware-specific settings for the target platform
  2. VM Configurations - Define settings for individual virtual machines

These configuration files are processed during the build process and at runtime to initialize the hypervisor and its virtual machines.

flowchart TD
subgraph subGraph1["Configuration Processing"]
    build_process["Build Process"]
    vmm_init["VMM::init()"]
    axconfig_rs["axconfig.rs"]
    vms["Virtual Machines"]
end
subgraph subGraph0["Configuration System"]
    config_files["TOML Configuration Files"]
    platform_config["Platform Configsconfigs/platforms/*.toml"]
    vm_config["VM Configsconfigs/vms/*.toml"]
    hardware_params["Hardware ParametersMemory layoutDevice mappingsArchitecture settings"]
    vm_params["VM ParametersCPU countMemory allocationDevice assignments"]
    build_config["Build ConfigurationMakefile parameters"]
end

build_config --> config_files
build_process --> axconfig_rs
config_files --> platform_config
config_files --> vm_config
platform_config --> build_process
platform_config --> hardware_params
vm_config --> vm_params
vm_config --> vmm_init
vmm_init --> vms

Sources: README.md(L71 - L74)  README.md(L77 - L79) 

Configuration File Structure

Platform Configuration Files

Platform configuration files define the hardware-specific settings for the target platform. These files are located in the configs/platforms/ directory with filenames that correspond to the platform, such as x86_64-qemu-q35.toml.

A platform configuration file has the following main sections:

SectionPurpose
Top-levelArchitecture and platform identifiers
[plat]Memory layout, address spaces, and offsets
[devices]Device mappings, MMIO regions, and hardware configuration

Example platform configuration excerpt:

# Architecture identifier
arch = "x86_64"
# Platform identifier
platform = "x86_64-qemu-q35"

[plat]
# Base virtual address of the kernel image
kernel-base-vaddr = "0xffff_8000_0020_0000"
# Linear mapping offset
phys-virt-offset = "0xffff_8000_0000_0000"

[devices]
# MMIO regions with format (`base_paddr`, `size`)
mmio-regions = [
    [0xfec0_0000, 0x1000],      # IO APIC
    [0xfed0_0000, 0x1000],      # HPET
    [0xfee0_0000, 0x1000],      # Local APIC
]

Sources: configs/platforms/x86_64-qemu-q35.toml(L1 - L54) 

VM Configuration Files

VM configuration files define the properties of individual virtual machines that will be managed by AxVisor. These files are located in the configs/vms/ directory with names like arceos-x86_64.toml.

A VM configuration file contains these main sections:

SectionPurpose
[base]Basic VM information (ID, name, type, CPU count)
[kernel]Kernel image details and loading parameters
[devices]Device specifications (emulated and passthrough devices)

Example VM configuration excerpt:

[base]
# Guest VM ID
id = 1
# Guest VM name
name = "arceos"
# Virtualization type
vm_type = 1
# The number of virtual CPUs
cpu_num = 1

[kernel]
# The entry point of the kernel image
entry_point = 0x8000
# The location of image: "memory" | "fs"
image_location = "fs"
# The file path of the kernel image
kernel_path = "arceos-x86_64.bin"
# The load address of the kernel image
kernel_load_addr = 0x20_0000

Sources: configs/vms/arceos-x86_64.toml(L1 - L37)  configs/vms/nimbos-x86_64.toml(L1 - L39) 

VM Configuration Options

The VM configuration file contains multiple sections that control various aspects of the virtual machine. Here's a detailed breakdown of the available options:

flowchart TD
subgraph subGraph0["VM Configuration Structure"]
    vm_config["VM Configuration File(.toml)"]
    base_section["[base] SectionVM Identity"]
    kernel_section["[kernel] SectionGuest Image & Boot"]
    devices_section["[devices] SectionDevice Configuration"]
    base_options["id: VM identifiername: VM namevm_type: Virtualization typecpu_num: Number of vCPUsphys_cpu_sets: CPU pinning"]
    kernel_options["entry_point: Entry addressimage_location: 'memory' or 'fs'kernel_path: Image pathkernel_load_addr: Load addressmemory_regions: Memory layout"]
    optional_kernel["bios_path: BIOS imagebios_load_addr: BIOS load addressramdisk_path: Ramdisk imagedisk_path: Disk image"]
    device_options["emu_devices: Emulated devicespassthrough_devices: Passthrough devices"]
end

base_section --> base_options
devices_section --> device_options
kernel_section --> kernel_options
kernel_section --> optional_kernel
vm_config --> base_section
vm_config --> devices_section
vm_config --> kernel_section

Sources: configs/vms/arceos-x86_64.toml(L1 - L78)  configs/vms/nimbos-x86_64.toml(L1 - L78) 

Base Section

The [base] section defines the core identity and properties of the VM:

OptionDescriptionExample
idUnique identifier for the VMid = 1
nameHuman-readable name for the VMname = "arceos"
vm_typeType of virtualizationvm_type = 1
cpu_numNumber of virtual CPUscpu_num = 1
phys_cpu_setsPhysical CPU pinningphys_cpu_sets = [1]

Kernel Section

The [kernel] section defines how the guest kernel is loaded and executed:

OptionDescriptionExample
entry_pointEntry point address of the kernelentry_point = 0x8000
image_locationSource location for guest image ("memory" or "fs")image_location = "fs"
kernel_pathPath to the kernel image filekernel_path = "arceos-x86_64.bin"
kernel_load_addrPhysical address to load the kernelkernel_load_addr = 0x20_0000
bios_pathPath to the BIOS image file (optional)bios_path = "axvm-bios.bin"
bios_load_addrAddress to load the BIOS (optional)bios_load_addr = 0x8000
ramdisk_pathPath to the ramdisk image (optional)ramdisk_path = "ramdisk.img"
ramdisk_load_addrAddress to load the ramdisk (optional)ramdisk_load_addr = 0x1000_0000
disk_pathPath to the disk image (optional)disk_path = "disk.img"
memory_regionsMemory regions with format (base_paddr,size,flags,map_type)memory_regions = [[0x0, 0x100_0000, 0x7, 0]]

Devices Section

The [devices] section defines the virtual and passthrough devices for the VM:

OptionDescriptionFormat
emu_devicesEmulated devices[Name, Base-Ipa, Ipa_len, Alloc-Irq, Emu-Type, EmuConfig]
passthrough_devicesPassthrough devices[Name, Base-Ipa, Base-Pa, Length, Alloc-Irq]

Example passthrough device entry:

passthrough_devices = [
    [
        "IO APIC",
        0xfec0_0000,
        0xfec0_0000,
        0x1000,
        0x1,
    ]
]

Sources: configs/vms/arceos-x86_64.toml(L50 - L78)  configs/vms/nimbos-x86_64.toml(L50 - L78) 

Image Loading Methods

AxVisor supports two methods for loading guest VM images:

  1. File System Loading (image_location = "fs"):
  • Loads the guest image from a FAT32 file system
  • Requires setting up a disk image file with the guest image(s)
  • Used when the guest images need to be changed without rebuilding the hypervisor
  1. Memory Loading (image_location = "memory"):
  • Loads the guest image from memory, bound to the hypervisor through static compilation
  • Uses include_bytes! to include the image in the hypervisor binary
  • Useful for embedded scenarios or when the guest image is fixed
flowchart TD
subgraph subGraph0["Image Loading Process"]
    config["VM Configuration"]
    load_method["image_location"]
    fs_load["Load from File System"]
    mem_load["Load from Memory"]
    mount["Mount FAT32 Filesystem"]
    read_fs["Read kernel_path from disk"]
    load_mem["Load to kernel_load_addr"]
    compile["Statically Compiled Imageinclude_bytes!()"]
    load_mem2["Load to kernel_load_addr"]
    start_vm["Start VM"]
end

compile --> load_mem2
config --> load_method
fs_load --> mount
load_mem --> start_vm
load_mem2 --> start_vm
load_method --> fs_load
load_method --> mem_load
mem_load --> compile
mount --> read_fs
read_fs --> load_mem

Sources: README.md(L78 - L112) 

Build Configuration

The build configuration controls how AxVisor is compiled and which features are enabled. The primary method for configuring the build is through the make command and its parameters.

Key build parameters include:

ParameterDescriptionExample
ARCHTarget architectureARCH=aarch64
LOGLog levelLOG=info
VM_CONFIGSPath to VM configuration file(s)VM_CONFIGS=configs/vms/arceos-aarch64.toml
ACCELEnable hardware accelerationACCEL=n
APP_FEATURESAdditional featuresAPP_FEATURES=fs

Example build command:

make ACCEL=n ARCH=aarch64 LOG=info VM_CONFIGS=configs/vms/arceos-aarch64.toml APP_FEATURES=fs run

Additionally, build-time dependencies are declared in Cargo.toml, including the TOML parser and configuration tools:

[build-dependencies]
toml = { git = "https://github.com/arceos-hypervisor/toml.git", branch = "no_std" }
axconfig = { git = "https://github.com/arceos-hypervisor/arceos.git", branch = "vmm" }

Sources: Cargo.toml(L47 - L52)  README.md(L112 - L113) 

Configuration Processing

Configuration files are processed both at build time and at runtime:

  1. Build-time Processing:
  • Platform configurations are processed during the build
  • Generate architecture-specific code based on the platform configuration
  • Set up memory layouts and device mappings
  1. Runtime Processing:
  • VM configurations are processed when AxVisor starts
  • The VMM (Virtual Machine Manager) reads and applies VM configurations
  • Creates VM instances according to the configuration

The configuration files are parsed using a TOML parser adapted for use in a no_std environment. The parsed configuration is then used to initialize various components of the hypervisor.

For example, timer configuration parameters are used to set up the timer subsystem:

// Timer interrupt frequency is configured in the platform configuration
const PERIODIC_INTERVAL_NANOS: u64 = axhal::time::NANOS_PER_SEC / axconfig::TICKS_PER_SEC as u64;

Sources: src/vmm/timer.rs(L12 - L13) 

Configuration Tools

AxVisor provides additional tools to help with configuration:

  1. axvmconfig: A tool for generating VM configuration files
  • Simplifies the process of creating complex VM configurations
  • Provides validation of configuration parameters
  1. dev_env.py: A script for setting up the development environment
  • Helps localize relevant crates for development and debugging
  • Makes it easier to work with the modular components of AxVisor

Sources: README.md(L77 - L79)  README.md(L210 - L214) 

Conclusion

AxVisor's configuration system provides a flexible and powerful way to define both the hypervisor platform and the virtual machines it manages. By using TOML files, the configuration is human-readable and easy to modify. The separation between platform and VM configurations allows for a modular approach, where the same hypervisor build can host different VMs without requiring recompilation.

Understanding the configuration options is essential for successfully deploying AxVisor and managing virtual machines with different requirements and characteristics.

VM Configuration

Relevant source files

This page describes how to configure virtual machines (VMs) in AxVisor. The configuration system uses TOML files to define VM properties, including basic VM information, kernel image details, memory layout, and device specifications. For information about platform-specific configuration options, see Platform-Specific Configuration.

Configuration Overview

AxVisor uses a structured configuration approach that allows users to declaratively define VM properties using TOML files. These configuration files are processed during VM initialization and determine how VMs are created, what resources they receive, and how they interact with the host system.

flowchart TD
subgraph subGraph1["VM Management"]
    vm["VM Object"]
    vcpu["VCPU Objects"]
    memory["Memory Regions"]
    devices["Device Configuration"]
end
subgraph subGraph0["VM Configuration System"]
    toml_config["TOML Configuration Files"]
    vmm_init["VMM::init()"]
    vm_creation["VM Creation"]
    image_loading["Image Loading"]
end

toml_config --> vmm_init
vm --> devices
vm --> memory
vm --> vcpu
vm_creation --> image_loading
vm_creation --> vm
vmm_init --> vm_creation

Sources: README.md(L69 - L78)  configs/vms/arceos-x86_64.toml(L1 - L78) 

Configuration File Structure

The VM configuration file is divided into three main sections: [base], [kernel], and [devices].

Base Configuration

The [base] section defines fundamental VM properties:

[base]
# Guest vm id.
id = 1
# Guest vm name.
name = "arceos"
# Virtualization type.
vm_type = 1
# The number of virtual CPUs.
cpu_num = 1
# Guest vm physical cpu sets.
phys_cpu_sets = [1]
ParameterDescriptionExample
idUnique identifier for the VM1
nameDescriptive name for the VM"arceos"
vm_typeType of virtualization (1 = full virtualization)1
cpu_numNumber of virtual CPUs allocated to the VM1
phys_cpu_setsPhysical CPU cores assigned to the VM[1]

Sources: configs/vms/arceos-x86_64.toml(L1 - L13)  configs/vms/nimbos-x86_64.toml(L1 - L13) 

Kernel Configuration

The [kernel] section specifies how the guest kernel image should be loaded and executed:

[kernel]
# The entry point of the kernel image.
entry_point = 0x8000
# The location of image: "memory" | "fs".
image_location = "fs"
# The file path of the BIOS image.
bios_path = "axvm-bios.bin"
# The load address of the BIOS image.
bios_load_addr = 0x8000
# The file path of the kernel image.
kernel_path = "arceos-x86_64.bin"
# The load address of the kernel image.
kernel_load_addr = 0x20_0000

# Memory regions with format (`base_paddr`, `size`, `flags`, `map_type`).
# For `map_type`, 0 means `MAP_ALLOC`, 1 means `MAP_IDENTICAL`.
memory_regions = [
    [0x0000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b111
]
ParameterDescriptionExample
entry_pointStarting execution address for the guest0x8000
image_locationSource of the guest image ("memory" or "fs")"fs"
bios_pathPath to BIOS image (for x86)"axvm-bios.bin"
bios_load_addrMemory address where BIOS should be loaded0x8000
kernel_pathPath to kernel image"arceos-x86_64.bin"
kernel_load_addrMemory address where kernel should be loaded0x20_0000
memory_regionsMemory regions allocated to the VM[[0x0, 0x100_0000, 0x7, 0]]

Sources: configs/vms/arceos-x86_64.toml(L14 - L44)  configs/vms/nimbos-x86_64.toml(L14 - L44) 

Device Configuration

The [devices] section defines both emulated and passthrough devices:

[devices]
# Emu_devices.
# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig.
emu_devices = []

# Pass-through devices.
# Name Base-Ipa Base-Pa Length Alloc-Irq.
passthrough_devices = [
    [
        "IO APIC",
        0xfec0_0000,
        0xfec0_0000,
        0x1000,
        0x1,
    ],
    [
        "Local APIC",
        0xfee0_0000,
        0xfee0_0000,
        0x1000,
        0x1,
    ],
]
ParameterDescriptionFormat
emu_devicesList of emulated devices[Name, Base-Ipa, Ipa_len, Alloc-Irq, Emu-Type, EmuConfig]
passthrough_devicesList of passthrough devices[Name, Base-Ipa, Base-Pa, Length, Alloc-Irq]

Sources: configs/vms/arceos-x86_64.toml(L46 - L78)  configs/vms/nimbos-x86_64.toml(L46 - L78) 

Memory Region Configuration

Memory regions in the memory_regions parameter use a structured format:

flowchart TD
subgraph subGraph0["Memory Region Format"]
    base_paddr["base_paddr: Starting Physical Address"]
    size["size: Region Size in Bytes"]
    flags["flags: Memory Permissions (Bit flags)"]
    map_type["map_type: Mapping Type (0=MAP_ALLOC, 1=MAP_IDENTICAL)"]
end
read["Read Permission"]
write["Write Permission"]
execute["Execute Permission"]

flags --> execute
flags --> read
flags --> write

Memory Flags

The flags field defines memory permissions as bit flags:

  • Bit 0 (0x1): Read permission
  • Bit 1 (0x2): Write permission
  • Bit 2 (0x4): Execute permission

Common combinations:

  • 0x7 (0b111): Read + Write + Execute
  • 0x3 (0b011): Read + Write
  • 0x1 (0b001): Read-only

Mapping Types

The map_type field defines how memory is mapped:

  • 0 (MAP_ALLOC): Allocate new memory for the VM
  • 1 (MAP_IDENTICAL): Identity map to physical memory (for passthrough)

Sources: configs/vms/arceos-x86_64.toml(L40 - L44)  configs/vms/nimbos-x86_64.toml(L40 - L44) 

Image Loading Methods

AxVisor supports two methods for loading guest images:

flowchart TD
subgraph subGraph1["Memory Loading"]
    mem_loading["Memory Loading"]
    static["Reference include_bytes! data"]
    load_mem["Load to kernel_load_addr"]
end
subgraph subGraph0["File System Loading"]
    fs_loading["File System Loading"]
    mount["Mount disk.img"]
    read_file["Read kernel_path from FS"]
    load_fs["Load to kernel_load_addr"]
end
config["VM Configuration"]
boot["Boot VM"]

config --> fs_loading
config --> mem_loading
fs_loading --> mount
load_fs --> boot
load_mem --> boot
mem_loading --> static
mount --> read_file
read_file --> load_fs
static --> load_mem

File System Loading

When image_location="fs" is specified:

  1. AxVisor mounts a FAT32 disk image file (typically disk.img)
  2. Reads the kernel image from the specified kernel_path
  3. Loads it into memory at kernel_load_addr

Required configuration:

  • image_location = "fs"
  • kernel_path (path within filesystem)
  • kernel_load_addr (memory address for loading)
  • Optionally, bios_path and bios_load_addr for x86

Memory Loading

When image_location="memory" is specified:

  1. The guest kernel image is statically compiled into the AxVisor binary
  2. The image is loaded from memory at runtime

Required configuration:

  • image_location = "memory"
  • kernel_path (must point to a file in the workspace)
  • kernel_load_addr (memory address for loading)
  • Optionally, bios_path and bios_load_addr for x86

Sources: README.md(L79 - L112) 

Configuration and Memory Layout Relationships

Each VM's memory layout is determined by both VM-specific configuration and platform-specific memory layouts:

flowchart TD
subgraph subGraph2["VM Memory Layout"]
    vm_memory["VM Physical Memory"]
    bios["BIOS Region"]
    kernel["Kernel Region"]
    device_mmio["Device MMIO Regions"]
end
subgraph subGraph1["VM Configuration"]
    vm_config["VM Config (arceos-x86_64.toml)"]
    memory_regions["Memory Regions"]
    kernel_load_addr["Kernel Load Address"]
    bios_load_addr["BIOS Load Address"]
end
subgraph subGraph0["Platform Configuration"]
    plat_config["Platform Config (x86_64-qemu-q35.toml)"]
    phys_memory["Physical Memory Base/Size"]
    kernel_base["Kernel Base Address"]
    aspace["Kernel Address Space"]
end

bios_load_addr --> bios
kernel_load_addr --> kernel
memory_regions --> vm_memory
phys_memory --> vm_memory
plat_config --> aspace
plat_config --> kernel_base
plat_config --> phys_memory
vm_config --> bios_load_addr
vm_config --> kernel_load_addr
vm_config --> memory_regions

Sources: configs/vms/arceos-x86_64.toml(L40 - L44)  configs/platforms/x86_64-qemu-q35.toml(L13 - L28) 

Using the axvmconfig Tool

For complex VM configurations, AxVisor provides the axvmconfig tool that can generate custom configuration files. To use this tool:

  1. Install the tool: cargo install axvmconfig
  2. Define your VM configuration using the tool's syntax
  3. Generate a TOML configuration file

See the axvmconfig documentation for detailed usage.

Sources: README.md(L72 - L74) 

Example Configuration: ArceOS on x86_64

Here's an example of configuring an ArceOS guest on x86_64:

# Vm base info configs
[base]
id = 1
name = "arceos"
vm_type = 1
cpu_num = 1
phys_cpu_sets = [1]

# Vm kernel configs
[kernel]
entry_point = 0x8000
image_location = "fs"
bios_path = "axvm-bios.bin"
bios_load_addr = 0x8000
kernel_path = "arceos-x86_64.bin"
kernel_load_addr = 0x20_0000

memory_regions = [
    [0x0000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M with RWX permissions
]

# Device specifications
[devices]
emu_devices = []
passthrough_devices = [
    ["IO APIC", 0xfec0_0000, 0xfec0_0000, 0x1000, 0x1],
    ["Local APIC", 0xfee0_0000, 0xfee0_0000, 0x1000, 0x1],
    ["HPET", 0xfed0_0000, 0xfed0_0000, 0x1000, 0x1],
]

Sources: configs/vms/arceos-x86_64.toml(L1 - L78) 

Boot Process with VM Configuration

During the boot process, AxVisor uses the VM configuration to set up and start the VM:

sequenceDiagram
    participant VMConfigFile as "VM Config File"
    participant VMMinit as "VMM::init()"
    participant VMObject as "VM Object"
    participant ImageLoader as "Image Loader"
    participant VCPUSetup as "VCPU Setup"

    VMConfigFile ->> VMMinit: Load configuration
    VMMinit ->> VMObject: Create VM with id, name, cpu_num
    VMObject ->> VMObject: Set up memory regions
    alt image_location = "fs"
        VMObject ->> ImageLoader: Load kernel from filesystem
    else image_location = "memory"
        VMObject ->> ImageLoader: Load kernel from memory
    end
    ImageLoader ->> VMObject: Load at kernel_load_addr
    VMObject ->> VCPUSetup: Create VCPUs
    VMObject ->> VMObject: Configure devices
    VMMinit ->> VMObject: Boot VM
    VMObject ->> VCPUSetup: Start execution at entry_point

Sources: README.md(L57 - L112)  src/vmm/timer.rs(L98 - L104) 

Timer Configuration

The VM timer subsystem is also affected by configuration settings:

flowchart TD
subgraph subGraph0["Timer Configuration"]
    timer_freq["timer-frequency in Platform Config"]
    periodic_interval["PERIODIC_INTERVAL_NANOS"]
    vmm_timer["VMM Timer Events"]
end
timer_events["Timer Events"]
vcpu_execution["VCPU Execution"]

periodic_interval --> vmm_timer
timer_events --> vcpu_execution
timer_freq --> periodic_interval
vmm_timer --> timer_events

Sources: src/vmm/timer.rs(L1 - L104)  configs/platforms/x86_64-qemu-q35.toml(L56 - L57) 

Conclusion

AxVisor's VM configuration system provides a flexible and declarative way to define virtual machine properties. By using TOML configuration files, users can easily customize VM attributes without modifying code. The configuration system supports different image loading methods, memory configurations, and device setups, enabling a wide range of virtualization scenarios.

For platform-specific configuration options that complement VM configurations, see Platform-Specific Configuration.

Platform-Specific Configuration

Relevant source files

This document describes the architecture-specific configuration options in AxVisor. It covers the configuration files and settings required for each supported hardware platform (x86_64, ARM/aarch64, and RISC-V). For information about virtual machine configuration, see VM Configuration.

Configuration System Overview

AxVisor uses TOML configuration files to define platform-specific settings, which are located in the configs/platforms/ directory. These files contain architecture-dependent parameters such as memory layout, device specifications, and timer frequencies.

flowchart TD
subgraph subGraph2["Runtime System"]
    vm_manager["VM Manager"]
    hal["Hardware Abstraction Layer"]
end
subgraph subGraph1["Build System"]
    axconfig["axconfigBuild-Time Config"]
    build_process["Build Process"]
end
subgraph subGraph0["Configuration Files"]
    platform_configs["Platform Configsconfigs/platforms/*.toml"]
    vm_configs["VM Configsconfigs/vms/*.toml"]
end

axconfig --> build_process
platform_configs --> axconfig
platform_configs --> hal
platform_configs --> vm_configs
vm_configs --> vm_manager

Sources: configs/platforms/x86_64-qemu-q35.toml(L1 - L58)  Cargo.toml(L47 - L52) 

Platform Configuration File Structure

Each platform configuration file follows a standardized format, with sections for platform identification, memory layout, and device specifications.

Example Configuration (x86_64-qemu-q35)

classDiagram
class PlatformConfig {
    arch: string
    platform: string
    plat: PlatformSettings
    devices: DeviceSettings
    
}

class PlatformSettings {
    family: string
    phys-memory-base: usize
    phys-memory-size: usize
    kernel-base-paddr: usize
    kernel-base-vaddr: usize
    phys-virt-offset: usize
    phys-bus-offset: usize
    kernel-aspace-base: usize
    kernel-aspace-size: usize
    
}

class DeviceSettings {
    pci-ecam-base: usize
    pci-bus-end: usize
    timer-frequency: usize
    mmio-regions: [(usize, usize) ]
    virtio-mmio-regions: [(usize, usize) ]
    pci-ranges: [(usize, usize) ]
}

PlatformConfig  -->  PlatformSettings
PlatformConfig  -->  DeviceSettings

Sources: configs/platforms/x86_64-qemu-q35.toml(L1 - L58) 

Memory Layout Configuration

The memory layout configuration is one of the most critical aspects of platform-specific settings. It defines the physical and virtual memory address spaces, kernel loading addresses, and memory mapping offsets.

Memory Layout Parameters

ParameterDescriptionExample (x86_64)
phys-memory-baseBase address of physical memory0x0
phys-memory-sizeSize of physical memory0x8000000 (128MB)
kernel-base-paddrPhysical address where kernel is loaded0x200000
kernel-base-vaddrVirtual address of the kernel image0xffff800000200000
phys-virt-offsetOffset for physical-to-virtual address translation0xffff800000000000
kernel-aspace-baseBase of kernel address space0xffff800000000000
kernel-aspace-sizeSize of kernel address space0x7fffffffff000

Sources: configs/platforms/x86_64-qemu-q35.toml(L13 - L30)  scripts/lds/linker.lds.S(L1 - L94) 

Device Configuration

Device configuration defines the memory-mapped I/O (MMIO) regions, PCI configuration, and other hardware-specific settings for each platform.

flowchart TD
subgraph subGraph1["Platform-Specific Implementations"]
    x86_impl["x86_64 ImplementationIO APIC, Local APIC, HPET"]
    arm_impl["ARM ImplementationGICv2, ARM-specific timers"]
    riscv_impl["RISC-V ImplementationSBI runtime, PLIC"]
end
subgraph subGraph0["Device Configuration Components"]
    mmio["MMIO RegionsPhysical address regionsfor memory-mapped devices"]
    virtio["VirtIO MMIO RegionsRegions for virtualized I/O devices"]
    pci["PCI ConfigurationECAM base, bus numbers,memory ranges"]
    timer["Timer SettingsFrequency and configuration"]
end

mmio --> arm_impl
mmio --> riscv_impl
mmio --> x86_impl
pci --> x86_impl
timer --> arm_impl
timer --> riscv_impl
timer --> x86_impl
virtio --> arm_impl
virtio --> riscv_impl
virtio --> x86_impl

Sources: configs/platforms/x86_64-qemu-q35.toml(L34 - L58)  configs/vms/arceos-x86_64.toml(L40 - L77)  configs/vms/nimbos-x86_64.toml(L40 - L77) 

MMIO Regions

The mmio-regions parameter defines the memory ranges reserved for memory-mapped devices. For x86_64-qemu-q35, these include:

  • PCI config space: 0xb0000000 - 0xc0000000
  • PCI devices: 0xfe000000 - 0xfec00000
  • IO APIC: 0xfec00000 - 0xfec01000
  • HPET: 0xfed00000 - 0xfed01000
  • Local APIC: 0xfee00000 - 0xfee01000

Sources: configs/platforms/x86_64-qemu-q35.toml(L36 - L45) 

PCI Configuration

PCI configuration varies by platform. For x86_64-qemu-q35, it includes:

  • PCI ECAM base: 0xb0000000
  • PCI bus end: 0xff

Sources: configs/platforms/x86_64-qemu-q35.toml(L49 - L54) 

Timer Configuration

Platform-specific timer configuration is essential for proper scheduling and timing in the hypervisor. The configuration specifies the timer frequency and related parameters.

flowchart TD
subgraph subGraph0["Timer Configuration"]
    timer_config["Platform Timer Configtimer-frequency"]
    periodic_interval["Periodic IntervalPERIODIC_INTERVAL_NANOS"]
    scheduler_next["scheduler_next_event()"]
    register_timer["register_timer()"]
    check_events["check_events()"]
end

periodic_interval --> scheduler_next
register_timer --> check_events
scheduler_next --> check_events
timer_config --> periodic_interval

Sources: configs/platforms/x86_64-qemu-q35.toml(L57)  src/vmm/timer.rs(L1 - L105) 

Timer Implementation

The timer subsystem is implemented in src/vmm/timer.rs and uses platform-specific timers through the Hardware Abstraction Layer (HAL). For x86_64-qemu-q35, the timer frequency is set at 4 GHz.

The timer system uses the following key parameters:

  • timer-frequency: Base frequency of the platform timer (e.g., 4 GHz for x86_64-qemu-q35)
  • PERIODIC_INTERVAL_NANOS: Derived from axconfig::TICKS_PER_SEC and axhal::time::NANOS_PER_SEC

Sources: src/vmm/timer.rs(L12)  configs/platforms/x86_64-qemu-q35.toml(L57) 

Relationship with VM Configuration

Platform-specific configurations form the foundation for VM configurations. VM configurations reference platform settings and may override or extend them for specific virtual machines.

flowchart TD
subgraph subGraph1["VM Config"]
    vm_base["VM Base Infoid, name, cpu_num"]
    vm_kernel["Kernel Configentry_point, image_location,kernel_path, kernel_load_addr"]
    vm_memory["Memory Regionsbase_paddr, size, flags, map_type"]
    vm_devices["Device Specsemu_devices, passthrough_devices"]
end
subgraph subGraph0["Platform Config"]
    plat_arch["arch: x86_64"]
    plat_platform["platform: x86_64-qemu-q35"]
    plat_memory["Memory LayoutPhysical and virtualaddress spaces"]
    plat_devices["Device ConfigurationMMIO regions, PCI, timers"]
end

plat_arch --> vm_base
plat_devices --> vm_devices
plat_memory --> vm_memory
plat_platform --> vm_base

Sources: configs/vms/arceos-x86_64.toml(L1 - L78)  configs/vms/nimbos-x86_64.toml(L1 - L78)  configs/platforms/x86_64-qemu-q35.toml(L1 - L58) 

Memory Region Configuration in VMs

VM configurations specify memory regions that must be compatible with the platform's memory layout. For example, a VM configuration might include:

memory_regions = [
    [0x0000_0000, 0x100_0000, 0x7, 0], # Low RAM 16M 0b111 R|W|EXECUTE
]

This defines a 16MB region starting at physical address 0, with read, write, and execute permissions (flags 0x7), using allocation mapping (map_type 0).

Sources: configs/vms/arceos-x86_64.toml(L42 - L44)  configs/vms/nimbos-x86_64.toml(L42 - L44) 

Device Passthrough in VMs

Platform-specific device configurations determine which devices can be passed through to VMs. The VM configuration specifies which devices to pass through and how to map them:

passthrough_devices = [
    ["IO APIC", 0xfec0_0000, 0xfec0_0000, 0x1000, 0x1],
    ["Local APIC", 0xfee0_0000, 0xfee0_0000, 0x1000, 0x1],
    ["HPET", 0xfed0_0000, 0xfed0_0000, 0x1000, 0x1],
]

Each entry specifies:

  1. Device name
  2. Base IPA (Intermediate Physical Address) in guest
  3. Base physical address in host
  4. Length of memory region
  5. Interrupt allocation flag

Sources: configs/vms/arceos-x86_64.toml(L55 - L77)  configs/vms/nimbos-x86_64.toml(L55 - L77) 

Architecture-Specific Considerations

Each supported architecture has specific configuration requirements and capabilities.

x86_64 Architecture

For x86_64 platforms, important considerations include:

  • APIC configuration (IO APIC and Local APIC)
  • HPET timer configuration
  • PCI configuration space mapping
  • Boot page table setup

Sources: configs/platforms/x86_64-qemu-q35.toml(L1 - L58)  configs/vms/arceos-x86_64.toml(L55 - L77)  configs/vms/nimbos-x86_64.toml(L55 - L77) 

ARM/aarch64 Architecture

While not explicitly shown in the provided files, support for ARM architecture is indicated in the Cargo.toml file with features like "arm-el2". ARM platforms would have specific considerations for:

  • GIC (Generic Interrupt Controller) configuration
  • ARM-specific timer devices
  • EL2 (Hypervisor) mode configuration

Sources: Cargo.toml(L43 - L45) 

RISC-V Architecture

RISC-V support is mentioned in the system overview diagrams. RISC-V platforms would have specific considerations for:

  • SBI (Supervisor Binary Interface) runtime configuration
  • PLIC (Platform-Level Interrupt Controller) setup
  • RISC-V privilege modes

Build Integration

The platform-specific configuration is integrated into the build process through the axconfig build dependency, which processes the configuration files at build time.

flowchart TD
subgraph subGraph1["Configuration Sources"]
    platform_config["Platform Config Filesconfigs/platforms/*.toml"]
    linker_script["Linker Scriptscripts/lds/linker.lds.S"]
end
subgraph subGraph0["Build Process"]
    toml_parser["TOML Parser"]
    axconfig["axconfig Tool"]
    build_system["Build System"]
    final_binary["Final Binary"]
end

axconfig --> build_system
axconfig --> linker_script
build_system --> final_binary
linker_script --> build_system
platform_config --> toml_parser
toml_parser --> axconfig

Sources: Cargo.toml(L47 - L52)  scripts/lds/linker.lds.S(L1 - L94) 

Building and Running

Relevant source files

This document provides detailed instructions on how to build AxVisor from source and run it with different guest virtual machines. For information about VM configuration options, see Configuration.

Prerequisites

Before building and running AxVisor, you need to set up your development environment with the following tools:

  • Rust development environment
  • cargo-binutils
  • QEMU (for testing)
  • Optional: musl-gcc (for building guest applications)
$ cargo install cargo-binutils

Sources: README.md(L64 - L68) 

Setting up the Development Environment

Automated Setup

AxVisor provides a Python script that automates the process of cloning dependent repositories and configuring the development environment.

$ python tool/dev_env.py

This script will:

  1. Create a crates directory
  2. Clone all required repositories
  3. Patch your Cargo.toml file
  4. Set up VSCode settings (if applicable)

You can specify a different repository URL using the --repo argument:

$ python tool/dev_env.py --repo https://github.com/your-fork/arceos-hypervisor

Sources: tool/dev_env.py(L1 - L106) 

Build System Overview

Build Process Diagram

flowchart TD
subgraph subGraph2["Make Targets"]
    make_build["make build"]
    make_run["make run"]
    make_debug["make debug"]
    make_clean["make clean"]
end
subgraph subGraph1["Configuration Inputs"]
    arch_opt["Architecture Options(x86_64, aarch64, riscv64)"]
    plat_opt["Platform Options"]
    feature_opt["Feature Options"]
    vm_config["VM Configuration Files"]
end
subgraph subGraph0["Build Process"]
    config["Configuration Generation"]
    build["Build AxVisor"]
    output["Output: ELF and Binary Files"]
end
binary["Binary file:APP_NAME_PLATFORM.bin"]
elf["ELF file:APP_NAME_PLATFORM.elf"]
asm["Assembly file:APP_NAME_PLATFORM.asm"]

arch_opt --> config
build --> output
config --> build
feature_opt --> config
make_build --> build
make_debug --> build
make_run --> build
output --> asm
output --> binary
output --> elf
plat_opt --> config
vm_config --> config

Sources: Makefile(L1 - L151)  README.md(L57 - L59) 

Building AxVisor

AxVisor uses a Makefile-based build system with numerous configuration options. The basic build command is:

$ make ARCH=<architecture> VM_CONFIGS=<config_path> build

Key Build Options

OptionDescriptionExample Values
ARCHTarget architecturex86_64, aarch64, riscv64
PLATFORMTarget platformaarch64-qemu-virt-hv
SMPNumber of CPUs1, 2, 4, etc.
MODEBuild moderelease, debug
LOGLogging levelwarn, error, info, debug, trace
VM_CONFIGSPath to VM configuration filesconfigs/vms/arceos-aarch64.toml
FEATURESFeatures to enablefs
APP_FEATURESApp-specific featuresfs
MEMMemory size128M, 4G

Sources: Makefile(L4 - L32)  README.md(L57 - L59) 

Example Build Commands

Build for aarch64 architecture with specific VM configurations:

$ make ARCH=aarch64 VM_CONFIGS=configs/vms/arceos-aarch64.toml build

Build with filesystem support:

$ make ARCH=aarch64 VM_CONFIGS=configs/vms/arceos-aarch64.toml APP_FEATURES=fs build

Sources: README.md(L99 - L112) 

Running AxVisor

After building AxVisor, you can run it using QEMU. AxVisor supports two methods for loading guest VM images:

  1. Loading from a filesystem
  2. Loading from memory (statically compiled)

Runtime Configuration Diagram

flowchart TD
subgraph subGraph2["Memory Loading"]
    buildImage["Build guest VM image"]
    specifyPath["Specify kernel_path in config"]
end
subgraph subGraph1["Filesystem Loading"]
    prepareDisk["Create disk.img with FAT32"]
    mountDisk["Mount disk.img"]
    copyImage["Copy guest VM image to disk"]
    umountDisk["Unmount disk"]
end
subgraph subGraph0["AxVisor Runtime Configuration"]
    startAxVisor["Start AxVisor"]
    loadConfig["Load VM Configuration Files"]
    checkImageLocation["Check image_location in config"]
    loadFromFs["Load Guest from Filesystem"]
    loadFromMem["Load Guest from Memory"]
    setupVM["Setup VM"]
    runVM["Run VM"]
end

buildImage --> specifyPath
checkImageLocation --> loadFromFs
checkImageLocation --> loadFromMem
copyImage --> umountDisk
loadConfig --> checkImageLocation
loadFromFs --> prepareDisk
loadFromFs --> setupVM
loadFromMem --> buildImage
loadFromMem --> setupVM
mountDisk --> copyImage
prepareDisk --> mountDisk
setupVM --> runVM
startAxVisor --> loadConfig

Sources: README.md(L59 - L112) 

Loading and Running from Filesystem

  1. Build a guest VM image for your architecture
  2. Create a disk image and place the guest VM image into it:
$ make disk_img               # Creates an empty FAT32 disk image
$ mkdir -p tmp
$ sudo mount disk.img tmp
$ sudo cp /PATH/TO/YOUR/GUEST/VM/IMAGE tmp/
$ sudo umount tmp
  1. Modify the VM configuration file in configs/vms/<ARCH_CONFIG>.toml:
  • Set image_location="fs"
  • Set kernel_path to the path of the kernel image in the filesystem
  • Set entry_point to the entry address of the kernel image
  • Set kernel_load_addr to the loading address of the kernel image
  1. Run AxVisor with the configured guest VM:
$ make ACCEL=n ARCH=aarch64 LOG=info VM_CONFIGS=configs/vms/arceos-aarch64.toml APP_FEATURES=fs run

Sources: README.md(L76 - L99) 

Loading and Running from Memory

  1. Build a guest VM image for your architecture
  2. Modify the VM configuration file in configs/vms/<ARCH_CONFIG>.toml:
  • Set image_location="memory"
  • Set kernel_path to the relative/absolute path of the kernel image in the workspace
  • Set entry_point to the entry address of the kernel image
  • Set kernel_load_addr to the loading address of the kernel image
  1. Run AxVisor with the configured guest VM:
$ make ACCEL=n ARCH=aarch64 LOG=info VM_CONFIGS=configs/vms/arceos-aarch64.toml run

Sources: README.md(L101 - L112) 

Guest VM Support

AxVisor has been verified to work with the following guest operating systems:

Guest OSArchitectureFeaturesConfiguration Template
ArceOSx86_64, aarch64, riscv64SMP supportconfigs/vms/arceos-*.toml
Starry-OSx86_64, aarch64, riscv64--
NimbOSx86_64, aarch64, riscv64Single-core onlyconfigs/vms/nimbos-*.toml
Linuxaarch64Passthrough deviceconfigs/vms/linux-qemu-aarch64.toml

Sources: README.md(L45 - L56)  doc/GuestVMs.md(L1 - L56) 

Hardware Platform Support

AxVisor has been verified on the following platforms:

  • QEMU ARM64 virt (qemu-max)
  • Rockchip RK3568 / RK3588
  • 黑芝麻华山 A1000 (Black Sesame A1000)

Sources: README.md(L37 - L44) 

Debugging

AxVisor provides several targets for debugging:

  1. To build and run the system with debugging support:
$ make ARCH=<architecture> VM_CONFIGS=<config_path> debug
  1. To attach GDB to a running instance:
$ make gdb

This will connect GDB to the running QEMU instance and set a breakpoint at the rust_entry function.

Sources: Makefile(L175 - L182) 

Troubleshooting and Development

Common Issues

  • Make sure your QEMU version is up-to-date
  • For hardware acceleration, ensure KVM is enabled on your system
  • VM configuration paths must be correctly specified

CI/CD Environment

The project uses GitHub Actions for continuous integration and testing. The CI setup includes:

  • Testing on multiple architectures (x86_64, aarch64, riscv64)
  • Setting up QEMU with necessary configurations
  • Remote testing for hardware-specific features

For more information about the remote CI setup, see .github/workflows/REMOTE-CI.md

Sources: .github/workflows/actions/setup-qemu/action.yml(L1 - L48)  .github/workflows/REMOTE-CI.md(L1 - L50) 

Advanced Configuration

For advanced configuration needs, including detailed VM settings, passthrough devices, and architecture-specific options, refer to VM Configuration and Platform-Specific Configuration.

Guest VMs

Relevant source files

This page documents the guest operating systems supported by AxVisor, how to configure them, and methods for loading and running guest VM images. For information about setting up the development environment for AxVisor itself, see Development Environment.

Overview of Supported Guest VMs

AxVisor currently supports running the following operating systems as guest VMs:

Guest OSArchitecture SupportFeaturesUse Case
ArceOSx86_64, aarch64, riscv64SMP, HypercallsSMP testing, General purpose
NimbOSx86_64, aarch64, riscv64Simple RTOSSingle-core testing
Starry-OSx86_64, aarch64Educational OSEducational purposes
Linuxaarch64Passthrough devicesProduction workloads

Sources: README.md(L46 - L55)  doc/GuestVMs.md(L1 - L13) 

Guest VM Architecture

Relationship Between AxVisor and Guest VMs

flowchart TD
subgraph subGraph1["Guest Virtual Machines"]
    vm1["VM: ArceOS"]
    vm2["VM: NimbOS"]
    vm3["VM: Linux"]
    vm4["VM: Starry-OS"]
end
subgraph subGraph0["AxVisor Hypervisor Components"]
    axvm["axvm - VM Management"]
    axvcpu["axvcpu - Virtual CPU"]
    axaddrspace["axaddrspace - Memory Management"]
    axdevice["axdevice - Device Emulation"]
end

axaddrspace --> vm1
axaddrspace --> vm2
axaddrspace --> vm3
axaddrspace --> vm4
axdevice --> vm1
axdevice --> vm2
axdevice --> vm3
axdevice --> vm4
axvcpu --> vm1
axvcpu --> vm2
axvcpu --> vm3
axvcpu --> vm4
axvm --> vm1
axvm --> vm2
axvm --> vm3
axvm --> vm4

Sources: README.md(L30 - L36) 

Guest VM Boot Process

sequenceDiagram
    participant TOMLConfiguration as "TOML Configuration"
    participant VMM as "VMM"
    participant VMInstance as "VM Instance"
    participant VCPU as "VCPU"
    participant GuestOS as "Guest OS"

    TOMLConfiguration ->> VMM: Load VM configuration
    VMM ->> VMInstance: Create VM instance
    VMM ->> VMInstance: Setup memory regions
    alt Load from filesystem
        VMM ->> VMInstance: Load kernel from FAT32 filesystem
    else Load from memory
        VMM ->> VMInstance: Load kernel from statically compiled image
    end
    VMM ->> VCPU: Create and setup VCPUs
    VMM ->> VMInstance: Boot VM
    VMInstance ->> VCPU: Notify primary VCPU
    VCPU ->> GuestOS: Start guest execution
    loop Until VM
    loop shutdown
        VCPU ->> VCPU: Execute guest code
        VCPU ->> VCPU: Handle VM exits
    end
    end

Sources: README.md(L58 - L112) 

Guest Operating Systems in Detail

ArceOS

ArceOS is a modular, arch-agnostic operating system focused on embedded systems, and it's also the foundation of AxVisor itself. As a guest VM, it's primarily used for SMP (Symmetric Multi-Processing) testing.

Key features when running as a guest:

  • Supports x86_64, aarch64, and riscv64 architectures
  • SMP support for testing multi-core scenarios
  • Hypercall support for communication with the hypervisor

Configuration templates are available at:

  • configs/vms/arceos-aarch64.toml
  • configs/vms/arceos-x86_64.toml
  • configs/vms/arceos-riscv64.toml

ArceOS Testcases

  • Hypercall Testing: A HelloWorld application is available that tests hypercall functionality.
  • Virtio-PCI Device Testing: The PCI branch can be used for testing virtio-pci devices through port I/O.

Sources: doc/GuestVMs.md(L10 - L23) 

NimbOS

NimbOS is a simple real-time operating system designed primarily for single-core testing scenarios. Its simplicity makes it ideal for testing basic hypervisor functionality.

Key features:

  • Single-core operation
  • Support for x86_64, aarch64, and riscv64 architectures
  • Simple design makes it suitable for basic functionality testing

Configuration templates are available at:

  • configs/vms/nimbos-aarch64.toml
  • configs/vms/nimbos-x86_64.toml
  • configs/vms/nimbos-riscv64.toml

Pre-built kernel binary images are available from the [NimbOS releases page](https://github.com/arceos-hypervisor/axvisor/blob/0c9b89a5/NimbOS releases page)

Sources: doc/GuestVMs.md(L3 - L8) 

Starry-OS

Starry-OS is an educational operating system that can be run as a guest VM on AxVisor. While less information is provided about its specific use cases compared to other guest OSes, it's included as a supported guest system.

Sources: README.md(L50) 

Linux

Linux is supported as a guest OS, primarily on the aarch64 architecture with passthrough devices. This allows for running production workloads in a virtualized environment.

Currently, Linux with passthrough devices on aarch64 has been tested in:

  • Single-core configuration: Configuration available at configs/vms/linux-qemu-aarch64.toml with device tree at configs/vms/linux-qemu.dts
  • SMP configuration: Configuration available at configs/vms/linux-qemu-aarch64-smp2.toml with device tree at configs/vms/linux-qemu-smp2.dts

Special instructions for running Linux on RK3588 boards are also available.

Sources: README.md(L52 - L55)  doc/GuestVMs.md(L30 - L44) 

axvm-bios (Bootloader)

For x86_64 guests, a simple BIOS called axvm-bios is available. This extremely simple BIOS acts as a bootloader for NimbOS and ArceOS on x86_64 platforms.

Pre-built binary is available from the [axvm-bios releases page](https://github.com/arceos-hypervisor/axvisor/blob/0c9b89a5/axvm-bios releases page)

Sources: doc/GuestVMs.md(L24 - L28) 

Guest VM Configuration

Configuration File Structure

VM configurations in AxVisor are managed through TOML files, which specify parameters such as:

  • VM ID and name
  • Number of virtual CPUs
  • Memory regions
  • Virtual devices
  • Passthrough devices
  • Guest image location and loading information
flowchart TD
subgraph subGraph3["VM Configuration File"]
    id["vm_id = 1"]
    name["name = 'arceos'"]
    vcpus["cpu_num = 1"]
    subgraph subGraph1["Image Configuration"]
        passthrough["passthrough_devices"]
        emulated["emulated_devices"]
        img_loc["image_location = 'fs'/'memory'"]
        kernel_path["kernel_path = '/path/to/image'"]
        entry["entry_point = 0x40080000"]
        load_addr["kernel_load_addr = 0x40080000"]
        dtb_path["dtb_path (optional)"]
        mem1["memory_region_1"]
        mem2["memory_region_2"]
        subgraph Devices["Devices"]
            subgraph subGraph0["Memory Regions"]
                passthrough["passthrough_devices"]
                emulated["emulated_devices"]
                img_loc["image_location = 'fs'/'memory'"]
                kernel_path["kernel_path = '/path/to/image'"]
                mem1["memory_region_1"]
                mem2["memory_region_2"]
            end
        end
    end
end

Example configuration templates can be found in the configs/vms/ directory, with specific templates for each supported guest OS and architecture combination.

Sources: README.md(L70 - L74) 

Loading and Running Guest VMs

AxVisor supports two primary methods for loading guest VM images:

Loading from a Filesystem

This method involves loading the guest image from a FAT32 filesystem:

  1. Build a client image file for your target architecture
  2. Create a disk image file and place the guest machine image into it:
  • Generate an empty FAT32 disk image using make disk_img
  • Mount the disk image and copy the guest image into it
  1. Configure the VM in the TOML file:
  • Set image_location = "fs" to indicate loading from the filesystem
  • Set kernel_path to the path of the kernel image in the filesystem
  • Configure entry_point and kernel_load_addr appropriately
  1. Build and run AxVisor with the appropriate configuration
make ACCEL=n ARCH=aarch64 LOG=info VM_CONFIGS=configs/vms/arceos-aarch64.toml APP_FEATURES=fs run

Sources: README.md(L76 - L99) 

Loading from Memory

This method involves statically compiling the guest image into the hypervisor:

  1. Build a client image file for your target architecture
  2. Configure the VM in the TOML file:
  • Set image_location = "memory" to indicate loading from memory
  • Set kernel_path to the path of the kernel image in your workspace
  • Configure entry_point and kernel_load_addr appropriately
  1. Build and run AxVisor with the appropriate configuration
make ACCEL=n ARCH=aarch64 LOG=info VM_CONFIGS=configs/vms/arceos-aarch64.toml run

Sources: README.md(L101 - L112) 

Platform-Specific Guest Support

RK3588 Board Support

AxVisor has been verified on the Rockchip RK3588 platform for running Linux guests. The process involves:

  1. Preparing the kernel file (linux-rk3588-aarch64.bin) and DTB file (rk3588.dtb)
  2. Configuring the paths in configs/vms/linux-rk3588-aarch64.toml
  3. Building the kernel image with make A=(pwd) ARCH=aarch64 VM_CONFIGS=configs/vms/linux-rk3588-aarch64.toml kernel
  4. Using RKDevTool to flash the image to the RK3588 board

Sources: doc/GuestVMs.md(L30 - L44)  README.md(L37 - L44) 

Guest VM Testing

For effective testing of guest VMs, several specific test configurations are available:

  1. Hypercall Tests: Using ArceOS HelloWorld application to test hypercall functionality
  2. PCI Device Tests: Using specific branches of ArceOS to test virtio-pci devices
  3. Single-core Tests: Using NimbOS for basic single-core functionality testing
  4. SMP Tests: Using ArceOS for multi-core functionality testing

Sources: doc/GuestVMs.md(L16 - L23) 

Development Environment

Relevant source files

This document outlines how to set up a development environment for AxVisor, a unified modular hypervisor based on ArceOS. It covers the necessary tools, dependencies, repository setup, and IDE configuration to effectively contribute to the project. For information about building and running AxVisor with guest VMs, see Building and Running.

Prerequisites

Before setting up the development environment for AxVisor, you need to install the following dependencies:

  • Rust Programming Language: The core language used to develop AxVisor
  • cargo-binutils: Required for tools like rust-objcopy and rust-objdump
  • musl-gcc (Optional): Needed when building certain guest applications

Installing Prerequisites

# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Install cargo-binutils
cargo install cargo-binutils

# Install musl-gcc (optional)
# Download from http://musl.cc/x86_64-linux-musl-cross.tgz if needed

Sources: README.md(L63 - L68) 

Development Environment Setup Script

AxVisor provides a Python script (tool/dev_env.py) to simplify the development environment setup. This script:

  1. Clones required repositories
  2. Patches Cargo.toml to use local dependencies
  3. Creates IDE configuration for VS Code

Development Environment Structure

The diagram below illustrates the structure created by the development environment setup:

flowchart TD
subgraph subGraph0["AxVisor Development Environment"]
    Root["AxVisor Repository"]
    Crates["crates/ Directory"]
    VSCode[".vscode/ Directory"]
    CargoConfig["Cargo.toml"]
    CargoBackup["Cargo.toml.bk"]
    Repo1["arceos"]
    Repo2["axvm"]
    Repo3["axvcpu"]
    Repo4["axaddrspace"]
    Repo5["arm_vcpu"]
    Repo6["axdevice"]
    Repo7["arm_vgic"]
    Repo8["arm_gicv2"]
    Repo9["axdevice_crates"]
    Settings["settings.json"]
end

Crates --> Repo1
Crates --> Repo2
Crates --> Repo3
Crates --> Repo4
Crates --> Repo5
Crates --> Repo6
Crates --> Repo7
Crates --> Repo8
Crates --> Repo9
Root --> CargoBackup
Root --> CargoConfig
Root --> Crates
Root --> VSCode
VSCode --> Settings

Sources: tool/dev_env.py(L1 - L106)  .gitignore(L20 - L21) 

Running the Setup Script

To set up the development environment, run:

python3 tool/dev_env.py

By default, the script clones repositories from git@github.com:arceos-hypervisor. You can specify a different repository base using the --repo parameter:

python3 tool/dev_env.py --repo https://github.com/arceos-hypervisor

Sources: tool/dev_env.py(L7 - L25) 

Dependencies and Repository Structure

The following diagram shows the core dependencies that are cloned during the setup process and how they relate to the main AxVisor repository:

flowchart TD
subgraph Repositories["Repositories"]
    axvisor["axvisor (main)"]
    arceos["arceos(ArceOS unikernel framework)"]
    axvm["axvm(Virtual Machine Management)"]
    axvcpu["axvcpu(Virtual CPU Interface)"]
    axaddrspace["axaddrspace(Address Space Management)"]
    axdevice["axdevice(Device Emulation)"]
    arm_vcpu["arm_vcpu(ARM VCPU Implementation)"]
    arm_vgic["arm_vgic(ARM Virtual GIC)"]
    arm_gicv2["arm_gicv2(ARM GICv2 Support)"]
    axdevice_crates["axdevice_crates(Device Model Base)"]
end

arm_vcpu --> arm_vgic
arm_vgic --> arm_gicv2
axdevice --> axdevice_crates
axvcpu --> arm_vcpu
axvisor --> arceos
axvisor --> axaddrspace
axvisor --> axdevice
axvisor --> axvcpu
axvisor --> axvm

Sources: tool/dev_env.py(L30 - L48) 

Cargo.toml Patching Process

The development environment script modifies the Cargo.toml file to use local paths for dependencies instead of fetching them from GitHub. This allows you to make changes to these dependencies locally and test them immediately.

The patching process works as follows:

flowchart TD
subgraph subGraph0["Patch Example"]
    E["[patch.'Unsupported markdown: linkpath = 'crates/axvm'"]
end
A["Read original Cargo.toml"]
B["Backup to Cargo.toml.bk"]
C["Add patch sections"]
D["Write modified Cargo.toml"]

A --> B
B --> C
C --> D

The script adds patch sections for all dependencies, redirecting them to local paths in the crates/ directory.

Sources: tool/dev_env.py(L52 - L84) 

VS Code Configuration

The development environment script creates a .vscode/settings.json file with configuration for Rust Analyzer:

{
    "rust-analyzer.cargo.target": "aarch64-unknown-none-softfloat",
    "rust-analyzer.check.allTargets": false,
    "rust-analyzer.cargo.features": ["fs"],
    "rust-analyzer.cargo.extraEnv": {
        "AX_CONFIG_PATH": "${workspaceFolder}/.axconfig.toml"
    }
}

This configuration:

  • Sets the target architecture to aarch64-unknown-none-softfloat
  • Enables the fs feature for Cargo
  • Sets the AX_CONFIG_PATH environment variable for configuration

Sources: tool/dev_env.py(L86 - L100) 

Development Workflow

Once your development environment is set up, you can start working on AxVisor:

  1. Make changes to the code in the main repository or any of the dependencies in the crates/ directory
  2. Build and test using the Makefile commands as described in Building and Running
  3. Use guest VM configurations from the configs/vms/ directory for testing

Typical Development Cycle

flowchart TD
A["Setup dev environmentwith dev_env.py"]
B["Make code changes"]
C["Build with make"]
D["Test with QEMU"]

A --> B
B --> C
C --> D
D --> B

Editing Dependency Code

With the development environment set up, you can edit code in any of the cloned repositories. Changes to dependencies will be immediately reflected when you build the main project, without needing to publish new versions of those dependencies.

Key directories to work with:

  • crates/arceos/: The ArceOS unikernel framework
  • crates/axvm/: Virtual machine management
  • crates/axvcpu/: Virtual CPU interface
  • crates/axaddrspace/: Address space management
  • crates/arm_vcpu/: ARM VCPU implementation (when working on ARM support)

Troubleshooting

Common Issues

  1. Git clone failures: Ensure you have proper SSH keys set up if using SSH URLs, or use HTTPS URLs with the --repo option.
  2. Rust compiler errors: Ensure you have the correct Rust toolchain installed. You may need to run:
rustup target add aarch64-unknown-none-softfloat
  1. Build errors after pulling updates: If updates to dependencies cause build errors, try running the setup script again to ensure all dependencies are in sync.

Cleaning the Environment

If you need to reset your development environment:

  1. Remove the crates/ directory:
rm -rf crates/
  1. Restore the original Cargo.toml:
mv Cargo.toml.bk Cargo.toml
  1. Run the setup script again:
python3 tool/dev_env.py

Sources: .gitignore(L20 - L21) 

Next Steps

After setting up your development environment, you can proceed to:

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:

  1. Loads VM configuration from TOML files via config::init_guest_vms()
  2. Creates VM instances using VM::new()
  3. Loads VM images according to configuration
  4. 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:

  1. Task is created for the VCPU with alloc_vcpu_task()
  2. VCPU task waits until the VM is in running state
  3. VCPU enters the execution loop via vcpu_run()
  4. VM executes guest code with vm.run_vcpu(vcpu_id)
  5. VCPU handles various exit reasons (hypercalls, interrupts, etc.)
  6. 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 VMVCpus structure containing a wait queue
  • VCPUs can wait on the queue using wait() or wait_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

StructurePurposeImplementation
RUNNING_VM_COUNTTracks number of running VMsstatic AtomicUsize
VMMWait queue for VMM synchronizationstatic AxWaitQueueHandle
VM_VCPU_TASK_WAIT_QUEUEMaps VM IDs to their VCPU wait queuesstatic 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 ReasonDescriptionHandling
HypercallGuest executed a hypercallLog hypercall details and arguments
ExternalInterruptInterrupt receivedHandle interrupt and continue execution
HaltGuest halted CPUVCPU waits on wait queue
CpuUpGuest wants to start another CPUCreate new VCPU task for target CPU
CpuDownGuest wants to stop a CPUVCPU waits on wait queue
SystemDownGuest wants to shutdownShutdown VM and exit VCPU task
FailEntryFailed to enter VMLog 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:

  1. Free: Initial state when VCPU is created
  2. Running: VCPU is executing guest code
  3. Halted: VCPU is waiting on a wait queue
  4. 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.

VMM Implementation

Relevant source files

This document details the implementation of the Virtual Machine Manager (VMM) in AxVisor. The VMM is the core component responsible for creating, managing, and supervising virtual machines in the hypervisor. For information about specific VCPU management, see VCPU Management.

Overview

The VMM in AxVisor orchestrates the complete lifecycle of virtual machines, from initialization and booting to handling runtime events and shutdown. It provides a unified framework for managing VMs across multiple architectures (x86_64, aarch64, and RISC-V) while abstracting hardware-specific details.

flowchart TD
subgraph subGraph2["VM Tracking"]
    running_vm_count["RUNNING_VM_COUNT"]
    vmm_wait_queue["VMM Wait Queue"]
    decrease_count["Decrease RUNNING_VM_COUNT"]
    wake_vmm["Wake VMM Wait Queue"]
    vmm_exit["VMM Exit"]
end
subgraph subGraph1["Runtime Execution"]
    run_vcpu["vm.run_vcpu()"]
    handle_exit["Handle Exit Reasons"]
    hypercall["Hypercall"]
    ext_interrupt["External Interrupt"]
    halt["Halt"]
    cpu_up["CPU Up"]
    sys_down["System Down"]
end
subgraph subGraph0["VMM Implementation"]
    vmm_init["VMM::init()"]
    vmm_start["VMM::start()"]
    config_init["config::init_guest_vms()"]
    vcpu_setup["vcpus::setup_vm_primary_vcpu()"]
    vcpu_notify["vcpus::notify_primary_vcpu()"]
    vm_new["VM::new()"]
    load_images["load_vm_images()"]
    alloc_vcpu_task["alloc_vcpu_task()"]
    vcpu_run["vcpu_run()"]
end

alloc_vcpu_task --> vcpu_run
config_init --> load_images
config_init --> vm_new
decrease_count --> wake_vmm
handle_exit --> cpu_up
handle_exit --> ext_interrupt
handle_exit --> halt
handle_exit --> hypercall
handle_exit --> sys_down
run_vcpu --> handle_exit
running_vm_count --> vmm_exit
sys_down --> decrease_count
vcpu_notify --> vcpu_run
vcpu_run --> run_vcpu
vcpu_setup --> alloc_vcpu_task
vmm_init --> config_init
vmm_init --> vcpu_setup
vmm_start --> vcpu_notify

Sources: src/vmm/mod.rs(L28 - L65)  src/vmm/vcpus.rs(L268 - L367) 

Key Components

The VMM implementation consists of several interconnected components that work together to manage virtual machines:

ComponentDescriptionKey Files
VMM CoreInitializes the VMM and starts VMssrc/vmm/mod.rs
VM ConfigurationLoads and processes VM configurationssrc/vmm/config.rs
VCPU ManagementManages VCPU tasks and executionsrc/vmm/vcpus.rs
Task ExtensionsExtends tasks to support VM and VCPU referencessrc/task.rs
VM ListMaintains a list of all VMssrc/vmm/vm_list.rs
Image LoadingHandles loading guest VM imagessrc/vmm/images.rs

Sources: src/vmm/mod.rs(L1 - L7)  src/task.rs(L1 - L19) 

Initialization and Startup Flow

The VMM initialization and startup process follows a specific sequence to properly set up and boot virtual machines.

sequenceDiagram
    participant MainApplication as "Main Application"
    participant VMMModule as "VMM Module"
    participant VMConfiguration as "VM Configuration"
    participant VMInstance as "VM Instance"
    participant VCPU as "VCPU"
    participant TaskSystem as "Task System"

    MainApplication ->> VMMModule: init()
    VMMModule ->> VMConfiguration: init_guest_vms()
    VMConfiguration ->> VMConfiguration: Load VM configs from TOML
    loop For each VM config
        VMConfiguration ->> VMInstance: VM::new(vm_config)
        VMConfiguration ->> VMConfiguration: push_vm(vm)
        VMConfiguration ->> VMConfiguration: load_vm_images(vm)
    end
    VMMModule ->> VMMModule: Setup VCPUs for each VM
    loop For each VM
        VMMModule ->> VCPU: setup_vm_primary_vcpu(vm)
        VCPU ->> TaskSystem: alloc_vcpu_task(vm, vcpu)
        TaskSystem -->> VCPU: Return task reference
    end
    MainApplication ->> VMMModule: start()
    loop For each VM
        VMMModule ->> VMInstance: boot()
        VMMModule ->> VCPU: notify_primary_vcpu(vm.id())
        VMMModule ->> VMMModule: Increment RUNNING_VM_COUNT
    end
    VMMModule ->> VMMModule: Wait for all VMs to stop
    Note over VMMModule: Wait on VMM wait queue until RUNNING_VM_COUNT = 0

Sources: src/vmm/mod.rs(L28 - L65)  src/vmm/config.rs(L25 - L43) 

VMM Initialization

The VMM initialization process begins with the init() function, which performs two main tasks:

  1. VM Configuration Loading: The system loads VM configurations from TOML files and creates VM instances for each configuration.
  2. VCPU Setup: For each VM, the primary VCPU is set up, and a task is created to run it.
#![allow(unused)]
fn main() {
pub fn init() {
    // Initialize guest VM according to config file.
    config::init_guest_vms();

    // Setup vcpus, spawn axtask for primary VCpu.
    info!("Setting up vcpus...");
    for vm in vm_list::get_vm_list() {
        vcpus::setup_vm_primary_vcpu(vm);
    }
}
}

Sources: src/vmm/mod.rs(L28 - L39) 

VMM Startup

After initialization, the start() function boots all VMs and waits for them to complete:

  1. VM Boot: Each VM is booted, and its primary VCPU is notified to start execution.
  2. VM Tracking: The system tracks the number of running VMs using the RUNNING_VM_COUNT atomic counter.
  3. Wait for Completion: The VMM waits on a wait queue until all VMs have stopped (RUNNING_VM_COUNT reaches 0).
pub fn start() {
    info!("VMM starting, booting VMs...");
    for vm in vm_list::get_vm_list() {
        match vm.boot() {
            Ok(_) => {
                vcpus::notify_primary_vcpu(vm.id());
                RUNNING_VM_COUNT.fetch_add(1, Ordering::Release);
                info!("VM[{}] boot success", vm.id())
            }
            Err(err) => warn!("VM[{}] boot failed, error {:?}", vm.id(), err),
        }
    }

    // Do not exit until all VMs are stopped.
    task::ax_wait_queue_wait_until(
        &VMM,
        || {
            let vm_count = RUNNING_VM_COUNT.load(Ordering::Acquire);
            info!("a VM exited, current running VM count: {}", vm_count);
            vm_count == 0
        },
        None,
    );
}

Sources: src/vmm/mod.rs(L42 - L65) 

VCPU Task Management

The VCPU management system is a critical component of the VMM, responsible for setting up, scheduling, and managing VCPU tasks.

VCPU Task Structure

Each VCPU runs in its own task, managed by the task system. The task extensions (TaskExt) store references to both the VM and VCPU, allowing easy access to these components from within the task context.

flowchart TD
subgraph subGraph2["VM Access"]
    vm["VM"]
    vcpu["VCPU"]
end
subgraph subGraph1["VCPU Management"]
    vm_vcpus["VMVCpus"]
    wait_queue["WaitQueue"]
    counter["AtomicUsize"]
end
subgraph subGraph0["Task System"]
    task["AxTaskRef"]
    task_ext["TaskExt"]
    vm_ref["VMRef"]
    vcpu_ref["VCpuRef"]
end

task --> task_ext
task_ext --> vcpu_ref
task_ext --> vm_ref
vcpu_ref --> vcpu
vm --> vcpu
vm_ref --> vm
vm_vcpus --> counter
vm_vcpus --> task
vm_vcpus --> wait_queue

Sources: src/task.rs(L1 - L19)  src/vmm/vcpus.rs(L27 - L40) 

VCPU Task Creation

The alloc_vcpu_task function creates a new task for a VCPU and initializes it with the appropriate VM and VCPU references:

  1. Create a new task with the vcpu_run function as its entry point
  2. Set the CPU mask if the VCPU has a dedicated physical CPU
  3. Initialize the task extension with VM and VCPU references
  4. Spawn the task on the scheduler
fn alloc_vcpu_task(vm: VMRef, vcpu: VCpuRef) -> AxTaskRef {
    let mut vcpu_task = TaskInner::new(
        vcpu_run,
        format!("VM[{}]-VCpu[{}]", vm.id(), vcpu.id()),
        KERNEL_STACK_SIZE,
    );

    if let Some(phys_cpu_set) = vcpu.phys_cpu_set() {
        vcpu_task.set_cpumask(AxCpuMask::from_raw_bits(phys_cpu_set));
    }
    vcpu_task.init_task_ext(TaskExt::new(vm, vcpu));

    axtask::spawn_task(vcpu_task)
}

Sources: src/vmm/vcpus.rs(L249 - L268) 

VCPU Execution Flow

The execution of a VCPU is managed by the vcpu_run function, which runs in the context of the VCPU task.

flowchart TD
start["vcpu_run() Start"]
wait["Wait for VM Running State"]
mark["Mark VCPU as Running"]
run_loop["Enter VCPU Run Loop"]
run_vcpu["vm.run_vcpu(vcpu_id)"]
exit_handler["Handle Exit Reason"]
handle_hypercall["Process Hypercall"]
handle_interrupt["Process Interrupt"]
handle_halt["Wait on VM Wait Queue"]
handle_cpu_up["Boot Secondary VCPU"]
handle_system_down["Shutdown VM"]
check_shutdown["Check VM Shutting Down"]
mark_exiting["Mark VCPU as Exiting"]
decrease_count["Decrease RUNNING_VM_COUNT"]
exit["Exit VCPU Task"]

check_shutdown --> mark_exiting
check_shutdown --> run_loop
exit_handler --> handle_cpu_up
exit_handler --> handle_halt
exit_handler --> handle_hypercall
exit_handler --> handle_interrupt
exit_handler --> handle_system_down
handle_cpu_up --> check_shutdown
handle_halt --> check_shutdown
handle_hypercall --> check_shutdown
handle_interrupt --> check_shutdown
handle_system_down --> check_shutdown
mark --> run_loop
mark_exiting --> decrease_count
mark_exiting --> exit
run_loop --> run_vcpu
run_vcpu --> exit_handler
start --> wait
wait --> mark

Sources: src/vmm/vcpus.rs(L275 - L367) 

VCPU Execution Loop

The VCPU execution loop performs the following steps:

  1. Wait for the VM to be in a running state
  2. Mark the VCPU as running
  3. Enter the main execution loop:
  • Run the VCPU
  • Handle various exit reasons
  • Check if the VM is shutting down
  1. If the VM is shutting down:
  • Mark the VCPU as exiting
  • If this is the last VCPU to exit, decrement the running VM count
  • Exit the VCPU task
fn vcpu_run() {
    let curr = axtask::current();
    let vm = curr.task_ext().vm.clone();
    let vcpu = curr.task_ext().vcpu.clone();
    // ... [Initialization code]
    
    // Wait for VM to be in running state
    wait_for(vm_id, || vm.running());
    
    // Mark VCPU as running
    mark_vcpu_running(vm_id);
    
    // Main execution loop
    loop {
        match vm.run_vcpu(vcpu_id) {
            Ok(exit_reason) => match exit_reason {
                // ... [Handle various exit reasons]
            },
            Err(err) => {
                // ... [Handle error]
            }
        }
        
        // Check if VM is shutting down
        if vm.shutting_down() {
            // ... [Handle shutdown]
            if mark_vcpu_exiting(vm_id) {
                // Last VCPU exiting
                super::RUNNING_VM_COUNT.fetch_sub(1, Ordering::Release);
                ax_wait_queue_wake(&super::VMM, 1);
            }
            break;
        }
    }
}

Sources: src/vmm/vcpus.rs(L275 - L367) 

VM and VCPU State Management

The VMM maintains the state of VMs and VCPUs through a combination of atomic counters and wait queues.

VM State Tracking

The VMM tracks running VMs using the RUNNING_VM_COUNT atomic counter. When all VMs have stopped (RUNNING_VM_COUNT reaches 0), the VMM is signaled to exit.

static RUNNING_VM_COUNT: AtomicUsize = AtomicUsize::new(0);
static VMM: AxWaitQueueHandle = AxWaitQueueHandle::new();

Sources: src/vmm/mod.rs(L22 - L25) 

VCPU State Management

The VCPU state management involves:

  1. Wait Queues: Each VM has a wait queue to manage its VCPUs
  2. VCPU Task List: Each VM maintains a list of VCPU tasks
  3. Running/Halting Count: The system tracks the number of running or halting VCPUs for each VM
pub struct VMVCpus {
    _vm_id: usize,
    wait_queue: WaitQueue,
    vcpu_task_list: Vec<AxTaskRef>,
    running_halting_vcpu_count: AtomicUsize,
}

Sources: src/vmm/vcpus.rs(L27 - L40) 

Secondary VCPU Management

While primary VCPUs are set up during VMM initialization, secondary VCPUs can be dynamically started by the guest OS through the CPU Up exit reason.

When a running VCPU requests to start a secondary VCPU, the system:

  1. Sets up the entry point and arguments for the new VCPU
  2. Creates a new VCPU task
  3. Adds the task to the VM's VCPU task list
fn vcpu_on(vm: VMRef, vcpu_id: usize, entry_point: GuestPhysAddr, arg: usize) {
    let vcpu = vm.vcpu_list()[vcpu_id].clone();
    
    // Setup VCPU entry point and arguments
    vcpu.set_entry(entry_point).expect("vcpu_on: set_entry failed");
    vcpu.set_gpr(0, arg);
    
    // Create and add VCPU task
    let vcpu_task = alloc_vcpu_task(vm.clone(), vcpu);
    unsafe { VM_VCPU_TASK_WAIT_QUEUE.get_mut(&vm.id()) }
        .unwrap()
        .add_vcpu_task(vcpu_task);
}

Sources: src/vmm/vcpus.rs(L179 - L208) 

VM Shutdown Process

When a VM shuts down, the following steps occur:

  1. The VM is marked as shutting down
  2. Each VCPU detects the shutdown state in its execution loop
  3. The last VCPU to exit decrements the RUNNING_VM_COUNT
  4. When RUNNING_VM_COUNT reaches 0, the VMM wait queue is signaled
  5. The VMM exits its wait loop and the hypervisor can terminate

This coordinated shutdown ensures proper cleanup of resources and allows the hypervisor to exit cleanly.

sequenceDiagram
    participant GuestOS as "Guest OS"
    participant VCPUTask as "VCPU Task"
    participant VMInstance as "VM Instance"
    participant VMMModule as "VMM Module"

    GuestOS ->> VCPUTask: System Down Exit
    VCPUTask ->> VMInstance: shutdown()
    VMInstance -->> VCPUTask: VM marked as shutting down
    loop For each VCPU
        VCPUTask ->> VCPUTask: Detect VM shutting down
        VCPUTask ->> VCPUTask: Mark VCPU as exiting
        VCPUTask ->> VCPUTask: Check if last VCPU
    end
    VCPUTask ->> VMMModule: Decrement RUNNING_VM_COUNT
    VCPUTask ->> VMMModule: Wake VMM wait queue
    VCPUTask ->> VCPUTask: Exit VCPU task
    VMMModule ->> VMMModule: Check if RUNNING_VM_COUNT = 0
    VMMModule ->> VMMModule: Exit VMM

Sources: src/vmm/vcpus.rs(L345 - L363)  src/vmm/mod.rs(L55 - L64) 

Summary

The VMM implementation in AxVisor provides a robust foundation for managing virtual machines across multiple architectures. Key aspects of this implementation include:

  1. Centralized VM Management: The VMM initializes, boots, and tracks all VMs in the system.
  2. VCPU Task System: Each VCPU runs in its own task, managed by the task scheduler.
  3. Coordinated Shutdown: The system ensures proper cleanup and coordination during VM shutdown.
  4. State Tracking: The VMM maintains clear state tracking for VMs and VCPUs.
  5. Secondary VCPU Support: The system supports dynamically starting secondary VCPUs.

This implementation enables AxVisor to efficiently manage multiple guest VMs while providing a clean architecture for future extensions.

VCPU Management

Relevant source files

This page documents the VCPU (Virtual CPU) management system in AxVisor, focusing on how virtual CPUs are implemented, initialized, scheduled, and monitored throughout their lifecycle. For information about the overall VM management, see VM Management. For timer-related aspects of VCPUs, see Timer Subsystem.

Overview

AxVisor implements a task-based VCPU execution model, where each VCPU runs as a separate task in the ArceOS task scheduler. This design allows multiple VCPUs to be efficiently managed across physical CPUs, with mechanisms for coordination, notification, and state tracking.

flowchart TD
subgraph subGraph1["External Components"]
    axvm["axvm - VM Management"]
    axtask["axtask - Task Scheduler"]
    arch_impl["Architecture-Specific Implementations"]
end
subgraph subGraph0["VCPU Management System"]
    vcpu_management["VCPU Management"]
    vcpu_task_alloc["VCPU Task Allocation"]
    vcpu_initialization["VCPU Initialization"]
    vcpu_execution["VCPU Execution Loop"]
    vcpu_exit["VCPU Exit Handling"]
    vcpu_notification["VCPU Notification System"]
    vm_shutdown["VM Shutdown Coordination"]
end

arch_impl --> vcpu_execution
axtask --> vcpu_execution
axtask --> vcpu_task_alloc
axvm --> vcpu_management
vcpu_management --> vcpu_execution
vcpu_management --> vcpu_exit
vcpu_management --> vcpu_initialization
vcpu_management --> vcpu_notification
vcpu_management --> vcpu_task_alloc
vcpu_management --> vm_shutdown

Sources: src/vmm/vcpus.rs(L1 - L368)  src/vmm/mod.rs(L1 - L66)  src/task.rs(L1 - L19) 

VCPU Architecture and Implementation

AxVisor's VCPU management is based on a cross-architecture design that abstracts hardware-specific details. The system includes platform-specific VCPU implementations for x86_64, ARM, and RISC-V architectures, all conforming to a common interface defined by axvcpu.

VCPU Type Hierarchy


Sources: src/vmm/mod.rs(L15 - L20)  src/vmm/vcpus.rs(L175 - L201) 

VCPU and Task Relationship

Each VCPU in AxVisor is associated with an ArceOS task, which is scheduled by the underlying task scheduler. This relationship is established through the TaskExt structure that extends ArceOS tasks with VCPU-specific information.

ComponentPurpose
TaskExtExtension to ArceOS tasks that holds references to VM and VCPU
VCpuRefReference to a VCPU instance, architecture-specific implementation
VMRefReference to the VM that owns the VCPU

Source: src/task.rs(L1 - L19) 

VCPU Lifecycle

VCPU Initialization

VCPUs are initialized during the VMM startup process. The primary VCPU (VCPU 0) for each VM is set up first, while secondary VCPUs can be started on demand.

sequenceDiagram
    participant VMMinit as "VMM::init"
    participant VM as "VM"
    participant VMVCpus as "VMVCpus"
    participant PrimaryVCPU as "Primary VCPU"
    participant VCPUTask as "VCPU Task"

    VMMinit ->> VM: init_guest_vms()
    VMMinit ->> VM: setup_vm_primary_vcpu(vm)
    VM ->> VMVCpus: new(vm)
    VM ->> PrimaryVCPU: Get primary VCPU (id=0)
    VM ->> VCPUTask: alloc_vcpu_task(vm, primary_vcpu)
    VCPUTask ->> VMVCpus: add_vcpu_task(primary_vcpu_task)
    VMMinit ->> VM: start()
    VMMinit ->> VM: boot()
    VMMinit ->> PrimaryVCPU: notify_primary_vcpu(vm_id)

Sources: src/vmm/mod.rs(L27 - L39)  src/vmm/vcpus.rs(L211 - L231) 

VCPU Execution Loop

The main VCPU execution loop is implemented in the vcpu_run function, which is the entry point for all VCPU tasks. The VCPU waits for the VM to be in a running state, then enters a loop where it runs the VCPU and handles various exit reasons.

flowchart TD
start["VCPU Task Start"]
wait_vm["Wait for VM running"]
mark_running["Mark VCPU running"]
run_vcpu["Run VCPU (vm.run_vcpu)"]
handle_exit["Handle exit reason"]
check_shutdown["VM shutting down?"]
last_vcpu["Last VCPU exiting?"]
dec_count["Decrease running VM count"]
wake_vmm["Wake VMM wait queue"]
exit["Exit VCPU task"]
wait_wakeup["Wait for notification"]
cpu_up["Start target VCPU"]
vm_shutdown["Shutdown VM"]

check_shutdown --> last_vcpu
check_shutdown --> run_vcpu
cpu_up --> run_vcpu
dec_count --> wake_vmm
handle_exit --> check_shutdown
handle_exit --> cpu_up
handle_exit --> run_vcpu
handle_exit --> vm_shutdown
handle_exit --> wait_wakeup
last_vcpu --> dec_count
last_vcpu --> exit
mark_running --> run_vcpu
run_vcpu --> handle_exit
start --> wait_vm
vm_shutdown --> check_shutdown
wait_vm --> mark_running
wait_wakeup --> run_vcpu
wake_vmm --> exit

Sources: src/vmm/vcpus.rs(L270 - L367) 

VCPU Task Management

Task Allocation

Each VCPU runs as a separate ArceOS task, allocated with a dedicated kernel stack. The alloc_vcpu_task function creates a task for a VCPU, initializes it with VCPU-specific information, and configures its CPU affinity if specified.

flowchart TD
subgraph subGraph1["Task Properties"]
    task_name["Task name: VM[id]-VCpu[id]"]
    entry_fn["Entry function: vcpu_run"]
    stack_size["Stack size: 256 KiB"]
    cpu_mask["Optional CPU mask"]
end
subgraph subGraph0["VCPU Task Allocation"]
    alloc_vcpu_task["alloc_vcpu_task()"]
    new_task["TaskInner::new()"]
    set_cpumask["Set CPU affinity mask"]
    init_task_ext["Initialize TaskExt"]
    spawn_task["Spawn task in scheduler"]
end

alloc_vcpu_task --> new_task
init_task_ext --> spawn_task
new_task --> entry_fn
new_task --> set_cpumask
new_task --> stack_size
new_task --> task_name
set_cpumask --> cpu_mask
set_cpumask --> init_task_ext

Sources: src/vmm/vcpus.rs(L232 - L268) 

Task Structure: VMVCpus

The VMVCpus structure maintains the state of all VCPUs for a specific VM, including:

FieldPurpose
_vm_idID of the VM to which these VCPUs belong
wait_queueWait queue for VCPU task scheduling
vcpu_task_listList of tasks associated with the VCPUs
running_halting_vcpu_countCounter for running/halting VCPUs to track VM shutdown

Sources: src/vmm/vcpus.rs(L25 - L40) 

VCPU Exit Handling

When a VCPU exits from guest mode, the exit reason is handled in the vcpu_run function. The system supports various exit reasons, including:

Exit ReasonDescriptionHandling Behavior
HypercallGuest OS making a call to the hypervisorProcess hypercall and continue
ExternalInterruptHardware interrupt delivered to hostHandle interrupt and continue
HaltVCPU in halted stateWait for notification to continue
CpuUpRequest to start another VCPUBoot target VCPU and continue
CpuDownVCPU being stoppedWait for notification to continue
SystemDownGuest OS shutting downShut down the VM
FailEntryFailed to enter guest modeReport error and continue

Sources: src/vmm/vcpus.rs(L290 - L343) 

Multi-VCPU Coordination

Primary and Secondary VCPUs

AxVisor distinguishes between the primary VCPU (typically VCPU 0) and secondary VCPUs. The primary VCPU is initialized during VM setup, while secondary VCPUs can be started on demand through the CpuUp exit reason.

sequenceDiagram
    participant PrimaryVCPU as "Primary VCPU"
    participant VMVCpus as "VMVCpus"
    participant SecondaryVCPU as "Secondary VCPU"

    PrimaryVCPU ->> PrimaryVCPU: Executes guest code
    Note over PrimaryVCPU: Guest OS executes CPU_UP operation
    PrimaryVCPU ->> PrimaryVCPU: Exits with CpuUp reason
    PrimaryVCPU ->> VMVCpus: vcpu_on(vm, target_cpu, entry, arg)
    VMVCpus ->> SecondaryVCPU: Create secondary VCPU task
    VMVCpus ->> SecondaryVCPU: Set entry point and arguments
    VMVCpus ->> VMVCpus: add_vcpu_task(secondary_task)
    SecondaryVCPU ->> SecondaryVCPU: Start execution (vcpu_run)
    SecondaryVCPU ->> SecondaryVCPU: Wait for VM running
    PrimaryVCPU ->> PrimaryVCPU: Continue execution

Sources: src/vmm/vcpus.rs(L179 - L208)  src/vmm/vcpus.rs(L319 - L330) 

VM Shutdown Coordination

When a VM is shutting down, all VCPUs must exit properly. The last VCPU to exit is responsible for notifying the VMM that the VM has fully shut down.

flowchart TD
vcpu_exit["VCPU detects VM is shutting down"]
mark_exiting["Mark VCPU as exiting"]
check_last["Is this the last VCPU?"]
dec_count["Decrease running VM count"]
wake_vmm["Wake VMM wait queue"]
exit_vcpu["Exit VCPU task"]

check_last --> dec_count
check_last --> exit_vcpu
dec_count --> wake_vmm
mark_exiting --> check_last
vcpu_exit --> mark_exiting
wake_vmm --> exit_vcpu

Sources: src/vmm/vcpus.rs(L345 - L366)  src/vmm/mod.rs(L55 - L64) 

Wait Queue System

AxVisor uses a wait queue system to coordinate VCPU execution. The WaitQueue in VMVCpus allows VCPUs to block when they are halted and to be woken up when needed.

Wait Queue OperationPurpose
wait()Block the current VCPU task unconditionally
wait_until(condition)Block until a condition is met
notify_one()Wake up one waiting VCPU task

Sources: src/vmm/vcpus.rs(L72 - L89)  src/vmm/vcpus.rs(L117 - L138) 

Cross-Architecture VCPU Implementation

AxVisor supports multiple architectures through architecture-specific VCPU implementations. Each platform has its own VCPU implementation that conforms to the axvcpu interface.

ArchitectureImplementationSpecial Considerations
ARM/aarch64arm_vcpuUses ARM-specific registers and virtualization extensions
x86_64x86_vcpuLeverages VT-x/VMX for virtualization
RISC-Vriscv_vcpuUses RISC-V virtualization extensions and SBI

Sources: src/vmm/mod.rs(L15 - L20)  src/vmm/vcpus.rs(L193 - L201) 

Timer Subsystem

Relevant source files

The Timer Subsystem in AxVisor provides time-based event scheduling and management for the hypervisor environment. This document describes the internal design and implementation of the timer framework that enables scheduling and execution of time-bound operations across the hypervisor.

For information about VCPU Management and how it interacts with timers, see VCPU Management.

Overview

The Timer Subsystem enables the hypervisor to schedule operations to be executed at specific times or after specific intervals. It manages a list of pending timer events per virtual CPU and provides an interface for registering, canceling, and processing timer events.

flowchart TD
subgraph subGraph2["Guest VMs"]
    VMTimer["VM Timer Devices"]
end
subgraph subGraph1["Hardware Layer"]
    HPET["HPET"]
    LAPIC["Local APIC Timer"]
end
subgraph subGraph0["Timer Subsystem Components"]
    Register["register_timer()"]
    Cancel["cancel_timer()"]
    CheckEvents["check_events()"]
    ScheduleNext["scheduler_next_event()"]
    InitPercpu["init_percpu()"]
    TimerList["TIMER_LIST(Per-CPU TimerList)"]
    VmmEvent["VmmTimerEvent(Token + Callback)"]
    HWTimer["Hardware Timer"]
end

Cancel --> TimerList
CheckEvents --> TimerList
HWTimer --> HPET
HWTimer --> LAPIC
InitPercpu --> TimerList
Register --> TimerList
ScheduleNext --> HWTimer
TimerList --> VmmEvent
VMTimer --> HPET

Sources: src/vmm/timer.rs(L1 - L105)  configs/vms/arceos-x86_64.toml(L57 - L77)  configs/platforms/x86_64-qemu-q35.toml(L56 - L57) 

Core Components

VmmTimerEvent Structure

The Timer Subsystem is built around the VmmTimerEvent struct, which encapsulates a timer event with:

  1. A unique token identifier
  2. A callback function to be executed when the timer expires
classDiagram
class VmmTimerEvent {
    +usize token
    +BoxUnsupported markdown: del timer_callback
    +new(token: usize, f: F) VmmTimerEvent
}

class TimerEvent {
    <<trait>>
    
    +callback(self, now: TimeValue)
}

VmmTimerEvent  ..|>  TimerEvent : implements

Sources: src/vmm/timer.rs(L15 - L41) 

The VmmTimerEvent implements the TimerEvent trait, which defines a callback method that will be invoked when the timer expires. Each timer event is uniquely identified by a token, which allows for cancellation or modification of pending timers.

Per-CPU Timer Lists

The Timer Subsystem maintains a separate timer list for each virtual CPU using the percpu crate. This approach:

  1. Avoids contention between CPUs for timer management
  2. Allows each CPU to handle its local timer events efficiently
  3. Ensures scalability as the number of CPUs increases
#[percpu::def_percpu]
static TIMER_LIST: LazyInit<SpinNoIrq<TimerList<VmmTimerEvent>>> = LazyInit::new();

Sources: src/vmm/timer.rs(L43 - L44) 

The TIMER_LIST is protected by a spinlock (SpinNoIrq) to prevent concurrent modifications from different contexts on the same CPU. The LazyInit wrapper ensures the timer list is initialized only when first accessed.

Timer Operations

Timer Registration

Timers are registered using the register_timer function, which:

  1. Accepts a deadline timestamp in nanoseconds
  2. Takes a callback function to execute when the deadline is reached
  3. Returns a unique token that can be used to cancel the timer
#![allow(unused)]
fn main() {
pub fn register_timer<F>(deadline: u64, handler: F) -> usize
where
    F: FnOnce(TimeValue) + Send + 'static
}

Sources: src/vmm/timer.rs(L54 - L64) 

Timer Cancellation

A previously registered timer can be canceled before its expiration using the cancel_timer function:

#![allow(unused)]
fn main() {
pub fn cancel_timer(token: usize)
}

The function removes the timer with the matching token from the timer list.

Sources: src/vmm/timer.rs(L70 - L74) 

Event Processing

The Timer Subsystem periodically checks for expired timers using the check_events function. This function:

  1. Obtains the current time
  2. Retrieves and executes all expired timer events
  3. Continues until no more expired events remain

Timer event processing happens in the context of the caller, which means callback functions are executed synchronously.

Sources: src/vmm/timer.rs(L77 - L89) 

Scheduling the Next Timer

The scheduler_next_event function sets up the next hardware timer interrupt based on a periodic interval:

pub fn scheduler_next_event() {
    let now_ns = axhal::time::monotonic_time_nanos();
    let deadline = now_ns + PERIODIC_INTERVAL_NANOS;
    axhal::time::set_oneshot_timer(deadline);
}

The interval is calculated from the system configuration (axconfig::TICKS_PER_SEC).

Sources: src/vmm/timer.rs(L92 - L97)  src/vmm/timer.rs(L12) 

Hardware Integration

The Timer Subsystem abstracts the underlying hardware timer mechanisms through the ArceOS hardware abstraction layer (axhal).

Timer Hardware

AxVisor supports several hardware timer mechanisms:

Timer TypeDescriptionUsage
HPETHigh Precision Event TimerPassed through to guest VMs for high-precision timing
Local APIC TimerPer-CPU timer in the Advanced Programmable Interrupt ControllerUsed for scheduling timer events on individual CPUs

Sources: configs/vms/arceos-x86_64.toml(L72 - L77)  configs/vms/nimbos-x86_64.toml(L72 - L77) 

Timer Frequency Configuration

The platform configuration specifies the timer frequency, which affects the precision and granularity of timer events:

# Timer interrupt frequency in Hz. (4.0GHz)
timer-frequency = 4_000_000_000     # uint

This high frequency enables precise timing for hypervisor operations.

Sources: configs/platforms/x86_64-qemu-q35.toml(L56 - L57) 

Initialization and Setup

The Timer Subsystem requires initialization on each CPU using the init_percpu function:

pub fn init_percpu() {
    info!("Initing HV Timer...");
    let timer_list = unsafe { TIMER_LIST.current_ref_mut_raw() };
    timer_list.init_once(SpinNoIrq::new(TimerList::new()));
}

This function initializes the per-CPU timer list and prepares it for use.

Sources: src/vmm/timer.rs(L99 - L104) 

Usage in AxVisor

The Timer Subsystem is used throughout AxVisor for various purposes:

flowchart TD
subgraph subGraph1["Timer Subsystem"]
    TimerReg["register_timer()"]
    TimerCancel["cancel_timer()"]
    TimerCheck["check_events()"]
end
subgraph subGraph0["Timer Use Cases"]
    VCPU["VCPU Scheduling"]
    VMEvents["VM Event Processing"]
    Watchdog["Watchdog Timers"]
    Preemption["Preemption Points"]
end

Preemption --> TimerReg
VCPU --> TimerCheck
VCPU --> TimerReg
VMEvents --> TimerReg
Watchdog --> TimerCancel
Watchdog --> TimerReg

Sources: src/vmm/timer.rs(L1 - L105) 

Examples

Below is a simplified example of how a timer might be used in the hypervisor:

  1. Register a timer to check VM progress after 100ms:
let deadline = current_time_ns() + 100_000_000; // 100ms in the future
let token = register_timer(deadline, |_| {
    // Check VM progress and take action if needed
    check_vm_progress();
});
  1. Cancel the timer if the VM makes progress before the deadline:
if vm_made_progress {
    cancel_timer(token);
}
  1. Process any expired timers:
// Called periodically from the hypervisor main loop
check_events();

Summary

The Timer Subsystem provides a fundamental service for the AxVisor hypervisor, enabling time-based operations and scheduling. Its per-CPU design ensures scalability, while the simple API makes it easy to use throughout the hypervisor codebase.

The timer framework successfully abstracts the underlying hardware timer mechanisms, allowing AxVisor to provide consistent timing services across different hardware platforms and architectures.

Contributing

Relevant source files

This document provides information and guidelines for contributing to the AxVisor project. It covers setting up a development environment, building and testing your changes, and understanding the CI/CD pipeline. For information about specific technical implementations, please refer to the Technical Reference section.

Development Environment Setup

To begin contributing to AxVisor, you'll need to set up a proper development environment with the necessary tools and dependencies.

Prerequisites

  • Rust toolchain (nightly version, specified in rust-toolchain.toml)
  • QEMU (version 8.2.0 or later recommended)
  • Required system packages (for Linux-based systems)
  • Git

Setting Up Your Environment

flowchart TD
A["Install Rust"]
B["Install Dependencies"]
C["Clone Repository"]
D["Setup QEMU"]
E["Prepare Test Images"]
F["Ready for Development"]

A --> B
B --> C
C --> D
D --> E
E --> F
  1. Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  1. Install Dependencies:
sudo apt-get update && sudo apt-get install -y ninja-build libslirp-dev glib-2.0
  1. Clone Repository:
git clone https://github.com/arceos-hypervisor/axvisor.git
cd axvisor
  1. Setup QEMU (if not using the system version):
# Download and build QEMU
wget https://download.qemu.org/qemu-8.2.0.tar.xz
tar -xJf qemu-8.2.0.tar.xz
cd qemu-8.2.0
./configure --target-list=x86_64-softmmu,riscv64-softmmu,aarch64-softmmu --enable-slirp
make -j
make install
  1. Prepare Test Images:
# Create disk image for testing
make DISK_IMG=disk.img disk_img

Sources: Makefile(L14 - L34)  .github/workflows/actions/setup-qemu/action.yml(L1 - L48) 

Building and Running

AxVisor uses a Makefile system adapted from ArceOS to manage the build process, with a variety of options for different architectures and configurations.

Build Options

Here are the key parameters you can configure when building AxVisor:

ParameterDescriptionPossible Values
ARCHTarget architecturex86_64,riscv64,aarch64
SMPNumber of CPUsAny integer (default: 1)
MODEBuild moderelease,debug
LOGLogging levelwarn,error,info,debug,trace
FEATURESFeatures to enableSpace-separated list of features
APP_FEATURESApp-specific featuresSpace-separated list of features
BLKEnable storage devicesy,n
NETEnable network devicesy,n
ACCELEnable hardware accelerationy,n

Basic Build Commands

flowchart TD
A["make defconfig"]
B["make build"]
C["make run"]
D["make debug"]
E["make test"]

A --> B
B --> C
B --> D
B --> E
  1. Generate default configuration:
make ARCH=x86_64 defconfig
  1. Build the project:
make ARCH=x86_64 build
  1. Run in QEMU:
make ARCH=x86_64 DISK_IMG=disk.img BLK=y ACCEL=y run
  1. Debug with GDB:
make ARCH=x86_64 debug
# In another terminal
make gdb

Sources: Makefile(L34 - L117)  Makefile(L164 - L183) 

Testing Your Changes

AxVisor has a comprehensive testing infrastructure to verify that your changes work correctly across different architectures and configurations.

Local Testing

Local testing can be performed on your development machine using the following commands:

  1. Run unit tests:
make unittest
  1. Run with a guest VM:
# Create disk image if not created yet
make DISK_IMG=disk.img disk_img

# Run with NimbOS as guest
make ARCH=x86_64 DISK_IMG=disk.img LOG=info BLK=y ACCEL=y \
  VM_CONFIGS=configs/vms/nimbos-x86_64.toml APP_FEATURES=fs run

Testing Infrastructure

The testing infrastructure in AxVisor supports multiple architectures (x86_64, riscv64, aarch64) and can run both locally and remotely.

flowchart TD
A["Developer Change"]
B["Local Testing"]
C["Push to GitHub"]
D["GitHub Actions"]
E["Local Runner Tests"]
F["Remote Runner Tests"]
G["Test on Ubuntu VMriscv64/aarch64"]
H["Test on Remote Serversx86_64 with KVM"]
I["All Tests Pass?"]
J["Merge Change"]
K["Fix Issues"]

A --> B
A --> C
C --> D
D --> E
D --> F
E --> G
F --> H
G --> I
H --> I
I --> J
I --> K
K --> A

Sources: .github/workflows/test.yml(L1 - L146)  Makefile(L203 - L210) 

CI/CD Pipeline

AxVisor employs a comprehensive CI/CD pipeline implemented with GitHub Actions to ensure code quality and compatibility across different architectures.

flowchart TD
A["Push/PR"]
B["GitHub Actions"]
C["test_local Job"]
D["test_remote Job"]
C1["Matrix: riscv64, aarch64"]
C2["Matrix: nightly-2024-12-25, nightly"]
D1["Matrix: x86_64"]
D2["Matrix: nightly-2024-12-25, nightly"]
D3["Matrix: remote_aarkegz, remote_x10dri"]
E["Setup Rust Toolchain"]
F["Setup QEMU"]
G["Setup NimbOS Guest Image"]
H["Run Guest Tests"]
I["Compress Source Code"]
J["Setup NimbOS Guest Image"]
K["Upload to Remote Server"]
L["Run Tests Remotely"]
M["Check for Panics"]

A --> B
B --> C
B --> D
C --> C1
C --> C2
C --> E
D --> D1
D --> D2
D --> D3
D --> I
E --> F
F --> G
G --> H
I --> J
J --> K
K --> L
L --> M

Local Runner Tests

The CI pipeline runs tests on GitHub-hosted runners for riscv64 and aarch64 architectures. These tests verify that AxVisor compiles and runs correctly on these architectures.

Remote Runner Tests

For x86_64 architecture, which requires specific hardware features (Intel VT-x), the CI pipeline uses remote runners. These are custom servers with the necessary hardware that allows running AxVisor with hardware acceleration.

Sources: .github/workflows/test.yml(L9 - L146)  .github/workflows/REMOTE-CI.md(L1 - L50) 

Contribution Guidelines

When contributing to AxVisor, please follow these guidelines to ensure your changes can be smoothly integrated.

Code Style and Formatting

  1. Rust Code Formatting:
cargo fmt --all
  1. Linting:
make clippy
  1. Documentation:
make doc

Git Workflow

flowchart TD
A["Fork Repository"]
B["Create Feature Branch"]
C["Make Changes"]
D["Run Tests"]
E["Create Pull Request"]
F["Address Review Feedback"]
G["Changes Merged"]

A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
  1. Fork the Repository: Create your own fork of the repository.
  2. Create a Feature Branch: Create a branch for your feature or bugfix.
  3. Make Changes: Implement your feature or fix.
  4. Run Tests: Ensure all tests pass locally.
  5. Create a Pull Request: Submit your changes for review.
  6. Address Review Feedback: Make any requested changes.
  7. Changes Merged: Once approved, your changes will be merged.

Sources: Makefile(L184 - L202) 

Testing Different Guest Operating Systems

AxVisor supports multiple guest operating systems, including NimbOS, ArceOS, Linux, and others. When testing your changes, it's important to verify that they work with the relevant guest OS.

flowchart TD
A["AxVisor"]
B["Guest VM Testing"]
C["NimbOS Guest"]
D["ArceOS Guest"]
E["Linux Guest"]
F["Setup NimbOS Image"]
G["Run with NimbOS Config"]
H["Setup ArceOS Image"]
I["Run with ArceOS Config"]
J["Setup Linux Image"]
K["Run with Linux Config"]

A --> B
B --> C
B --> D
B --> E
C --> F
D --> H
E --> J
F --> G
H --> I
J --> K

Setting Up Guest Images

  1. NimbOS Guest:
# The CI/CD pipeline uses this action to set up NimbOS
# You can download releases from the NimbOS repository
  1. Ubuntu Guest (for Linux testing):
make DISK_IMG=ubuntu.img ubuntu_img

Sources: .github/workflows/actions/setup-nimbos-guest-image/action.yml(L1 - L71)  Makefile(L219 - L224) 

Remote Testing Infrastructure

If you need to test changes that require specific hardware features (like Intel VT-x for x86_64), you can use the remote testing infrastructure. This is particularly important for hypervisor development where architectural differences can impact functionality.

Why Remote Testing is Needed

Remote testing allows testing on specific hardware configurations that may not be available in GitHub's runners. In particular:

  • Testing Intel VT-x features
  • Testing with high-end hardware
  • Testing with specific hardware configurations

Sources: .github/workflows/REMOTE-CI.md(L1 - L50) 

This document provides an overview of how to contribute to AxVisor. For more specific information about components, please refer to other sections of the wiki.

Testing Infrastructure

Relevant source files

This page documents the testing infrastructure for AxVisor, explaining how testing is automated, configured, and executed. It covers both local and remote testing setups, guest image preparation, and how to run and interpret tests. For CI/CD pipeline details beyond testing, see CI/CD Pipeline.

Overview

AxVisor employs a comprehensive testing infrastructure to ensure that changes don't break functionality across supported architectures (x86_64, aarch64, and riscv64). The testing system runs virtual machines with guest operating systems to verify that virtualization works correctly.

flowchart TD
subgraph subGraph1["Testing Infrastructure"]
    test["Testing System"]
    local["Local Testing"]
    remote["Remote Testing"]
    localSetup["Setup Environment"]
    localRun["Run Tests"]
    localCheck["Check Results"]
    remoteSetup["Setup Environment"]
    remoteTransfer["Transfer Files"]
    remoteRun["Run Tests"]
    remoteCheck["Check Results"]
    subgraph subGraph0["Environment Setup"]
        setupToolchain["Set Rust Toolchain"]
        setupDeps["Install Dependencies"]
        setupQEMU["Set up QEMU"]
        setupGuestImage["Prepare Guest Images"]
    end
end

local --> localCheck
local --> localRun
local --> localSetup
localSetup --> setupDeps
localSetup --> setupGuestImage
localSetup --> setupQEMU
localSetup --> setupToolchain
remote --> remoteCheck
remote --> remoteRun
remote --> remoteSetup
remote --> remoteTransfer
remoteSetup --> setupToolchain
test --> local
test --> remote

Sources: .github/workflows/test.yml(L1 - L150) 

Testing Strategy

The AxVisor testing infrastructure is built around running actual guest virtual machines to validate functionality. Tests are executed both in local GitHub Actions runners and on specialized remote hardware for architecture-specific testing.

Test Matrix

Tests are conducted across a matrix of configurations:

Test TypeArchitecturesRust ToolchainsHardware Acceleration
Localriscv64, aarch64nightly-2024-12-25, nightlyNo (QEMU)
Remotex86_64nightly-2024-12-25, nightlyYes (KVM)

Sources: .github/workflows/test.yml(L10 - L59) 

Local Testing Workflow

Local testing runs on GitHub Actions runners and focuses on riscv64 and aarch64 architectures using QEMU emulation.

sequenceDiagram
    participant GitHubActions as "GitHub Actions"
    participant checkoutv4 as "checkout@v4"
    participant rusttoolchainstable as "rust-toolchain@stable"
    participant setupqemuv01 as "setup-qemu@v0.1"
    participant setupnimbosguestimage as "setup-nimbos-guest-image"
    participant TestRunner as "Test Runner"

    GitHubActions ->> checkoutv4: Checkout repository
    GitHubActions ->> rusttoolchainstable: Setup Rust toolchain
    Note over rusttoolchainstable: nightly-2024-12-25 or latest nightly
    GitHubActions ->> setupqemuv01: Setup QEMU v8.2.0
    GitHubActions ->> setupnimbosguestimage: Prepare NimbOS guest image
    Note over setupnimbosguestimage: Download NimbOS v0.7, create disk image
    GitHubActions ->> TestRunner: Configure KVM permissions
    GitHubActions ->> TestRunner: Update rust-toolchain.toml
    GitHubActions ->> TestRunner: Run tests with make
    Note over TestRunner: ARCH=(riscv64/aarch64), ACCEL=n

Sources: .github/workflows/test.yml(L10 - L50)  .github/workflows/actions/setup-nimbos-guest-image/action.yml(L1 - L70) 

Local Test Execution Steps

  1. Check out the repository
  2. Set up the specified Rust toolchain with rust-src component
  3. Install cargo-binutils
  4. Set up QEMU for the target architecture
  5. Download and prepare NimbOS guest image
  6. Configure KVM permissions
  7. Update rust-toolchain.toml with the specified toolchain
  8. Run the test using make with:
  • Target architecture
  • Disk image path
  • Verbose logging
  • Block device enabled
  • Hardware acceleration disabled
  • VM configuration file path
  • Filesystem support

Sources: .github/workflows/test.yml(L19 - L50) 

Remote Testing Workflow

Remote testing targets x86_64 architecture on specialized hardware with KVM acceleration enabled.

sequenceDiagram
    participant GitHubActions as "GitHub Actions"
    participant checkoutv4 as "checkout@v4"
    participant setupnimbosguestimage as "setup-nimbos-guest-image"
    participant scpactionv017 as "scp-action@v0.1.7"
    participant sshactionv122 as "ssh-action@v1.2.2"
    participant RemoteRunner as "Remote Runner"

    GitHubActions ->> checkoutv4: Checkout repository
    GitHubActions ->> GitHubActions: Update rust-toolchain.toml
    GitHubActions ->> GitHubActions: Compress source code
    GitHubActions ->> setupnimbosguestimage: Prepare NimbOS guest image
    GitHubActions ->> scpactionv017: Copy files to remote runner
    Note over scpactionv017: Transfer disk image and source code
    GitHubActions ->> sshactionv122: Execute test on remote runner
    sshactionv122 ->> RemoteRunner: Extract source code
    sshactionv122 ->> RemoteRunner: Run make defconfig
    sshactionv122 ->> RemoteRunner: Run make with VM configs
    Note over RemoteRunner: ARCH=x86_64, ACCEL=y
    sshactionv122 ->> RemoteRunner: Check for panic in output
    sshactionv122 ->> RemoteRunner: Clean up

Sources: .github/workflows/test.yml(L52 - L149) 

Remote Test Execution Steps

  1. Check out the repository
  2. Update rust-toolchain.toml with the specified toolchain
  3. Compress the source code
  4. Prepare NimbOS guest image locally
  5. Transfer files to the remote runner:
  • Compressed source code
  • Disk image
  1. On the remote runner:
  • Extract the source code
  • Run make defconfig for x86_64
  • Execute test with hardware acceleration enabled
  • Check output for panic messages
  • Clean up temporary files

Sources: .github/workflows/test.yml(L72 - L149) 

Guest VM Image Preparation

The testing infrastructure uses a custom action to prepare guest VM images (specifically NimbOS) for testing.

flowchart TD
subgraph subGraph0["setup-nimbos-guest-image Action"]
    setupStart["Start Setup"]
    downloadNimbOS["Download NimbOS Release"]
    unzipNimbOS["Unzip NimbOS Image"]
    renameNimbOS["Rename to nimbos-{arch}.bin"]
    isX86["Is x86_64?"]
    downloadBIOS["Download AXVM BIOS"]
    createDiskImage["Create Disk Image"]
    mountDisk["Mount Disk Image"]
    copyFiles["Copy Files to Disk"]
    unmountDisk["Unmount Disk"]
    cleanup["Cleanup Temporary Files"]
end

copyFiles --> unmountDisk
createDiskImage --> mountDisk
downloadBIOS --> createDiskImage
downloadNimbOS --> unzipNimbOS
isX86 --> createDiskImage
isX86 --> downloadBIOS
mountDisk --> copyFiles
renameNimbOS --> isX86
setupStart --> downloadNimbOS
unmountDisk --> cleanup
unzipNimbOS --> renameNimbOS

Sources: .github/workflows/actions/setup-nimbos-guest-image/action.yml(L1 - L70) 

Guest Image Setup Process

The setup process:

  1. Creates a temporary directory
  2. Downloads the appropriate NimbOS release for the target architecture
  3. Unzips and renames the NimbOS binary
  4. For x86_64, also downloads the AXVM BIOS
  5. Creates a disk image using the makefile
  6. Mounts the disk image
  7. Copies the NimbOS binary and BIOS (if applicable) to the disk image
  8. Unmounts the disk image and cleans up temporary files

Sources: .github/workflows/actions/setup-nimbos-guest-image/action.yml(L32 - L70) 

Running Tests Manually

To run tests manually in a local development environment, follow these steps:

  1. Set up a disk image with a guest OS (like NimbOS)
  2. Configure the test environment:
# For local testing (without hardware acceleration)
make ARCH=<arch> defconfig
make ARCH=<arch> DISK_IMG=<path_to_disk_img> LOG=info BLK=y ACCEL=n VM_CONFIGS=<path_to_vm_config> APP_FEATURES=fs run

# For testing with hardware acceleration (KVM)
make ARCH=<arch> defconfig
make ARCH=<arch> DISK_IMG=<path_to_disk_img> LOG=info BLK=y ACCEL=y VM_CONFIGS=<path_to_vm_config> APP_FEATURES=fs run

Replace <arch> with the target architecture (x86_64, aarch64, or riscv64), <path_to_disk_img> with the path to your guest disk image, and <path_to_vm_config> with the path to your VM configuration file.

Sources: .github/workflows/test.yml(L45 - L50)  .github/workflows/test.yml(L133 - L134) 

Interpreting Test Results

Tests are considered successful if they complete without generating "panic" messages in the output. The CI system checks for these messages automatically:

if grep -q "panic" make_output.log; then
  # Test failed
  exit 1
else
  # Test passed
fi

For debugging failed tests, examine the log output for:

  • Panic messages
  • Errors during VM boot
  • Issues with guest OS execution
  • Problems with virtual device emulation

Sources: .github/workflows/test.yml(L139 - L146) 

Architecture-Specific Considerations

Each supported architecture has specific testing requirements:

x86_64

  • Tested on remote runners with KVM acceleration
  • Requires AXVM BIOS for guest OS boot
  • Uses hardware virtualization extensions (VT-x)

aarch64

  • Tested on local GitHub Actions runners
  • Uses QEMU for emulation
  • Leverages virtualization extensions (EL2 mode)

riscv64

  • Tested on local GitHub Actions runners
  • Uses QEMU for emulation
  • Relies on SBI runtime for hypervisor operations

Sources: .github/workflows/test.yml(L16)  .github/workflows/test.yml(L57)  .github/workflows/actions/setup-nimbos-guest-image/action.yml(L52 - L58) 

CI/CD Pipeline

Relevant source files

This page documents the Continuous Integration and Continuous Deployment (CI/CD) pipeline used by the AxVisor hypervisor project. The pipeline handles automated building, testing, and verification across multiple architectures (x86_64, aarch64, riscv64) and environments to ensure the hypervisor operates correctly on various platforms.

For information about the testing infrastructure and methodology, see Testing Infrastructure.

Overview of the CI/CD Pipeline

The AxVisor CI/CD pipeline is implemented using GitHub Actions and consists of two main testing strategies:

  1. Local Testing: Tests run directly on GitHub-hosted runners
  2. Remote Testing: Tests run on remote machines for hardware-specific testing (particularly for Intel VT-x)

The pipeline automatically triggers on both push events and pull requests, ensuring all code changes are verified before merging.

flowchart TD
subgraph subGraph2["CI/CD Pipeline"]
    trigger["Code Push or PR"]
    gh_actions["GitHub Actions Workflow"]
    subgraph subGraph1["Remote Testing (Custom Servers)"]
        remote_test["test_remote Job"]
        compress["Compress Source"]
        copy["Copy to Remote Server"]
        extract["Extract Source"]
        run_remote["Run Tests on Remote"]
    end
    subgraph subGraph0["Local Testing (GitHub Runners)"]
        local_test["test_local Job"]
        setup_qemu["Setup QEMU Action"]
        setup_img["Setup NimbOS Guest Image"]
        run_test["Run Tests"]
    end
end

compress --> copy
copy --> extract
extract --> run_remote
gh_actions --> local_test
gh_actions --> remote_test
local_test --> setup_qemu
remote_test --> compress
setup_img --> run_test
setup_qemu --> setup_img
trigger --> gh_actions

Sources: .github/workflows/test.yml(L1 - L150) 

Local Testing Process

Local testing runs on GitHub-hosted runners with multiple architecture and Rust toolchain configurations. This testing strategy covers non-hardware-specific features and ensures compatibility across architectures.

Configuration Matrix

The local testing strategy uses a configuration matrix to test multiple combinations:

ArchitectureRust Toolchain
riscv64nightly-2024-12-25
riscv64nightly
aarch64nightly-2024-12-25
aarch64nightly

Local Testing Workflow

sequenceDiagram
    participant GitHubActions as "GitHub Actions"
    participant GitHubRunner as "GitHub Runner"
    participant QEMUVM as "QEMU VM"
    participant NimbOSGuest as "NimbOS Guest"

    GitHubActions ->> GitHubRunner: Start test_local job
    GitHubRunner ->> GitHubRunner: Install Rust toolchain
    GitHubRunner ->> GitHubRunner: Install cargo-binutils
    GitHubRunner ->> GitHubRunner: Setup QEMU
    GitHubRunner ->> GitHubRunner: Prepare NimbOS guest image
    GitHubRunner ->> GitHubRunner: Configure KVM permissions
    GitHubRunner ->> GitHubRunner: Update rust-toolchain.toml
    GitHubRunner ->> QEMUVM: Launch QEMU with NimbOS guest
    QEMUVM ->> NimbOSGuest: Boot NimbOS
    NimbOSGuest ->> GitHubRunner: Test results
    GitHubRunner ->> GitHubActions: Report test status

Sources: .github/workflows/test.yml(L10 - L50)  .github/workflows/actions/setup-qemu/action.yml(L1 - L48)  .github/workflows/actions/setup-nimbos-guest-image/action.yml(L1 - L71) 

Remote Testing Process

Remote testing is used specifically for hardware-dependent features (such as Intel VT-x virtualization) that cannot be properly tested on GitHub's AMD-based runners. This is essential for AxVisor as a hypervisor project that requires specific CPU virtualization features.

Remote Testing Architecture

flowchart TD
subgraph subGraph1["Remote Server"]
    remote_dir["~/runner/remote_test-{run_id}"]
    extract["Extract Source"]
    build["Build AxVisor"]
    run["Run With NimbOS Guest"]
    verify["Verify No Panics"]
    cleanup["Clean Up"]
end
subgraph subGraph0["GitHub Actions"]
    gh_action["test_remote Job"]
    prepare["Prepare Source & Images"]
    scp["SCP Action"]
    ssh["SSH Action"]
end

build --> run
extract --> build
gh_action --> prepare
prepare --> scp
run --> verify
scp --> remote_dir
scp --> ssh
ssh --> extract
verify --> cleanup

Sources: .github/workflows/test.yml(L52 - L150)  .github/workflows/REMOTE-CI.md(L1 - L50) 

Remote Server Configuration

Remote testing currently uses two server configurations:

Server IDPurpose
remote_aarkegzIntel CPU for VT-x testing
remote_x10driAdditional Intel platform

Credentials and connection details for these servers are stored as GitHub secrets and accessed securely during the workflow execution.

Sources: .github/workflows/test.yml(L59 - L70) 

Guest VM Testing System

AxVisor is tested with real guest VMs to verify its functionality as a hypervisor. The primary guest OS used for testing is NimbOS, which is lightweight and designed specifically for testing purposes.

Guest Image Preparation Process

flowchart TD
subgraph subGraph0["Guest Image Setup"]
    action["setup-nimbos-guest-image Action"]
    download["Download NimbOS Release"]
    extract["Extract Image"]
    bios["Download BIOS (x86_64 only)"]
    create["Create Disk Image"]
    mount["Mount Image"]
    copy["Copy Files to Image"]
    unmount["Unmount Image"]
end

action --> download
bios --> create
copy --> unmount
create --> mount
download --> extract
extract --> bios
mount --> copy

Sources: .github/workflows/actions/setup-nimbos-guest-image/action.yml(L1 - L71) 

Guest Testing Configuration

The testing configuration for guest VMs includes:

  • Disk Image: A FAT32 filesystem image containing the guest OS
  • Virtual Hardware: Configured through QEMU with appropriate architecture-specific settings
  • VM Configuration: Loaded from configs/vms/nimbos-{arch}.toml files

Sources: .github/workflows/test.yml(L47 - L50)  .github/workflows/test.yml(L134) 

QEMU Setup and Integration

QEMU is the virtualization platform used for testing AxVisor. A custom GitHub Action is used to set up QEMU with the required configurations.

QEMU Installation Process

flowchart TD
subgraph subGraph0["QEMU Setup Action"]
    setup["setup-qemu Action"]
    cache_check["Check Cache"]
    download["Download QEMU Source"]
    build["Build QEMU"]
    install["Install QEMU"]
    verify["Verify Installation"]
end

build --> install
cache_check --> download
cache_check --> install
download --> build
install --> verify
setup --> cache_check

Sources: .github/workflows/actions/setup-qemu/action.yml(L1 - L48) 

QEMU Configuration

The QEMU setup configures several architecture-specific system emulators:

  • qemu-system-x86_64 for x86_64 architecture
  • qemu-system-aarch64 for ARM/aarch64 architecture
  • qemu-system-riscv64 for RISC-V architecture

Each emulator is built with slirp networking support and appropriate target configurations.

Sources: .github/workflows/actions/setup-qemu/action.yml(L28 - L31)  .github/workflows/actions/setup-qemu/action.yml(L42 - L47) 

Integration with Makefile Test Targets

The CI/CD pipeline integrates with the project's Makefile test targets to execute tests within both local and remote environments.

Makefile Testing Commands

flowchart TD
subgraph subGraph0["Makefile Testing Targets"]
    make_test["make test"]
    app_test["App-level Tests"]
    make_unittest["make unittest"]
    unit_test["Unit Tests"]
    make_unit_no_fail["make unittest_no_fail_fast"]
    unit_test_nff["Unit Tests (No Fail Fast)"]
end

make_test --> app_test
make_unit_no_fail --> unit_test_nff
make_unittest --> unit_test

Sources: Makefile(L203 - L210) 

CI Testing Execution

In the CI/CD pipeline, tests are executed using a specific command that includes several parameters:

make ARCH={arch} DISK_IMG={disk_img} LOG=info BLK=y ACCEL={accel} VM_CONFIGS={vm_configs} APP_FEATURES=fs run

This command configures:

  • The target architecture
  • The disk image path
  • Logging level
  • Block device support
  • Hardware acceleration (KVM on x86_64)
  • VM configuration files
  • Additional features

Sources: .github/workflows/test.yml(L49 - L50)  .github/workflows/test.yml(L134) 

Development Workflow Integration

The CI/CD pipeline is integrated into the development workflow to provide automated testing and verification for all code changes.

Pull Request Workflow

sequenceDiagram
    participant Developer as Developer
    participant GitHub as GitHub
    participant CICDPipeline as "CI/CD Pipeline"
    participant Reviewers as Reviewers

    Developer ->> GitHub: Create Pull Request
    GitHub ->> CICDPipeline: Trigger CI Workflow
    CICDPipeline ->> CICDPipeline: Run Local Tests
    CICDPipeline ->> CICDPipeline: Run Remote Tests
    CICDPipeline ->> GitHub: Report Test Results
    Reviewers ->> GitHub: Review Code
    GitHub ->> Developer: Feedback
    Developer ->> GitHub: Address Feedback
    GitHub ->> CICDPipeline: Trigger CI Again
    CICDPipeline ->> GitHub: Updated Results
    Reviewers ->> GitHub: Approve PR
    GitHub ->> GitHub: Merge PR

Multi-Architecture Support

The CI/CD pipeline is designed to test AxVisor across multiple CPU architectures to ensure broad compatibility.

Architecture Matrix

The pipeline supports testing on three key architectures:

ArchitectureLocal TestingRemote TestingVirtualization Technology
x86_64NoYesIntel VT-x
aarch64YesNoARM Virtualization Extensions
riscv64YesNoRISC-V H-Extension

Sources: .github/workflows/test.yml(L16)  .github/workflows/test.yml(L57) 

Future Improvements

Based on the documentation, several potential improvements to the CI/CD pipeline are planned:

  1. Better Tool Integration: Using the act tool to run CI tests more consistently
  2. Improved SSH Tooling: Finding better alternatives to the current SSH action tools
  3. Enhanced Caching: Implementing more efficient caching strategies for QEMU and other environments
  4. Automated Environment Setup: Creating automated setup for remote testing environments
  5. Additional Hardware Resources: Acquiring more stable and powerful servers for remote testing

Sources: .github/workflows/REMOTE-CI.md(L40 - L49) 

Overview

Relevant source files

This document provides an overview of the axdevice_crates repository, which contains foundational crates for building emulated device subsystems within the ArceOS hypervisor ecosystem. The repository specifically targets no_std embedded environments and provides the core abstractions needed for device emulation in virtualized guest systems.

For detailed information about the core device abstraction layer, see Core Architecture. For implementation guidance, see Development Guide. For specifics about external dependencies and ArceOS integration, see Dependencies and Integration.

Purpose and Scope

The axdevice_crates repository serves as the foundation for device emulation in the ArceOS hypervisor. It provides:

  • Core traits and abstractions for emulated devices (BaseDeviceOps)
  • A comprehensive device type taxonomy (EmuDeviceType)
  • Integration points with ArceOS hypervisor address space management
  • Support for multiple architectures and virtualization approaches

The repository is designed for no_std environments, making it suitable for embedded systems and hypervisor contexts where the standard library is not available.

Sources: README.md(L1 - L5)  Cargo.toml(L1 - L16) 

Repository Architecture

System Architecture Overview

The repository follows a workspace-based structure with axdevice_base as the foundational crate:

flowchart TD
subgraph subGraph3["no_std Environment"]
    EMB["Embedded RuntimeConstraints"]
end
subgraph subGraph2["External Dependencies"]
    AE["axerrno"]
    AA["axaddrspace"]
    MA["memory_addr"]
    SE["serde"]
end
subgraph subGraph1["ArceOS Hypervisor Ecosystem"]
    AH["ArceOS Hypervisor"]
    AS["Address Space Manager"]
    VM["Virtual Machines"]
end
subgraph subGraph0["axdevice_crates Repository"]
    WS["Workspace Root(Cargo.toml)"]
    AB["axdevice_base Crate"]
    README["README.md"]
end

AB --> AA
AB --> AE
AB --> AH
AB --> EMB
AB --> MA
AB --> SE
AH --> AS
AH --> VM
WS --> AB

Sources: Cargo.toml(L1 - L16)  README.md(L3) 

Core Component Relationships

The following diagram shows how the main code entities relate to each other:

classDiagram
class BaseDeviceOps {
    <<trait>>
    
    +emu_type() EmuDeviceType
    +address_range() AddrRange~GuestPhysAddr~
    +handle_read(addr, width) AxResult~usize~
    +handle_write(addr, width, val) AxResult~() ~
}

class EmuDeviceType {
    <<enum>>
    EmuDeviceTConsole
    EmuDeviceTGicdV2
    EmuDeviceTGPPT
    EmuDeviceTVirtioBlk
    EmuDeviceTVirtioNet
    EmuDeviceTVirtioConsole
    EmuDeviceTIOMMU
    EmuDeviceTICCSRE
    EmuDeviceTSGIR
    EmuDeviceTGICR
    EmuDeviceTMeta
    +removable() bool
    +from_usize(value) Option~EmuDeviceType~
}

class AddrRange~GuestPhysAddr~ {
    <<from axaddrspace>>
    
    
}

class AxResult~T~ {
    <<from axerrno>>
    
    
}

class GuestPhysAddr {
    <<from axaddrspace>>
    
    
}

BaseDeviceOps  -->  EmuDeviceType : "returns"
BaseDeviceOps  -->  AddrRange : "uses"
BaseDeviceOps  -->  AxResult : "returns"
BaseDeviceOps  -->  GuestPhysAddr : "uses"

Sources: Based on architectural analysis from provided diagrams

Device Type Categories

The EmuDeviceType enum categorizes emulated devices into several functional groups:

CategoryDevice TypesRemovablePurpose
Console & I/OEmuDeviceTConsole,EmuDeviceTVirtioConsoleMixedGuest system I/O
ARM Interrupt ControllersEmuDeviceTGicdV2,EmuDeviceTGPPT,EmuDeviceTICCSRE,EmuDeviceTSGIR,EmuDeviceTGICRYesARM-specific interrupt handling
Virtio ParavirtualizationEmuDeviceTVirtioBlk,EmuDeviceTVirtioNetYesHigh-performance paravirtualized devices
System InfrastructureEmuDeviceTIOMMU,EmuDeviceTMetaNoCore system components

The majority of device types (8 out of 11) are removable, indicating support for dynamic device configuration in virtualized environments.

Sources: Based on EmuDeviceType analysis from architectural diagrams

Integration with ArceOS Ecosystem

The axdevice_crates repository integrates tightly with the ArceOS hypervisor ecosystem through several key dependencies:

flowchart TD
subgraph subGraph3["ArceOS Hypervisor"]
    HV["Hypervisor Core"]
    ASM["Address SpaceManager"]
end
subgraph subGraph2["Standard Dependencies"]
    SE["serdeSerialization"]
    CF["cfg-ifConditionalCompilation"]
end
subgraph subGraph1["ArceOS Core Dependencies"]
    AE["axerrnoError Handling"]
    AA["axaddrspaceGuest PhysicalAddress Management"]
    MA["memory_addrMemory Utilities"]
end
subgraph axdevice_base["axdevice_base"]
    BDO["BaseDeviceOps"]
    EDT["EmuDeviceType"]
end

AA --> ASM
AE --> HV
BDO --> AA
BDO --> AE
BDO --> HV
BDO --> MA
EDT --> CF
EDT --> SE

Sources: Cargo.toml(L15 - L16)  architectural analysis

Development Environment

The repository targets multiple architectures and maintains compatibility with no_std environments:

  • Architecture Support: x86_64, RISC-V (riscv64gc), ARM64 (aarch64)
  • Environment: no_std compatible for embedded systems
  • License: Triple-licensed under GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0
  • Workspace Structure: Single workspace with axdevice_base as the foundational member

The no_std constraint ensures the crates can be used in hypervisor and embedded contexts where the standard library is not available, making them suitable for system-level virtualization components.

Sources: Cargo.toml(L8 - L13)  README.md(L3) 

Next Steps

This overview establishes the foundational understanding of the axdevice_crates repository. For implementation details:

Project Structure

Relevant source files

This document covers the workspace organization and architectural layout of the axdevice_crates repository. It explains how the axdevice_base foundation crate is structured and integrates with the ArceOS hypervisor ecosystem. For detailed information about the core traits and device abstractions, see Core Architecture. For build system specifics and development workflows, see Build System and CI.

Workspace Organization

The axdevice_crates repository follows a Cargo workspace pattern with a single foundational crate that serves as the basis for device emulation abstractions.

Repository Structure

flowchart TD
subgraph subGraph2["Future Extensions"]
    FUTURE["Additional device crates..."]
end
subgraph subGraph1["axdevice_base Crate"]
    CBASE["Cargo.toml"]
    SRCBASE["src/"]
    LIBRS["src/lib.rs"]
    EMUTY["src/emu_type.rs"]
end
subgraph subGraph0["Repository Root"]
    CWS["Cargo.toml (workspace)"]
    README["README.md"]
    GH[".github/workflows/"]
end

CWS --> CBASE
CWS --> FUTURE
SRCBASE --> EMUTY
SRCBASE --> LIBRS

Sources: Cargo.toml(L1 - L16)  axdevice_base/Cargo.toml(L1 - L17) 

The workspace is configured with resolver version 2 and includes the axdevice_base crate as its primary member. The workspace-level configuration defines common package metadata including version 0.1.0, authorship, and licensing under GPL-3.0-or-later, Apache-2.0, or MulanPSL-2.0.

Workspace Dependencies

DependencyVersionPurpose
axdevice_base0.1Foundation crate for device abstractions

Sources: Cargo.toml(L15 - L16) 

axdevice_base Crate Architecture

The axdevice_base crate serves as the foundational layer providing core traits and types for device emulation in the ArceOS hypervisor environment.

Crate Configuration

flowchart TD
subgraph subGraph2["Standard Dependencies"]
    SERDE["serde: 1.0.204"]
    CFGIF["cfg-if: 1.0"]
end
subgraph subGraph1["ArceOS Dependencies"]
    AXERRNO["axerrno: 0.1.0"]
    AXADDR["axaddrspace: git"]
    MEMADDR["memory_addr: 0.3"]
end
subgraph subGraph0["Package Metadata"]
    NAME["name: axdevice_base"]
    EDITION["edition: 2024"]
    VERSION["version: workspace"]
end

NAME --> AXADDR
NAME --> AXERRNO
NAME --> CFGIF
NAME --> MEMADDR
NAME --> SERDE

Sources: axdevice_base/Cargo.toml(L1 - L9)  axdevice_base/Cargo.toml(L10 - L16) 

The crate uses Rust edition 2024 and inherits workspace-level version and metadata. Key configuration aspects include:

  • No-std compatibility: Uses alloc feature for collections without requiring standard library
  • Serialization support: Serde with derive and alloc features enabled
  • Conditional compilation: cfg-if for platform-specific code paths

Dependency Architecture

The crate's dependencies are strategically chosen to support hypervisor device emulation while maintaining compatibility with embedded environments.

flowchart TD
subgraph subGraph3["Dependency Purposes"]
    ERRHAND["Error handling and result types"]
    GUESTMEM["Guest physical address management"]
    MEMUTIL["Memory address utilities"]
    SERIAL["Device type serialization"]
    CONDITIONAL["Platform-specific compilation"]
end
subgraph subGraph2["axdevice_base Dependencies"]
    subgraph subGraph1["External Crates"]
        SERDE["serde"]
        CFGIF["cfg-if"]
    end
    subgraph subGraph0["ArceOS Ecosystem"]
        ERRNO["axerrno"]
        ADDRSPACE["axaddrspace"]
        MEMADDR["memory_addr"]
    end
end

ADDRSPACE --> GUESTMEM
CFGIF --> CONDITIONAL
ERRNO --> ERRHAND
MEMADDR --> MEMUTIL
SERDE --> SERIAL

Sources: axdevice_base/Cargo.toml(L11 - L16) 

ArceOS-Specific Dependencies

  • axerrno: Provides AxResult<T> type for consistent error handling across the ArceOS ecosystem
  • axaddrspace: Supplies GuestPhysAddr and address range management for hypervisor memory operations
  • memory_addr: Low-level memory address manipulation utilities

External Dependencies

  • serde: Enables serialization of device types with derive macros and alloc feature for no-std environments
  • cfg-if: Facilitates conditional compilation for different target architectures

Integration Points

ArceOS Hypervisor Integration

The axdevice_base crate integrates with the broader ArceOS hypervisor ecosystem through well-defined interfaces:

flowchart TD
subgraph subGraph2["Device Implementations"]
    DEVS["Future Device Crates"]
end
subgraph subGraph1["axdevice_base Integration"]
    BDO["BaseDeviceOps trait"]
    EDT["EmuDeviceType enum"]
    AR["AddrRange"]
end
subgraph subGraph0["ArceOS Hypervisor Core"]
    HV["Hypervisor Engine"]
    AS["Address Space Manager"]
    VM["Virtual Machine Instance"]
end

AS --> AR
BDO --> EDT
DEVS --> BDO
DEVS --> EDT
HV --> BDO
VM --> HV

Sources: axdevice_base/Cargo.toml(L14 - L15) 

Target Architecture Support

The crate is designed to support multiple target architectures commonly used in embedded hypervisor environments:

ArchitectureTarget TriplePurpose
x86_64x86_64-unknown-linux-gnuDevelopment and testing
x86_64 bare-metalx86_64-unknown-noneHypervisor deployment
RISC-V 64-bitriscv64gc-unknown-none-elfEmbedded RISC-V systems
ARM64aarch64-unknown-none-softfloatARM-based hypervisors

The architecture support reflects the project's focus on ARM virtualization (evidenced by the prevalence of ARM interrupt controller device types) while maintaining cross-platform compatibility.

Extensibility Design

The workspace structure anticipates future expansion with additional device implementation crates:

flowchart TD
subgraph subGraph1["Future Device Crates"]
    CONSOLE["axdevice_console"]
    VIRTIO["axdevice_virtio"]
    ARM["axdevice_arm_gic"]
    IOMMU["axdevice_iommu"]
end
subgraph subGraph0["Current Structure"]
    BASE["axdevice_base"]
end

BASE --> ARM
BASE --> CONSOLE
BASE --> IOMMU
BASE --> VIRTIO

Sources: Cargo.toml(L4 - L6) 

The workspace member list currently contains only axdevice_base, but the structure supports adding device-specific implementation crates that would depend on the foundation crate and implement the BaseDeviceOps trait for specific hardware categories.

Core Architecture

Relevant source files

This document outlines the fundamental design patterns, traits, and abstractions that form the foundation of the axdevice_crates device emulation system. It covers the high-level architectural decisions, the trait-based abstraction model, and how the core components interact within the ArceOS hypervisor ecosystem.

For detailed documentation of the BaseDeviceOps trait implementation, see BaseDeviceOps Trait. For comprehensive coverage of device type classifications, see Device Type System. For specifics on memory management and guest physical addressing, see Address Space Management.

Architectural Overview

The axdevice_crates system implements a trait-based device emulation architecture designed specifically for the ArceOS hypervisor. The architecture centers around a unified abstraction layer that enables consistent device emulation across different hardware types while maintaining the performance characteristics required for hypervisor environments.

Core Architectural Components

flowchart TD
subgraph subGraph3["External Dependencies"]
    AE["axerrno::AxResult"]
    AA["axaddrspace::GuestPhysAddr"]
    MA["memory_addr::AddrRange"]
end
subgraph subGraph2["Device Implementation Layer"]
    DEV1["Console Device"]
    DEV2["Interrupt Controller"]
    DEV3["Virtio Device"]
    DEVN["... Other Devices"]
end
subgraph subGraph1["Device Abstraction Layer"]
    BDO["BaseDeviceOps Trait"]
    EDT["EmuDeviceType Enum"]
    AR["AddrRange"]
end
subgraph subGraph0["Hypervisor Integration Layer"]
    HV["ArceOS Hypervisor Core"]
    AS["Address Space Manager"]
end

AR --> AA
AR --> MA
AS --> AA
BDO --> AE
BDO --> AR
BDO --> EDT
DEV1 --> BDO
DEV1 --> EDT
DEV2 --> BDO
DEV2 --> EDT
DEV3 --> BDO
DEV3 --> EDT
DEVN --> BDO
DEVN --> EDT
HV --> BDO

Sources: axdevice_base/src/lib.rs(L1 - L31) 

Trait-Based Device Abstraction

The system employs a trait-based abstraction model where all emulated devices implement the BaseDeviceOps trait. This design provides a uniform interface for device operations while allowing each device type to implement specific emulation logic.

BaseDeviceOps Contract

classDiagram
class BaseDeviceOps {
    <<trait>>
    
    +emu_type() EmuDeviceType
    +address_range() AddrRange~GuestPhysAddr~
    +handle_read(addr: GuestPhysAddr, width: usize) AxResult~usize~
    +handle_write(addr: GuestPhysAddr, width: usize, val: usize)
}

class EmuDeviceType {
    <<enum>>
    EmuDeviceTConsole
    EmuDeviceTGicdV2
    EmuDeviceTVirtioBlk
    +removable() bool
}

class AddrRange~GuestPhysAddr~ {
    +start GuestPhysAddr
    +end GuestPhysAddr
    +contains(addr: GuestPhysAddr) bool
}

class GuestPhysAddr {
    <<from axaddrspace>>
    
    
}

class AxResult~T~ {
    <<from axerrno>>
    
    Ok(T)
    Err(AxError)
}

BaseDeviceOps  -->  EmuDeviceType : "returns"
BaseDeviceOps  -->  AddrRange : "returns"
BaseDeviceOps  -->  AxResult : "returns"
AddrRange  -->  GuestPhysAddr : "contains"

Sources: axdevice_base/src/lib.rs(L20 - L30) 

The trait defines four essential operations that every emulated device must support:

MethodPurposeReturn Type
emu_type()Device type identificationEmuDeviceType
address_range()Memory mapping boundariesAddrRange
handle_read()Read operation emulationAxResult
handle_write()Write operation emulation()

Memory-Mapped Device Emulation Model

The architecture implements a memory-mapped I/O (MMIO) model where devices are accessed through specific guest physical address ranges. This design mirrors how real hardware devices are typically accessed and provides natural integration with existing hypervisor memory management systems.

Device Access Flow

sequenceDiagram
    participant GuestVM as "Guest VM"
    participant ArceOSHypervisor as "ArceOS Hypervisor"
    participant BaseDeviceOpsImplementation as "BaseDeviceOps Implementation"
    participant EmuDeviceType as "EmuDeviceType"

    GuestVM ->> ArceOSHypervisor: "Memory Access (GuestPhysAddr)"
    ArceOSHypervisor ->> BaseDeviceOpsImplementation: "address_range()"
    BaseDeviceOpsImplementation ->> ArceOSHypervisor: "AddrRange<GuestPhysAddr>"
    alt "Address in device range"
        ArceOSHypervisor ->> BaseDeviceOpsImplementation: "emu_type()"
        BaseDeviceOpsImplementation ->> EmuDeviceType: "Get device classification"
        EmuDeviceType ->> BaseDeviceOpsImplementation: "Device type metadata"
        BaseDeviceOpsImplementation ->> ArceOSHypervisor: "EmuDeviceType"
    alt "Read Operation"
        ArceOSHypervisor ->> BaseDeviceOpsImplementation: "handle_read(addr, width)"
        BaseDeviceOpsImplementation ->> BaseDeviceOpsImplementation: "Perform device-specific read logic"
        BaseDeviceOpsImplementation ->> ArceOSHypervisor: "AxResult<usize>"
        ArceOSHypervisor ->> GuestVM: "Return read data"
    else "Write Operation"
        ArceOSHypervisor ->> BaseDeviceOpsImplementation: "handle_write(addr, width, val)"
        BaseDeviceOpsImplementation ->> BaseDeviceOpsImplementation: "Perform device-specific write logic"
        ArceOSHypervisor ->> GuestVM: "Complete write operation"
    end
    else "Address not in device range"
        ArceOSHypervisor ->> GuestVM: "Handle as regular memory access"
    end

Sources: axdevice_base/src/lib.rs(L24 - L29) 

Integration with Hypervisor Core

The device emulation system integrates tightly with the ArceOS hypervisor through several key interfaces and design decisions:

Dependency Architecture

ComponentPurposeIntegration Point
axerrno::AxResultError handling consistencyAll fallible operations returnAxResult
axaddrspace::GuestPhysAddrGuest memory addressingDevice address ranges and access parameters
memory_addr::AddrRangeAddress range managementDevice memory mapping boundaries

No Standard Library Constraint

The entire system operates under #![no_std] constraints, reflecting its target deployment in embedded hypervisor environments. This architectural decision influences several design choices:

  • Use of alloc crate for heap-allocated collections when needed
  • Reliance on external crates for core functionality (error handling, address management)
  • Emphasis on zero-cost abstractions and compile-time optimizations

Sources: axdevice_base/src/lib.rs(L1) 

Design Principles

The architecture embodies several key design principles that guide the system's development and usage:

Uniform Interface: All devices implement identical trait methods, enabling generic device management code in the hypervisor.

Type Safety: Strong typing through EmuDeviceType enum and GuestPhysAddr wrapper prevents common addressing errors.

Performance: Direct trait dispatch and no_std environment minimize runtime overhead.

Extensibility: New device types can be added by implementing BaseDeviceOps and extending EmuDeviceType.

ArceOS Integration: Deep integration with ArceOS ecosystem through shared address space management and error handling patterns.

Sources: axdevice_base/src/lib.rs(L3 - L18) 

BaseDeviceOps Trait

Relevant source files

This document covers the BaseDeviceOps trait, which defines the core interface that all emulated devices must implement in the ArceOS hypervisor device emulation system. The trait provides a uniform contract for device identification, address mapping, and memory access handling across different types of virtualized hardware.

For information about specific device types that implement this trait, see Device Type System. For details about address space management and memory mapping, see Address Space Management.

Trait Overview

The BaseDeviceOps trait serves as the fundamental abstraction layer for device emulation in the ArceOS hypervisor. It defines four essential operations that enable the hypervisor to uniformly interact with different types of emulated devices, from simple console devices to complex interrupt controllers.

flowchart TD
subgraph Parameters["Parameters"]
    GPA["GuestPhysAddr"]
    WIDTH["usize (width)"]
    VAL["usize (val)"]
end
subgraph subGraph1["Return Types"]
    EDT["EmuDeviceType"]
    AR["AddrRange"]
    AXR["AxResult"]
    UNIT["()"]
end
subgraph subGraph0["BaseDeviceOps Contract"]
    BDO["BaseDeviceOps trait"]
    EMU["emu_type()"]
    ADDR["address_range()"]
    READ["handle_read()"]
    WRITE["handle_write()"]
end

ADDR --> AR
BDO --> ADDR
BDO --> EMU
BDO --> READ
BDO --> WRITE
EMU --> EDT
READ --> AXR
READ --> GPA
READ --> WIDTH
WRITE --> GPA
WRITE --> UNIT
WRITE --> VAL
WRITE --> WIDTH

The trait is designed for no_std environments and integrates with the ArceOS ecosystem through typed address spaces and standardized error handling.

Sources: axdevice_base/src/lib.rs(L20 - L30) 

Method Specifications

Device Type Identification

flowchart TD
subgraph subGraph0["Device Categories"]
    CONSOLE["EmuDeviceTConsole"]
    VIRTIO["EmuDeviceTVirtioBlk"]
    GIC["EmuDeviceTGicdV2"]
    OTHER["Unsupported markdown: list"]
end
DEV["Device Implementation"]
EMU_TYPE["emu_type()"]
EDT["EmuDeviceType"]

DEV --> EMU_TYPE
EDT --> CONSOLE
EDT --> GIC
EDT --> OTHER
EDT --> VIRTIO
EMU_TYPE --> EDT

The emu_type() method returns an EmuDeviceType enum value that identifies the specific type of device being emulated. This enables the hypervisor to apply device-specific logic and optimizations.

MethodSignaturePurpose
emu_typefn emu_type(&self) -> EmuDeviceTypeDevice type identification for hypervisor routing

Sources: axdevice_base/src/lib.rs(L22 - L23) 

Address Range Management

flowchart TD
subgraph subGraph1["Hypervisor Usage"]
    LOOKUP["Address lookup"]
    ROUTING["Device routing"]
    VALIDATION["Access validation"]
end
subgraph subGraph0["Address Space Components"]
    GPA_START["start: GuestPhysAddr"]
    GPA_END["end: GuestPhysAddr"]
    SIZE["size calculation"]
end
ADDR_RANGE["address_range()"]
AR["AddrRange"]

ADDR_RANGE --> AR
AR --> GPA_END
AR --> GPA_START
AR --> LOOKUP
AR --> ROUTING
AR --> SIZE
AR --> VALIDATION

The address_range() method defines the guest physical address space region that the device occupies. The hypervisor uses this information to route memory accesses to the appropriate device implementation.

MethodSignaturePurpose
address_rangefn address_range(&self) -> AddrRangeMemory-mapped I/O address space definition

Sources: axdevice_base/src/lib.rs(L24 - L25) 

Memory Access Handlers

flowchart TD
subgraph subGraph2["Parameters and Results"]
    GPA_PARAM["GuestPhysAddr addr"]
    WIDTH_PARAM["usize width"]
    VAL_PARAM["usize val"]
    RESULT["AxResult"]
    VOID_RESULT["()"]
end
subgraph subGraph1["BaseDeviceOps Methods"]
    HANDLE_READ["handle_read(addr, width)"]
    HANDLE_WRITE["handle_write(addr, width, val)"]
end
subgraph subGraph0["Memory Access Flow"]
    GUEST["Guest VM Memory Access"]
    HV_DECODE["Hypervisor Address Decode"]
    DEVICE_CHECK["Device Range Check"]
end

DEVICE_CHECK --> HANDLE_READ
DEVICE_CHECK --> HANDLE_WRITE
GUEST --> HV_DECODE
HANDLE_READ --> GPA_PARAM
HANDLE_READ --> RESULT
HANDLE_READ --> WIDTH_PARAM
HANDLE_WRITE --> GPA_PARAM
HANDLE_WRITE --> VAL_PARAM
HANDLE_WRITE --> VOID_RESULT
HANDLE_WRITE --> WIDTH_PARAM
HV_DECODE --> DEVICE_CHECK

The memory access handlers implement the core device emulation logic. The handle_read() method processes guest read operations and returns emulated device state, while handle_write() processes guest write operations that may modify device behavior.

MethodSignatureReturnPurpose
handle_readfn handle_read(&self, addr: GuestPhysAddr, width: usize) -> AxResultAxResultProcess guest memory read operations
handle_writefn handle_write(&self, addr: GuestPhysAddr, width: usize, val: usize)()Process guest memory write operations

Sources: axdevice_base/src/lib.rs(L26 - L29) 

Implementation Requirements

Error Handling

Read operations return AxResult<usize> to handle potential emulation errors such as invalid register addresses or unsupported access widths. Write operations use a void return type, with error conditions typically handled through device-specific state changes or logging.

Access Width Support

The width parameter specifies the memory access size in bytes (typically 1, 2, 4, or 8). Device implementations must handle the access widths supported by their emulated hardware, potentially returning errors for unsupported widths.

Address Validation

Device implementations should validate that the provided addr parameter falls within their declared address range and corresponds to a valid device register or memory location.

Integration Patterns

flowchart TD
subgraph subGraph2["External Dependencies"]
    AXADDRSPACE["axaddrspace::GuestPhysAddr"]
    AXERRNO["axerrno::AxResult"]
    MEMORY_ADDR["memory_addr::AddrRange"]
end
subgraph subGraph1["Device Implementation"]
    DEVICE["Device Struct"]
    BDO_IMPL["BaseDeviceOps impl"]
    DEV_STATE["Device State"]
end
subgraph subGraph0["ArceOS Hypervisor"]
    HV_CORE["Hypervisor Core"]
    ADDR_MGR["Address Space Manager"]
    VM_EXIT["VM Exit Handler"]
end

ADDR_MGR --> BDO_IMPL
BDO_IMPL --> AXADDRSPACE
BDO_IMPL --> AXERRNO
BDO_IMPL --> DEVICE
BDO_IMPL --> MEMORY_ADDR
DEVICE --> DEV_STATE
HV_CORE --> ADDR_MGR
VM_EXIT --> HV_CORE

The trait integrates with the ArceOS hypervisor through standardized address space management and error handling. Device implementations typically maintain internal state and use the trait methods as the primary interface for hypervisor interaction.

Sources: axdevice_base/src/lib.rs(L11 - L18) 

Device Type System

Relevant source files

This document covers the EmuDeviceType enumeration and associated type system that categorizes and manages different classes of emulated devices in the ArceOS hypervisor. The device type system provides unified identification, classification, and removability management for all supported emulated hardware devices.

For information about how devices implement behavior through the trait system, see BaseDeviceOps Trait. For details about memory mapping and address ranges, see Address Space Management.

Overview

The device type system is built around the EmuDeviceType enum defined in axdevice_base/src/emu_type.rs(L3 - L28)  This enumeration serves as the central registry of all supported emulated device types in the ArceOS hypervisor ecosystem, providing type identification, categorization, and operational characteristics for each device class.

flowchart TD
subgraph subGraph1["Integration Points"]
    BDO["BaseDeviceOps trait"]
    HV["ArceOS Hypervisor"]
end
subgraph subGraph0["Device Type System Core"]
    EDT["EmuDeviceType enum"]
    REM["removable() method"]
    CONV["from_usize() method"]
    DISP["Display implementation"]
end

BDO --> EDT
EDT --> CONV
EDT --> DISP
EDT --> HV
EDT --> REM
HV --> BDO

Sources: axdevice_base/src/emu_type.rs(L3 - L28)  axdevice_base/src/lib.rs(L20 - L30) 

Device Type Enumeration

The EmuDeviceType enum defines 11 distinct device types, each assigned a unique numeric identifier from 0 to 10. The enumeration uses explicit discriminant values to ensure stable serialization and conversion.

Device TypeValueDescription
EmuDeviceTConsole0Console device
EmuDeviceTGicdV21ARM interrupt controller V2 device
EmuDeviceTGPPT2Partial passthrough interrupt controller device
EmuDeviceTVirtioBlk3Virtio block device
EmuDeviceTVirtioNet4Virtio net device
EmuDeviceTVirtioConsole5Virtio console device
EmuDeviceTIOMMU6IOMMU device
EmuDeviceTICCSRE7Interrupt ICC SRE device
EmuDeviceTSGIR8Interrupt ICC SGIR device
EmuDeviceTGICR9Interrupt controller GICR device
EmuDeviceTMeta10Meta device

Sources: axdevice_base/src/emu_type.rs(L5 - L28) 

Device Categories

Device Type Taxonomy by Functional Category

flowchart TD
subgraph subGraph2["Virtio Paravirtualization"]
    IOMMU["EmuDeviceTIOMMU"]
    META["EmuDeviceTMeta"]
    VBLK["EmuDeviceTVirtioBlk"]
    VNET["EmuDeviceTVirtioNet"]
    VCONS2["EmuDeviceTVirtioConsole"]
    GICD["EmuDeviceTGicdV2"]
    GPPT["EmuDeviceTGPPT"]
    ICCSRE["EmuDeviceTICCSRE"]
    CONS["EmuDeviceTConsole"]
    VCONS["EmuDeviceTVirtioConsole"]
    subgraph subGraph0["Console and I/O Devices"]
        subgraph subGraph3["System Infrastructure"]
            IOMMU["EmuDeviceTIOMMU"]
            META["EmuDeviceTMeta"]
            VBLK["EmuDeviceTVirtioBlk"]
            VNET["EmuDeviceTVirtioNet"]
            GICD["EmuDeviceTGicdV2"]
            GPPT["EmuDeviceTGPPT"]
            CONS["EmuDeviceTConsole"]
            VCONS["EmuDeviceTVirtioConsole"]
        end
    end
end
subgraph subGraph1["ARM Interrupt Controllers"]
    IOMMU["EmuDeviceTIOMMU"]
    META["EmuDeviceTMeta"]
    VBLK["EmuDeviceTVirtioBlk"]
    VNET["EmuDeviceTVirtioNet"]
    VCONS2["EmuDeviceTVirtioConsole"]
    GICD["EmuDeviceTGicdV2"]
    GPPT["EmuDeviceTGPPT"]
    ICCSRE["EmuDeviceTICCSRE"]
    SGIR["EmuDeviceTSGIR"]
    GICR["EmuDeviceTGICR"]
    CONS["EmuDeviceTConsole"]
    VCONS["EmuDeviceTVirtioConsole"]
end

Sources: axdevice_base/src/emu_type.rs(L5 - L28)  axdevice_base/src/emu_type.rs(L34 - L45) 

ARM Interrupt Controller Focus

The device type system shows a strong focus on ARM architecture virtualization, with 5 out of 11 device types (45%) dedicated to ARM Generic Interrupt Controller (GIC) components:

  • EmuDeviceTGicdV2: ARM GICv2 distributor interface
  • EmuDeviceTGPPT: Partial passthrough interrupt controller
  • EmuDeviceTICCSRE: ICC System Register Enable interface
  • EmuDeviceTSGIR: ICC Software Generated Interrupt Register
  • EmuDeviceTGICR: GIC redistributor interface

Virtio Paravirtualization Support

Three device types implement Virtio paravirtualization standards:

  • EmuDeviceTVirtioBlk: Block storage device
  • EmuDeviceTVirtioNet: Network interface device
  • EmuDeviceTVirtioConsole: Console device

Sources: axdevice_base/src/emu_type.rs(L5 - L28) 

Removability Classification

The device type system categorizes devices by removability using the removable() method defined in axdevice_base/src/emu_type.rs(L52 - L64)  This classification determines whether devices can be dynamically added or removed from a running virtual machine.

Device Removability Classification

flowchart TD
subgraph subGraph0["Removable Devices (8 of 11)"]
    GPPT_R["EmuDeviceTGPPT"]
    VBLK_R["EmuDeviceTVirtioBlk"]
    VNET_R["EmuDeviceTVirtioNet"]
    GICR_R["EmuDeviceTGICR"]
    VCONS_R["EmuDeviceTVirtioConsole"]
    subgraph subGraph1["Non-Removable Devices (3 of 11)"]
        CONS_NR["EmuDeviceTConsole"]
        IOMMU_NR["EmuDeviceTIOMMU"]
        META_NR["EmuDeviceTMeta"]
        GICD_R["EmuDeviceTGicdV2"]
        SGIR_R["EmuDeviceTSGIR"]
        ICCSRE_R["EmuDeviceTICCSRE"]
    end
end

Sources: axdevice_base/src/emu_type.rs(L52 - L64) 

Removable Devices

The following 8 device types are classified as removable:

  • All ARM interrupt controller devices
  • All Virtio paravirtualization devices

Non-Removable Devices

The following 3 device types are classified as non-removable:

  • Console device: Essential for basic VM operation
  • IOMMU device: Critical system infrastructure
  • Meta device: Core hypervisor functionality

Sources: axdevice_base/src/emu_type.rs(L53 - L63) 

Type Conversion and Identification

Numeric Conversion

The from_usize() method provides conversion from numeric identifiers to device types, supporting serialization and external configuration systems.

// Example conversion pattern from axdevice_base/src/emu_type.rs:67-82
pub fn from_usize(value: usize) -> EmuDeviceType {
    match value {
        0 => EmuDeviceType::EmuDeviceTConsole,
        1 => EmuDeviceType::EmuDeviceTGicdV2,
        // ... additional mappings
        _ => panic!("Unknown EmuDeviceType value: {}", value),
    }
}

String Representation

The Display trait implementation in axdevice_base/src/emu_type.rs(L30 - L47)  provides human-readable names for debugging and logging:

  • EmuDeviceTConsole → "console"
  • EmuDeviceTGicdV2 → "Arm interrupt controller V2"
  • EmuDeviceTVirtioBlk → "virtio block"

Sources: axdevice_base/src/emu_type.rs(L30 - L47)  axdevice_base/src/emu_type.rs(L67 - L82) 

Integration with BaseDeviceOps

The device type system integrates with the BaseDeviceOps trait through the emu_type() method, enabling runtime device identification:

Device Type Integration Flow

sequenceDiagram
    participant ArceOSHypervisor as "ArceOS Hypervisor"
    participant DeviceBaseDeviceOps as "Device (BaseDeviceOps)"
    participant EmuDeviceType as "EmuDeviceType"

    ArceOSHypervisor ->> DeviceBaseDeviceOps: "emu_type()"
    DeviceBaseDeviceOps ->> EmuDeviceType: "return device type"
    EmuDeviceType ->> DeviceBaseDeviceOps: "EmuDeviceType variant"
    DeviceBaseDeviceOps ->> ArceOSHypervisor: "device type identifier"
    ArceOSHypervisor ->> EmuDeviceType: "removable()"
    EmuDeviceType ->> ArceOSHypervisor: "bool result"
    ArceOSHypervisor ->> EmuDeviceType: "Display format"
    EmuDeviceType ->> ArceOSHypervisor: "human readable name"

Sources: axdevice_base/src/lib.rs(L22 - L23)  axdevice_base/src/emu_type.rs(L50 - L83) 

This integration enables the hypervisor to:

  1. Identify device types at runtime
  2. Determine removability characteristics
  3. Apply type-specific handling logic
  4. Generate debugging output with descriptive names

Sources: axdevice_base/src/lib.rs(L20 - L30)  axdevice_base/src/emu_type.rs(L1 - L84) 

Address Space Management

Relevant source files

This document covers the address space management system used by emulated devices in the ArceOS hypervisor. It explains how guest physical addresses (GuestPhysAddr) are mapped to device handlers, how address ranges (AddrRange<GuestPhysAddr>) define device memory regions, and the integration with the axaddrspace crate for hypervisor address space management.

For information about device type classification and the BaseDeviceOps trait interface, see Device Type System and BaseDeviceOps Trait.

Core Address Space Concepts

The address space management system is built around two fundamental types from external crates:

  • GuestPhysAddr from the axaddrspace crate represents individual guest physical addresses
  • AddrRange<GuestPhysAddr> from the memory_addr crate represents contiguous address ranges

Every emulated device must define its memory-mapped region through the address_range() method in the BaseDeviceOps trait, which returns an AddrRange<GuestPhysAddr> defining the device's location in guest physical memory space.

Address Space Type Hierarchy

flowchart TD
subgraph subGraph0["External Crates"]
    AS["axaddrspace crate"]
    MA["memory_addr crate"]
end
GPA["GuestPhysAddr"]
AR["AddrRange<GuestPhysAddr>"]
BDO["BaseDeviceOps::address_range()"]
HR["BaseDeviceOps::handle_read(addr: GuestPhysAddr)"]
HW["BaseDeviceOps::handle_write(addr: GuestPhysAddr)"]

AR --> BDO
AS --> GPA
GPA --> AR
GPA --> HR
GPA --> HW
MA --> AR

Sources: axdevice_base/src/lib.rs(L11 - L14)  axdevice_base/src/lib.rs(L25)  axdevice_base/src/lib.rs(L27 - L29) 

Guest Physical Address Mapping

Guest physical addresses represent the memory layout as seen by the virtual machine. Each emulated device occupies a specific range within this address space, allowing the hypervisor to route memory accesses to the appropriate device handler.

The BaseDeviceOps trait defines the contract for address space integration:

MethodParameter TypeReturn TypePurpose
address_range()NoneAddrRangeDefine device memory region
handle_read()addr: GuestPhysAddr, width: usizeAxResultHandle guest read access
handle_write()addr: GuestPhysAddr, width: usize, val: usizeNoneHandle guest write access

The width parameter specifies the access size (1, 2, 4, or 8 bytes), allowing devices to handle different data types appropriately.

Sources: axdevice_base/src/lib.rs(L25 - L29) 

Device Address Range Management

Each device implementation must return a valid AddrRange<GuestPhysAddr> that defines its memory-mapped region. This range is used by the hypervisor's address space manager to route memory accesses to the correct device handler.

Device Address Range Resolution Flow


The address space manager maintains a mapping between guest physical address ranges and device handlers, enabling efficient routing of memory operations without requiring linear searches through all devices.

Sources: axdevice_base/src/lib.rs(L25) 

Memory Access Routing Flow

When a guest virtual machine accesses memory, the hypervisor must determine whether the access targets an emulated device or regular memory. This determination is made using the address ranges returned by each device's address_range() method.

Memory Access Decision Tree

flowchart TD
subgraph subGraph1["Hypervisor Memory Manager"]
    MEM["Handle as regular memory"]
end
subgraph subGraph0["Device Handler Methods"]
    READ["handle_read(addr, width)"]
    WRITE["handle_write(addr, width, val)"]
end
MA["Guest Memory Access at GuestPhysAddr"]
AR["Check address_range() for all devices"]
HIT["Address matches device range"]
MISS["Address does not match any device"]

AR --> HIT
AR --> MISS
HIT --> READ
HIT --> WRITE
MA --> AR
MISS --> MEM

The routing decision is based on whether the guest physical address falls within any device's declared address range. If multiple devices claim overlapping ranges, the behavior is implementation-defined by the hypervisor's address space manager.

Sources: axdevice_base/src/lib.rs(L27 - L29) 

Integration with axaddrspace

The axaddrspace crate provides the GuestPhysAddr type that represents addresses in the guest's physical memory space. This integration ensures type safety and prevents confusion between different address spaces (host physical, guest physical, guest virtual).

The choice of GuestPhysAddr as the address type in BaseDeviceOps methods indicates that device emulation operates at the guest physical memory level, after any guest virtual-to-physical translation has occurred within the guest VM.

Address Space Integration Architecture

flowchart TD
subgraph subGraph2["Host System"]
    HPA["Host Physical Address"]
end
subgraph subGraph1["ArceOS Hypervisor"]
    ASPCM["axaddrspace::GuestPhysAddr"]
    AR["memory_addr::AddrRange"]
    BDO["BaseDeviceOps methods"]
end
subgraph subGraph0["Guest VM"]
    GVA["Guest Virtual Address"]
    GPA["Guest Physical Address"]
end

ASPCM --> AR
ASPCM --> BDO
BDO --> HPA
GPA --> ASPCM
GPA --> HPA
GVA --> GPA

This architecture ensures that emulated devices receive addresses in a consistent format regardless of the guest's internal memory management configuration.

Sources: axdevice_base/src/lib.rs(L13)  axdevice_base/src/lib.rs(L25)  axdevice_base/src/lib.rs(L27 - L29) 

Dependencies and Integration

Relevant source files

This document covers the external crate dependencies used by axdevice_base and explains how they integrate to support device emulation within the ArceOS hypervisor ecosystem. The focus is on dependency selection rationale, integration patterns, and the no_std compatibility requirements.

For detailed coverage of how ArceOS-specific dependencies (axerrno, axaddrspace, memory_addr) enable hypervisor functionality, see ArceOS Integration. For information about how dependencies support the core device abstraction, see BaseDeviceOps Trait.

Dependency Architecture Overview

The axdevice_base crate maintains a minimal dependency footprint while supporting essential hypervisor device emulation capabilities. All dependencies are carefully selected for no_std compatibility and embedded system constraints.

Dependency Categories and Integration

flowchart TD
subgraph subGraph3["Core Allocator"]
    AL["alloc::collections"]
end
subgraph subGraph2["Standard Utilities"]
    SE["serde (derive, alloc)"]
    CF["cfg-if"]
end
subgraph subGraph1["ArceOS Ecosystem Dependencies"]
    AE["axerrno::AxResult"]
    AA["axaddrspace::AddrRange"]
    MA["memory_addr::GuestPhysAddr"]
end
subgraph axdevice_base["axdevice_base"]
    BDO["BaseDeviceOps"]
    EMU["EmuDeviceType"]
end

AA --> MA
BDO --> AA
BDO --> AE
BDO --> MA
EMU --> CF
EMU --> SE
SE --> AL

Sources: axdevice_base/Cargo.toml(L10 - L16) 

Dependency Analysis

ArceOS Hypervisor Dependencies

The three ArceOS-specific dependencies form the core integration layer that enables device emulation within the hypervisor:

DependencyVersionPurposeIntegration Point
axerrno0.1.0Error handling for hypervisor operationsBaseDeviceOpsreturn types
axaddrspacegit latestGuest physical address managementDevice address range mapping
memory_addr0.3Memory address utilitiesAddress range boundaries

ArceOS Integration Pattern

sequenceDiagram
    participant DeviceImplementation as "Device Implementation"
    participant BaseDeviceOps as "BaseDeviceOps"
    participant axerrnoAxResult as "axerrno::AxResult"
    participant axaddrspaceAddrRange as "axaddrspace::AddrRange"
    participant memory_addrGuestPhysAddr as "memory_addr::GuestPhysAddr"

    DeviceImplementation ->> BaseDeviceOps: "implement trait"
    BaseDeviceOps ->> axaddrspaceAddrRange: "address_range()"
    axaddrspaceAddrRange ->> memory_addrGuestPhysAddr: "AddrRange<GuestPhysAddr>"
    memory_addrGuestPhysAddr ->> axaddrspaceAddrRange: "physical address boundaries"
    axaddrspaceAddrRange ->> BaseDeviceOps: "device memory mapping"
    BaseDeviceOps ->> axerrnoAxResult: "handle_read() -> AxResult<usize>"
    axerrnoAxResult ->> DeviceImplementation: "hypervisor-compatible result"

Sources: axdevice_base/Cargo.toml(L14 - L16) 

Standard Library Dependencies

serde Configuration

The serde dependency uses a specific feature configuration optimized for no_std environments:

serde = { version = "1.0.204", default-features = false, features = ["derive", "alloc"] }
  • default-features = false: Removes std dependency
  • derive feature: Enables #[derive(Serialize, Deserialize)] macros for EmuDeviceType
  • alloc feature: Provides collections support without requiring full standard library

Conditional Compilation Support

The cfg-if crate enables clean conditional compilation patterns:

cfg-if = "1.0"

This dependency supports platform-specific device behavior while maintaining code clarity across different target architectures (x86_64, RISC-V, ARM64).

Sources: axdevice_base/Cargo.toml(L11 - L12) 

Integration Patterns

Error Propagation Chain

The error handling integration follows a consistent pattern from hypervisor operations through device emulation:

Error Flow Through Dependencies

flowchart TD
subgraph subGraph3["Address Resolution"]
    AA["AddrRange"]
    MA["GuestPhysAddr"]
end
subgraph subGraph2["Error Types"]
    AR["AxResult"]
    AE["AxError"]
end
subgraph subGraph1["Device Layer"]
    BD["BaseDeviceOps::handle_read()"]
    BDW["BaseDeviceOps::handle_write()"]
end
subgraph subGraph0["Hypervisor Layer"]
    HV["ArceOS Hypervisor"]
end

AA --> MA
AR --> AE
BD --> AA
BD --> AR
BDW --> AA
BDW --> AR
HV --> BD
HV --> BDW

Sources: axdevice_base/Cargo.toml(L14 - L16) 

Memory Address Integration

The address management dependencies create a unified addressing model:

  1. memory_addr::GuestPhysAddr: Provides type-safe guest physical addresses
  2. axaddrspace::AddrRange: Wraps GuestPhysAddr into address ranges
  3. BaseDeviceOps::address_range(): Returns AddrRange<GuestPhysAddr> for device mapping

This integration ensures that device emulation operates within the hypervisor's guest address space management without requiring device implementations to handle low-level address translation.

Serialization Integration

The serde integration with EmuDeviceType enables device type persistence and configuration management:

  • Device type serialization for hypervisor state saving
  • Configuration file parsing for device topology
  • Inter-component communication with serialized device descriptors

The alloc feature dependency provides the necessary collection types for handling serialized device configurations without requiring full standard library support.

Sources: axdevice_base/Cargo.toml(L12) 

Dependency Version Strategy

ArceOS Ecosystem Tracking

  • axerrno: Uses semantic versioning (0.1.0) for stable error handling interface
  • axaddrspace: Tracks git repository HEAD for active development coordination
  • memory_addr: Uses stable crate version (0.3) for mature address utilities

External Dependencies

  • serde: Pinned to specific version (1.0.204) for build reproducibility
  • cfg-if: Uses caret range (1.0) allowing compatible updates

This strategy balances stability for core functionality with active development tracking for hypervisor-specific components.

Sources: axdevice_base/Cargo.toml(L11 - L16) 

ArceOS Integration

Relevant source files

This page documents how axdevice_base integrates with the ArceOS hypervisor ecosystem through its core dependencies: axerrno, axaddrspace, and memory_addr. These dependencies enable standardized error handling, guest physical address management, and memory range abstractions essential for device emulation in the hypervisor context.

For information about the core device abstraction patterns, see Core Architecture. For implementation details of the traits and types, see BaseDeviceOps Trait.

ArceOS Dependency Overview

The axdevice_base crate relies on three key ArceOS ecosystem dependencies that provide fundamental hypervisor capabilities. These dependencies are tightly integrated into the BaseDeviceOps trait interface to ensure consistent device emulation across the ArceOS hypervisor.

Dependency Architecture

flowchart TD
subgraph subGraph2["External Dependencies"]
    SE["serde"]
    CF["cfg-if"]
    AL["alloc"]
end
subgraph subGraph1["ArceOS Core Dependencies"]
    AXE["axerrno::AxResult<T>"]
    AXA["axaddrspace::GuestPhysAddr"]
    MA["memory_addr::AddrRange<T>"]
end
subgraph axdevice_base["axdevice_base"]
    BDO["BaseDeviceOps trait"]
    AR["address_range()"]
    HR["handle_read()"]
    HW["handle_write()"]
    ET["emu_type()"]
end

AR --> AXA
AR --> MA
BDO --> AL
BDO --> AR
BDO --> CF
BDO --> ET
BDO --> HR
BDO --> HW
BDO --> SE
HR --> AXA
HR --> AXE
HW --> AXA

Sources: axdevice_base/src/lib.rs(L11 - L14)  axdevice_base/Cargo.toml(L10 - L16) 

Dependency Declaration

The dependencies are declared in the crate's Cargo.toml with specific version requirements:

DependencyVersionSourcePurpose
axerrno0.1.0crates.ioStandardized error handling
axaddrspacegit mainGitHub repositoryGuest physical address types
memory_addr0.3crates.ioMemory address range abstractions

Sources: axdevice_base/Cargo.toml(L14 - L16) 

Error Handling Integration:axerrno

The axerrno crate provides standardized error handling across the ArceOS ecosystem. In axdevice_base, it's used exclusively in the handle_read method of the BaseDeviceOps trait to indicate success or failure of read operations.

AxResult Usage Pattern

flowchart TD
subgraph subGraph1["AxResult Variants"]
    OK["Ok(data: usize)"]
    ERR["Err(AxError)"]
end
subgraph subGraph0["Device Read Operation Flow"]
    GVM["Guest VM Memory Access"]
    HV["ArceOS Hypervisor"]
    DEV["Device Implementation"]
    RES["AxResult<usize>"]
end

DEV --> RES
GVM --> HV
HV --> DEV
HV --> GVM
RES --> ERR
RES --> HV
RES --> OK

The handle_read method signature demonstrates this integration:

#![allow(unused)]
fn main() {
fn handle_read(&self, addr: GuestPhysAddr, width: usize) -> AxResult<usize>
}

This design allows device implementations to return either:

  • Ok(data) - successful read with the actual data value
  • Err(error) - standardized error indicating why the read failed

Sources: axdevice_base/src/lib.rs(L27) 

Address Space Integration:axaddrspace

The axaddrspace crate provides hypervisor-specific address space management types, particularly GuestPhysAddr which represents guest physical addresses in the virtualized environment.

GuestPhysAddr in BaseDeviceOps

The GuestPhysAddr type appears in three critical methods of the BaseDeviceOps trait:

flowchart TD
subgraph subGraph2["Hypervisor Context"]
    GAS["Guest Address Space"]
    EMU["Device Emulation"]
    MMU["Memory Management"]
end
subgraph subGraph1["GuestPhysAddr Usage"]
    ART["AddrRange<GuestPhysAddr>"]
    RPA["Read Parameter Address"]
    WPA["Write Parameter Address"]
end
subgraph subGraph0["BaseDeviceOps Methods Using GuestPhysAddr"]
    AR["address_range()"]
    HR["handle_read(addr: GuestPhysAddr, ...)"]
    HW["handle_write(addr: GuestPhysAddr, ...)"]
end

AR --> ART
ART --> GAS
EMU --> MMU
GAS --> MMU
HR --> RPA
HW --> WPA
RPA --> EMU
WPA --> EMU

The integration enables:

  • Address Range Definition: address_range() returns AddrRange<GuestPhysAddr> to define the guest physical memory region this device occupies
  • Read Access: handle_read() receives a GuestPhysAddr parameter indicating which guest physical address is being read
  • Write Access: handle_write() receives a GuestPhysAddr parameter indicating which guest physical address is being written

Sources: axdevice_base/src/lib.rs(L25 - L29) 

Memory Range Integration:memory_addr

The memory_addr crate provides the AddrRange<T> generic type used to represent contiguous memory address ranges. In axdevice_base, it's specialized with GuestPhysAddr to define device memory regions.

AddrRange Specialization

flowchart TD
subgraph subGraph2["Device Memory Mapping"]
    DEV1["Console Device Range"]
    DEV2["GICD Device Range"]
    DEV3["Virtio Device Range"]
    DEVN["Other Device Ranges"]
end
subgraph subGraph1["axdevice_base Specialization"]
    GPAR["AddrRange<GuestPhysAddr>"]
    BDO["BaseDeviceOps::address_range()"]
end
subgraph subGraph0["memory_addr Crate"]
    AR["AddrRange<T>"]
    GEN["Generic Address Range"]
end

AR --> GPAR
BDO --> DEV1
BDO --> DEV2
BDO --> DEV3
BDO --> DEVN
GPAR --> BDO

This specialization allows each device implementation to precisely define:

  • Start Address: Beginning of the device's guest physical address range
  • Size/End Address: Extent of the device's memory-mapped region
  • Bounds Checking: Automatic validation that access addresses fall within the device's range

The address_range() method is fundamental to the hypervisor's memory management, enabling it to route guest memory accesses to the appropriate emulated device based on the target address.

Sources: axdevice_base/src/lib.rs(L11 - L25) 

Integration Patterns in BaseDeviceOps

The BaseDeviceOps trait demonstrates a cohesive integration pattern where each ArceOS dependency serves a specific role in the device emulation interface:

Method Integration Matrix

Methodaxerrnoaxaddrspacememory_addrPurpose
emu_type()Device type identification
address_range()Memory region definition
handle_read()Read operation with error handling
handle_write()Write operation (no error return)

Trait Method Signatures

The complete trait definition shows the integration points:

#![allow(unused)]
fn main() {
pub trait BaseDeviceOps {
    fn emu_type(&self) -> EmuDeviceType;
    fn address_range(&self) -> AddrRange<GuestPhysAddr>;      // memory_addr + axaddrspace
    fn handle_read(&self, addr: GuestPhysAddr, width: usize) -> AxResult<usize>;  // axaddrspace + axerrno
    fn handle_write(&self, addr: GuestPhysAddr, width: usize, val: usize);        // axaddrspace
}
}

This design ensures that device implementations can seamlessly integrate with the ArceOS hypervisor's memory management and error handling systems.

Sources: axdevice_base/src/lib.rs(L21 - L30) 

Hypervisor Ecosystem Context

The ArceOS integration dependencies position axdevice_base as a bridge between the hypervisor core and device-specific implementations. This integration enables:

  • Unified Address Space: All devices operate within the same GuestPhysAddr address space managed by the hypervisor
  • Consistent Error Handling: Device operations return standardized AxResult types that the hypervisor can process uniformly
  • Memory Region Management: Device address ranges integrate with the hypervisor's memory management unit for efficient routing

The dependency choices reflect ArceOS's design philosophy of providing specialized, composable components for hypervisor functionality while maintaining no_std compatibility for embedded and constrained environments.

Sources: axdevice_base/src/lib.rs(L1 - L14)  axdevice_base/Cargo.toml(L14 - L16) 

Development Guide

Relevant source files

This document provides practical information for developers working with or extending the axdevice_crates system. It covers the development environment setup, build processes, code quality standards, and contribution workflows specific to this hypervisor device emulation framework.

For detailed information about implementing specific device types, see Implementing New Devices. For comprehensive build system documentation, see Build System and CI.

Development Environment Requirements

The axdevice_crates project requires a specific development environment configuration to support its no_std embedded target and multi-architecture requirements.

Rust Toolchain Configuration

The project exclusively uses the nightly Rust toolchain to access cutting-edge features required for hypervisor development and no_std environments. The required toolchain components are:

ComponentPurpose
rust-srcSource code for cross-compilation to embedded targets
clippyAdvanced linting for code quality enforcement
rustfmtCode formatting standardization

Supported Target Architectures

The codebase maintains compatibility across four distinct target architectures, reflecting the diverse embedded and hypervisor deployment scenarios:

flowchart TD
subgraph subGraph3["Development Capabilities"]
    UT["Unit Testing"]
    DOC["Documentation Generation"]
    BE["Bare Metal Execution"]
    HV["Hypervisor Integration"]
end
subgraph subGraph2["Target Architecture Matrix"]
    subgraph subGraph1["Bare Metal Targets"]
        T2["x86_64-unknown-none"]
        T3["riscv64gc-unknown-none-elf"]
        T4["aarch64-unknown-none-softfloat"]
    end
    subgraph subGraph0["Hosted Environment"]
        T1["x86_64-unknown-linux-gnu"]
    end
end

T1 --> DOC
T1 --> UT
T2 --> BE
T2 --> HV
T3 --> BE
T3 --> HV
T4 --> BE
T4 --> HV

Sources: .github/workflows/ci.yml(L12)  .github/workflows/ci.yml(L19) 

Development Workflow Pipeline

The development process follows a strict automated pipeline that enforces code quality and multi-architecture compatibility:

flowchart TD
subgraph subGraph1["Documentation Workflow"]
    DOC["Documentation Pipeline"]
    DOCBUILD["cargo doc --no-deps --all-features"]
    DOCCHECK["RUSTDOCFLAGS Validation"]
    DEPLOY["GitHub Pages Deployment"]
end
subgraph subGraph0["Parallel Architecture Builds"]
    BUILD["cargo build --target TARGET --all-features"]
    B1["Build x86_64-unknown-linux-gnu"]
    B2["Build x86_64-unknown-none"]
    B3["Build riscv64gc-unknown-none-elf"]
    B4["Build aarch64-unknown-none-softfloat"]
end
PR["Pull Request / Push"]
CI["CI Pipeline Trigger"]
FMT["cargo fmt --all --check"]
CLIPPY["cargo clippy --target TARGET --all-features"]
TEST["cargo test --target x86_64-unknown-linux-gnu"]
SUCCESS["Pipeline Success"]

B1 --> SUCCESS
B2 --> SUCCESS
B3 --> SUCCESS
B4 --> SUCCESS
BUILD --> B1
BUILD --> B2
BUILD --> B3
BUILD --> B4
BUILD --> TEST
CI --> FMT
CLIPPY --> BUILD
DEPLOY --> SUCCESS
DOC --> DOCBUILD
DOCBUILD --> DOCCHECK
DOCCHECK --> DEPLOY
FMT --> CLIPPY
PR --> CI
TEST --> DOC

Sources: .github/workflows/ci.yml(L22 - L30)  .github/workflows/ci.yml(L44 - L48) 

Code Quality Standards

Formatting Requirements

All code must pass cargo fmt --all --check without modifications. The project uses the standard Rust formatting conventions with no custom overrides.

Linting Configuration

The Clippy linting process runs with --all-features enabled and includes one specific allowance:

  • clippy::new_without_default is explicitly allowed via -A clippy::new_without_default

This exception acknowledges that hypervisor device emulation patterns may require new() methods without corresponding Default implementations due to the specialized initialization requirements of emulated hardware.

Documentation Standards

Documentation generation enforces strict quality standards through RUSTDOCFLAGS:

RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs

These flags ensure:

  • Broken intra-doc links: All internal documentation links must resolve correctly
  • Missing docs: All public APIs must include documentation

Sources: .github/workflows/ci.yml(L25)  .github/workflows/ci.yml(L40) 

Testing Strategy

Unit Testing Scope

Unit tests execute exclusively on the x86_64-unknown-linux-gnu target, which provides the hosted environment necessary for test infrastructure. The testing command includes --nocapture to ensure full test output visibility.

Multi-Architecture Validation

While unit tests run only on the hosted target, build verification occurs across all four supported architectures. This approach ensures:

  • Code compiles correctly for all deployment targets
  • no_std constraints are properly maintained
  • Architecture-specific conditional compilation works correctly

Test Execution Conditions

Tests run conditionally based on the target architecture:

if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}

This constraint reflects the practical limitation that bare-metal targets cannot execute standard Rust test infrastructure.

Sources: .github/workflows/ci.yml(L28 - L30) 

Documentation Workflow

Automated Documentation Generation

The documentation pipeline operates independently from the main CI pipeline and includes sophisticated deployment automation:

flowchart TD
subgraph subGraph2["Deployment Conditions"]
    DC1["Default Branch Only"]
    DC2["Single Commit Strategy"]
    DC3["gh-pages Branch Target"]
end
subgraph subGraph1["Quality Controls"]
    BIL["Broken Intra-doc Links Check"]
    MD["Missing Docs Check"]
end
subgraph subGraph0["Documentation Generation Process"]
    SRC["Source Code"]
    DOCGEN["cargo doc --no-deps --all-features"]
    VALIDATE["RUSTDOCFLAGS Validation"]
    INDEX["Auto-generated index.html"]
    DEPLOY["GitHub Pages Deployment"]
end

DEPLOY --> DC1
DEPLOY --> DC2
DEPLOY --> DC3
DOCGEN --> VALIDATE
INDEX --> DEPLOY
SRC --> DOCGEN
VALIDATE --> BIL
VALIDATE --> INDEX
VALIDATE --> MD

Index Generation Logic

The documentation system automatically generates a redirect index using shell commands:

printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html

This creates an automatic redirect from the documentation root to the primary crate's documentation.

Deployment Permissions

The documentation job requires contents: write permissions to deploy to GitHub Pages, reflecting the automated nature of the documentation publication process.

Sources: .github/workflows/ci.yml(L44 - L48)  .github/workflows/ci.yml(L50 - L55) 

Development Environment Setup

Repository Setup

# Clone the repository
git clone https://github.com/arceos-hypervisor/axdevice_crates
cd axdevice_crates

# Install nightly toolchain with required components
rustup toolchain install nightly
rustup default nightly
rustup component add rust-src clippy rustfmt

# Add required targets
rustup target add x86_64-unknown-none
rustup target add riscv64gc-unknown-none-elf  
rustup target add aarch64-unknown-none-softfloat

Verification Commands

Verify your development environment matches CI requirements:

# Check formatting
cargo fmt --all -- --check

# Run linting
cargo clippy --all-features -- -A clippy::new_without_default

# Test build for all targets
cargo build --target x86_64-unknown-linux-gnu --all-features
cargo build --target x86_64-unknown-none --all-features
cargo build --target riscv64gc-unknown-none-elf --all-features
cargo build --target aarch64-unknown-none-softfloat --all-features

# Run tests
cargo test --target x86_64-unknown-linux-gnu -- --nocapture

# Generate documentation
cargo doc --no-deps --all-features

Sources: .github/workflows/ci.yml(L15 - L19)  .github/workflows/ci.yml(L22 - L30) 

Build System and CI

Relevant source files

This page documents the automated build system and continuous integration (CI) pipeline for the axdevice_crates repository. It covers the multi-architecture build strategy, code quality enforcement, testing procedures, and documentation generation workflow that ensures the codebase maintains compatibility across different target platforms in the no_std embedded environment.

For information about implementing new devices within this build system, see Implementing New Devices. For details about the core architecture that this build system validates, see Core Architecture.

CI Pipeline Overview

The repository uses GitHub Actions to implement a comprehensive CI pipeline that validates code across multiple architectures and maintains documentation quality. The pipeline consists of two primary jobs: ci for code validation and doc for documentation generation.

CI Job Architecture

flowchart TD
subgraph subGraph2["Build Steps"]
    CHECKOUT["actions/checkout@v4"]
    TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    VERSION["rustc --version --verbose"]
    FMT["cargo fmt --all -- --check"]
    CLIPPY["cargo clippy --target TARGET --all-features"]
    BUILD["cargo build --target TARGET --all-features"]
    TEST["cargo test --target x86_64-unknown-linux-gnu"]
end
subgraph subGraph1["CI Job Matrix"]
    RUST["rust-toolchain: nightly"]
    T1["x86_64-unknown-linux-gnu"]
    T2["x86_64-unknown-none"]
    T3["riscv64gc-unknown-none-elf"]
    T4["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["GitHub Actions Triggers"]
    PUSH["push events"]
    PR["pull_request events"]
end

BUILD --> TEST
CHECKOUT --> TOOLCHAIN
CLIPPY --> BUILD
FMT --> CLIPPY
PR --> RUST
PUSH --> RUST
RUST --> T1
RUST --> T2
RUST --> T3
RUST --> T4
T1 --> CHECKOUT
T2 --> CHECKOUT
T3 --> CHECKOUT
T4 --> CHECKOUT
TOOLCHAIN --> VERSION
VERSION --> FMT

Sources: .github/workflows/ci.yml(L1 - L31) 

Documentation Job Pipeline

flowchart TD
subgraph subGraph2["Deployment Conditions"]
    MAIN_BRANCH["github.ref == default-branch"]
    GH_PAGES["branch: gh-pages"]
    DOC_FOLDER["folder: target/doc"]
end
subgraph subGraph1["Environment Configuration"]
    RUSTDOCFLAGS["-D rustdoc::broken_intra_doc_links -D missing-docs"]
    DEFAULT_BRANCH["refs/heads/default_branch"]
    PERMISSIONS["contents: write"]
end
subgraph subGraph0["Documentation Generation"]
    DOC_CHECKOUT["actions/checkout@v4"]
    DOC_TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    DOC_BUILD["cargo doc --no-deps --all-features"]
    INDEX_GEN["Generate target/doc/index.html redirect"]
    PAGES_DEPLOY["JamesIves/github-pages-deploy-action@v4"]
end

DEFAULT_BRANCH --> MAIN_BRANCH
DOC_BUILD --> INDEX_GEN
DOC_CHECKOUT --> DOC_TOOLCHAIN
DOC_TOOLCHAIN --> DOC_BUILD
INDEX_GEN --> MAIN_BRANCH
MAIN_BRANCH --> PAGES_DEPLOY
PAGES_DEPLOY --> DOC_FOLDER
PAGES_DEPLOY --> GH_PAGES
PERMISSIONS --> PAGES_DEPLOY
RUSTDOCFLAGS --> DOC_BUILD

Sources: .github/workflows/ci.yml(L32 - L56) 

Multi-Architecture Build Strategy

The CI pipeline validates compatibility across four distinct target architectures, each serving different use cases in the hypervisor ecosystem:

TargetPurposeTest Coverage
x86_64-unknown-linux-gnuStandard Linux development and unit testingFull (includingcargo test)
x86_64-unknown-noneBare metal x86_64 hypervisor environmentsBuild and lint only
riscv64gc-unknown-none-elfRISC-V hypervisor platformsBuild and lint only
aarch64-unknown-none-softfloatARM64 embedded hypervisor systemsBuild and lint only

Build Matrix Configuration

The build matrix uses a fail-fast: false strategy to ensure all target architectures are tested independently, preventing early termination when one target fails.

flowchart TD
subgraph subGraph1["Build Validation Steps"]
    VERSION_CHECK["rustc --version --verbose"]
    FORMAT_CHECK["cargo fmt --all -- --check"]
    LINT_CHECK["cargo clippy --target TARGET --all-features"]
    BUILD_CHECK["cargo build --target TARGET --all-features"]
    UNIT_TEST["cargo test (x86_64-linux only)"]
end
subgraph subGraph0["Rust Toolchain Setup"]
    NIGHTLY["nightly toolchain"]
    COMPONENTS["rust-src, clippy, rustfmt"]
    TARGETS["matrix.targets"]
end

BUILD_CHECK --> UNIT_TEST
COMPONENTS --> FORMAT_CHECK
COMPONENTS --> LINT_CHECK
FORMAT_CHECK --> LINT_CHECK
LINT_CHECK --> BUILD_CHECK
NIGHTLY --> VERSION_CHECK
TARGETS --> BUILD_CHECK
TARGETS --> LINT_CHECK
VERSION_CHECK --> FORMAT_CHECK

Sources: .github/workflows/ci.yml(L8 - L19)  .github/workflows/ci.yml(L25 - L30) 

Code Quality Enforcement

The CI pipeline enforces multiple layers of code quality validation before any changes can be merged.

Formatting and Linting

Check TypeCommandPurpose
Code Formattingcargo fmt --all -- --checkEnsures consistent code style across the codebase
Clippy Lintingcargo clippy --target $TARGET --all-features -- -A clippy::new_without_defaultCatches common mistakes and enforces Rust best practices
DocumentationRUSTDOCFLAGS="-D rustdoc::broken_intra_doc_links -D missing-docs"Enforces complete documentation with valid links

The Clippy configuration specifically allows the clippy::new_without_default lint, indicating that the codebase may contain new() methods without corresponding Default implementations, which is common in no_std environments.

Sources: .github/workflows/ci.yml(L22 - L25)  .github/workflows/ci.yml(L40) 

Testing Strategy

The testing approach is pragmatic, recognizing the constraints of no_std embedded development:

Unit Testing Limitations

flowchart TD
subgraph subGraph2["Quality Assurance"]
    CLIPPY_ALL["Clippy runs on all targets"]
    FORMAT_ALL["Format check runs on all targets"]
    COMPILE_ALL["Compilation check on all targets"]
end
subgraph subGraph1["Test Execution"]
    LINUX_ONLY["Unit tests run only on x86_64-unknown-linux-gnu"]
    TEST_CMD["cargo test --target x86_64-unknown-linux-gnu -- --nocapture"]
    BUILD_VALIDATION["Build validation on all targets"]
end
subgraph subGraph0["Testing Constraints"]
    NOSTD["no_std environment"]
    EMBEDDED["Embedded targets"]
    LIMITED["Limited std library access"]
end

BUILD_VALIDATION --> COMPILE_ALL
CLIPPY_ALL --> FORMAT_ALL
COMPILE_ALL --> CLIPPY_ALL
EMBEDDED --> BUILD_VALIDATION
LIMITED --> LINUX_ONLY
LINUX_ONLY --> TEST_CMD
NOSTD --> LINUX_ONLY

Unit tests execute only on x86_64-unknown-linux-gnu because the other targets are bare metal environments that lack the standard library infrastructure required for Rust's test framework. However, the build validation ensures that the code compiles correctly for all target architectures.

Sources: .github/workflows/ci.yml(L28 - L30) 

Documentation Generation and Deployment

The documentation system automatically generates and deploys API documentation using a dedicated job that runs in parallel with the main CI validation.

Documentation Build Process

The documentation generation uses strict flags to ensure high-quality documentation:

flowchart TD
subgraph Deployment["Deployment"]
    BRANCH_CHECK["github.ref == default-branch"]
    DEPLOY_ACTION["JamesIves/github-pages-deploy-action@v4"]
    SINGLE_COMMIT["single-commit: true"]
    GH_PAGES_BRANCH["branch: gh-pages"]
    DOC_FOLDER["folder: target/doc"]
end
subgraph subGraph1["Build Process"]
    DOC_CMD["cargo doc --no-deps --all-features"]
    INDEX_CREATE["printf redirect to generated docs"]
    TREE_PARSE["cargo tree | head -1 | cut -d' ' -f1"]
end
subgraph subGraph0["Documentation Flags"]
    BROKEN_LINKS["-D rustdoc::broken_intra_doc_links"]
    MISSING_DOCS["-D missing-docs"]
    RUSTDOCFLAGS["RUSTDOCFLAGS environment"]
end

BRANCH_CHECK --> DEPLOY_ACTION
BROKEN_LINKS --> RUSTDOCFLAGS
DEPLOY_ACTION --> DOC_FOLDER
DEPLOY_ACTION --> GH_PAGES_BRANCH
DEPLOY_ACTION --> SINGLE_COMMIT
DOC_CMD --> TREE_PARSE
INDEX_CREATE --> BRANCH_CHECK
MISSING_DOCS --> RUSTDOCFLAGS
RUSTDOCFLAGS --> DOC_CMD
TREE_PARSE --> INDEX_CREATE

The --no-deps flag ensures that only the crate's own documentation is generated, not its dependencies. The automatic index.html generation creates a redirect to the main crate documentation.

Sources: .github/workflows/ci.yml(L40)  .github/workflows/ci.yml(L44 - L48)  .github/workflows/ci.yml(L49 - L55) 

Development Workflow Integration

The CI system is designed to support the typical development workflow for embedded hypervisor development:

Branch Protection and Quality Gates

StageValidationBlocking
Pull RequestAll CI checks must passYes
Format Checkcargo fmtvalidationYes
Lint CheckClippy on all targetsYes
Build CheckCompilation on all targetsYes
Unit TestsTests on Linux target onlyYes
DocumentationDoc generation and link validationYes for main branch

Error Handling Strategy

The documentation job uses conditional error handling with continue-on-error set based on branch and event type, allowing documentation builds to fail on non-main branches and non-pull-request events without blocking the overall workflow.

flowchart TD
subgraph subGraph1["Build Outcomes"]
    MAIN_BRANCH_STRICT["Main branch: strict documentation"]
    PR_STRICT["Pull requests: strict documentation"]
    OTHER_LENIENT["Other contexts: lenient documentation"]
end
subgraph subGraph0["Error Handling Logic"]
    BRANCH_CHECK["github.ref != default-branch"]
    EVENT_CHECK["github.event_name != pull_request"]
    CONTINUE_ERROR["continue-on-error: true"]
    STRICT_MODE["continue-on-error: false"]
end

BRANCH_CHECK --> CONTINUE_ERROR
CONTINUE_ERROR --> OTHER_LENIENT
EVENT_CHECK --> CONTINUE_ERROR
STRICT_MODE --> MAIN_BRANCH_STRICT
STRICT_MODE --> PR_STRICT

This approach ensures that documentation quality is enforced where it matters most (main branch and pull requests) while allowing experimental branches to have temporary documentation issues.

Sources: .github/workflows/ci.yml(L45)  .github/workflows/ci.yml(L39) 

Implementing New Devices

Relevant source files

This page provides a comprehensive guide for implementing new emulated devices within the ArceOS hypervisor ecosystem using the BaseDeviceOps trait foundation. It covers the step-by-step process of creating device implementations, configuring address ranges, implementing read/write handlers, and integrating with the device type system.

For information about the core architecture and trait definitions, see Core Architecture. For details about the device type enumeration system, see Device Type System.

Implementation Overview

Creating a new emulated device requires implementing the BaseDeviceOps trait and integrating with the EmuDeviceType system. The implementation process follows a standardized pattern that ensures compatibility with the ArceOS hypervisor's device emulation framework.

Device Implementation Architecture

flowchart TD
subgraph subGraph2["Core Types"]
    T1["EmuDeviceType"]
    T2["AddrRange"]
    T3["AxResult"]
    T4["GuestPhysAddr"]
end
subgraph subGraph1["BaseDeviceOps Methods"]
    M1["emu_type()"]
    M2["address_range()"]
    M3["handle_read()"]
    M4["handle_write()"]
end
subgraph subGraph0["Implementation Steps"]
    S1["Unsupported markdown: list"]
    S2["Unsupported markdown: list"]
    S3["Unsupported markdown: list"]
    S4["Unsupported markdown: list"]
    S5["Unsupported markdown: list"]
    S6["Unsupported markdown: list"]
end

M1 --> T1
M2 --> T2
M3 --> T3
M4 --> T4
S1 --> S2
S2 --> M1
S2 --> M2
S2 --> M3
S2 --> M4
S2 --> S3
S3 --> S4
S4 --> S5
S5 --> S6

Sources: axdevice_base/src/lib.rs(L20 - L30)  axdevice_base/src/emu_type.rs(L3 - L28) 

Device Structure Definition

The first step in implementing a new device is defining the device structure that will hold the device's state and configuration. The structure should contain all necessary data for device emulation, including memory mappings, configuration registers, and operational state.

Device Implementation Pattern

classDiagram
class BaseDeviceOps {
    <<trait>>
    
    +emu_type() EmuDeviceType
    +address_range() AddrRange~GuestPhysAddr~
    +handle_read(addr, width) AxResult~usize~
    +handle_write(addr, width, val)
}

class YourNewDevice {
    -base_address: GuestPhysAddr
    -size: usize
    -registers: DeviceRegisters
    -state: DeviceState
    +new(base_addr, size) Self
    +reset()
    +configure()
}

class DeviceRegisters {
    -control_reg: u32
    -status_reg: u32
    -data_reg: u32
    -interrupt_reg: u32
    
}

class DeviceState {
    -enabled: bool
    -interrupt_pending: bool
    -operation_mode: OperationMode
    
}

class EmuDeviceType {
    <<enum>>
    EmuDeviceTConsole
    EmuDeviceTGicdV2
    EmuDeviceTVirtioBlk
    "... other types"
    YourNewDeviceType
    
}

YourNewDevice  ..|>  BaseDeviceOps : implements
YourNewDevice  -->  DeviceRegisters : contains
YourNewDevice  -->  DeviceState : contains
YourNewDevice  -->  EmuDeviceType : returns via emu_type()

Sources: axdevice_base/src/lib.rs(L21 - L30)  axdevice_base/src/emu_type.rs(L5 - L28) 

BaseDeviceOps Implementation

The core of device implementation involves implementing the four required methods of the BaseDeviceOps trait. Each method serves a specific purpose in the device emulation lifecycle.

Required Method Implementation

MethodPurposeReturn TypeParameters
emu_type()Device type identificationEmuDeviceType&self
address_range()Memory mapping definitionAddrRange&self
handle_read()Read operation processingAxResult&self, addr, width
handle_write()Write operation processing()&self, addr, width, val

The emu_type() method returns the device's type identifier from the EmuDeviceType enumeration. This method enables the hypervisor to identify and categorize the device for management purposes.

The address_range() method defines the guest physical address range that the device occupies in the virtual machine's memory space. This range determines which memory accesses will be routed to the device's read and write handlers.

Sources: axdevice_base/src/lib.rs(L22 - L29) 

Address Range Configuration

Device address range configuration requires careful consideration of the guest physical address space layout and potential conflicts with other devices or memory regions. The address range must be properly aligned and sized according to the device's requirements.

Address Range Management Flow

sequenceDiagram
    participant ArceOSHypervisor as "ArceOS Hypervisor"
    participant AddressSpaceManager as "Address Space Manager"
    participant YourNewDevice as "YourNewDevice"
    participant AddrRangeGuestPhysAddr as "AddrRange<GuestPhysAddr>"

    Note over ArceOSHypervisor,AddrRangeGuestPhysAddr: Device Registration Flow
    ArceOSHypervisor ->> YourNewDevice: address_range()
    YourNewDevice ->> AddrRangeGuestPhysAddr: create range
    Note over AddrRangeGuestPhysAddr: base_address: GuestPhysAddr<br>size: usize
    AddrRangeGuestPhysAddr ->> YourNewDevice: AddrRange instance
    YourNewDevice ->> ArceOSHypervisor: return address range
    ArceOSHypervisor ->> AddressSpaceManager: register_device_range()
    AddressSpaceManager ->> AddressSpaceManager: validate no conflicts
    AddressSpaceManager ->> ArceOSHypervisor: registration result
    Note over ArceOSHypervisor,AddrRangeGuestPhysAddr: Memory Access Flow
    ArceOSHypervisor ->> AddressSpaceManager: guest_memory_access(gpa)
    AddressSpaceManager ->> AddressSpaceManager: check address ranges
    alt Address in device range
        AddressSpaceManager ->> ArceOSHypervisor: route to device
        ArceOSHypervisor ->> YourNewDevice: handle_read/write()
    else Address not in range
        AddressSpaceManager ->> ArceOSHypervisor: handle as memory
    end

Sources: axdevice_base/src/lib.rs(L24 - L25) 

The address range should be configured during device initialization and remain constant throughout the device's lifetime. Dynamic address range changes are not supported by the current architecture.

Read Handler Implementation

The handle_read() method processes guest read operations targeting the device's address range. The implementation must decode the address offset, validate the access width, and return appropriate data based on the device's current state.

Read Handler Patterns

flowchart TD
subgraph subGraph2["Error Handling"]
    E1["Invalid Address"]
    E2["Unsupported Width"]
    E3["Device Not Ready"]
    E4["Permission Denied"]
end
subgraph subGraph1["Common Read Patterns"]
    P1["Register Bank Access"]
    P2["FIFO/Buffer Reading"]
    P3["Status Register Polling"]
    P4["Interrupt Status Check"]
end
subgraph subGraph0["handle_read() Implementation Flow"]
    A["Guest Read Access"]
    B["Validate Address Range"]
    C["Calculate Register Offset"]
    D["Validate Access Width"]
    E["Read Register Value"]
    F["Apply Device Logic"]
    G["Return AxResult"]
end

A --> B
B --> C
C --> D
D --> E
D --> E1
D --> E2
E --> E3
E --> F
F --> E4
F --> G
F --> P1
F --> P2
F --> P3
F --> P4

Sources: axdevice_base/src/lib.rs(L27) 

The read handler should support standard access widths (1, 2, 4, and 8 bytes) and return data in the appropriate format. Register values should be calculated based on the device's current state and any side effects of the read operation should be applied.

Write Handler Implementation

The handle_write() method processes guest write operations to the device's address range. Unlike read operations, write handlers do not return data but may trigger device state changes, interrupt generation, or other side effects.

Write Handler Architecture

flowchart TD
subgraph subGraph2["Side Effects"]
    S1["Interrupt Generation"]
    S2["DMA Transfer Start"]
    S3["Device Reset"]
    S4["Mode Change"]
end
subgraph subGraph1["Write Operations"]
    O1["Control Register Write"]
    O2["Data Buffer Write"]
    O3["Configuration Update"]
    O4["Command Execution"]
end
subgraph subGraph0["handle_write() Processing"]
    W1["Guest Write Access"]
    W2["Validate Address Range"]
    W3["Calculate Register Offset"]
    W4["Validate Access Width"]
    W5["Extract Write Value"]
    W6["Apply Register Logic"]
    W7["Update Device State"]
    W8["Trigger Side Effects"]
end

W1 --> W2
W2 --> W3
W3 --> W4
W4 --> W5
W5 --> W6
W6 --> O1
W6 --> O2
W6 --> O3
W6 --> O4
W6 --> W7
W7 --> W8
W8 --> S1
W8 --> S2
W8 --> S3
W8 --> S4

Sources: axdevice_base/src/lib.rs(L29) 

Write handlers should validate input data, apply appropriate bit masks for register fields, and handle write-only or read-only register access restrictions. The handler should also manage any required atomicity or ordering constraints.

Device Type Selection

Choosing the appropriate EmuDeviceType is crucial for proper device categorization and management. The device type affects removability, hypervisor behavior, and integration with other system components.

Device Type Categories

CategoryDevice TypesRemovableUse Cases
Console/IOEmuDeviceTConsole,EmuDeviceTVirtioConsoleMixedTerminal, logging
Interrupt ControllersEmuDeviceTGicdV2,EmuDeviceTGPPT,EmuDeviceTICCSRE,EmuDeviceTSGIR,EmuDeviceTGICRYesARM interrupt management
Virtio DevicesEmuDeviceTVirtioBlk,EmuDeviceTVirtioNetYesParavirtualized I/O
System InfrastructureEmuDeviceTIOMMU,EmuDeviceTMetaNoCore system functions

For new device types not covered by existing categories, the EmuDeviceType enumeration must be extended with appropriate variants. The removable() method implementation should be updated to reflect the new device's characteristics.

Sources: axdevice_base/src/emu_type.rs(L5 - L28)  axdevice_base/src/emu_type.rs(L52 - L64) 

Implementation Best Practices

State Management

Device state should be properly encapsulated and protected against concurrent access. Use appropriate synchronization primitives when device state may be accessed from multiple contexts.

Error Handling

All operations that can fail should return appropriate AxResult types. Use specific error codes from the axerrno crate to indicate different failure conditions.

Memory Access Validation

Always validate guest physical addresses against the device's configured address range and ensure access widths are supported by the target registers.

Performance Considerations

Minimize computational overhead in read and write handlers, as these methods are called frequently during device emulation. Cache frequently accessed values and avoid unnecessary calculations.

Testing Strategy

Implement comprehensive test coverage for all register access patterns, error conditions, and state transitions. Verify compatibility with the target guest operating systems and device drivers.

Sources: axdevice_base/src/lib.rs(L1 - L31)  axdevice_base/src/emu_type.rs(L1 - L84) 

Overview

Relevant source files

This document provides a comprehensive overview of the arm_vcpu hypervisor system, an AArch64 virtualization implementation that provides virtual CPU management and hardware abstraction for hypervisor environments. The system implements low-level virtualization primitives including CPU context switching, exception handling, and guest state management for AArch64 platforms.

The arm_vcpu crate serves as a foundational component for hypervisor implementations, providing the core VCPU abstraction and associated hardware interfaces. For detailed information about specific subsystems, see Virtual CPU Management, Exception Handling System, and System Integration.

System Purpose and Scope

The arm_vcpu system implements virtualization support for AArch64 architecture, providing:

  • Virtual CPU Management: Core VCPU lifecycle, state management, and execution control
  • Hardware Abstraction: Platform-independent interfaces for virtualization hardware features
  • Exception Handling: Comprehensive trap and interrupt handling for guest VMs
  • Context Switching: Efficient state preservation and restoration between host and guest execution
  • Security Integration: Secure Monitor Call (SMC) interface for trusted firmware interaction

Sources: README.md(L1 - L5)  src/lib.rs(L1 - L33) 

Primary System Components

The following diagram illustrates the main architectural components and their relationships within the arm_vcpu system:

Core Component Architecture

flowchart TD
subgraph subGraph2["External Dependencies"]
    AxVCpu["axvcpuCore VCPU Traits"]
    AxAddrSpace["axaddrspaceAddress Space Management"]
    PerCpuCrate["percpuPer-CPU Storage"]
    Aarch64Cpu["aarch64-cpuRegister Definitions"]
end
subgraph subGraph1["Internal Modules"]
    ContextFrame["context_frameState Structures"]
    Exception["exceptionHandler Logic"]
    ExceptionUtils["exception_utilsRegister Parsing"]
    PCpuMod["pcpuPer-CPU Implementation"]
    SMC["smcSecure Monitor Interface"]
    VCpuMod["vcpuVCPU Implementation"]
end
subgraph subGraph0["Primary Interfaces"]
    VCpu["Aarch64VCpuMain VCPU Implementation"]
    PerCpu["Aarch64PerCpuPer-CPU State Management"]
    TrapFrame["TrapFrame(Aarch64ContextFrame)"]
    Config["Aarch64VCpuCreateConfigVCPU Configuration"]
end

Exception --> Aarch64Cpu
Exception --> ExceptionUtils
PerCpu --> PCpuMod
PerCpu --> PerCpuCrate
TrapFrame --> ContextFrame
VCpu --> AxAddrSpace
VCpu --> AxVCpu
VCpu --> Config
VCpu --> PerCpu
VCpu --> TrapFrame
VCpu --> VCpuMod
VCpuMod --> Exception
VCpuMod --> SMC

Sources: src/lib.rs(L9 - L21)  Cargo.toml(L6 - L19) 

Code Entity Mapping

This diagram maps the natural language system concepts to specific code entities and file structures:

Implementation Structure and Code Entities

flowchart TD
subgraph subGraph2["Core Data Structures"]
    Aarch64VCpuStruct["struct Aarch64VCpu"]
    Aarch64PerCpuStruct["struct Aarch64PerCpu"]
    ContextFrameStruct["struct Aarch64ContextFrame"]
    ConfigStruct["struct Aarch64VCpuCreateConfig"]
end
subgraph subGraph1["Source File Organization"]
    VCpuRs["vcpu.rsVirtual CPU Logic"]
    PCpuRs["pcpu.rsPer-CPU Management"]
    ContextRs["context_frame.rsState Structures"]
    ExceptionRs["exception.rsException Handlers"]
    ExceptionS["exception.SAssembly Vectors"]
    UtilsRs["exception_utils.rsRegister Utilities"]
    SmcRs["smc.rsSecure Monitor Calls"]
end
subgraph subGraph0["Public API Surface"]
    LibRs["lib.rsMain Module"]
    PubVCpu["pub use Aarch64VCpu"]
    PubPerCpu["pub use Aarch64PerCpu"]
    PubConfig["pub use Aarch64VCpuCreateConfig"]
    PubTrapFrame["pub type TrapFrame"]
    HwSupport["has_hardware_support()"]
end

ContextRs --> ContextFrameStruct
ExceptionRs --> ExceptionS
ExceptionRs --> UtilsRs
LibRs --> HwSupport
LibRs --> PubConfig
LibRs --> PubPerCpu
LibRs --> PubTrapFrame
LibRs --> PubVCpu
PCpuRs --> Aarch64PerCpuStruct
PubPerCpu --> PCpuRs
PubTrapFrame --> ContextRs
PubVCpu --> VCpuRs
VCpuRs --> Aarch64VCpuStruct
VCpuRs --> ConfigStruct
VCpuRs --> ExceptionRs
VCpuRs --> SmcRs

Sources: src/lib.rs(L17 - L21)  src/lib.rs(L9 - L15) 

Virtualization Hardware Integration

The system provides hardware abstraction for AArch64 virtualization extensions through a layered approach:

LayerComponentResponsibility
HardwareAArch64 CPUVirtualization extensions (EL2, VHE)
Low-Levelexception.SAssembly exception vectors and context switch
AbstractionAxVCpuHaltraitHardware abstraction interface
ImplementationAarch64VCpuConcrete VCPU implementation
IntegrationaxvcpucrateGeneric VCPU traits and interfaces

Hardware Feature Detection and Support

flowchart TD
subgraph subGraph2["Per-CPU Control"]
    HwSupport["has_hardware_support()Feature Detection"]
    IdRegs["ID_AA64MMFR1_EL1Virtualization Host Extensions"]
    DefaultTrue["Currently returns truePlaceholder Implementation"]
    PerCpuEnable["Aarch64PerCpu::hardware_enable()"]
    PerCpuDisable["Aarch64PerCpu::hardware_disable()"]
    VirtualizationState["Per-CPU Virtualization State"]
end
subgraph subGraph1["Virtualization Extensions"]
    EL2["Exception Level 2Hypervisor Mode"]
    VHE["Virtualization Host ExtensionsEnhanced Host Support"]
    HCR["HCR_EL2 RegisterHypervisor Configuration"]
    VBAR["VBAR_EL2 RegisterVector Base Address"]
end
subgraph subGraph0["Platform Detection"]
    HwSupport["has_hardware_support()Feature Detection"]
    IdRegs["ID_AA64MMFR1_EL1Virtualization Host Extensions"]
    DefaultTrue["Currently returns truePlaceholder Implementation"]
end

HwSupport --> DefaultTrue
HwSupport --> IdRegs
PerCpuEnable --> EL2
PerCpuEnable --> HCR
PerCpuEnable --> VBAR
VirtualizationState --> PerCpuDisable
VirtualizationState --> PerCpuEnable

Sources: src/lib.rs(L23 - L32)  Cargo.toml(L15) 

External Ecosystem Integration

The arm_vcpu crate integrates with the broader ArceOS hypervisor ecosystem through well-defined dependency relationships:

DependencyPurposeIntegration Point
axvcpuCore VCPU traitsAxVCpuHaltrait implementation
axaddrspaceAddress space managementMemory virtualization support
percpuPer-CPU data structuresHardware state management
aarch64-cpuRegister definitionsLow-level hardware access
aarch64_sysregSystem register accessControl register manipulation

The system operates at Exception Level 2 (EL2) and provides virtualization services for guest operating systems running at Exception Level 1 (EL1) and applications at Exception Level 0 (EL0).

Sources: Cargo.toml(L14 - L19)  README.md(L5) 

System Architecture

Relevant source files

This document provides a detailed explanation of the overall system architecture for the arm_vcpu hypervisor implementation. It covers the core components, their relationships, and the data flow patterns that enable AArch64 virtualization. For specific implementation details of individual components, see Virtual CPU Management, Exception Handling System, and Context Switching and State Management.

Architecture Overview

The arm_vcpu system implements a Type-1 hypervisor architecture for AArch64 platforms, providing virtualization capabilities through the AArch64 virtualization extensions. The system operates at Exception Level 2 (EL2) and manages guest virtual machines running at EL1/EL0.

Core Component Relationships


Sources: src/vcpu.rs(L39 - L51)  src/pcpu.rs(L10 - L16)  src/lib.rs(L17 - L21) 

System Initialization and Lifecycle

sequenceDiagram
    participant HostSystem as "Host System"
    participant Aarch64PerCpu as "Aarch64PerCpu"
    participant Aarch64VCpu as "Aarch64VCpu"
    participant GuestVM as "Guest VM"

    HostSystem ->> Aarch64PerCpu: new(cpu_id)
    Aarch64PerCpu ->> Aarch64PerCpu: Register IRQ_HANDLER
    HostSystem ->> Aarch64PerCpu: hardware_enable()
    Aarch64PerCpu ->> Aarch64PerCpu: Set VBAR_EL2 to exception_vector_base_vcpu
    Aarch64PerCpu ->> Aarch64PerCpu: Configure HCR_EL2 for virtualization
    HostSystem ->> Aarch64VCpu: new(Aarch64VCpuCreateConfig)
    Aarch64VCpu ->> Aarch64VCpu: Initialize TrapFrame and GuestSystemRegisters
    HostSystem ->> Aarch64VCpu: setup()
    Aarch64VCpu ->> Aarch64VCpu: init_hv() - Configure initial state
    HostSystem ->> Aarch64VCpu: set_entry(guest_entry_point)
    HostSystem ->> Aarch64VCpu: set_ept_root(page_table_root)
    loop VM Execution Cycle
        HostSystem ->> Aarch64VCpu: run()
        Aarch64VCpu ->> Aarch64VCpu: save_host_sp_el0()
        Aarch64VCpu ->> Aarch64VCpu: restore_vm_system_regs()
        Aarch64VCpu ->> GuestVM: Context switch to guest
        GuestVM -->> Aarch64VCpu: VM-Exit (trap/exception)
        Aarch64VCpu ->> Aarch64VCpu: vmexit_handler()
        Aarch64VCpu -->> HostSystem: Return AxVCpuExitReason
    end

Sources: src/vcpu.rs(L69 - L85)  src/vcpu.rs(L99 - L111)  src/pcpu.rs(L49 - L67) 

Core Components

Virtual CPU (Aarch64VCpu)

The Aarch64VCpu<H: AxVCpuHal> structure serves as the primary abstraction for a virtual CPU. It maintains both guest context and runtime state required for virtualization.

FieldTypePurpose
ctxTrapFrameGuest general-purpose registers and execution state
host_stack_topu64Host stack pointer for context switching
guest_system_regsGuestSystemRegistersGuest system control and configuration registers
mpidru64Multiprocessor Affinity Register value for guest

The VCPU implements the AxArchVCpu trait, providing standardized interfaces for:

  • Creation and configuration via Aarch64VCpuCreateConfig
  • Guest execution through the run() method
  • Entry point and page table configuration
  • Register manipulation interfaces

Sources: src/vcpu.rs(L39 - L51)  src/vcpu.rs(L64 - L124) 

Per-CPU Management (Aarch64PerCpu)

The Aarch64PerCpu<H: AxVCpuHal> structure manages hardware virtualization features on a per-CPU basis:

flowchart TD
PCPU["Aarch64PerCpu"]
HCR["HCR_EL2 RegisterHypervisor Configuration"]
VBAR["VBAR_EL2 RegisterException Vector Base"]
IRQ["IRQ_HANDLERPer-CPU IRQ Dispatch"]
ORIG["ORI_EXCEPTION_VECTOR_BASEOriginal Vector Base"]
VM["VM Bit - Virtualization"]
RW["RW Bit - 64-bit Guest"]
IMO["IMO/FMO - Virtual Interrupts"]
TSC["TSC - SMC Instructions"]

HCR --> IMO
HCR --> RW
HCR --> TSC
HCR --> VM
PCPU --> HCR
PCPU --> IRQ
PCPU --> ORIG
PCPU --> VBAR

Sources: src/pcpu.rs(L10 - L16)  src/pcpu.rs(L49 - L67)  src/pcpu.rs(L18 - L26) 

Hardware Abstraction Layer

The system uses the AxVCpuHal trait to abstract platform-specific functionality:


Sources: src/vcpu.rs(L278)  src/pcpu.rs(L35 - L37) 

Context Switching Architecture

The system implements a sophisticated context switching mechanism that preserves both general-purpose and system registers across VM entries and exits:

flowchart TD
subgraph subGraph2["Guest Context"]
    TRAP["TrapFrame(GPRs, SP_EL0, ELR, SPSR)"]
    GSYS["GuestSystemRegisters(System Control Registers)"]
end
subgraph subGraph1["Context Switch Operations"]
    SAVE_HOST["save_host_sp_el0()"]
    RESTORE_VM["restore_vm_system_regs()"]
    RUN_GUEST["run_guest()(Naked Function)"]
    VMEXIT["vmexit_handler()"]
end
subgraph subGraph0["Host Context"]
    HSP["Host SP_EL0(HOST_SP_EL0 per-CPU)"]
    HSTACK["Host Stack(save_regs_to_stack!)"]
    HSYS["Host System Registers(Original Hardware State)"]
end

GSYS --> VMEXIT
HSP --> SAVE_HOST
RESTORE_VM --> GSYS
RESTORE_VM --> RUN_GUEST
RUN_GUEST --> TRAP
SAVE_HOST --> RESTORE_VM
TRAP --> VMEXIT
VMEXIT --> HSP
VMEXIT --> HSTACK

Sources: src/vcpu.rs(L182 - L214)  src/vcpu.rs(L226 - L244)  src/vcpu.rs(L255 - L282) 

Exception and Interrupt Handling

The system provides a multi-layered exception handling architecture that processes VM-exits and routes them appropriately:

flowchart TD
subgraph subGraph3["Exit Reasons"]
    MMIO["AxVCpuExitReason::MmioRead/Write"]
    EXT_IRQ["AxVCpuExitReason::ExternalInterrupt"]
    SYSREG["AxVCpuExitReason::SystemRegister*"]
    PSCI["AxVCpuExitReason::CpuUp/Down"]
end
subgraph subGraph2["High-Level Dispatch"]
    SYNC_HANDLER["handle_exception_sync()"]
    IRQ_DISPATCH["IRQ_HANDLER per-CPU"]
    PANIC["invalid_exception_el2"]
end
subgraph subGraph1["Low-Level Processing"]
    VECTORS["exception_vector_base_vcpu(Assembly Vector Table)"]
    TRAMPOLINE["vmexit_trampoline(Context Save)"]
end
subgraph subGraph0["Exception Sources"]
    SYNC["Synchronous Exceptions(Data Aborts, HVC, SMC)"]
    IRQ["IRQ Interrupts"]
    INVALID["Invalid Exceptions"]
end

INVALID --> VECTORS
IRQ --> VECTORS
IRQ_DISPATCH --> EXT_IRQ
SYNC --> VECTORS
SYNC_HANDLER --> MMIO
SYNC_HANDLER --> PSCI
SYNC_HANDLER --> SYSREG
TRAMPOLINE --> IRQ_DISPATCH
TRAMPOLINE --> PANIC
TRAMPOLINE --> SYNC_HANDLER
VECTORS --> TRAMPOLINE

Sources: src/vcpu.rs(L275 - L281)  src/pcpu.rs(L18 - L26)  src/pcpu.rs(L55 - L57) 

This architecture enables efficient virtualization by maintaining clear separation between host and guest contexts while providing comprehensive exception handling capabilities for all VM-exit scenarios.

Dependencies and Build System

Relevant source files

This document covers the external dependencies, build configuration, and continuous integration setup for the arm_vcpu crate. It details the Rust crate dependencies required for AArch64 virtualization, the build system configuration, and the automated testing pipeline.

For information about how these dependencies are used within the VCPU implementation, see Virtual CPU Management. For details about the hardware abstraction interfaces, see Hardware Abstraction and Platform Support.

Purpose and Scope

The arm_vcpu crate is a no-std Rust library that implements AArch64 virtualization capabilities for hypervisors. This page documents the dependency management, build requirements, and continuous integration infrastructure that support the development and deployment of this hypervisor component.

External Dependencies

The crate relies on several categories of dependencies to provide AArch64 virtualization functionality:

Core Rust Dependencies

DependencyVersionPurpose
log0.4.21Logging infrastructure for debug and runtime information
spin0.9Spinlock primitives for no-std synchronization

Sources: Cargo.toml(L7 - L8) 

AArch64 Hardware Interface Dependencies

DependencyVersionPurpose
aarch64-cpu9.3AArch64 CPU register definitions and operations
tock-registers0.8Register field manipulation macros and utilities
aarch64_sysreg0.1.1AArch64 system register definitions and access
numeric-enum-macro0.2Macro support for numeric enum conversions

Sources: Cargo.toml(L10 - L16) 

System Infrastructure Dependencies

DependencyVersionPurpose
axerrno0.1.0Error code definitions for the ArceOS ecosystem
percpu0.2.0Per-CPU data storage with ARM EL2 feature support

The percpu crate is configured with the arm-el2 feature to enable Exception Level 2 support required for hypervisor operation.

Sources: Cargo.toml(L14 - L15) 

ArceOS Ecosystem Dependencies

The crate integrates with the broader ArceOS hypervisor ecosystem through Git-based dependencies:

Dependency Architecture

flowchart TD
subgraph subGraph3["System Support"]
    PERCPU["percpu(Per-CPU Storage)"]
    LOG["log(Logging)"]
    SPIN["spin(Synchronization)"]
    AXERRNO["axerrno(Error Codes)"]
end
subgraph subGraph2["Hardware Layer"]
    AARCH64["aarch64-cpu(Register Definitions)"]
    SYSREG["aarch64_sysreg(System Registers)"]
    TOCK["tock-registers(Register Macros)"]
end
subgraph subGraph1["ArceOS Core Dependencies"]
    AXVCPU["axvcpu(Core VCPU Traits)"]
    AXADDR["axaddrspace(Address Space Management)"]
end
subgraph subGraph0["arm_vcpu Crate"]
    MAIN["arm_vcpu"]
end

MAIN --> AARCH64
MAIN --> AXADDR
MAIN --> AXERRNO
MAIN --> AXVCPU
MAIN --> LOG
MAIN --> PERCPU
MAIN --> SPIN
MAIN --> SYSREG
MAIN --> TOCK

Git Dependencies

RepositoryPurpose
axvcpuDefines core VCPU traits (AxVCpu,AxVCpuHal) implemented byAarch64VCpu
axaddrspaceProvides address space management interfaces for guest memory virtualization

Both dependencies are sourced from the arceos-hypervisor GitHub organization, indicating a modular hypervisor architecture where arm_vcpu provides the AArch64-specific implementation.

Sources: Cargo.toml(L18 - L19) 

Build Configuration

Package Metadata

The crate is configured as a library with the following specifications:

[package]
name = "arm_vcpu"
version = "0.1.0"
edition = "2024"

The use of Rust edition 2024 indicates adoption of the latest Rust language features and optimizations.

Sources: Cargo.toml(L1 - L4) 

Target Architecture

The primary build target is aarch64-unknown-none-softfloat, which specifies:

  • aarch64: 64-bit ARM architecture
  • unknown: No specific vendor
  • none: Bare metal (no operating system)
  • softfloat: Software floating-point implementation

This target configuration is appropriate for hypervisor code running at Exception Level 2 (EL2) without OS dependencies.

Sources: .github/workflows/ci.yml(L12) 

Continuous Integration System

CI Pipeline Architecture

flowchart TD
subgraph subGraph3["CI Jobs"]
    DOC_DEPLOY["Deploy to GitHub Pages"]
    subgraph subGraph1["Primary CI Job"]
        CHECKOUT["Checkout Code"]
        SETUP["Setup Rust Toolchain"]
        VERSION["Check Rust Version"]
        FORMAT["Format Check (rustfmt)"]
        CLIPPY["Clippy Linting"]
        BUILD["Build"]
        TEST["Unit Tests"]
    end
end
subgraph subGraph0["Build Matrix"]
    TOOLCHAIN1["nightly-2024-12-25"]
    TOOLCHAIN2["nightly"]
    TARGET["aarch64-unknown-none-softfloat"]
end
subgraph subGraph2["Documentation Job"]
    DOC_CHECKOUT["Checkout Code"]
    DOC_SETUP["Setup Rust Toolchain"]
    DOC_BUILD["Build Documentation"]
    DOC_DEPLOY["Deploy to GitHub Pages"]
end
TRIGGER["Push/Pull Request"]
MATRIX["Matrix Strategy"]

BUILD --> TEST
CHECKOUT --> SETUP
CLIPPY --> BUILD
DOC_BUILD --> DOC_DEPLOY
DOC_CHECKOUT --> DOC_SETUP
DOC_SETUP --> DOC_BUILD
FORMAT --> CLIPPY
MATRIX --> TOOLCHAIN1
MATRIX --> TOOLCHAIN2
SETUP --> VERSION
TOOLCHAIN1 --> CHECKOUT
TOOLCHAIN2 --> CHECKOUT
TRIGGER --> MATRIX
VERSION --> FORMAT

CI Job Configuration

The continuous integration system uses GitHub Actions with two primary jobs:

Primary CI Job

  • Rust Toolchains: Tests against both nightly-2024-12-25 (pinned) and nightly (latest)
  • Components: Includes rust-src, clippy, and rustfmt
  • Quality Checks: Format verification, linting, and build verification
  • Testing: Unit tests (currently limited to x86_64 targets)

Documentation Job

  • Environment: Uses RUSTDOCFLAGS to enforce documentation standards
  • Deployment: Automatically publishes documentation to GitHub Pages on main branch
  • Documentation Quality: Requires all public APIs to be documented

Sources: .github/workflows/ci.yml(L6 - L59) 

Build Quality Gates

The CI system enforces several quality standards:

CheckToolPurpose
Code FormattingrustfmtEnforces consistent code style
LintingclippyCatches common mistakes and suggests improvements
DocumentationrustdocEnsures all public APIs are documented
Build Verificationcargo buildConfirms code compiles for target architecture

The configuration includes continue-on-error flags for the latest nightly toolchain to accommodate potential breaking changes in unstable Rust features.

Sources: .github/workflows/ci.yml(L22 - L49) 

Development Environment

Git Configuration

The repository excludes common build artifacts and development files:

  • Build outputs: target/, *.asm, *.img, *.bin, *.elf
  • Development artifacts: qemu.log, actual.out, rusty-tags.vi
  • IDE settings: .vscode/
  • System files: .DS_Store

Notably, Cargo.lock is ignored since arm_vcpu is distributed as a library crate.

Sources: .gitignore(L1 - L18) 

Virtual CPU Management

Relevant source files

This document covers the core virtual CPU implementation in the arm_vcpu hypervisor system. It focuses on the Aarch64VCpu struct, its lifecycle management, configuration mechanisms, and the fundamental execution model for running guest virtual machines on AArch64 hardware.

For detailed information about VCPU lifecycle operations and VM exit handling, see VCPU Lifecycle and Operations. For per-CPU hardware state management and virtualization control, see Per-CPU State Management. For low-level context switching mechanics, see Context Switching and State Management.

Core VCPU Implementation

The Aarch64VCpu<H: AxVCpuHal> struct is the central component implementing virtual CPU functionality. It maintains both guest execution context and system register state, providing the foundation for running guest operating systems under hypervisor control.

VCPU Structure and Components

classDiagram
class Aarch64VCpu {
    +TrapFrame ctx
    +u64 host_stack_top
    +GuestSystemRegisters guest_system_regs
    +u64 mpidr
    +PhantomData~H~ _phantom
    +new(config) AxResult~Self~
    +setup(config) AxResult
    +set_entry(entry) AxResult
    +set_ept_root(ept_root) AxResult
    +run() AxResult~AxVCpuExitReason~
    +bind() AxResult
    +unbind() AxResult
    +set_gpr(idx, val)
}

class TrapFrame {
    +guest execution context
    +GPRs, SP_EL0, ELR, SPSR
    
}

class GuestSystemRegisters {
    +system control registers
    +timer registers
    +memory management registers
    
}

class VmCpuRegisters {
    +TrapFrame trap_context_regs
    +GuestSystemRegisters vm_system_regs
    
}

class Aarch64VCpuCreateConfig {
    +u64 mpidr_el1
    +usize dtb_addr
    
}

Aarch64VCpu  *--  TrapFrame : "contains"
Aarch64VCpu  *--  GuestSystemRegisters : "contains"
VmCpuRegisters  *--  TrapFrame : "aggregates"
VmCpuRegisters  *--  GuestSystemRegisters : "aggregates"
Aarch64VCpu  ..>  Aarch64VCpuCreateConfig : "uses for creation"

The VCPU structure maintains critical ordering constraints where ctx and host_stack_top must remain at the beginning of the struct to support assembly code expectations for context switching operations.

Sources: src/vcpu.rs(L40 - L51)  src/vcpu.rs(L30 - L37)  src/vcpu.rs(L54 - L62) 

Hardware Abstraction Integration


The generic H: AxVCpuHal parameter allows the VCPU implementation to work with different hardware abstraction layers while maintaining platform independence.

Sources: src/vcpu.rs(L8)  src/vcpu.rs(L64 - L65)  src/vcpu.rs(L278) 

VCPU Configuration and Initialization

Creation and Setup Process

The VCPU follows a two-phase initialization pattern: creation with hardware-specific configuration, followed by hypervisor setup.

PhaseFunctionPurposeConfiguration
Creationnew(config)Basic structure allocationAarch64VCpuCreateConfig
Setupsetup(config)Hypervisor initialization()(empty)
Entry Configurationset_entry(entry)Guest entry pointGuestPhysAddr
Memory Setupset_ept_root(ept_root)Extended page table rootHostPhysAddr

Sources: src/vcpu.rs(L69 - L84)  src/vcpu.rs(L87 - L96) 

System Register Initialization

flowchart TD
init_hv["init_hv()"]
init_spsr["Configure SPSR_EL1EL1h mode, masks set"]
init_vm_context["init_vm_context()"]
timer_config["Configure TimerCNTHCTL_EL2, CNTVOFF_EL2"]
sctlr_config["Configure SCTLR_EL10x30C50830"]
vtcr_config["Configure VTCR_EL240-bit PA, 4KB granules"]
hcr_config["Configure HCR_EL2VM enable, AArch64, SMC trap"]
vmpidr_config["Configure VMPIDR_EL2Based on mpidr parameter"]
ready["VCPU Ready for Execution"]

hcr_config --> ready
init_hv --> init_spsr
init_hv --> init_vm_context
init_vm_context --> hcr_config
init_vm_context --> sctlr_config
init_vm_context --> timer_config
init_vm_context --> vmpidr_config
init_vm_context --> vtcr_config
sctlr_config --> ready
timer_config --> ready
vmpidr_config --> ready
vtcr_config --> ready

The initialization process configures essential system registers to establish the proper hypervisor and guest execution environment, including memory management, exception handling, and CPU identification.

Sources: src/vcpu.rs(L128 - L166) 

Execution Model and Context Management

Host-Guest Context Switching

sequenceDiagram
    participant HostContext as "Host Context"
    participant Aarch64VCpurun as "Aarch64VCpu::run()"
    participant GuestExecution as "Guest Execution"
    participant ExceptionHandler as "Exception Handler"

    HostContext ->> Aarch64VCpurun: run()
    Aarch64VCpurun ->> Aarch64VCpurun: save_host_sp_el0()
    Aarch64VCpurun ->> Aarch64VCpurun: restore_vm_system_regs()
    Aarch64VCpurun ->> Aarch64VCpurun: run_guest() [naked asm]
    Note over Aarch64VCpurun: save_regs_to_stack!()
    Note over Aarch64VCpurun: context_vm_entry
    Aarch64VCpurun ->> GuestExecution: Enter guest execution
    GuestExecution ->> ExceptionHandler: VM Exit (trap/interrupt)
    ExceptionHandler ->> Aarch64VCpurun: return exit_reason
    Aarch64VCpurun ->> Aarch64VCpurun: vmexit_handler(trap_kind)
    Aarch64VCpurun ->> Aarch64VCpurun: guest_system_regs.store()
    Aarch64VCpurun ->> Aarch64VCpurun: restore_host_sp_el0()
    Aarch64VCpurun ->> HostContext: return AxVCpuExitReason

The execution model implements a clean separation between host and guest contexts, with careful register state management and stack handling to ensure proper isolation.

Sources: src/vcpu.rs(L99 - L111)  src/vcpu.rs(L182 - L214)  src/vcpu.rs(L255 - L282) 

Per-CPU State Management

flowchart TD
subgraph subGraph2["VCPU Execution"]
    run_start["run() entry"]
    run_exit["vmexit_handler()"]
end
subgraph subGraph1["Context Operations"]
    save_host["save_host_sp_el0()"]
    restore_host["restore_host_sp_el0()"]
    SP_EL0_reg["SP_EL0 register"]
end
subgraph subGraph0["Per-CPU Storage"]
    HOST_SP_EL0["HOST_SP_EL0percpu static"]
end

HOST_SP_EL0 --> restore_host
restore_host --> SP_EL0_reg
run_exit --> restore_host
run_start --> save_host
save_host --> HOST_SP_EL0
save_host --> SP_EL0_reg

The per-CPU state management ensures that host context is properly preserved across guest execution cycles, using percpu storage to maintain thread-local state.

Sources: src/vcpu.rs(L15 - L26)  src/vcpu.rs(L102 - L104)  src/vcpu.rs(L270 - L272) 

VM Exit Processing

Exit Reason Classification

The VCPU handles different types of VM exits through a structured dispatch mechanism:

flowchart TD
vmexit_handler["vmexit_handler(exit_reason)"]
store_guest["guest_system_regs.store()"]
restore_host_sp["restore_host_sp_el0()"]
dispatch["Match exit_reason"]
sync_handler["handle_exception_sync(&ctx)"]
irq_handler["AxVCpuExitReason::ExternalInterrupt"]
panic_handler["panic!(Unhandled exception)"]
return_reason["Return AxVCpuExitReason"]
irq_fetch["H::irq_fetch() for vector"]
system_halt["System Halt"]

dispatch --> irq_handler
dispatch --> panic_handler
dispatch --> sync_handler
irq_fetch --> return_reason
irq_handler --> irq_fetch
panic_handler --> system_halt
restore_host_sp --> dispatch
store_guest --> restore_host_sp
sync_handler --> return_reason
vmexit_handler --> store_guest

The exit processing maintains proper state transitions and delegates complex synchronous exceptions to specialized handlers while handling interrupts directly.

Sources: src/vcpu.rs(L255 - L282) 

Register State Preservation

During VM exits, the system carefully manages register state to maintain proper isolation:

Register SetStorage LocationTiming
Guest GPRsTrapFrame.ctxDuring exception entry (assembly)
Guest System RegsGuestSystemRegistersInvmexit_handler()
Host SP_EL0HOST_SP_EL0percpuBefore guest execution
Guest SP_EL0ctx.sp_el0Fromguest_system_regs.sp_el0

Sources: src/vcpu.rs(L262 - L272) 

VCPU Lifecycle and Operations

Relevant source files

This document details the virtual CPU (VCPU) creation, configuration, execution cycle, and VM exit handling mechanisms within the arm_vcpu hypervisor system. It covers the core Aarch64VCpu implementation, context management, and the complete lifecycle from VCPU instantiation to guest execution and exit handling.

For hardware abstraction and platform integration details, see Hardware Abstraction and Platform Support. For low-level exception vector implementation, see Assembly Exception Vectors. For per-CPU state management across multiple VCPUs, see Per-CPU State Management.

VCPU Structure and Components

The Aarch64VCpu struct serves as the primary virtual CPU implementation, containing all necessary state for guest execution and host-guest context switching.

Core VCPU Structure

classDiagram
class Aarch64VCpu {
    +TrapFrame ctx
    +u64 host_stack_top
    +GuestSystemRegisters guest_system_regs
    +u64 mpidr
    +PhantomData~H~ _phantom
    +new(config) AxResult~Self~
    +setup(config) AxResult
    +run() AxResult~AxVCpuExitReason~
    +set_entry(entry) AxResult
    +set_ept_root(ept_root) AxResult
}

class TrapFrame {
    +u64[31] gpr
    +u64 sp_el0
    +u64 elr
    +u64 spsr
    +set_exception_pc(pc)
    +set_argument(arg)
    +set_gpr(idx, val)
    +gpr(idx) usize
}

class GuestSystemRegisters {
    +u64 cntvoff_el2
    +u32 cntkctl_el1
    +u64 sp_el0
    +u32 sctlr_el1
    +u64 hcr_el2
    +u64 vttbr_el2
    +u64 vmpidr_el2
    +store()
    +restore()
}

class Aarch64VCpuCreateConfig {
    +u64 mpidr_el1
    +usize dtb_addr
    
}

Aarch64VCpu  *--  TrapFrame : "contains"
Aarch64VCpu  *--  GuestSystemRegisters : "contains"
Aarch64VCpu  ..>  Aarch64VCpuCreateConfig : "uses for creation"

The VCPU structure maintains strict field ordering requirements for assembly code interaction. The ctx and host_stack_top fields must remain in their current positions and order to support the low-level context switching assembly routines.

Sources: src/vcpu.rs(L40 - L51)  src/context_frame.rs(L17 - L28)  src/context_frame.rs(L145 - L197) 

VCPU Creation and Configuration

Creation Process

The VCPU creation follows a two-phase initialization pattern through the AxArchVCpu trait implementation:

sequenceDiagram
    participant Client as Client
    participant Aarch64VCpu as Aarch64VCpu
    participant TrapFrame as TrapFrame
    participant GuestSystemRegisters as GuestSystemRegisters

    Client ->> Aarch64VCpu: new(Aarch64VCpuCreateConfig)
    Aarch64VCpu ->> TrapFrame: TrapFrame::default()
    Aarch64VCpu ->> TrapFrame: set_argument(config.dtb_addr)
    Aarch64VCpu ->> GuestSystemRegisters: GuestSystemRegisters::default()
    Aarch64VCpu -->> Client: Aarch64VCpu instance
    Client ->> Aarch64VCpu: setup(())
    Aarch64VCpu ->> Aarch64VCpu: init_hv()
    Aarch64VCpu ->> Aarch64VCpu: init_vm_context()
    Aarch64VCpu -->> Client: AxResult
    Client ->> Aarch64VCpu: set_entry(guest_entry_point)
    Aarch64VCpu ->> TrapFrame: set_exception_pc(entry)
    Client ->> Aarch64VCpu: set_ept_root(page_table_root)
    Aarch64VCpu ->> GuestSystemRegisters: vttbr_el2 = ept_root

The new() method creates a VCPU instance with minimal initialization, setting up the trap context with the device tree address and initializing default system registers. The setup() method performs hypervisor-specific initialization including guest execution state configuration.

Sources: src/vcpu.rs(L69 - L80)  src/vcpu.rs(L82 - L85)  src/vcpu.rs(L87 - L97) 

Hypervisor Context Initialization

The init_hv() and init_vm_context() methods configure the guest execution environment:

Register/FieldValuePurpose
SPSR_EL1EL1h + All exceptions maskedGuest starts in EL1 with interrupts disabled
CNTHCTL_EL2EL1PCEN + EL1PCTENEnable timer access from EL1
SCTLR_EL10x30C50830System control register defaults
VTCR_EL240-bit PA, 4KB granule, stage-2 configStage-2 translation control
HCR_EL2VM + RW + TSCEnable virtualization, 64-bit EL1, trap SMC
VMPIDR_EL2Configured MPIDRVirtual multiprocessor ID

Sources: src/vcpu.rs(L128 - L136)  src/vcpu.rs(L139 - L166) 

VCPU Execution Cycle

VM Entry and Exit Flow

The VCPU execution follows a carefully orchestrated context switching process:

flowchart TD
start["vcpu.run()"]
save_host["save_host_sp_el0()"]
restore_vm["restore_vm_system_regs()"]
run_guest["run_guest()"]
save_regs["save_regs_to_stack!()"]
save_stack["Save host stack to host_stack_top"]
vm_entry["context_vm_entry"]
guest_exec["Guest Execution"]
exception["Exception/Interrupt"]
vmexit["VM Exit"]
save_guest["SAVE_REGS_FROM_EL1"]
handler["vmexit_handler()"]
store_regs["guest_system_regs.store()"]
restore_host_sp["restore_host_sp_el0()"]
dispatch["Exception dispatch"]
sync["handle_exception_sync()"]
irq["ExternalInterrupt"]
exit_reason["AxVCpuExitReason"]

dispatch --> irq
dispatch --> sync
exception --> vmexit
exit_reason --> start
guest_exec --> exception
handler --> store_regs
irq --> exit_reason
restore_host_sp --> dispatch
restore_vm --> run_guest
run_guest --> save_regs
save_guest --> handler
save_host --> restore_vm
save_regs --> save_stack
save_stack --> vm_entry
start --> save_host
store_regs --> restore_host_sp
sync --> exit_reason
vm_entry --> guest_exec
vmexit --> save_guest

The execution cycle begins with the run() method, which saves host state, restores guest system registers, and transitions to guest execution through the run_guest() naked function.

Sources: src/vcpu.rs(L99 - L111)  src/vcpu.rs(L186 - L214)  src/vcpu.rs(L255 - L282) 

Context Switching Implementation

The run_guest() function implements a naked assembly function that performs the critical host-to-guest transition:

flowchart TD
subgraph subGraph2["Guest Context"]
    guest_regs["Guest GPRs"]
    guest_sys_regs["Guest System Registers"]
    guest_exec["Guest Execution"]
end
subgraph Transition["Transition"]
    save_macro["save_regs_to_stack!()"]
    save_sp["Save stack pointer to host_stack_top"]
    vm_entry["Branch to context_vm_entry"]
end
subgraph subGraph0["Host Context"]
    host_regs["Host GPRs (x19-x30)"]
    host_sp["Host SP_EL0"]
    host_stack["Host Stack"]
end

guest_regs --> guest_exec
guest_sys_regs --> guest_exec
host_regs --> save_macro
host_stack --> save_sp
save_macro --> vm_entry
save_sp --> vm_entry
vm_entry --> guest_regs
vm_entry --> guest_sys_regs

The naked function ensures no compiler-generated prologue/epilogue interferes with the precise register state management required for hypervisor operation.

Sources: src/vcpu.rs(L186 - L214)  src/vcpu.rs(L225 - L244) 

VM Exit Handling

Exit Reason Processing

The vmexit_handler() method processes VM exits and converts low-level trap information into structured exit reasons:

flowchart TD
vmexit["vmexit_handler(TrapKind)"]
store["guest_system_regs.store()"]
save_sp["Save guest SP_EL0 to ctx.sp_el0"]
restore_host["restore_host_sp_el0()"]
match_trap["Match TrapKind"]
sync["TrapKind::Synchronous"]
irq["TrapKind::Irq"]
other["Other TrapKind"]
handle_sync["handle_exception_sync(&mut ctx)"]
fetch_irq["H::irq_fetch()"]
panic["panic!(Unhandled exception)"]
exit_reason["AxVCpuExitReason"]
ext_int["AxVCpuExitReason::ExternalInterrupt"]
return_result["Return to hypervisor"]

exit_reason --> return_result
ext_int --> return_result
fetch_irq --> ext_int
handle_sync --> exit_reason
irq --> fetch_irq
match_trap --> irq
match_trap --> other
match_trap --> sync
other --> panic
restore_host --> match_trap
save_sp --> restore_host
store --> save_sp
sync --> handle_sync
vmexit --> store

The exit handler ensures proper state preservation by storing guest system registers and restoring host SP_EL0 before processing the specific exit cause.

Sources: src/vcpu.rs(L255 - L282) 

Exit Reason Types

The system generates specific exit reasons that inform the hypervisor about the cause of the VM exit:

Exit ReasonTriggerHandler Location
MmioRead/WriteData abort to device memoryhandle_data_abort
SystemRegisterRead/WriteTrapped system register accesshandle_system_register
HypercallHVC instructionhandle_psci_call
CpuUp/Down/SystemDownPSCI power managementhandle_psci_call
ExternalInterruptPhysical interruptvmexit_handler

Sources: src/vcpu.rs(L276 - L281) 

Context Management

Register State Preservation

The VCPU maintains two primary context structures for complete state preservation:

flowchart TD
subgraph subGraph2["Hardware Registers"]
    hw_regs["Actual AArch64 System Registers"]
end
subgraph subGraph1["GuestSystemRegisters (System State)"]
    el1_state["EL1 State (sctlr_el1, vbar_el1, etc)"]
    subgraph subGraph0["TrapFrame (GPR State)"]
        timer["Timer Registers (cntvoff_el2, cntkctl_el1)"]
        vm_id["VM Identity (vmpidr_el2, vpidr_el2)"]
        memory["Memory Management (ttbr0/1_el1, tcr_el1)"]
        hyp_ctrl["Hypervisor Control (hcr_el2, vttbr_el2)"]
        gpr["gpr[31] - General Purpose Registers"]
        sp_el0["sp_el0 - EL0 Stack Pointer"]
        elr["elr - Exception Link Register"]
        spsr["spsr - Saved Program Status"]
    end
end
TrapFrame["TrapFrame"]
GuestSystemRegisters["GuestSystemRegisters"]

GuestSystemRegisters --> hw_regs
TrapFrame --> hw_regs

The TrapFrame captures the basic CPU execution state, while GuestSystemRegisters preserves the complete virtual machine system configuration.

Sources: src/context_frame.rs(L17 - L136)  src/context_frame.rs(L213 - L300) 

Host State Management

Host state preservation uses a combination of stack storage and per-CPU variables:

sequenceDiagram
    participant Host as Host
    participant PerCPU as PerCPU
    participant Guest as Guest
    participant stack as stack

    Note over Host,Guest: VM Entry Sequence
    Host ->> stack: save_regs_to_stack!() - GPRs x19-x30
    Host ->> PerCPU: save_host_sp_el0() - HOST_SP_EL0
    Host ->> Guest: Context switch to guest
    Note over Host,Guest: VM Exit Sequence
    Guest ->> PerCPU: Store guest SP_EL0 to ctx.sp_el0
    Guest ->> PerCPU: restore_host_sp_el0() - Restore HOST_SP_EL0
    Guest ->> stack: restore_regs_from_stack!() - Restore GPRs
    Guest ->> Host: Return to host execution

The per-CPU HOST_SP_EL0 variable ensures each CPU core maintains independent host state, supporting multi-core hypervisor operation.

Sources: src/vcpu.rs(L15 - L26)  src/vcpu.rs(L262 - L273) 

Per-CPU State Management

Relevant source files

This document covers the per-CPU state management system in the arm_vcpu hypervisor, which handles virtualization control and interrupt management on a per-processor basis. The system is responsible for enabling/disabling hardware virtualization features, managing exception vectors, and coordinating interrupt handling between the hypervisor and host OS.

For information about the overall VCPU lifecycle and operations, see 2.1. For details about context switching and register state management, see 3.

Per-CPU Data Structure

The core of per-CPU state management is the Aarch64PerCpu structure, which implements the AxArchPerCpu trait to provide processor-specific virtualization control.

Aarch64PerCpu Structure

classDiagram
note for Aarch64PerCpu "4096-byte alignedRepresents per-CPU statefor virtualization control"
class Aarch64PerCpu {
    +cpu_id: usize
    -_phantom: PhantomData~H~
    +new(cpu_id: usize) AxResult~Self~
    +is_enabled() bool
    +hardware_enable() AxResult
    +hardware_disable() AxResult
}

class AxArchPerCpu {
    <<trait>>
    
    +new(cpu_id: usize) AxResult~Self~
    +is_enabled() bool
    +hardware_enable() AxResult
    +hardware_disable() AxResult
}

class AxVCpuHal {
    <<trait>>
    
    +irq_hanlder()
}

Aarch64PerCpu  ..|>  AxArchPerCpu : implements
Aarch64PerCpu  -->  AxVCpuHal : uses H generic

Sources: src/pcpu.rs(L10 - L16) 

The structure is aligned to 4096 bytes and contains minimal data, serving primarily as a coordination point for per-CPU virtualization operations. The generic parameter H provides hardware abstraction through the AxVCpuHal trait.

Per-CPU Variables

The system uses two key per-CPU variables to manage state across processor cores:

VariableTypePurpose
ORI_EXCEPTION_VECTOR_BASEusizeStores originalVBAR_EL2value before virtualization
IRQ_HANDLEROnceCell<&(dyn Fn() + Send + Sync)>Host OS IRQ handler for this CPU

Exception Vector Management

stateDiagram-v2
state HostVectors {
    [*] --> OriginalVBAR
    OriginalVBAR : "VBAR_EL2 = host exception vectors"
    OriginalVBAR : "Stored in ORI_EXCEPTION_VECTOR_BASE"
}
state VCpuVectors {
    [*] --> VCpuVBAR
    VCpuVBAR : "VBAR_EL2 = exception_vector_base_vcpu"
    VCpuVBAR : "Handles VM exits and traps"
}
[*] --> HostVectors : "Initial state"
HostVectors --> VCpuVectors : "hardware_enable()"
VCpuVectors --> HostVectors : "hardware_disable()"
note left of VCpuVectors : ['exception_vector_base_vcpu()<br>defined in assembly']

Sources: src/pcpu.rs(L18 - L19)  src/pcpu.rs(L28 - L30)  src/pcpu.rs(L53 - L57)  src/pcpu.rs(L74 - L77) 

Hardware Virtualization Control

The per-CPU system manages the AArch64 virtualization extensions through the Hypervisor Configuration Register (HCR_EL2).

Virtualization State Management

flowchart TD
subgraph subGraph0["HCR_EL2 Configuration"]
    G1["HCR_EL2::VM::Enable"]
    G2["HCR_EL2::RW::EL1IsAarch64"]
    G3["HCR_EL2::IMO::EnableVirtualIRQ"]
    G4["HCR_EL2::FMO::EnableVirtualFIQ"]
    G5["HCR_EL2::TSC::EnableTrapEl1SmcToEl2"]
end
A["new(cpu_id)"]
B["Register IRQ handler"]
C["Per-CPU instance ready"]
D["hardware_enable()"]
E["Save original VBAR_EL2"]
F["Set VCPU exception vectors"]
G["Configure HCR_EL2"]
H["Virtualization enabled"]
I["is_enabled() returns true"]
J["hardware_disable()"]
K["Restore original VBAR_EL2"]
L["Clear HCR_EL2.VM"]
M["Virtualization disabled"]

A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> G1
G --> G2
G --> G3
G --> G4
G --> G5
G --> H
H --> I
H --> J
J --> K
K --> L
L --> M

Sources: src/pcpu.rs(L32 - L78) 

HCR_EL2 Register Configuration

When enabling virtualization, the system configures several key control bits:

Bit FieldSettingPurpose
VMEnableEnables virtualization extensions
RWEL1IsAarch64Sets EL1 execution state to AArch64
IMOEnableVirtualIRQRoutes IRQs to virtual interrupt controller
FMOEnableVirtualFIQRoutes FIQs to virtual interrupt controller
TSCEnableTrapEl1SmcToEl2Traps SMC instructions from EL1 to EL2

Sources: src/pcpu.rs(L59 - L65) 

IRQ Handler Integration

The per-CPU system coordinates interrupt handling between the hypervisor and host OS through a registered handler mechanism.

IRQ Handler Registration

sequenceDiagram
    participant HostOS as Host OS
    participant Aarch64PerCpunew as Aarch64PerCpu::new()
    participant IRQ_HANDLER as IRQ_HANDLER
    participant AxVCpuHalirq_hanlder as AxVCpuHal::irq_hanlder()

    HostOS ->> Aarch64PerCpunew: Create per-CPU instance
    Aarch64PerCpunew ->> IRQ_HANDLER: Get current CPU's IRQ_HANDLER
    Aarch64PerCpunew ->> AxVCpuHalirq_hanlder: Call H::irq_hanlder()
    AxVCpuHalirq_hanlder -->> Aarch64PerCpunew: Return handler function
    Aarch64PerCpunew ->> IRQ_HANDLER: Set handler in OnceCell
    IRQ_HANDLER -->> Aarch64PerCpunew: Registration complete
    Aarch64PerCpunew -->> HostOS: Per-CPU instance ready
    Note over IRQ_HANDLER: Handler stored as<br>per-CPU variable<br>for current processor

Sources: src/pcpu.rs(L21 - L26)  src/pcpu.rs(L34 - L37) 

The IRQ handler is set once per CPU during initialization and provides a mechanism for the hypervisor's exception handling system to dispatch interrupts back to the host OS when appropriate.

Integration with VCPU System

The per-CPU state management integrates closely with the broader VCPU lifecycle:

Per-CPU and VCPU Relationship

flowchart TD
subgraph subGraph2["Host OS Integration"]
    HostIRQ["Host IRQ Handler"]
    HostVectors["Host Exception Vectors"]
end
subgraph subGraph1["VCPU Operations"]
    VCPUCreate["Aarch64VCpu creation"]
    VCPURun["vcpu.run()"]
    ExceptionVectors["exception_vector_base_vcpu"]
end
subgraph subGraph0["CPU Core N"]
    PerCPU["Aarch64PerCpu<H>"]
    VBAR["VBAR_EL2 Register"]
    HCR["HCR_EL2 Register"]
    IRQVar["IRQ_HANDLER per-CPU var"]
end
note1["hardware_enable() sets upvirtualization for this CPU"]
note2["hardware_disable() restoresoriginal host state"]

ExceptionVectors --> VBAR
IRQVar --> HostIRQ
PerCPU --> HCR
PerCPU --> HostVectors
PerCPU --> IRQVar
PerCPU --> VBAR
PerCPU --> note1
PerCPU --> note2
VCPUCreate --> PerCPU
VCPURun --> PerCPU

Sources: src/pcpu.rs(L45 - L78) 

The per-CPU system provides the foundation for VCPU operations by ensuring that hardware virtualization extensions are properly configured and that interrupt handling is coordinated between the hypervisor and host OS on each processor core.

Context Switching and State Management

Relevant source files

This document covers the data structures and mechanisms used for saving and restoring CPU state during context switches between the hypervisor (host) and guest virtual machines. The context switching system is responsible for preserving and restoring both general-purpose registers and system control registers to enable seamless transitions between execution contexts.

For information about the detailed register layouts and data structure implementations, see TrapFrame and System Registers. For the broader exception handling pipeline that triggers context switches, see Exception Handling System.

Context Switching Overview

The hypervisor implements a two-level context switching mechanism that manages transitions between host (EL2) and guest (EL1) execution contexts. Context switches occur during VM entry when launching guest execution and during VM exits when handling exceptions or interrupts from the guest.

Context Switch Data Flow

flowchart TD
Host["Host Execution (EL2)"]
SaveHost["save_regs_to_stack!()"]
SaveSP["save_host_sp_el0()"]
RestoreGuest["restore_vm_system_regs()"]
GuestEntry["context_vm_entry"]
Guest["Guest Execution (EL1)"]
TrapEntry["Exception Vector"]
SaveGuestCtx["SAVE_REGS_FROM_EL1"]
SaveGuestSys["guest_system_regs.store()"]
RestoreHostSP["restore_host_sp_el0()"]
RestoreHostCtx["restore_regs_from_stack!()"]

Guest --> TrapEntry
GuestEntry --> Guest
Host --> SaveHost
RestoreGuest --> GuestEntry
RestoreHostCtx --> Host
RestoreHostSP --> RestoreHostCtx
SaveGuestCtx --> SaveGuestSys
SaveGuestSys --> RestoreHostSP
SaveHost --> SaveSP
SaveSP --> RestoreGuest
TrapEntry --> SaveGuestCtx

Sources: src/vcpu.rs(L182 - L283)  src/context_frame.rs(L199 - L300) 

State Management Data Structures

The system uses two primary data structures to manage CPU state during context switches:

Core Context Structures

classDiagram
class VmCpuRegisters {
    +TrapFrame trap_context_regs
    +GuestSystemRegisters vm_system_regs
    
}

class Aarch64ContextFrame {
    +u64[31] gpr
    +u64 sp_el0
    +u64 elr
    +u64 spsr
    +set_gpr(index, val)
    +gpr(index) usize
    +set_exception_pc(pc)
}

class GuestSystemRegisters {
    +u64 cntvoff_el2
    +u32 cntkctl_el1
    +u64 sp_el0
    +u32 sctlr_el1
    +u64 hcr_el2
    +u64 vttbr_el2
    +u64 vtcr_el2
    +store()
    +restore()
}

class Aarch64VCpu {
    +TrapFrame ctx
    +u64 host_stack_top
    +GuestSystemRegisters guest_system_regs
    +run() AxVCpuExitReason
}

VmCpuRegisters  -->  Aarch64ContextFrame
VmCpuRegisters  -->  GuestSystemRegisters
Aarch64VCpu  -->  Aarch64ContextFrame
Aarch64VCpu  -->  GuestSystemRegisters

Sources: src/vcpu.rs(L28 - L51)  src/context_frame.rs(L17 - L28)  src/context_frame.rs(L145 - L197) 

StructurePurposeKey Fields
Aarch64ContextFrameGeneral-purpose register contextgpr[31],sp_el0,elr,spsr
GuestSystemRegistersSystem control registersTimer, MMU, hypervisor control registers
VmCpuRegistersCombined guest stateCombines both context types

Context Switch Mechanisms

Host-to-Guest Transition

The transition from host to guest execution follows a carefully orchestrated sequence implemented in the run_guest() function:

sequenceDiagram
    participant HostContext as "Host Context"
    participant HostStack as "Host Stack"
    participant PerCPUStorage as "Per-CPU Storage"
    participant SystemRegisters as "System Registers"
    participant GuestContext as "Guest Context"

    HostContext ->> HostStack: "save_regs_to_stack!()"
    HostContext ->> PerCPUStorage: "save_host_sp_el0()"
    Note over HostContext,PerCPUStorage: "Store HOST_SP_EL0 to per-CPU region"
    HostContext ->> SystemRegisters: "restore_vm_system_regs()"
    SystemRegisters ->> GuestContext: "context_vm_entry"
    Note over GuestContext: "Guest begins execution"

Sources: src/vcpu.rs(L182 - L214)  src/vcpu.rs(L15 - L26)  src/vcpu.rs(L225 - L244) 

The run_guest() function is implemented as a naked function to ensure precise control over the stack and register state:

  • Host Register Preservation: Uses save_regs_to_stack!() macro to save callee-saved registers (x19-x30)
  • Stack Pointer Management: Stores current stack pointer in host_stack_top field of the VCPU structure
  • SP_EL0 Handling: Saves host's SP_EL0 to per-CPU storage using save_host_sp_el0()
  • System Register Restoration: Calls restore_vm_system_regs() to load guest system state

Guest-to-Host Transition

Guest-to-host transitions occur during VM exits and are handled by the exception vector table and the vmexit_handler():

flowchart TD
Exception["Guest Exception/Interrupt"]
SaveRegs["SAVE_REGS_FROM_EL1"]
StoreGuest["guest_system_regs.store()"]
SaveSP["ctx.sp_el0 = guest_system_regs.sp_el0"]
RestoreSP["restore_host_sp_el0()"]
Handler["Exception Handler"]
Return["Return to Host"]

Exception --> SaveRegs
Handler --> Return
RestoreSP --> Handler
SaveRegs --> StoreGuest
SaveSP --> RestoreSP
StoreGuest --> SaveSP

Sources: src/vcpu.rs(L255 - L282)  src/context_frame.rs(L213 - L253) 

The guest-to-host transition process:

  1. Exception Context Capture: Assembly code saves guest GPRs to TrapFrame
  2. System Register Storage: guest_system_regs.store() captures current system register state
  3. SP_EL0 Coordination: Guest's SP_EL0 is transferred from system registers to the context frame
  4. Host SP_EL0 Restoration: restore_host_sp_el0() restores host's stack pointer
  5. Exception Processing: Control passes to exception handlers

State Preservation Mechanisms

Per-CPU State Management

The system uses per-CPU storage to maintain host state across context switches:

flowchart TD
subgraph subGraph2["System Register"]
    SP_EL0_REG["SP_EL0 Register"]
end
subgraph subGraph1["Host Context Operations"]
    SaveSP["save_host_sp_el0()"]
    RestoreSP["restore_host_sp_el0()"]
end
subgraph subGraph0["Per-CPU Storage"]
    HOST_SP_EL0["HOST_SP_EL0: u64"]
end

HOST_SP_EL0 --> RestoreSP
RestoreSP --> SP_EL0_REG
SP_EL0_REG --> SaveSP
SaveSP --> HOST_SP_EL0

Sources: src/vcpu.rs(L15 - L26) 

System Register State Management

The GuestSystemRegisters structure provides comprehensive system register preservation through assembly-based store() and restore() methods:

Register CategoryKey RegistersPurpose
Timer RegistersCNTVOFF_EL2,CNTKCTL_EL1,CNTV_CTL_EL0Virtual timer state
Memory ManagementTTBR0_EL1,TTBR1_EL1,TCR_EL1,MAIR_EL1Address translation setup
Hypervisor ControlHCR_EL2,VTTBR_EL2,VTCR_EL2Virtualization configuration
Exception StateVBAR_EL1,ESR_EL1,FAR_EL1Exception handling state

Sources: src/context_frame.rs(L149 - L197)  src/context_frame.rs(L213 - L253)  src/context_frame.rs(L263 - L299) 

Integration with Exception Handling

Context switching is tightly integrated with the exception handling system. The context switch mechanisms are triggered by:

  • VM Entry: Initiated by calls to Aarch64VCpu::run()
  • VM Exit: Triggered by exceptions caught by the vector table in exception.S
  • Exception Return: Managed by assembly routines that restore guest context

The vmexit_handler() function coordinates the transition from low-level exception handling back to the hypervisor control flow:

flowchart TD
VMExit["VM Exit Event"]
SaveState["Save Guest State"]
RestoreHost["Restore Host State"]
ExceptionSync["handle_exception_sync()"]
ExitReason["Return AxVCpuExitReason"]
Hypervisor["Hypervisor Handles Exit"]

ExceptionSync --> ExitReason
ExitReason --> Hypervisor
RestoreHost --> ExceptionSync
SaveState --> RestoreHost
VMExit --> SaveState

Sources: src/vcpu.rs(L255 - L282) 

The context switching system ensures that state transitions are atomic and that both host and guest contexts are properly preserved across the hypervisor's execution boundaries.

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

CategoryRegistersPurposeAccess Pattern
General Purposex0-x30, SP_EL0Function arguments, local variables, stack managementEvery VM exit/entry via assembly
Execution StateELR, SPSRProgram counter and processor statusEvery VM exit/entry via assembly
Memory ManagementTTBR0_EL1, TTBR1_EL1, TCR_EL1, MAIR_EL1Page table configuration, memory attributesRestored on VM entry
System ControlSCTLR_EL1, CPACR_EL1, ACTLR_EL1Processor features, access controlRestored on VM entry
Exception HandlingVBAR_EL1, ESR_EL1, FAR_EL1Vector table, exception informationPreserved across exits
Timer ManagementCNTVOFF_EL2, CNTKCTL_EL1, timer control/value registersVirtual timer stateManaged by hypervisor
Hypervisor ControlHCR_EL2, VTTBR_EL2, VTCR_EL2Virtualization configuration, stage-2 MMUSet 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) 

Exception Handling System

Relevant source files

This document covers the multi-layered exception handling architecture that manages VM exits, traps, and interrupts when a guest VM encounters exceptional conditions. The system provides a complete pipeline from low-level assembly exception vectors through register analysis utilities to high-level exception dispatch and handling.

For information about the VCPU lifecycle and context switching mechanisms, see Virtual CPU Management. For details about the assembly exception vectors specifically, see Assembly Exception Vectors.

Architecture Overview

The exception handling system operates across three distinct layers, each responsible for different aspects of exception processing:

Exception Handling System Architecture

flowchart TD
subgraph subGraph4["VCPU Integration"]
    TRAP_FRAME["TrapFrame"]
    EXIT_REASON["AxVCpuExitReason"]
    VCPU_RUN["Aarch64VCpu::run()"]
end
subgraph subGraph3["Handler Layer (exception.rs)"]
    HANDLE_EXCEPTION_SYNC["handle_exception_sync()"]
    HANDLE_DATA_ABORT["handle_data_abort()"]
    HANDLE_SYSTEM_REGISTER["handle_system_register()"]
    HANDLE_PSCI_CALL["handle_psci_call()"]
    HANDLE_SMC64["handle_smc64_exception()"]
end
subgraph subGraph2["Utility Layer (exception_utils.rs)"]
    EXCEPTION_ESR["exception_esr()"]
    EXCEPTION_CLASS["exception_class()"]
    EXCEPTION_FAULT_ADDR["exception_fault_addr()"]
    DATA_ABORT_UTILS["Data Abort Analysis Functions"]
    SYSREG_UTILS["System Register Analysis Functions"]
end
subgraph subGraph1["Assembly Layer (exception.S)"]
    VECTOR_TABLE["exception_vector_base_vcpu"]
    SAVE_REGS["SAVE_REGS_FROM_EL1"]
    RESTORE_REGS["RESTORE_REGS_INTO_EL1"]
    VMEXIT_TRAMP["vmexit_trampoline"]
    CONTEXT_VM_ENTRY["context_vm_entry"]
end
subgraph subGraph0["Guest VM (EL1/EL0)"]
    GUEST_CODE["Guest Code Execution"]
    GUEST_EXC["Exception/Trap Triggered"]
end

CONTEXT_VM_ENTRY --> RESTORE_REGS
EXIT_REASON --> VCPU_RUN
GUEST_CODE --> GUEST_EXC
GUEST_EXC --> VECTOR_TABLE
HANDLE_DATA_ABORT --> DATA_ABORT_UTILS
HANDLE_DATA_ABORT --> EXCEPTION_FAULT_ADDR
HANDLE_DATA_ABORT --> EXIT_REASON
HANDLE_EXCEPTION_SYNC --> EXCEPTION_CLASS
HANDLE_EXCEPTION_SYNC --> EXCEPTION_ESR
HANDLE_EXCEPTION_SYNC --> HANDLE_DATA_ABORT
HANDLE_EXCEPTION_SYNC --> HANDLE_PSCI_CALL
HANDLE_EXCEPTION_SYNC --> HANDLE_SMC64
HANDLE_EXCEPTION_SYNC --> HANDLE_SYSTEM_REGISTER
HANDLE_PSCI_CALL --> EXIT_REASON
HANDLE_SMC64 --> EXIT_REASON
HANDLE_SYSTEM_REGISTER --> EXIT_REASON
HANDLE_SYSTEM_REGISTER --> SYSREG_UTILS
RESTORE_REGS --> GUEST_CODE
RESTORE_REGS --> TRAP_FRAME
SAVE_REGS --> TRAP_FRAME
SAVE_REGS --> VMEXIT_TRAMP
VCPU_RUN --> CONTEXT_VM_ENTRY
VECTOR_TABLE --> SAVE_REGS
VMEXIT_TRAMP --> HANDLE_EXCEPTION_SYNC

Sources: src/exception.S(L106 - L141)  src/exception.rs(L72 - L126)  src/exception_utils.rs(L1 - L312) 

Exception Processing Pipeline

The system processes exceptions through a structured pipeline that preserves guest state while analyzing and dispatching exceptions to appropriate handlers:

Exception Processing Flow

flowchart TD
subgraph subGraph4["Exit Reason Generation"]
    MMIO_READ["AxVCpuExitReason::MmioRead"]
    MMIO_WRITE["AxVCpuExitReason::MmioWrite"]
    HYPERCALL["AxVCpuExitReason::Hypercall"]
    CPU_UP["AxVCpuExitReason::CpuUp"]
    CPU_DOWN["AxVCpuExitReason::CpuDown"]
    SYSTEM_DOWN["AxVCpuExitReason::SystemDown"]
    SYSREG_READ["AxVCpuExitReason::SysRegRead"]
    SYSREG_WRITE["AxVCpuExitReason::SysRegWrite"]
    NOTHING["AxVCpuExitReason::Nothing"]
end
subgraph subGraph3["Specific Handlers"]
    DATA_ABORT["DataAbortLowerEL → handle_data_abort()"]
    HVC_CALL["HVC64 → handle_psci_call() or Hypercall"]
    SYS_REG["TrappedMsrMrs → handle_system_register()"]
    SMC_CALL["SMC64 → handle_smc64_exception()"]
    UNHANDLED["Unhandled → panic!"]
end
subgraph subGraph2["Exception Classification"]
    GET_ESR["exception_esr()"]
    GET_CLASS["exception_class()"]
    CLASS_MATCH["Match ESR_EL2::EC"]
end
subgraph subGraph1["Context Save & Dispatch"]
    SAVE_CONTEXT["SAVE_REGS_FROM_EL1 macro"]
    VMEXIT_FUNC["vmexit_trampoline()"]
    SYNC_HANDLER["handle_exception_sync()"]
end
subgraph subGraph0["Assembly Vector Dispatch"]
    VECTOR_BASE["exception_vector_base_vcpu"]
    CURRENT_EL_SYNC["current_el_sync_handler"]
    CURRENT_EL_IRQ["current_el_irq_handler"]
    LOWER_EL_SYNC["HANDLE_LOWER_SYNC_VCPU"]
    LOWER_EL_IRQ["HANDLE_LOWER_IRQ_VCPU"]
    INVALID_EXC["invalid_exception_el2"]
end
GUEST_TRAP["Guest VM Exception/Trap"]

CLASS_MATCH --> DATA_ABORT
CLASS_MATCH --> HVC_CALL
CLASS_MATCH --> SMC_CALL
CLASS_MATCH --> SYS_REG
CLASS_MATCH --> UNHANDLED
DATA_ABORT --> MMIO_READ
DATA_ABORT --> MMIO_WRITE
GET_CLASS --> CLASS_MATCH
GUEST_TRAP --> VECTOR_BASE
HVC_CALL --> CPU_DOWN
HVC_CALL --> CPU_UP
HVC_CALL --> HYPERCALL
HVC_CALL --> SYSTEM_DOWN
LOWER_EL_IRQ --> SAVE_CONTEXT
LOWER_EL_SYNC --> SAVE_CONTEXT
SAVE_CONTEXT --> VMEXIT_FUNC
SMC_CALL --> NOTHING
SYNC_HANDLER --> GET_CLASS
SYNC_HANDLER --> GET_ESR
SYS_REG --> SYSREG_READ
SYS_REG --> SYSREG_WRITE
VECTOR_BASE --> CURRENT_EL_IRQ
VECTOR_BASE --> CURRENT_EL_SYNC
VECTOR_BASE --> INVALID_EXC
VECTOR_BASE --> LOWER_EL_IRQ
VECTOR_BASE --> LOWER_EL_SYNC
VMEXIT_FUNC --> SYNC_HANDLER

Sources: src/exception.S(L108 - L141)  src/exception.rs(L72 - L126)  src/exception.rs(L276 - L290) 

Exception Types and Dispatch Logic

The handle_exception_sync function serves as the primary dispatcher, using the Exception Class (EC) field from ESR_EL2 to route exceptions to specialized handlers:

Exception ClassESR_EL2::EC ValueHandler FunctionExit Reason Generated
Data Abort Lower ELDataAbortLowerELhandle_data_abort()MmioRead,MmioWrite
Hypervisor CallHVC64handle_psci_call()or HypercallCpuUp,CpuDown,SystemDown,Hypercall
System Register AccessTrappedMsrMrshandle_system_register()SysRegRead,SysRegWrite
Secure Monitor CallSMC64handle_smc64_exception()Nothing(forwarded to ATF)

Sources: src/exception.rs(L72 - L126) 

Data Abort Handling

Data aborts occur when the guest VM accesses memory that triggers a fault. The handle_data_abort function processes these by:

  1. Fault Analysis: Using exception_fault_addr() to determine the guest physical address
  2. Access Classification: Determining read/write direction and access width using utility functions
  3. Register Identification: Finding which guest register was involved in the access
  4. MMIO Translation: Converting the fault into an appropriate AxVCpuExitReason

The function performs several safety checks using exception_data_abort_handleable() and exception_data_abort_is_translate_fault() before proceeding with MMIO emulation.

Sources: src/exception.rs(L128 - L182)  src/exception_utils.rs(L132 - L142)  src/exception_utils.rs(L202 - L254) 

System Register Access Handling

System register accesses are trapped when the guest VM attempts to read or write control registers. The handle_system_register function:

  1. ISS Parsing: Extracts the Instruction Specific Syndrome from ESR_EL2
  2. Register Identification: Uses exception_sysreg_addr() to identify the target register
  3. Direction Detection: Determines read vs write using exception_sysreg_direction_write()
  4. GPR Mapping: Identifies the guest register involved using exception_sysreg_gpr()

Sources: src/exception.rs(L184 - L211)  src/exception_utils.rs(L174 - L195) 

PSCI and Hypervisor Call Handling

The system supports both PSCI (Power State Coordination Interface) calls and general hypervisor calls through the HVC instruction. The handle_psci_call function recognizes PSCI function IDs in specific ranges:

  • 32-bit PSCI: Function IDs 0x8400_0000..=0x8400_001F
  • 64-bit PSCI: Function IDs 0xC400_0000..=0xC400_001F

Supported PSCI operations include:

  • PSCI_FN_CPU_OFFAxVCpuExitReason::CpuDown
  • PSCI_FN_CPU_ONAxVCpuExitReason::CpuUp
  • PSCI_FN_SYSTEM_OFFAxVCpuExitReason::SystemDown

Non-PSCI HVC calls are treated as general hypervisor calls with the call number in x0 and arguments in x1-x6.

Sources: src/exception.rs(L220 - L254)  src/exception.rs(L80 - L102) 

SMC Call Handling

Secure Monitor Calls (SMC) are either handled as PSCI calls or forwarded directly to the ARM Trusted Firmware (ATF). The handle_smc64_exception function first checks for PSCI compatibility, then uses the smc_call function to forward non-PSCI SMCs to the secure world.

Sources: src/exception.rs(L256 - L271)  src/exception.rs(L104 - L109) 

Integration with Context Management

The exception handling system integrates tightly with the VCPU context management through several mechanisms:

Context Integration Points

flowchart TD
subgraph subGraph2["Exception Exit"]
    RESTORE_MACRO["RESTORE_REGS_INTO_EL1"]
    CONTEXT_ENTRY["context_vm_entry"]
    ERET_INST["eret instruction"]
end
subgraph subGraph1["Handler Processing"]
    CONTEXT_PARAM["ctx: &mut TrapFrame"]
    GPR_ACCESS["ctx.gpr[n] access"]
    PC_UPDATE["ctx.set_exception_pc()"]
end
subgraph subGraph0["Exception Entry"]
    SAVE_MACRO["SAVE_REGS_FROM_EL1"]
    TRAP_FRAME_SAVE["TrapFrame Population"]
end

CONTEXT_ENTRY --> ERET_INST
CONTEXT_PARAM --> GPR_ACCESS
CONTEXT_PARAM --> PC_UPDATE
GPR_ACCESS --> RESTORE_MACRO
PC_UPDATE --> RESTORE_MACRO
RESTORE_MACRO --> CONTEXT_ENTRY
SAVE_MACRO --> TRAP_FRAME_SAVE
TRAP_FRAME_SAVE --> CONTEXT_PARAM

The system maintains guest CPU state through the TrapFrame structure, allowing handlers to:

  • Read guest register values for processing hypervisor calls and data aborts
  • Update the program counter when advancing past trapped instructions
  • Modify register contents for return values (e.g., MMIO read results)

Sources: src/exception.S(L1 - L30)  src/exception.S(L32 - L58)  src/exception.rs(L75 - L77)  src/exception.rs(L105 - L107) 

Assembly Exception Vectors

Relevant source files

This document covers the low-level assembly exception vector table and context switching implementation that forms the foundation of the hypervisor's exception handling system. The assembly code provides the first-level exception entry points, register preservation, and routing to higher-level exception handlers.

For information about the high-level exception dispatch logic and handler implementations, see High-Level Exception Handling. For details about the register parsing and analysis utilities, see Exception Analysis and Utilities.

Exception Vector Table Structure

The exception vector table exception_vector_base_vcpu implements the AArch64 exception vector layout with 16 entries, each aligned to 128-byte boundaries. The table handles exceptions from different execution states and privilege levels.

flowchart TD
subgraph exception_vector_base_vcpu["exception_vector_base_vcpu"]
    subgraph subGraph3["Lower EL AArch32 (0x600-0x7FF)"]
        LE32_SYNC["0x600: INVALID_EXCP_EL2 0 3"]
        LE32_IRQ["0x680: INVALID_EXCP_EL2 1 3"]
        LE32_FIQ["0x700: INVALID_EXCP_EL2 2 3"]
        LE32_ERR["0x780: INVALID_EXCP_EL2 3 3"]
    end
    subgraph subGraph2["Lower EL AArch64 (0x400-0x5FF)"]
        LE64_SYNC["0x400: HANDLE_LOWER_SYNC_VCPU"]
        LE64_IRQ["0x480: HANDLE_LOWER_IRQ_VCPU"]
        LE64_FIQ["0x500: INVALID_EXCP_EL2 2 2"]
        LE64_ERR["0x580: INVALID_EXCP_EL2 3 2"]
    end
    subgraph subGraph1["Current EL with SP_ELx (0x200-0x3FF)"]
        CEX_SYNC["0x200: HANDLE_CURRENT_SYNC"]
        CEX_IRQ["0x280: HANDLE_CURRENT_IRQ"]
        CEX_FIQ["0x300: INVALID_EXCP_EL2 2 1"]
        CEX_ERR["0x380: INVALID_EXCP_EL2 3 1"]
    end
    subgraph subGraph0["Current EL with SP_EL0 (0x000-0x1FF)"]
        CE0_SYNC["0x000: INVALID_EXCP_EL2 0 0"]
        CE0_IRQ["0x080: INVALID_EXCP_EL2 1 0"]
        CE0_FIQ["0x100: INVALID_EXCP_EL2 2 0"]
        CE0_ERR["0x180: INVALID_EXCP_EL2 3 0"]
    end
end
CURR_SYNC["current_el_sync_handler"]
CURR_IRQ["current_el_irq_handler"]
VMEXIT["vmexit_trampoline(exception_sync)"]
VMEXIT_IRQ["vmexit_trampoline(exception_irq)"]
INVALID["invalid_exception_el2"]

CE0_SYNC --> INVALID
CEX_IRQ --> CURR_IRQ
CEX_SYNC --> CURR_SYNC
LE32_SYNC --> INVALID
LE64_IRQ --> VMEXIT_IRQ
LE64_SYNC --> VMEXIT

Sources: src/exception.S(L105 - L131) 

The table supports four types of exceptions for each execution state:

  • Synchronous exceptions (offset 0x000): System calls, data aborts, instruction aborts
  • IRQ interrupts (offset 0x080): Asynchronous hardware interrupts
  • FIQ interrupts (offset 0x100): Fast interrupt requests
  • System Error/SError (offset 0x180): Asynchronous system errors

Register Context Management

The assembly code implements comprehensive register preservation using two primary macros that save and restore the complete processor state during exception handling.

Register Save Operation

flowchart TD
subgraph subGraph0["Register Storage Layout"]
    X0_X1["[sp + 0]: x0, x1"]
    X2_X3["[sp + 16]: x2, x3"]
    DOTS["..."]
    X28_X29["[sp + 224]: x28, x29"]
    X30_SP["[sp + 240]: x30, sp_el0"]
    ELR_SPSR["[sp + 256]: elr_el2, spsr_el2"]
end
ENTRY["Exception Entry"]
SAVE_START["SAVE_REGS_FROM_EL1"]
SP_ADJ["sub sp, sp, 34 * 8"]
GPR_SAVE["Save GPRs x0-x29"]
SPECIAL_SAVE["Save x30, sp_el0"]
ELR_SAVE["Save elr_el2, spsr_el2"]
HANDLER["Jump to Exception Handler"]

ELR_SAVE --> ELR_SPSR
ELR_SAVE --> HANDLER
ENTRY --> SAVE_START
GPR_SAVE --> SPECIAL_SAVE
GPR_SAVE --> X0_X1
GPR_SAVE --> X28_X29
GPR_SAVE --> X2_X3
SAVE_START --> SP_ADJ
SPECIAL_SAVE --> ELR_SAVE
SPECIAL_SAVE --> X30_SP
SP_ADJ --> GPR_SAVE

Sources: src/exception.S(L1 - L30) 

Register Restore Operation

The RESTORE_REGS_INTO_EL1 macro reverses the save operation, restoring all registers and returning to the guest execution context:

Restore OrderRegistersAssembly Instructions
1. Exception Stateelr_el2,spsr_el2msr elr_el2, x10; msr spsr_el2, x11
2. Stack Pointersp_el0msr sp_el0, x9
3. General Purposex0-x29ldpinstructions in reverse order
4. ReturnStack adjustmentadd sp, sp, 34 * 8

Sources: src/exception.S(L32 - L58) 

Exception Routing and Handler Dispatch

The exception vectors route different exception types to specialized handlers based on the source execution level and exception characteristics.

flowchart TD
subgraph subGraph2["Handler Functions"]
    VMEXIT_SYNC["vmexit_trampoline(exception_sync)"]
    VMEXIT_IRQ["vmexit_trampoline(exception_irq)"]
    CURRENT_SYNC["current_el_sync_handler"]
    CURRENT_IRQ["current_el_irq_handler"]
    INVALID_HANDLER["invalid_exception_el2"]
end
subgraph subGraph1["Assembly Macros"]
    HANDLE_LOWER_SYNC["HANDLE_LOWER_SYNC_VCPU"]
    HANDLE_LOWER_IRQ["HANDLE_LOWER_IRQ_VCPU"]
    HANDLE_CURRENT_SYNC["HANDLE_CURRENT_SYNC"]
    HANDLE_CURRENT_IRQ["HANDLE_CURRENT_IRQ"]
    INVALID_EXCP["INVALID_EXCP_EL2"]
end
subgraph subGraph0["Exception Types"]
    GUEST_SYNC["Guest Sync Exception"]
    GUEST_IRQ["Guest IRQ"]
    HOST_SYNC["Host EL2 Sync"]
    HOST_IRQ["Host EL2 IRQ"]
    INVALID["Invalid Exception"]
end

GUEST_IRQ --> HANDLE_LOWER_IRQ
GUEST_SYNC --> HANDLE_LOWER_SYNC
HANDLE_CURRENT_IRQ --> CURRENT_IRQ
HANDLE_CURRENT_SYNC --> CURRENT_SYNC
HANDLE_LOWER_IRQ --> VMEXIT_IRQ
HANDLE_LOWER_SYNC --> VMEXIT_SYNC
HOST_IRQ --> HANDLE_CURRENT_IRQ
HOST_SYNC --> HANDLE_CURRENT_SYNC
INVALID --> INVALID_EXCP
INVALID_EXCP --> INVALID_HANDLER

Sources: src/exception.S(L71 - L101) 

Handler Macro Implementations

Each handler macro follows a consistent pattern:

  1. Alignment: .p2align 7 ensures 128-byte vector alignment
  2. Context Save: SAVE_REGS_FROM_EL1 preserves processor state
  3. Parameter Setup: Load appropriate parameters into registers
  4. Handler Call: Branch to the corresponding C handler function
  5. Return: Jump to common return path or let handler manage return

The HANDLE_LOWER_SYNC_VCPU and HANDLE_LOWER_IRQ_VCPU macros use parameterized calls to vmexit_trampoline with exception type constants {exception_sync} and {exception_irq}.

Sources: src/exception.S(L87 - L101) 

VM Entry and Exit Points

The context_vm_entry function provides the entry point for transitioning from host context back to guest execution, implementing the counterpart to exception-based VM exits.

sequenceDiagram
    participant HostHypervisor as "Host/Hypervisor"
    participant context_vm_entry as "context_vm_entry"
    participant RESTORE_REGS_INTO_EL1 as "RESTORE_REGS_INTO_EL1"
    participant GuestVM as "Guest VM"

    HostHypervisor ->> context_vm_entry: Call with host_stack_top in x0
    context_vm_entry ->> context_vm_entry: Set sp = x0 (host_stack_top)
    context_vm_entry ->> context_vm_entry: Adjust sp to TrapFrame base
    context_vm_entry ->> RESTORE_REGS_INTO_EL1: Execute macro
    RESTORE_REGS_INTO_EL1 ->> RESTORE_REGS_INTO_EL1: Restore elr_el2, spsr_el2
    RESTORE_REGS_INTO_EL1 ->> RESTORE_REGS_INTO_EL1: Restore sp_el0
    RESTORE_REGS_INTO_EL1 ->> RESTORE_REGS_INTO_EL1: Restore GPRs x0-x29
    RESTORE_REGS_INTO_EL1 ->> RESTORE_REGS_INTO_EL1: Adjust sp back to host_stack_top
    RESTORE_REGS_INTO_EL1 ->> GuestVM: eret instruction
    Note over GuestVM: Guest execution resumes
    GuestVM -->> context_vm_entry: Exception occurs
    Note over context_vm_entry: Exception vector called
    context_vm_entry ->> HostHypervisor: Eventually returns to host

Sources: src/exception.S(L132 - L141) 

The VM entry process:

  1. Stack Setup: Sets stack pointer to host_stack_top address passed in x0
  2. Frame Positioning: Adjusts stack pointer to point to guest TrapFrame base
  3. Register Restoration: Uses RESTORE_REGS_INTO_EL1 to restore guest state
  4. Guest Entry: Executes eret to return to guest execution at EL1

The .Lexception_return_el2 label provides a common return path used by all exception handlers, ensuring consistent state restoration regardless of the exception type or handler complexity.

Sources: src/exception.S(L138 - L141) 

Exception Analysis and Utilities

Relevant source files

This document covers the exception analysis and parsing utilities provided by the exception_utils.rs module. These utilities form a critical layer between the low-level assembly exception vectors and high-level exception handlers, providing functions to extract and interpret information from AArch64 exception syndrome registers, perform address translations, and manage register context during exception handling.

For information about the assembly exception vectors that capture exceptions, see Assembly Exception Vectors. For details about high-level exception dispatch and handling logic, see High-Level Exception Handling.

Exception Syndrome Register Analysis

The exception analysis system provides a comprehensive interface for reading and interpreting the Exception Syndrome Register (ESR_EL2), which contains detailed information about the cause and nature of exceptions that occur during guest execution.

Exception Syndrome Register Reading Functions

flowchart TD
subgraph subGraph1["Exception Classification"]
    EC["Exception Class (EC)"]
    ISS["Instruction Specific Syndrome (ISS)"]
    IL["Instruction Length (IL)"]
end
subgraph subGraph0["Core Reading Functions"]
    exception_esr["exception_esr()"]
    exception_class["exception_class()"]
    exception_class_value["exception_class_value()"]
    exception_iss["exception_iss()"]
end
ESR_EL2["ESR_EL2 Hardware Register"]
DataAbort["Data Abort Analysis"]
SystemReg["System Register Analysis"]
HVC["HVC Call Analysis"]

EC --> DataAbort
EC --> HVC
EC --> SystemReg
ESR_EL2 --> exception_class
ESR_EL2 --> exception_class_value
ESR_EL2 --> exception_esr
ESR_EL2 --> exception_iss
ISS --> DataAbort
ISS --> SystemReg
exception_class --> EC
exception_esr --> IL
exception_iss --> ISS

The system provides several core functions for accessing different fields of the ESR_EL2 register:

Sources: src/exception_utils.rs(L7 - L32)  src/exception_utils.rs(L165 - L172) 

Instruction Analysis

The system provides utilities for analyzing instruction-related information from exceptions:

FunctionPurposeReturn Value
exception_instruction_length()Determines if instruction is 16-bit or 32-bit0 for 16-bit, 1 for 32-bit
exception_next_instruction_step()Calculates step size for instruction pointer advancement2 for 16-bit, 4 for 32-bit

Sources: src/exception_utils.rs(L144 - L163) 

Fault Address Translation System

The fault address translation system handles the complex process of converting virtual fault addresses to physical addresses using the ARM Address Translation (AT) instructions and register coordination.

Address Translation Flow

flowchart TD
subgraph subGraph2["Final Address Calculation"]
    Page_Offset["FAR & 0xfff"]
    Page_Number["HPFAR << 8"]
    Final_GPA["GuestPhysAddr"]
end
subgraph subGraph1["Translation Process"]
    arm_at["arm_at!(s1e1r, far)"]
    translate_far_to_hpfar["translate_far_to_hpfar()"]
    par_to_far["par_to_far() helper"]
    exception_hpfar["exception_hpfar()"]
end
subgraph subGraph0["Translation Decision Logic"]
    S1PTW_Check["Check ESR_ELx_S1PTW bit"]
    Permission_Check["exception_data_abort_is_permission_fault()"]
    Translation_Needed["Translation Needed?"]
end
FAR_EL2["FAR_EL2: Fault Address Register"]
HPFAR_EL2["HPFAR_EL2: Hypervisor Physical Fault Address Register"]
PAR_EL1["PAR_EL1: Physical Address Register"]

FAR_EL2 --> Page_Offset
FAR_EL2 --> S1PTW_Check
PAR_EL1 --> par_to_far
Page_Number --> Final_GPA
Page_Offset --> Final_GPA
Permission_Check --> Translation_Needed
S1PTW_Check --> Permission_Check
Translation_Needed --> exception_hpfar
Translation_Needed --> translate_far_to_hpfar
arm_at --> PAR_EL1
exception_hpfar --> Page_Number
par_to_far --> Page_Number
translate_far_to_hpfar --> arm_at

The exception_fault_addr() function src/exception_utils.rs(L133 - L142)  implements a sophisticated address translation algorithm that:

  1. Reads the FAR_EL2 register to get the virtual fault address
  2. Determines whether address translation is needed based on the S1PTW bit and fault type
  3. Either performs address translation using translate_far_to_hpfar() or directly reads HPFAR_EL2
  4. Combines the page offset from FAR_EL2 with the page number from HPFAR_EL2

The translate_far_to_hpfar() function src/exception_utils.rs(L92 - L113)  uses the ARM Address Translation instruction to convert a virtual address to physical, handling the PAR_EL1 register state and error conditions.

Sources: src/exception_utils.rs(L34 - L142) 

Data Abort Exception Analysis

The system provides comprehensive analysis capabilities for data abort exceptions, which are among the most common exceptions in virtualization scenarios involving MMIO operations.

Data Abort Analysis Functions

flowchart TD
subgraph subGraph1["Access Pattern Analysis"]
    exception_data_abort_access_is_write["exception_data_abort_access_is_write()"]
    exception_data_abort_access_width["exception_data_abort_access_width()"]
    exception_data_abort_access_reg["exception_data_abort_access_reg()"]
    exception_data_abort_access_reg_width["exception_data_abort_access_reg_width()"]
    exception_data_abort_access_is_sign_ext["exception_data_abort_access_is_sign_ext()"]
end
subgraph subGraph0["Data Abort Classification"]
    exception_data_abort_is_permission_fault["exception_data_abort_is_permission_fault()"]
    exception_data_abort_is_translate_fault["exception_data_abort_is_translate_fault()"]
    exception_data_abort_handleable["exception_data_abort_handleable()"]
end
ESR_ISS["ESR_EL2.ISS Field"]

ESR_ISS --> exception_data_abort_access_is_sign_ext
ESR_ISS --> exception_data_abort_access_is_write
ESR_ISS --> exception_data_abort_access_reg
ESR_ISS --> exception_data_abort_access_reg_width
ESR_ISS --> exception_data_abort_access_width
ESR_ISS --> exception_data_abort_handleable
ESR_ISS --> exception_data_abort_is_permission_fault
ESR_ISS --> exception_data_abort_is_translate_fault

The data abort analysis functions extract specific information from the ISS field of ESR_EL2:

FunctionBit FieldsPurpose
exception_data_abort_is_permission_fault()ISS[5:0] & 0xF0 == 12Identifies permission faults vs translation faults
exception_data_abort_is_translate_fault()ISS[5:0] & 0xF0 == 4Identifies translation faults
exception_data_abort_access_is_write()ISS[6]Determines read vs write access
exception_data_abort_access_width()ISS[23:22]Access width (1, 2, 4, or 8 bytes)
exception_data_abort_access_reg()ISS[20:16]Register index (0-31)
exception_data_abort_handleable()ISS[24] | !ISS[10]Determines if abort can be handled

Sources: src/exception_utils.rs(L197 - L255) 

System Register Access Analysis

The system provides specialized parsing for system register access exceptions, which occur when guests attempt to access system registers that require hypervisor intervention.

System Register Parsing Functions

flowchart TD
subgraph subGraph1["Parsing Functions"]
    exception_sysreg_direction_write["exception_sysreg_direction_write()"]
    exception_sysreg_gpr["exception_sysreg_gpr()"]
    exception_sysreg_addr["exception_sysreg_addr()"]
end
subgraph subGraph0["System Register ISS Format"]
    ISS_Field["ESR_EL2.ISS[24:0]"]
    Op0["Op0[21:20]"]
    Op2["Op2[19:17]"]
    Op1["Op1[16:14]"]
    CRn["CRn[13:10]"]
    CRm["CRm[4:1]"]
    Direction["Direction[0]"]
    RT["RT[9:5]"]
end

CRm --> exception_sysreg_addr
CRn --> exception_sysreg_addr
Direction --> exception_sysreg_direction_write
ISS_Field --> CRm
ISS_Field --> CRn
ISS_Field --> Direction
ISS_Field --> Op0
ISS_Field --> Op1
ISS_Field --> Op2
ISS_Field --> RT
Op0 --> exception_sysreg_addr
Op1 --> exception_sysreg_addr
Op2 --> exception_sysreg_addr
RT --> exception_sysreg_gpr

The system register analysis functions provide:

The exception_sysreg_addr() function implements the ARMv8 system register encoding format, combining Op0, Op1, Op2, CRn, and CRm fields into a unique register identifier.

Sources: src/exception_utils.rs(L174 - L195) 

Register Context Management Macros

The system provides assembly macros for managing host register context during exception handling, ensuring proper preservation and restoration of callee-saved registers.

Context Switching Macros

flowchart TD
subgraph subGraph1["Exception Flow Integration"]
    VM_Entry["VM Entry from Aarch64VCpu::run()"]
    VM_Exit["VM Exit to Exception Handler"]
    Return_to_Host["Return to Aarch64VCpu::run()"]
end
subgraph subGraph0["Host Context Management"]
    Host_Registers["Host Callee-Saved Registersx19-x30"]
    Stack_Frame["Stack Frame12 * 8 bytes"]
    save_regs_to_stack["save_regs_to_stack!"]
    restore_regs_from_stack["restore_regs_from_stack!"]
end
Exception_Processing["Exception Processing"]

Exception_Processing --> restore_regs_from_stack
Host_Registers --> Return_to_Host
Host_Registers --> Stack_Frame
Stack_Frame --> Host_Registers
VM_Entry --> save_regs_to_stack
VM_Exit --> Exception_Processing
restore_regs_from_stack --> Stack_Frame
save_regs_to_stack --> Host_Registers

The register context management system consists of two complementary macros:

save_regs_to_stack! src/exception_utils.rs(L278 - L289) :

  • Allocates 96 bytes (12 * 8) on the stack
  • Saves registers x19-x30 using stp (store pair) instructions
  • Used during VM entry to preserve host state

restore_regs_from_stack! src/exception_utils.rs(L300 - L311) :

  • Restores registers x19-x30 using ldp (load pair) instructions
  • Deallocates the 96-byte stack frame
  • Used during VM exit to restore host state

These macros ensure that the host's callee-saved registers are properly preserved across guest execution, maintaining the calling convention for the Aarch64VCpu::run() function.

Sources: src/exception_utils.rs(L267 - L311) 

Integration with Exception Handling Pipeline

The exception utilities integrate seamlessly with the broader exception handling system, providing the analytical foundation for exception dispatch and handling decisions.

Utility Function Usage Pattern

flowchart TD
subgraph subGraph2["Exception Handlers"]
    handle_data_abort["handle_data_abort()"]
    handle_system_register["handle_system_register()"]
    handle_psci_call["handle_psci_call()"]
end
subgraph subGraph1["Exception Type Specific Analysis"]
    Data_Abort["Data Abort Functions"]
    System_Register["System Register Functions"]
    HVC_Analysis["HVC Call Analysis"]
end
subgraph subGraph0["Exception Analysis Phase"]
    exception_esr["exception_esr()"]
    exception_class["exception_class()"]
    exception_fault_addr["exception_fault_addr()"]
end
Exception_Vector["Assembly Exception Vector"]
Exception_Context["Exception Context Capture"]

Data_Abort --> exception_fault_addr
Data_Abort --> handle_data_abort
Exception_Context --> exception_esr
Exception_Vector --> Exception_Context
HVC_Analysis --> handle_psci_call
System_Register --> handle_system_register
exception_class --> Data_Abort
exception_class --> HVC_Analysis
exception_class --> System_Register
exception_esr --> exception_class

The utilities serve as the foundational layer that enables higher-level exception handlers to make informed decisions about how to process different types of exceptions, providing both classification and detailed analysis capabilities.

Sources: src/exception_utils.rs(L1 - L311) 

High-Level Exception Handling

Relevant source files

This document covers the high-level exception dispatch logic and handler implementations that process exceptions after they have been captured by the assembly exception vectors and analyzed by the parsing utilities. The high-level handlers determine the appropriate response to each exception type and return structured exit reasons to the VCPU management layer.

For information about the low-level assembly exception vector table and context switching, see Assembly Exception Vectors. For details about register parsing and exception analysis utilities, see Exception Analysis and Utilities.

Exception Dispatch Architecture

The high-level exception handling system centers around the handle_exception_sync function, which serves as the main dispatcher for synchronous exceptions from guest VMs. This function examines the Exception Class (EC) field in ESR_EL2 to determine the exception type and routes it to the appropriate specialized handler.

Exception Dispatch Flow

flowchart TD
A["handle_exception_sync(ctx: &mut TrapFrame)"]
B["exception_class()"]
C["Exception Class"]
D["handle_data_abort()"]
E["handle_psci_call()"]
F["Return CpuUp/CpuDown/SystemDown"]
G["Return Hypercall"]
H["handle_system_register()"]
I["handle_smc64_exception()"]
J["panic!()"]
K["AxVCpuExitReason::MmioRead/Write"]
L["AxVCpuExitReason::SysRegRead/Write"]
M["AxVCpuExitReason::Nothing or PSCI"]
N["Return to Aarch64VCpu::run()"]

A --> B
B --> C
C --> D
C --> E
C --> H
C --> I
C --> J
D --> K
E --> F
E --> G
F --> N
G --> N
H --> L
I --> M
K --> N
L --> N
M --> N

Exception Class Mapping:

Exception ClassHandler FunctionExit Reason
DataAbortLowerELhandle_data_abortMmioRead/MmioWrite
HVC64handle_psci_callor hypercallCpuUp/CpuDown/SystemDown/Hypercall
TrappedMsrMrshandle_system_registerSysRegRead/SysRegWrite
SMC64handle_smc64_exceptionNothingor PSCI

Sources: src/exception.rs(L72 - L126) 

Data Abort Handler

The handle_data_abort function processes memory access exceptions from guest VMs, typically resulting from MMIO operations to unmapped or restricted memory regions.

Data Abort Processing Logic

flowchart TD
A["handle_data_abort(context_frame)"]
B["exception_fault_addr()"]
C["exception_data_abort_access_width()"]
D["exception_data_abort_access_is_write()"]
E["exception_data_abort_access_reg()"]
F["Fault Analysis"]
G["exception_data_abort_handleable()"]
H["panic!()"]
I["exception_data_abort_is_translate_fault()"]
J["exception_data_abort_is_permission_fault()"]
K["Return AxError::Unsupported"]
L["panic!()"]
M["Write Access?"]
N["AxVCpuExitReason::MmioWrite"]
O["AxVCpuExitReason::MmioRead"]
P["Extract data from context_frame.gpr[reg]"]
Q["Return reg and reg_width for host to fill"]

A --> B
A --> C
A --> D
A --> E
B --> F
C --> F
D --> F
E --> F
F --> G
G --> H
G --> I
I --> J
I --> M
J --> K
J --> L
M --> N
M --> O
N --> P
O --> Q

The handler extracts key information from the exception:

  • Fault Address: Guest physical address that caused the exception
  • Access Width: Size of the memory operation (1, 2, 4, or 8 bytes)
  • Direction: Read or write operation
  • Register: Which guest register was involved in the access

Sources: src/exception.rs(L128 - L182)  src/exception_utils.rs(L211 - L254) 

System Register Handler

The handle_system_register function processes trapped system register accesses when the guest VM attempts to read or write control registers that are managed by the hypervisor.

System Register Access Processing

flowchart TD
A["handle_system_register(context_frame)"]
B["ESR_EL2.read(ESR_EL2::ISS)"]
C["exception_sysreg_addr(iss)"]
D["exception_sysreg_direction_write(iss)"]
E["exception_sysreg_gpr(iss)"]
F["Write Access?"]
G["AxVCpuExitReason::SysRegWrite"]
H["AxVCpuExitReason::SysRegRead"]
I["Extract value from context_frame.gpr[reg]"]
J["Return reg for host to populate"]
K["Advance exception_pc by instruction step"]

A --> B
B --> C
B --> D
B --> E
C --> F
D --> F
E --> F
F --> G
F --> H
G --> I
H --> J
I --> K
J --> K

The system register address encoding follows the ARM ISS format: <op0><op2><op1><CRn>00000<CRm>0, allowing the hypervisor to identify exactly which system register the guest attempted to access.

Sources: src/exception.rs(L184 - L211)  src/exception_utils.rs(L174 - L195) 

PSCI Support Implementation

The Power State Coordination Interface (PSCI) support enables guest VMs to perform power management operations like CPU power on/off and system shutdown through standardized function calls.

PSCI Call Handling

flowchart TD
A["handle_psci_call(ctx)"]
B["Check ctx.gpr[0] function ID"]
C["Function Range"]
D["32-bit PSCI range"]
E["64-bit PSCI range"]
F["Return None (not PSCI)"]
G["Function Offset"]
H["AxVCpuExitReason::CpuDown"]
I["AxVCpuExitReason::CpuUp"]
J["AxVCpuExitReason::SystemDown"]
K["Return None (forward to ATF)"]
L["Extract state from ctx.gpr[1]"]
M["Extract target_cpu, entry_point, arg"]
N["No additional parameters"]
O["target_cpu: ctx.gpr[1]entry_point: GuestPhysAddr(ctx.gpr[2])arg: ctx.gpr[3]"]

A --> B
B --> C
C --> D
C --> E
C --> F
D --> G
E --> G
G --> H
G --> I
G --> J
G --> K
H --> L
I --> M
J --> N
M --> O

PSCI Function Support:

Function IDNameParametersExit Reason
0x2CPU_OFFstateCpuDown
0x3CPU_ONtarget_cpu,entry_point,argCpuUp
0x8SYSTEM_OFFNoneSystemDown
0x0, 0x5, 0x9Version/Migrate/ResetVariousForwarded to ATF

Sources: src/exception.rs(L213 - L254) 

SMC Handler and ATF Integration

The handle_smc64_exception function processes Secure Monitor Calls (SMC), which are used to communicate with secure firmware like ARM Trusted Firmware (ATF).

SMC Processing Flow

flowchart TD
A["handle_smc64_exception(ctx)"]
B["handle_psci_call(ctx)"]
C["Return PSCI exit reason"]
D["Forward to ATF"]
E["crate::smc::smc_call()"]
F["Update ctx.gpr[0-3] with ATF response"]
G["AxVCpuExitReason::Nothing"]
H["Advance exception_pc"]

A --> B
B --> C
B --> D
C --> H
D --> E
E --> F
F --> G
G --> H

The SMC handler first checks if the call is a PSCI operation. If not, it forwards the call directly to the ARM Trusted Firmware using the smc_call function, which executes the actual SMC instruction and returns the results to the guest VM registers.

Sources: src/exception.rs(L256 - L271) 

Current EL Exception Handlers

The system also handles exceptions that occur at the current exception level (EL2) while the hypervisor itself is running.

Current EL Handler Architecture

flowchart TD
A["Current EL Exceptions"]
B["Exception Type"]
C["current_el_irq_handler(tf)"]
D["current_el_sync_handler(tf)"]
E["crate::pcpu::IRQ_HANDLER.current_ref_raw()"]
F["Dispatch to host OS IRQ handler"]
G["panic!() with TrapFrame details"]
H["Return to hypervisor execution"]
I["System halt"]

A --> B
B --> C
B --> D
C --> E
D --> G
E --> F
F --> H
G --> I

The IRQ handler delegates to the host OS interrupt handler registered during Aarch64PerCpu::new(), while synchronous exceptions at EL2 are considered fatal errors and cause a system panic.

Sources: src/exception.rs(L273 - L290) 

VM Exit Trampoline Mechanism

The vmexit_trampoline function provides the critical bridge between guest exception handling and returning control to the host VCPU management code.

Stack Layout and Transition

flowchart TD
A["Guest Exception Occurs"]
B["SAVE_REGS_FROM_EL1 macro saves to stack"]
C["Stack pointer at TrapFrame base"]
D["vmexit_trampoline()"]
E["add x9, sp, 34 * 8  // Skip TrapFrame"]
F["ldr x10, [x9]       // Load host_stack_top"]
G["mov sp, x10         // Restore host stack"]
H["restore_regs_from_stack!()"]
I["ret  // Return to Aarch64VCpu::run()"]
J["Host continues with AxVCpuExitReason"]

A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
H --> I
I --> J

Memory Layout During VM Exit:

Stack Layout:
┌─────────────────┐ ← sp (initially)
│   TrapFrame     │ (34 * 8 bytes)
│   (guest regs)  │
├─────────────────┤ ← sp + 34*8
│ host_stack_top  │ (pointer to host stack)
├─────────────────┤
│ Host saved regs │ (callee-saved x19-x30)
│                 │
└─────────────────┘ ← host_stack_top

Sources: src/exception.rs(L292 - L341) 

Integration with VCPU Lifecycle

The high-level exception handlers integrate seamlessly with the VCPU execution cycle by returning structured AxVCpuExitReason values that inform the host hypervisor about what action triggered the VM exit.

Exit Reason Types and Usage

Exit ReasonTriggered ByHost Action Required
MmioRead/MmioWriteGuest MMIO accessEmulate device operation
SysRegRead/SysRegWriteTrapped system registerEmulate register access
HypercallGuest HVC instructionProcess hypercall
CpuUp/CpuDownPSCI power managementManage CPU state
SystemDownPSCI system shutdownShutdown guest system
NothingHandled internallyContinue guest execution

This structured approach allows the host hypervisor to implement device emulation, power management, and other virtualization services based on the specific needs indicated by each exit reason.

Sources: src/exception.rs(L49 - L125) 

System Integration

Relevant source files

This document covers the integration interfaces and hardware abstraction mechanisms that enable the arm_vcpu hypervisor to work with host systems, hardware platforms, and external components. It focuses on how the core virtualization functionality interfaces with the broader hypervisor ecosystem and underlying hardware.

For detailed information about the Secure Monitor Call interface, see Secure Monitor Interface. For platform-specific hardware support details, see Hardware Abstraction and Platform Support.

Integration Architecture Overview

The arm_vcpu crate serves as a hardware-specific implementation that integrates with higher-level hypervisor frameworks through well-defined interfaces. The integration occurs at multiple layers, from hardware abstraction to external crate dependencies.

Integration Architecture with Code Entities

flowchart TD
subgraph subGraph3["Hardware/Firmware Interface"]
    SMC_IF["smc.rssmc_call()"]
    HW_DETECT["Hardware ExtensionDetection"]
    SECURE_FW["Secure Firmware(ATF/EL3)"]
end
subgraph subGraph2["External Crate Dependencies"]
    AXVCPU_CRATE["axvcpu crateCore VCPU traits"]
    AXADDR_CRATE["axaddrspace crateAddress space mgmt"]
    PERCPU_CRATE["percpu cratePer-CPU storage"]
    AARCH64_CRATE["aarch64-cpu crateRegister definitions"]
end
subgraph subGraph1["arm_vcpu Public Interface"]
    LIB["lib.rs"]
    VCPU_EXPORT["Aarch64VCpu"]
    PCPU_EXPORT["Aarch64PerCpu"]
    CONFIG_EXPORT["Aarch64VCpuCreateConfig"]
    TRAPFRAME_EXPORT["TrapFrame"]
    HW_SUPPORT["has_hardware_support()"]
end
subgraph subGraph0["Host Hypervisor System"]
    HOST["Host OS/Hypervisor"]
    HAL_TRAIT["AxVCpuHal trait(from axvcpu crate)"]
end

HAL_TRAIT --> VCPU_EXPORT
HOST --> HAL_TRAIT
HW_SUPPORT --> HW_DETECT
LIB --> AARCH64_CRATE
LIB --> CONFIG_EXPORT
LIB --> HW_SUPPORT
LIB --> PCPU_EXPORT
LIB --> TRAPFRAME_EXPORT
LIB --> VCPU_EXPORT
PCPU_EXPORT --> PERCPU_CRATE
SMC_IF --> SECURE_FW
VCPU_EXPORT --> AXADDR_CRATE
VCPU_EXPORT --> AXVCPU_CRATE

The integration architecture centers around the public interface defined in src/lib.rs which exports the core components that host hypervisors use to manage AArch64 virtual CPUs.

Sources: src/lib.rs(L17 - L18)  src/lib.rs(L21)  src/lib.rs(L24 - L32) 

Hardware Abstraction Layer Interface

The arm_vcpu crate implements hardware-specific functionality while depending on abstract interfaces for integration with higher-level hypervisor components. The primary abstraction mechanism is the AxVCpuHal trait from the axvcpu crate.

Hardware Abstraction Layer Structure

flowchart TD
subgraph subGraph2["Hardware Platform"]
    VIRT_EXT["AArch64 VirtualizationExtensions (VHE/EL2)"]
    CPU_REGS["aarch64-cpu crateRegister definitions"]
    PLATFORM_HW["Platform HardwareFeatures"]
end
subgraph subGraph1["arm_vcpu Implementation"]
    AARCH64_VCPU["Aarch64VCpuimplements AxVCpuHal"]
    AARCH64_PCPU["Aarch64PerCpuPer-CPU management"]
    HW_SUPPORT_FUNC["has_hardware_support()Platform detection"]
end
subgraph subGraph0["Abstract Interface Layer"]
    AXVCPU_HAL["AxVCpuHal trait(axvcpu crate)"]
    AXVCPU_CORE["AxVCpu core traits(axvcpu crate)"]
end

AARCH64_PCPU --> VIRT_EXT
AARCH64_VCPU --> AARCH64_PCPU
AARCH64_VCPU --> CPU_REGS
AXVCPU_CORE --> AARCH64_VCPU
AXVCPU_HAL --> AARCH64_VCPU
HW_SUPPORT_FUNC --> PLATFORM_HW

The hardware abstraction enables the arm_vcpu implementation to provide AArch64-specific virtualization functionality while remaining compatible with generic hypervisor frameworks that depend on the abstract AxVCpuHal interface.

Sources: src/lib.rs(L17 - L18)  src/lib.rs(L24 - L32) 

External Dependency Integration

The arm_vcpu crate integrates with several external crates that provide fundamental hypervisor infrastructure. Each dependency serves a specific role in the overall system architecture.

CratePurposeIntegration Point
axvcpuCore VCPU traits and interfacesAarch64VCpuimplementsAxVCpuHal
axaddrspaceAddress space managementUsed byAarch64VCpufor memory virtualization
percpuPer-CPU data storageUsed byAarch64PerCpufor CPU-local state
aarch64-cpuAArch64 register definitionsUsed throughout for hardware register access
logLogging infrastructureUsed for debug and error reporting

External Dependency Flow

flowchart TD
subgraph subGraph1["External Crate APIs"]
    AXVCPU_API["axvcpu::AxVCpuHalHardware abstraction"]
    AXADDR_API["axaddrspaceMemory management"]
    PERCPU_API["percpuPer-CPU storage"]
    AARCH64_API["aarch64_cpuRegister access"]
    LOG_API["log macrosdebug!, warn!, error!"]
end
subgraph subGraph0["arm_vcpu Components"]
    VCPU_IMPL["src/vcpu.rsAarch64VCpu"]
    PCPU_IMPL["src/pcpu.rsAarch64PerCpu"]
    CONTEXT["src/context_frame.rsContext management"]
    EXCEPTION["src/exception.rsException handling"]
end

CONTEXT --> AARCH64_API
EXCEPTION --> AARCH64_API
EXCEPTION --> LOG_API
PCPU_IMPL --> PERCPU_API
VCPU_IMPL --> AXADDR_API
VCPU_IMPL --> AXVCPU_API

The external dependencies provide the foundational infrastructure that the arm_vcpu implementation builds upon, allowing it to focus on AArch64-specific virtualization concerns.

Sources: src/lib.rs(L6 - L7)  src/lib.rs(L17 - L18) 

Platform Hardware Integration

The arm_vcpu crate provides platform hardware integration through hardware capability detection and secure firmware communication interfaces.

Hardware Integration Points

flowchart TD
subgraph subGraph2["Hardware Features"]
    VIRT_EXT["AArch64 VirtualizationExtensions"]
    EL2_MODE["Exception Level 2Hypervisor mode"]
    SECURE_STATE["Secure/Non-secureState separation"]
end
subgraph subGraph1["Secure Communication"]
    SMC_CALL["smc_call()smc.rs:10-26"]
    SMC_ASM["smc #0Assembly instruction"]
    ATF_EL3["ARM Trusted FirmwareEL3 Secure Monitor"]
end
subgraph subGraph0["Platform Detection"]
    HAS_HW_SUPPORT["has_hardware_support()lib.rs:24-32"]
    VHE_CHECK["ID_AA64MMFR1_EL1Virtualization Host Extensions"]
    CORTEX_A78["Cortex-A78Example platform"]
end

HAS_HW_SUPPORT --> VHE_CHECK
HAS_HW_SUPPORT --> VIRT_EXT
SMC_ASM --> ATF_EL3
SMC_CALL --> SECURE_STATE
SMC_CALL --> SMC_ASM
VHE_CHECK --> CORTEX_A78
VIRT_EXT --> EL2_MODE

The platform integration provides two key capabilities:

  1. Hardware Capability Detection: The has_hardware_support() function determines if the current platform supports AArch64 virtualization extensions
  2. Secure Firmware Communication: The smc_call() function enables communication with secure firmware components

Sources: src/lib.rs(L24 - L32)  src/smc.rs(L3 - L26) 

Integration Safety and Constraints

The integration interfaces include several safety-critical components that require careful handling by host hypervisor systems.

The smc_call() function in src/smc.rs(L10 - L26)  is marked as unsafe and requires the caller to ensure:

  • x0 contains a valid SMC function number per the SMC Calling Convention
  • Arguments x1, x2, x3 are valid for the specified SMC function
  • The calling context is appropriate for secure monitor calls

The hardware support detection in src/lib.rs(L24 - L32)  currently returns true by default but includes documentation for implementing proper detection using ID_AA64MMFR1_EL1 register checks on platforms like Cortex-A78.

These safety constraints ensure that the arm_vcpu crate integrates properly with both hardware platforms and secure firmware components while maintaining the security boundaries required for hypervisor operation.

Sources: src/smc.rs(L5 - L9)  src/lib.rs(L25 - L30) 

Secure Monitor Interface

Relevant source files

This document covers the Secure Monitor Call (SMC) interface implementation in the ARM vCPU hypervisor. The SMC interface provides a mechanism for the hypervisor to communicate with secure firmware, typically ARM Trusted Firmware (ATF), running at the secure monitor level (EL3). This interface is essential for implementing security features, power management operations, and other privileged system functions that require secure world access.

For information about how SMC exceptions from guest VMs are handled, see the exception handling documentation in High-Level Exception Handling.

ARM Security Architecture and SMC Role

The SMC interface operates within ARM's TrustZone security architecture, which divides the system into secure and non-secure worlds across different exception levels.

ARM Security Model with SMC Interface

flowchart TD
subgraph subGraph1["Non-Secure World"]
    EL2_NS["EL2 Non-SecureHypervisorarm_vcpu"]
    EL1_NS["EL1 Non-SecureGuest OSVirtual Machines"]
    EL0_NS["EL0 Non-SecureGuest Applications"]
end
subgraph subGraph0["Secure World"]
    EL3_S["EL3 Secure MonitorARM Trusted FirmwareSecure Boot, PSCI"]
    EL1_S["EL1 SecureTrusted OSTEE Services"]
    EL0_S["EL0 SecureTrusted Applications"]
end

EL0_NS --> EL1_NS
EL0_S --> EL1_S
EL1_NS --> EL2_NS
EL1_S --> EL3_S
EL2_NS --> EL3_S
EL3_S --> EL2_NS

Sources: src/smc.rs(L1 - L27) 

SMC Call Implementation

The core SMC functionality is implemented through the smc_call function, which provides a direct interface to invoke secure monitor calls from the hypervisor.

SMC Call Function Interface

flowchart TD
subgraph subGraph2["Secure Monitor"]
    EL3_HANDLER["EL3 SMC HandlerFunction DispatchService Implementation"]
    RETURN["Return Valuesr0: Result/Statusr1-r3: Return Data"]
end
subgraph subGraph1["smc_call Function"]
    UNSAFE["unsafe fn smc_call(x0, x1, x2, x3)"]
    ASM["Inline Assemblysmc #0inout registers"]
end
subgraph subGraph0["Hypervisor Context"]
    CALLER["Caller FunctionPSCI HandlerSecure Service Request"]
    PARAMS["Parametersx0: Function IDx1-x3: Arguments"]
end

ASM --> EL3_HANDLER
CALLER --> PARAMS
EL3_HANDLER --> RETURN
PARAMS --> UNSAFE
RETURN --> CALLER
UNSAFE --> ASM

The smc_call function is marked as unsafe and #[inline(never)] to ensure proper handling of the security-sensitive operation:

ParameterPurposeSMC Calling Convention
x0Function identifierSMC function number per ARM SMC Calling Convention
x1-x3Function argumentsService-specific parameters
Returnr0Status/resultSuccess/error code or primary return value
Returnr1-r3Additional dataService-specific return values

Sources: src/smc.rs(L3 - L26) 

SMC Assembly Implementation

The actual secure monitor call is implemented using inline assembly with specific constraints to ensure safe execution.

SMC Assembly Execution Flow

flowchart TD
subgraph subGraph0["EL3 Processing"]
    EL3_ENTRY["EL3 SMC VectorContext Save"]
    EL3_DISPATCH["Function DispatchBased on x0"]
    EL3_SERVICE["Service ImplementationPSCI, Secure Services"]
    EL3_RETURN["Prepare Return ValuesLoad r0-r3"]
    EL3_EXIT["Context RestoreWorld Switch to EL2"]
end
ENTRY["Function Entrysmc_call(x0, x1, x2, x3)"]
SETUP["Register SetupLoad input parametersinto x0-x3"]
SMC_INST["Execute SMC #0Secure Monitor CallWorld Switch to EL3"]
CAPTURE["Register CaptureExtract r0-r3from output registers"]
RETURN_TUPLE["Return Tuple(r0, r1, r2, r3)"]

CAPTURE --> RETURN_TUPLE
EL3_DISPATCH --> EL3_SERVICE
EL3_ENTRY --> EL3_DISPATCH
EL3_EXIT --> CAPTURE
EL3_RETURN --> EL3_EXIT
EL3_SERVICE --> EL3_RETURN
ENTRY --> SETUP
SETUP --> SMC_INST
SMC_INST --> EL3_ENTRY

The assembly implementation uses specific constraints:

  • inout constraints for registers x0-x3 to handle both input and output
  • options(nomem, nostack) to indicate the SMC instruction doesn't access memory or modify the stack
  • #[inline(never)] attribute to prevent inlining and ensure the SMC happens at the correct execution context

Sources: src/smc.rs(L15 - L25) 

Integration with Exception Handling

When guest VMs attempt to execute SMC instructions, they are trapped by the hypervisor and can be handled through the exception system before potentially being forwarded to the secure monitor.

Guest SMC Exception Flow

flowchart TD
subgraph subGraph1["SMC Execution Paths"]
    FORWARD_SMC["Forward via smc_callHypervisor → EL3"]
    EMULATE_SMC["Emulate ResponseSynthetic Return Values"]
    DENY_SMC["Deny OperationInject Exception to Guest"]
end
subgraph subGraph0["SMC Decision Logic"]
    POLICY_CHECK["Security Policy CheckFunction ID Validation"]
    FORWARD_DECISION["Forward to EL3?"]
    EMULATE_DECISION["Emulate in EL2?"]
end
GUEST_SMC["Guest VMExecutes SMC"]
TRAP["HCR_EL2.TSC TrapException to EL2"]
EXC_HANDLER["handle_smc64_exceptionException Analysis"]
RETURN_GUEST["Return to GuestWith Results"]

DENY_SMC --> RETURN_GUEST
EMULATE_DECISION --> EMULATE_SMC
EMULATE_SMC --> RETURN_GUEST
EXC_HANDLER --> POLICY_CHECK
FORWARD_DECISION --> DENY_SMC
FORWARD_DECISION --> FORWARD_SMC
FORWARD_SMC --> RETURN_GUEST
GUEST_SMC --> TRAP
POLICY_CHECK --> EMULATE_DECISION
POLICY_CHECK --> FORWARD_DECISION
TRAP --> EXC_HANDLER

Sources: Based on system architecture patterns referenced in overview diagrams

Security Considerations and Trust Boundaries

The SMC interface represents a critical trust boundary in the system, requiring careful consideration of security implications.

Trust Boundary and Attack Surface

flowchart TD
subgraph subGraph2["Secure Monitor Domain"]
    EL3_MONITOR["ARM Trusted FirmwareEL3 Secure MonitorRoot of Trust"]
    SECURE_SERVICES["Secure ServicesPSCI, Crypto, Boot"]
end
subgraph subGraph1["Hypervisor Trust Domain"]
    HYPERVISOR["arm_vcpu HypervisorEL2 Non-SecureTrusted Compute Base"]
    SMC_FILTER["SMC Policy EngineFunction ID FilteringParameter Validation"]
    SMC_CALL["smc_call FunctionDirect EL3 Interface"]
end
subgraph subGraph0["Untrusted Domain"]
    GUEST_VM["Guest VMsPotentially MaliciousEL1/EL0 Non-Secure"]
    GUEST_SMC["Guest SMC CallsTrapped and Filtered"]
end

EL3_MONITOR --> SECURE_SERVICES
GUEST_SMC --> SMC_FILTER
GUEST_VM --> GUEST_SMC
HYPERVISOR --> SMC_CALL
SMC_CALL --> EL3_MONITOR
SMC_FILTER --> SMC_CALL

Security Properties

PropertyImplementationRationale
Function ID validationPolicy checking before forwardingPrevents unauthorized access to secure services
Parameter sanitizationInput validation in exception handlersMitigates parameter injection attacks
Direct hypervisor accesssmc_callfunction for hypervisor useEnables privileged operations like PSCI
Guest call filteringException trapping and analysisMaintains security boundaries between VMs

Sources: src/smc.rs(L6 - L9)  for safety documentation

Hardware Abstraction and Platform Support

Relevant source files

This document covers the hardware abstraction mechanisms and platform support capabilities in the arm_vcpu hypervisor implementation. It explains how the system abstracts AArch64 virtualization hardware features, detects platform capabilities, and integrates with host operating systems through well-defined interfaces.

For information about the specific per-CPU state management implementation, see Per-CPU State Management. For details about secure monitor integration, see Secure Monitor Interface.

Purpose and Scope

The hardware abstraction layer serves as the bridge between the generic virtualization logic and platform-specific implementations. It provides a clean interface for host operating systems to integrate the arm_vcpu hypervisor while isolating hardware-specific details from the core virtualization algorithms.

Hardware Abstraction Layer Design

The system uses the AxVCpuHal trait as the primary abstraction mechanism. This trait defines the interface that host systems must implement to provide platform-specific services to the hypervisor.

HAL Integration Architecture

flowchart TD
subgraph subGraph3["AArch64 Hardware"]
    CPU_FEATURES["ID_AA64MMFR1_EL1Feature Register"]
    VIRT_REGS["HCR_EL2VBAR_EL2Virtualization Registers"]
    EL2_MODE["Exception Level 2Hypervisor Mode"]
end
subgraph subGraph2["Hardware Interface Layer"]
    HW_DETECT["has_hardware_support()"]
    REG_ACCESS["AArch64 Register Access"]
    VIRT_EXT["Virtualization Extensions"]
end
subgraph subGraph1["arm_vcpu Hardware Abstraction"]
    HAL_TRAIT["AxVCpuHal Trait"]
    PCPU_GENERIC["Aarch64PerCpu"]
    VCPU_GENERIC["Aarch64VCpu"]
end
subgraph subGraph0["Host Operating System Layer"]
    HOST_OS["Host OS Implementation"]
    HAL_IMPL["AxVCpuHal Implementation"]
end

HAL_IMPL --> HAL_TRAIT
HAL_TRAIT --> PCPU_GENERIC
HAL_TRAIT --> VCPU_GENERIC
HOST_OS --> HAL_IMPL
HW_DETECT --> CPU_FEATURES
PCPU_GENERIC --> HW_DETECT
PCPU_GENERIC --> REG_ACCESS
REG_ACCESS --> VIRT_REGS
VIRT_EXT --> EL2_MODE

Sources: src/pcpu.rs(L1 - L80)  src/lib.rs(L1 - L33) 

HAL Trait Interface

The AxVCpuHal trait provides the following key abstractions:

MethodPurposeImplementation Responsibility
irq_hanlder()Host IRQ dispatchHost OS provides IRQ routing logic
Platform servicesMemory management, device accessHost OS provides platform-specific implementations

The generic design allows the same hypervisor core to work across different host environments by parameterizing the per-CPU and VCPU structures with the HAL implementation.

Sources: src/pcpu.rs(L7 - L8)  src/pcpu.rs(L32 - L43) 

Platform Detection and Capabilities

Hardware Support Detection

The system provides platform capability detection through the has_hardware_support() function:

flowchart TD
INIT["System Initialization"]
DETECT["has_hardware_support()"]
CHECK_REG["Read ID_AA64MMFR1_EL1"]
VHE_CHECK["Check Virtualization Host Extensions"]
CURRENT_IMPL["Return true (default)"]
SUPPORTED["Platform Supported"]
note1["Note: Currently simplifiedimplementation returns trueFuture: Parse feature register"]

CHECK_REG --> VHE_CHECK
CHECK_REG --> note1
CURRENT_IMPL --> SUPPORTED
DETECT --> CHECK_REG
INIT --> DETECT
VHE_CHECK --> CURRENT_IMPL

Sources: src/lib.rs(L24 - L32) 

Feature Register Analysis

The implementation includes provisions for proper hardware feature detection using AArch64 feature registers:

  • ID_AA64MMFR1_EL1: Memory Model Feature Register for Virtualization Host Extensions detection
  • Cortex-A78 Support: Specific mention of Cortex-A78 feature detection capabilities
  • Future Extensibility: Framework for expanding platform detection logic

The current implementation uses a conservative approach by defaulting to supported status, but includes documentation for proper feature register parsing implementation.

Sources: src/lib.rs(L25 - L29) 

Hardware Feature Management

Virtualization Hardware Control

The per-CPU implementation manages AArch64 virtualization hardware features through register manipulation:

flowchart TD
subgraph subGraph1["HCR_EL2 Configuration"]
    VM_ENABLE["VM: Enable virtualization"]
    RW_MODE["RW: EL1 is AArch64"]
    IRQ_VIRT["IMO: Virtual IRQ enable"]
    FIQ_VIRT["FMO: Virtual FIQ enable"]
    SMC_TRAP["TSC: Trap SMC to EL2"]
end
subgraph subGraph0["Hardware Enable Sequence"]
    SAVE_VBAR["Save Original VBAR_EL2ORI_EXCEPTION_VECTOR_BASE"]
    SET_VBAR["Set VBAR_EL2 toexception_vector_base_vcpu"]
    CONFIG_HCR["Configure HCR_EL2Virtualization Features"]
end
subgraph subGraph2["Hardware Disable Sequence"]
    RESTORE_VBAR["Restore Original VBAR_EL2"]
    DISABLE_VM["HCR_EL2.VM: Disable"]
end

CONFIG_HCR --> FIQ_VIRT
CONFIG_HCR --> IRQ_VIRT
CONFIG_HCR --> RW_MODE
CONFIG_HCR --> SMC_TRAP
CONFIG_HCR --> VM_ENABLE
RESTORE_VBAR --> DISABLE_VM
SAVE_VBAR --> SET_VBAR
SET_VBAR --> CONFIG_HCR

Sources: src/pcpu.rs(L49 - L78) 

Register Management Implementation

The hardware abstraction manages critical AArch64 system registers:

RegisterPurposeManagement
VBAR_EL2Exception vector baseSaved/restored during enable/disable
HCR_EL2Hypervisor configurationConfigured with virtualization features
Per-CPU storageIRQ handlers, original stateManaged through percpu crate

The implementation ensures proper state management by saving original register values before modification and restoring them during hardware disable operations.

Sources: src/pcpu.rs(L53 - L57)  src/pcpu.rs(L59 - L65)  src/pcpu.rs(L74 - L76) 

Integration with Host Systems

Per-CPU Integration Model

The system integrates with host operating systems through a per-CPU abstraction model:

flowchart TD
subgraph subGraph2["Hardware State"]
    VIRT_ENABLED["Virtualization Enabled"]
    EXCEPTION_VECTORS["Custom Exception Vectors"]
    IRQ_ROUTING["IRQ Routing Active"]
end
subgraph subGraph1["arm_vcpu Per-CPU Layer"]
    PCPU_NEW["Aarch64PerCpu::new(cpu_id)"]
    IRQ_CELL["IRQ_HANDLER OnceCell"]
    HW_ENABLE["hardware_enable()"]
    HW_DISABLE["hardware_disable()"]
end
subgraph subGraph0["Host OS Integration Points"]
    HOST_INIT["Host System Initialization"]
    IRQ_REG["IRQ Handler Registration"]
    PERCPU_SETUP["Per-CPU Data Setup"]
end

HOST_INIT --> PCPU_NEW
HW_DISABLE --> HOST_INIT
HW_ENABLE --> EXCEPTION_VECTORS
HW_ENABLE --> IRQ_ROUTING
HW_ENABLE --> VIRT_ENABLED
IRQ_REG --> IRQ_CELL
PERCPU_SETUP --> HW_ENABLE

Sources: src/pcpu.rs(L18 - L26)  src/pcpu.rs(L33 - L43) 

IRQ Handler Abstraction

The system provides IRQ handling abstraction through per-CPU handler registration:

  • OnceCell Storage: Thread-safe per-CPU IRQ handler storage using OnceCell<&(dyn Fn() + Send + Sync)>
  • Host Integration: Host OS registers IRQ handlers during per-CPU initialization
  • HAL Dispatch: IRQ handlers are called through the AxVCpuHal::irq_hanlder() interface

This design allows the hypervisor to route interrupts back to the host OS while maintaining isolation and type safety.

Sources: src/pcpu.rs(L21 - L26)  src/pcpu.rs(L35 - L37) 

Export Interface

The crate provides a clean public interface for host system integration:

ExportTypePurpose
Aarch64PerCpuGeneric structPer-CPU management implementation
Aarch64VCpuGeneric structVirtual CPU implementation
Aarch64VCpuCreateConfigConfigurationVCPU creation parameters
TrapFrameType aliasContext frame for AArch64
has_hardware_support()FunctionPlatform capability detection

These exports provide the complete interface needed for host systems to integrate the arm_vcpu hypervisor functionality.

Sources: src/lib.rs(L17 - L22)  src/lib.rs(L24 - L32) 

Overview

Relevant source files

This document provides an overview of the axvcpu crate, which serves as a virtual CPU abstraction layer for the ArceOS hypervisor. The crate provides architecture-independent VCPU management capabilities, comprehensive exit handling, and multi-platform support through a trait-based design.

For detailed information about VCPU lifecycle management and state transitions, see Core VCPU Management. For comprehensive coverage of VM exit handling and processing, see Exit Handling System. For implementation specifics including per-CPU state and hardware abstraction, see Implementation Details.

Purpose and Scope

The axvcpu crate implements a virtualization abstraction that enables hypervisors to manage virtual CPUs across different hardware architectures. It provides a unified interface for VCPU creation, execution control, exit handling, and resource management while abstracting away architecture-specific implementation details.

The crate operates in a no_std environment and integrates with the broader ArceOS hypervisor ecosystem through well-defined interfaces and dependency relationships.

Sources: src/lib.rs(L1 - L4)  README.md(L1 - L3) 

System Architecture

The following diagram illustrates the high-level architecture of the axvcpu system, showing how the main code entities relate to each other:

AxVCpu Core Architecture

flowchart TD
subgraph subGraph3["External Dependencies"]
    AxErrno["axerrnoError Types"]
    MemoryAddr["memory_addrAddress Abstractions"]
    Percpu["percpuPer-CPU Variables"]
    AxAddrspace["axaddrspaceAddress Space Management"]
end
subgraph subGraph2["Core Modules"]
    VcpuMod["vcpu.rsVCPU Implementation"]
    ExitMod["exit.rsExit Reason Definitions"]
    ArchVcpuMod["arch_vcpu.rsArchitecture Interface"]
    HalMod["hal.rsHardware Layer"]
    PercpuMod["percpu.rsPer-CPU State"]
end
subgraph subGraph1["Abstraction Layer"]
    AxArchVCpu["AxArchVCpuArchitecture Trait"]
    AxVCpuHal["AxVCpuHalHardware Abstraction"]
    AccessWidth["AccessWidthMemory Access Specification"]
end
subgraph subGraph0["Public API Layer"]
    LibRS["lib.rsPublic Exports"]
    AxVCpu["AxVCpuMain VCPU Manager"]
    AxVCpuExitReason["AxVCpuExitReasonExit Event Types"]
end

AxArchVCpu --> ArchVcpuMod
AxVCpu --> VcpuMod
AxVCpuExitReason --> ExitMod
AxVCpuHal --> HalMod
HalMod --> AxAddrspace
LibRS --> AccessWidth
LibRS --> AxArchVCpu
LibRS --> AxErrno
LibRS --> AxVCpu
LibRS --> AxVCpuExitReason
LibRS --> AxVCpuHal
LibRS --> MemoryAddr
LibRS --> Percpu
VcpuMod --> ArchVcpuMod
VcpuMod --> ExitMod
VcpuMod --> PercpuMod

This diagram shows the modular design where the public API layer exposes the main abstractions (AxVCpu, AxVCpuExitReason, AxArchVCpu, AxVCpuHal) which are implemented by corresponding core modules, with clear dependency relationships to external crates.

Sources: src/lib.rs(L9 - L22)  Cargo.toml(L7 - L12) 

Core Component Overview

VCPU Management Components

ComponentModulePurpose
AxVCpuvcpu.rsPrimary VCPU abstraction managing lifecycle and execution
AxArchVCpuarch_vcpu.rsArchitecture-specific trait for platform implementations
AxVCpuExitReasonexit.rsEnumeration of all possible VM exit conditions
AxVCpuHalhal.rsHardware abstraction layer for memory and interrupt management
Per-CPU statepercpu.rsManagement of per-CPU virtualization state

The system follows a layered approach where the generic AxVCpu manager coordinates with architecture-specific implementations through the AxArchVCpu trait, while exit events are processed through the comprehensive AxVCpuExitReason enumeration.

Sources: src/lib.rs(L15 - L21) 

VCPU Execution and Exit Flow

sequenceDiagram
    participant HypervisorbrClientCode as "Hypervisor<br>Client Code"
    participant AxVCpubrManager as "AxVCpu<br>Manager"
    participant AxArchVCpubrArchitectureImpl as "AxArchVCpu<br>Architecture Impl"
    participant AxVCpuExitReasonbrExitHandler as "AxVCpuExitReason<br>Exit Handler"
    participant AxVCpuHalbrHardwareLayer as "AxVCpuHal<br>Hardware Layer"

    HypervisorbrClientCode ->> AxVCpubrManager: "new() + setup()"
    AxVCpubrManager ->> AxArchVCpubrArchitectureImpl: "create architecture instance"
    AxVCpubrManager ->> AxVCpuHalbrHardwareLayer: "initialize hardware resources"
    HypervisorbrClientCode ->> AxVCpubrManager: "run()"
    AxVCpubrManager ->> AxArchVCpubrArchitectureImpl: "execute guest code"
    alt "VM Exit Event"
        AxArchVCpubrArchitectureImpl ->> AxVCpuExitReasonbrExitHandler: "exit event data"
        AxVCpuExitReasonbrExitHandler ->> AxVCpubrManager: "processed exit reason"
        AxVCpubrManager ->> HypervisorbrClientCode: "return exit information"
    end
    alt "Memory Access Required"
        AxVCpubrManager ->> AxVCpuHalbrHardwareLayer: "handle memory operation"
        AxVCpuHalbrHardwareLayer ->> AxVCpubrManager: "memory access result"
    end

This sequence diagram demonstrates how the core components interact during VCPU execution, with the AxVCpu manager orchestrating between architecture-specific implementations and hardware abstraction layers to handle VM exits and memory operations.

Sources: src/lib.rs(L9 - L21) 

Integration with ArceOS Ecosystem

The axvcpu crate serves as a foundational component in the ArceOS hypervisor stack, providing the essential virtual CPU management capabilities that higher-level hypervisor components depend on. It integrates with several key external dependencies:

  • axerrno: Provides standardized error handling across the hypervisor
  • memory_addr: Offers memory address abstractions for guest physical addressing
  • percpu: Enables per-CPU variable management for hypervisor state
  • axaddrspace: Provides address space management for guest memory virtualization

The crate's no_std design ensures compatibility with kernel-level hypervisor environments while maintaining portability across different host operating systems and hardware platforms.

Sources: Cargo.toml(L7 - L12)  src/lib.rs(L4) 

Core VCPU Management

Relevant source files

This document covers the central AxVCpu abstraction that provides the primary interface for virtual CPU management in the axvcpu crate. The AxVCpu struct serves as an architecture-independent wrapper that delegates platform-specific operations to architecture implementations while maintaining consistent state management and lifecycle control.

For detailed information about VCPU state transitions and lifecycle stages, see VCPU State Machine and Lifecycle. For architecture-specific implementation details, see Architecture Abstraction Layer. For information about handling VCPU exits and events, see Exit Handling System.

AxVCpu Structure and Components

The AxVCpu struct is designed as a generic wrapper that provides architecture-independent VCPU management by delegating specific operations to types implementing the AxArchVCpu trait.

Core Data Structure

classDiagram
class AxVCpu {
    -AxVCpuInnerConst inner_const
    -RefCell~AxVCpuInnerMut~ inner_mut
    -UnsafeCell~A~ arch_vcpu
    +new(id, favor_phys_cpu, phys_cpu_set, arch_config) AxResult~Self~
    +setup(entry, ept_root, arch_config) AxResult
    +run() AxResult~AxVCpuExitReason~
    +bind() AxResult
    +unbind() AxResult
    +state() VCpuState
    +id() usize
    +is_bsp() bool
    +set_gpr(reg, val)
}

class AxVCpuInnerConst {
    -usize id
    -usize favor_phys_cpu
    -Option~usize~ phys_cpu_set
    
}

class AxVCpuInnerMut {
    -VCpuState state
    
}

class VCpuState {
    <<enumeration>>
    Invalid
    Created
    Free
    Ready
    Running
    Blocked
    
}

AxVCpu  *--  AxVCpuInnerConst
AxVCpu  *--  AxVCpuInnerMut
AxVCpuInnerMut  *--  VCpuState

The AxVCpu structure separates concerns into three main components:

ComponentPurposeMutability
AxVCpuInnerConstImmutable VCPU configuration (ID, CPU affinity)Constant
AxVCpuInnerMutMutable state information managed throughRefCellInterior mutable
arch_vcpuArchitecture-specific implementation viaUnsafeCellUnsafe interior mutable

Sources: src/vcpu.rs(L8 - L63) 

Architecture Independence Design

The AxVCpu achieves architecture independence by using the AxArchVCpu trait as a type parameter, allowing the same interface to work with different CPU architectures.

Trait Integration Pattern


The AxArchVCpu trait defines the contract that architecture-specific implementations must fulfill:

MethodPurposeState Requirement
new()Create new architecture VCPU-
set_entry()Set guest entry pointBefore setup
set_ept_root()Set memory translation rootBefore setup
setup()Complete VCPU initializationAfter entry/EPT set
run()Execute guest codeRunning state
bind()/unbind()CPU binding operationsReady/Free transition

Sources: src/arch_vcpu.rs(L6 - L44)  src/vcpu.rs(L53 - L63) 

VCPU Lifecycle Operations

The AxVCpu provides key lifecycle operations that manage VCPU state transitions and delegate to architecture-specific implementations.

Creation and Initialization Flow


Runtime Operations

The main runtime operations follow a consistent pattern of state validation and architecture delegation:

flowchart TD
subgraph subGraph1["Key Operations"]
    Bind["bind(): Free → Ready"]
    Run["run(): Ready → Running → Ready"]
    Unbind["unbind(): Ready → Free"]
end
subgraph subGraph0["VCPU Operation Pattern"]
    StateCheck["Check Current State"]
    ArchDelegate["Delegate to AxArchVCpu"]
    StateTransition["Update VCPU State"]
    Result["Return Result"]
end

ArchDelegate --> StateTransition
Bind --> StateCheck
Run --> StateCheck
StateCheck --> ArchDelegate
StateTransition --> Result
Unbind --> StateCheck

Sources: src/vcpu.rs(L205 - L225)  src/vcpu.rs(L86 - L99) 

Current VCPU Management

The crate maintains per-CPU tracking of the currently executing VCPU using a percpu variable, enabling architecture implementations to access their containing AxVCpu instance.

Per-CPU VCPU Tracking

flowchart TD
subgraph subGraph2["Access Functions"]
    GetCurrent["get_current_vcpu<A>()"]
    SetCurrent["set_current_vcpu(vcpu)"]
    ClearCurrent["clear_current_vcpu<A>()"]
end
subgraph subGraph1["Physical CPU 1"]
    PCPU1["CURRENT_VCPU: Option<*mut u8>"]
    VCPU1["AxVCpu instance"]
end
subgraph subGraph0["Physical CPU 0"]
    PCPU0["CURRENT_VCPU: Option<*mut u8>"]
    VCPU0["AxVCpu instance"]
end

ClearCurrent --> PCPU0
ClearCurrent --> PCPU1
GetCurrent --> PCPU0
GetCurrent --> PCPU1
PCPU0 --> VCPU0
PCPU1 --> VCPU1
SetCurrent --> PCPU0
SetCurrent --> PCPU1

The current VCPU mechanism provides:

FunctionPurposeSafety
get_current_vcpu()Get current VCPU referenceSafe
get_current_vcpu_mut()Get mutable current VCPU referenceSafe
set_current_vcpu()Set current VCPU pointerUnsafe
clear_current_vcpu()Clear current VCPU pointerUnsafe

The with_current_cpu_set method automatically manages current VCPU setup and cleanup:

sequenceDiagram
    participant Caller as Caller
    participant AxVCpu as AxVCpu
    participant CURRENT_VCPU as "CURRENT_VCPU"
    participant UserOperation as "User Operation"

    Caller ->> AxVCpu: "with_current_cpu_set(closure)"
    AxVCpu ->> CURRENT_VCPU: "check if already set"
    alt Already Set
        AxVCpu -->> AxVCpu: "panic!(nested operation)"
    else Not Set
        AxVCpu ->> CURRENT_VCPU: "set_current_vcpu(self)"
        AxVCpu ->> UserOperation: "execute closure"
        UserOperation -->> AxVCpu: "result"
        AxVCpu ->> CURRENT_VCPU: "clear_current_vcpu()"
        AxVCpu -->> Caller: "result"
    end

Sources: src/vcpu.rs(L238 - L290)  src/vcpu.rs(L164 - L180) 

Interior Mutability Pattern

The AxVCpu uses a sophisticated interior mutability design to handle the different access patterns required for VCPU state and architecture-specific data.

Mutability Strategy

flowchart TD
subgraph Methods["Methods"]
    GetId["id(), favor_phys_cpu()"]
    GetState["state(), with_state_transition()"]
    GetArch["get_arch_vcpu()"]
end
subgraph subGraph1["Access Patterns"]
    ConstAccess["ID, CPU affinity queries"]
    StateAccess["State transitions via RefCell"]
    ArchAccess["Direct architecture operations"]
end
subgraph subGraph0["AxVCpu Mutability Design"]
    InnerConst["AxVCpuInnerConst(Immutable)"]
    InnerMut["RefCell<AxVCpuInnerMut>(Safe Interior Mutability)"]
    ArchVCpu["UnsafeCell<A>(Unsafe Interior Mutability)"]
end

ArchAccess --> GetArch
ArchVCpu --> ArchAccess
ConstAccess --> GetId
InnerConst --> ConstAccess
InnerMut --> StateAccess
StateAccess --> GetState

The design rationale for different mutability approaches:

ComponentMutability TypeReason
inner_constNoneImmutable configuration data
inner_mutRefCellSafe runtime borrowing for state
arch_vcpuUnsafeCellDirect access needed during VCPU execution

The manipulate_arch_vcpu method combines state transition with architecture delegation:

flowchart TD
Start["manipulate_arch_vcpu(from, to, closure)"]
StateTransition["with_state_transition(from, to)"]
CurrentSet["with_current_cpu_set()"]
ArchAccess["get_arch_vcpu()"]
Execute["Execute closure with &mut A"]
Result["Return AxResult<T>"]

ArchAccess --> Execute
CurrentSet --> ArchAccess
Execute --> Result
Start --> StateTransition
StateTransition --> CurrentSet

Sources: src/vcpu.rs(L53 - L63)  src/vcpu.rs(L185 - L192)  src/vcpu.rs(L199 - L203) 

VCPU State Machine and Lifecycle

Relevant source files

This document covers the state machine and lifecycle management of virtual CPUs in the axvcpu crate. It details the five-state VCPU model, state transition rules, lifecycle operations, and the per-CPU current VCPU tracking mechanism. For architecture-specific VCPU implementations, see Architecture Abstraction Layer. For VCPU exit handling during execution, see Exit Handling System.

VCPU State Model

The VCpuState enum defines a five-state model that governs VCPU lifecycle management. Each state represents a distinct phase in the VCPU's existence and determines which operations are permitted.


State Definitions

StateValueDescription
Invalid0Error state indicating the VCPU is unusable
Created1VCPU exists but requires setup before use
Free2VCPU is configured and can be bound to a physical CPU
Ready3VCPU is bound to a physical CPU and ready to execute
Running4VCPU is actively executing guest code
Blocked5VCPU is temporarily blocked (currently unused)

Sources: src/vcpu.rs(L20 - L35) 

State Transition Management

The AxVCpu struct enforces strict state transition rules through the with_state_transition method, which atomically checks the current state and transitions to a new state only if the precondition is met.

flowchart TD
A["with_state_transition(from, to, f)"]
B["current_state == from?"]
C["state = Invalid"]
D["return BadState error"]
E["execute f()"]
F["f() succeeded?"]
G["state = Invalid"]
H["return error from f()"]
I["state = to"]
J["return Ok(result)"]

A --> B
B --> C
B --> E
C --> D
E --> F
F --> G
F --> I
G --> H
I --> J

State Transition Methods

The VCPU provides several methods for managing state transitions:

  • with_state_transition() - Core method that atomically checks current state and transitions if valid
  • transition_state() - Simple state transition without executing additional operations
  • manipulate_arch_vcpu() - Combines state transition with architecture-specific operations
  • unsafe set_state() - Bypasses validation for emergency use

Sources: src/vcpu.rs(L136 - L197) 

VCPU Lifecycle Operations

The VCPU lifecycle consists of four main phases: creation, setup, binding, and execution. Each phase corresponds to specific state transitions and operations.

sequenceDiagram
    participant Hypervisor as "Hypervisor"
    participant AxVCpu as "AxVCpu"
    participant AxArchVCpu as "AxArchVCpu"
    participant PhysicalCPU as "Physical CPU"

    Hypervisor ->> AxVCpu: "new(id, favor_cpu, cpu_set, config)"
    AxVCpu ->> AxArchVCpu: "A::new(arch_config)"
    AxVCpu ->> AxVCpu: "state = Created"
    Hypervisor ->> AxVCpu: "setup(entry, ept_root, config)"
    AxVCpu ->> AxVCpu: "transition Created → Free"
    AxVCpu ->> AxArchVCpu: "set_entry(), set_ept_root(), setup()"
    Hypervisor ->> AxVCpu: "bind()"
    AxVCpu ->> AxVCpu: "transition Free → Ready"
    AxVCpu ->> AxArchVCpu: "bind()"
    AxArchVCpu ->> PhysicalCPU: "configure CPU for this VCPU"
    loop "Execution Loop"
        Hypervisor ->> AxVCpu: "run()"
        AxVCpu ->> AxVCpu: "transition Ready → Running"
        AxVCpu ->> AxArchVCpu: "run()"
        AxArchVCpu ->> AxArchVCpu: "execute guest code"
        AxArchVCpu -->> AxVCpu: "AxVCpuExitReason"
        AxVCpu ->> AxVCpu: "transition Running → Ready"
    end
    Hypervisor ->> AxVCpu: "unbind()"
    AxVCpu ->> AxVCpu: "transition Ready → Free"
    AxVCpu ->> AxArchVCpu: "unbind()"

Creation Phase

The new() method creates a VCPU instance with immutable configuration and initializes it in the Created state. The VCPU stores its ID, favored physical CPU, and CPU set restrictions.

Setup Phase

The setup() method configures the VCPU with guest entry point, extended page table root, and architecture-specific settings, transitioning from Created to Free state.

Binding Phase

The bind() method associates the VCPU with the current physical CPU, transitioning from Free to Ready state. This prepares the hardware for VCPU execution.

Execution Phase

The run() method executes the VCPU, transitioning from Ready to Running and back to Ready upon VM exit. This phase handles guest code execution and exit processing.

Sources: src/vcpu.rs(L66 - L225) 

Current VCPU Management

The system maintains a per-CPU pointer to the currently active VCPU using the CURRENT_VCPU per-CPU variable. This enables architecture-specific code to access the containing AxVCpu instance.

flowchart TD
subgraph subGraph3["API Functions"]
    GET["get_current_vcpu()"]
    GETMUT["get_current_vcpu_mut()"]
    SET["set_current_vcpu()"]
    CLEAR["clear_current_vcpu()"]
end
subgraph subGraph2["Physical CPU N"]
    CPUN["CURRENT_VCPU[N]"]
    VCPUN["AxVCpuinstance"]
end
subgraph subGraph1["Physical CPU 1"]
    CPU1["CURRENT_VCPU[1]"]
    VCPU1["AxVCpuinstance"]
end
subgraph subGraph0["Physical CPU 0"]
    CPU0["CURRENT_VCPU[0]"]
    VCPU0["AxVCpuinstance"]
end

CPU0 --> VCPU0
CPU1 --> VCPU1
CPUN --> VCPUN
GET --> CPU0
GET --> CPU1
GET --> CPUN
GETMUT --> CPU0
GETMUT --> CPU1
GETMUT --> CPUN

Current VCPU API

FunctionPurpose
get_current_vcpu()Returns immutable reference to current VCPU
get_current_vcpu_mut()Returns mutable reference to current VCPU
set_current_vcpu()Sets the current VCPU (unsafe)
clear_current_vcpu()Clears the current VCPU (unsafe)

The with_current_cpu_set() method ensures proper setup and cleanup of the current VCPU context, preventing nested VCPU operations which could cause corruption.

Sources: src/vcpu.rs(L238 - L290) 

Error Handling and Invalid States

The state machine includes comprehensive error handling that transitions VCPUs to the Invalid state when errors occur. This prevents continued use of corrupted VCPU instances.

Error Scenarios

  • State Mismatch: Attempting operations in wrong state transitions to Invalid
  • Architecture Errors: Failures in AxArchVCpu operations transition to Invalid
  • Nested Operations: Attempting nested VCPU operations causes panic for safety
  • Setup Failures: Errors during setup(), bind(), or run() transition to Invalid

Recovery Strategy

Once a VCPU enters the Invalid state, it cannot be recovered and must be recreated. This fail-fast approach prevents cascade failures and maintains system integrity.

flowchart TD
A["Any State"]
B["Operation Fails"]
C["State = Invalid"]
D["VCPU Unusable"]
E["Must Recreate"]

A --> B
B --> C
C --> D
D --> E

Sources: src/vcpu.rs(L136 - L161) 

Architecture Abstraction Layer

Relevant source files

This document covers the architecture abstraction mechanisms in the axvcpu crate that enable support for multiple CPU architectures through trait-based interfaces. The abstraction layer consists of two primary traits: AxArchVCpu for architecture-specific virtual CPU operations and AxVCpuHal for hardware abstraction layer functionality.

For information about the core VCPU management and state machine, see VCPU State Machine and Lifecycle. For details about how these abstractions integrate with the broader hypervisor ecosystem, see Dependencies and Integration.

AxArchVCpu Trait Interface

The AxArchVCpu trait serves as the primary abstraction for architecture-specific virtual CPU implementations. This trait defines a common interface that enables the same hypervisor code to manage virtual CPUs across different hardware architectures such as x86_64, ARM64, and RISC-V.

Trait Definition and Core Methods

The trait interface defines essential lifecycle methods and configuration types:

MethodPurposeTiming Constraints
new()Create new VCPU instanceFirst call
set_entry()Configure guest entry pointBeforesetup()
set_ept_root()Configure extended page tablesBeforesetup()
setup()Complete VCPU initializationAfter entry/EPT configuration
run()Execute guest until VM exitDuring VCPU execution
bind()/unbind()Manage physical CPU bindingDuring lifecycle transitions
set_gpr()Set general-purpose registersRuntime configuration

AxArchVCpu Trait Interface

classDiagram
class AxArchVCpu {
    <<trait>>
    +CreateConfig: Type
    +SetupConfig: Type
    +new(config) AxResult~Self~
    +set_entry(entry: GuestPhysAddr) AxResult
    +set_ept_root(ept_root: HostPhysAddr) AxResult
    +setup(config) AxResult
    +run() AxResult~AxVCpuExitReason~
    +bind() AxResult
    +unbind() AxResult
    +set_gpr(reg: usize, val: usize)
}

class CreateConfig {
    <<associated type>>
    "Architecture-specific creation parameters"
    
}

class SetupConfig {
    <<associated type>>
    "Architecture-specific setup parameters"
    
}

AxArchVCpu  -->  CreateConfig
AxArchVCpu  -->  SetupConfig

Sources: src/arch_vcpu.rs(L9 - L44) 

Configuration Types and Lifecycle Management

The trait uses associated types CreateConfig and SetupConfig to allow each architecture implementation to define its own configuration parameters. This design enables architecture-specific customization while maintaining a consistent interface.

The lifecycle methods follow a strict calling sequence with timing guarantees enforced by the trait documentation:

  1. new() creates the VCPU instance with architecture-specific configuration
  2. set_entry() and set_ept_root() configure memory layout (called exactly once each)
  3. setup() completes initialization after memory configuration
  4. bind()/unbind() manage physical CPU association during runtime
  5. run() executes the guest and returns exit reasons for hypervisor handling

Sources: src/arch_vcpu.rs(L10 - L13)  src/arch_vcpu.rs(L15 - L31) 

Hardware Abstraction Layer (AxVCpuHal)

The AxVCpuHal trait provides a hardware abstraction layer for memory management and interrupt handling operations that the VCPU system requires from the underlying kernel or hypervisor.

Memory Management Interface

The HAL defines essential memory operations for frame allocation and address translation:

MethodOperationReturn Type
alloc_frame()Allocate physical frameOption
dealloc_frame()Deallocate physical frame()
phys_to_virt()Physical to virtual address translationHostVirtAddr
virt_to_phys()Virtual to physical address translationHostPhysAddr

Interrupt Handling Interface

The HAL provides interrupt management capabilities with default implementations:

  • irq_fetch() returns the current interrupt number (defaults to 0)
  • irq_hanlder() dispatches interrupts to the host OS (unimplemented by default)

AxVCpuHal Interface Structure

classDiagram
class AxVCpuHal {
    <<trait>>
    
    +alloc_frame() Option~HostPhysAddr~
    +dealloc_frame(paddr: HostPhysAddr)
    +phys_to_virt(paddr: HostPhysAddr) HostVirtAddr
    +virt_to_phys(vaddr: HostVirtAddr) HostPhysAddr
    +irq_fetch() usize
    +irq_hanlder()
}

class HostPhysAddr {
    "Physical address in host address space"
    
}

class HostVirtAddr {
    "Virtual address in host address space"
    
}

AxVCpuHal  -->  HostPhysAddr
AxVCpuHal  -->  HostVirtAddr

Sources: src/hal.rs(L4 - L54) 

Multi-Architecture Support Design

The abstraction layer enables support for multiple CPU architectures through a trait-based design that separates generic VCPU management from architecture-specific implementation details.

Architecture Implementation Pattern

Each supported architecture provides its own implementation of the AxArchVCpu trait with architecture-specific:

  • Hardware virtualization support (VMX for x86, VHE for ARM, H-extension for RISC-V)
  • System register access patterns
  • Exception and interrupt handling mechanisms
  • Memory management unit integration
  • Power management interfaces

Multi-Architecture Abstraction Pattern


Sources: src/arch_vcpu.rs(L6 - L9)  src/hal.rs(L3 - L4) 

Lifecycle and Method Call Sequences

The architecture abstraction enforces a specific sequence of method calls to ensure proper VCPU initialization and execution.

VCPU Initialization Sequence

The trait design guarantees that initialization methods are called in the correct order through documentation contracts and type system constraints:

VCPU Lifecycle Method Sequence

sequenceDiagram
    participant Hypervisor as "Hypervisor"
    participant AxVCpuT as "AxVCpu<T>"
    participant TAxArchVCpu as "T: AxArchVCpu"
    participant AxVCpuHal as "AxVCpuHal"

    Hypervisor ->> AxVCpuT: "new(create_config)"
    AxVCpuT ->> TAxArchVCpu: "AxArchVCpu::new(config)"
    TAxArchVCpu ->> AxVCpuHal: "alloc_frame() (if needed)"
    TAxArchVCpu -->> AxVCpuT: "Return VCPU instance"
    Hypervisor ->> AxVCpuT: "set_entry(guest_entry)"
    AxVCpuT ->> TAxArchVCpu: "set_entry(entry)"
    Note over TAxArchVCpu: "Called exactly once before setup()"
    Hypervisor ->> AxVCpuT: "set_ept_root(page_table)"
    AxVCpuT ->> TAxArchVCpu: "set_ept_root(ept_root)"
    Note over TAxArchVCpu: "Called exactly once before setup()"
    Hypervisor ->> AxVCpuT: "setup(setup_config)"
    AxVCpuT ->> TAxArchVCpu: "setup(config)"
    Note over TAxArchVCpu: "Called after entry and EPT configuration"
    Hypervisor ->> AxVCpuT: "bind_to_cpu()"
    AxVCpuT ->> TAxArchVCpu: "bind()"
    Note over TAxArchVCpu: "Bind to physical CPU for execution"
    loop "Execution Loop"
        Hypervisor ->> AxVCpuT: "run()"
        AxVCpuT ->> TAxArchVCpu: "run()"
        TAxArchVCpu -->> AxVCpuT: "AxVCpuExitReason"
        Note over AxVCpuT: "Handle VM exit and return control"
    end
    Hypervisor ->> AxVCpuT: "unbind_from_cpu()"
    AxVCpuT ->> TAxArchVCpu: "unbind()"

Sources: src/arch_vcpu.rs(L15 - L41) 

Exit Handling Integration

The run() method returns AxVCpuExitReason values that integrate with the exit handling system, allowing architecture-specific implementations to communicate VM exit events through a common interface.

The abstraction enables architecture implementations to:

  • Map hardware-specific exit conditions to common exit reason types
  • Provide exit-specific data through standardized structures
  • Maintain consistent error handling across different architectures

Sources: src/arch_vcpu.rs(L34)  src/arch_vcpu.rs(L4) 

Exit Handling System

Relevant source files

Purpose and Scope

The Exit Handling System provides a comprehensive framework for managing VCPU exits in the ArceOS hypervisor. When guest code execution encounters events that require hypervisor intervention, the VCPU exits to the host and provides detailed information about the cause through the AxVCpuExitReason enum. This system abstracts exit handling across different CPU architectures while providing sufficient detail for the hypervisor to emulate devices, handle hypercalls, manage power states, and process memory operations.

For detailed categorization of specific exit types, see Exit Reasons and Categories. For information about memory and I/O operation handling specifics, see Memory Access and I/O Operations. For the broader VCPU management context, see Core VCPU Management.

Exit Handling Architecture

The exit handling system centers around two primary data structures that work together to provide precise information about why a VCPU exited and what data is associated with that exit.

VCPU Exit Flow

flowchart TD
A["Guest Execution"]
B["Exit Event Occurs"]
C["Hardware Exit"]
D["AxArchVCpu::run()"]
E["AxVCpuExitReason Creation"]
F["Return to Hypervisor"]
G["Exit Type Analysis"]
H["Memory Operations"]
I["System Control"]
J["I/O Operations"]
K["Interrupts & Faults"]
L["Power Management"]
M["Error Conditions"]
N["Device Emulation"]
O["Hypercall Processing"]
P["Port I/O Emulation"]
Q["Interrupt Injection"]
R["CPU Management"]
S["Error Recovery"]
T["Resume Guest"]
U["CPU State Change"]
V["Exit or Recovery"]

A --> B
B --> C
C --> D
D --> E
E --> F
F --> G
G --> H
G --> I
G --> J
G --> K
G --> L
G --> M
H --> N
I --> O
J --> P
K --> Q
L --> R
M --> S
N --> T
O --> T
P --> T
Q --> T
R --> U
S --> V

Sources: src/exit.rs(L66 - L210) 

Access Width Specification System

The system uses a precise access width specification to handle different data sizes across memory and I/O operations:

flowchart TD
A["AccessWidth Enum"]
B["Byte (8-bit)"]
C["Word (16-bit)"]
D["Dword (32-bit)"]
E["Qword (64-bit)"]
F["usize Conversion"]
G["Size Calculation"]
H["Bit Range Mapping"]
I["1 byte operations"]
J["2 byte operations"]
K["4 byte operations"]
L["8 byte operations"]

A --> B
A --> C
A --> D
A --> E
A --> G
A --> H
B --> I
C --> J
D --> K
E --> L
F --> A

Sources: src/exit.rs(L6 - L61) 

Core Data Structures

AccessWidth Enum

The AccessWidth enum provides standardized representation of memory and I/O access sizes. It supports conversion from and to usize values and provides utility methods for size calculations and bit range mapping.

Access WidthSize (bytes)Bit RangeUsage
Byte10..8Byte-level operations
Word20..1616-bit operations (x86 terminology)
Dword40..3232-bit operations
Qword80..6464-bit operations

Sources: src/exit.rs(L6 - L61) 

AxVCpuExitReason Enum

The AxVCpuExitReason enum is marked as #[non_exhaustive] to allow future expansion and contains comprehensive exit information for all supported virtualization scenarios.

Exit Reason Categories

Code Entity Mapping

flowchart TD
subgraph subGraph6["Supporting Types"]
    AW["AccessWidth"]
    GPA["GuestPhysAddr"]
    MF["MappingFlags"]
    P["Port (u16)"]
end
subgraph subGraph4["Power Management"]
    CU["CpuUp"]
    CD["CpuDown"]
    SD["SystemDown"]
end
subgraph subGraph2["I/O Operations"]
    IOR["IoRead"]
    IOW["IoWrite"]
end
subgraph subGraph1["System Control"]
    HC["Hypercall"]
    SRR["SysRegRead"]
    SRW["SysRegWrite"]
end
subgraph subGraph0["Memory Operations"]
    MR["MmioRead"]
    MW["MmioWrite"]
    NPF["NestedPageFault"]
end
subgraph subGraph3["Interrupts & Faults"]
    subgraph subGraph5["Control Flow"]
        N["Nothing"]
        FE["FailEntry"]
        EI["ExternalInterrupt"]
        H["Halt"]
    end
end

CU --> GPA
IOR --> AW
IOR --> P
IOW --> AW
IOW --> P
MR --> AW
MR --> GPA
MW --> AW
MW --> GPA
NPF --> GPA
NPF --> MF
SRR --> AW

Sources: src/exit.rs(L70 - L210) 

Exit Processing Workflow

Memory and I/O Exit Handling

Memory-mapped I/O operations (MmioRead, MmioWrite) and port I/O operations (IoRead, IoWrite) include precise access width information and target addresses. The system distinguishes between different register widths for MMIO reads to support complex instruction emulation.

System Register Access

System register operations (SysRegRead, SysRegWrite) provide architecture-specific register addressing schemes:

  • x86_64: MSR addresses
  • ARM64: ESR_EL2.ISS format encoding (<op0><op2><op1><CRn>00000<CRm>0)
  • RISC-V: CSR addresses

Power Management Operations

Power management exits (CpuUp, CpuDown, SystemDown) handle CPU lifecycle events with architecture-specific target CPU identification:

  • ARM64: MPIDR affinity fields
  • x86_64: APIC ID values
  • RISC-V: Hart ID values

Error and Control Flow

The system provides Nothing exits for polling opportunities and FailEntry exits for hardware-level virtualization failures with architecture-specific failure reason codes.

Sources: src/exit.rs(L70 - L210) 

Integration Points

The exit handling system integrates with several key components:

  • AxArchVCpu trait: Architecture implementations return AxVCpuExitReason from their run() methods
  • GuestPhysAddr: Physical address representation from axaddrspace crate
  • MappingFlags: Memory permission flags for nested page fault handling
  • Hypervisor: Processes exit reasons to emulate devices and manage guest state

The #[non_exhaustive] attribute on AxVCpuExitReason ensures forward compatibility as new exit types are added for expanded virtualization features.

Sources: src/exit.rs(L1 - L4)  src/exit.rs(L66 - L67) 

Exit Reasons and Categories

Relevant source files

This page documents the comprehensive set of virtual CPU exit reasons that can occur during guest execution in the AxVCpu system. It covers the AxVCpuExitReason enum and related types that define when and why a virtual CPU exits guest mode to return control to the hypervisor.

For information about how these exit reasons are processed and handled by the hypervisor, see Memory Access and I/O Operations. For details about the VCPU state management during exits, see VCPU State Machine and Lifecycle.

Exit Reason Overview

The AxVCpuExitReason enum defined in src/exit.rs(L68 - L210)  provides a comprehensive taxonomy of all possible reasons why a virtual CPU might exit guest execution. Each exit reason includes specific data fields relevant to that type of exit, enabling the hypervisor to process the exit appropriately.

The exit system is designed to be architecture-independent while accommodating architecture-specific details where necessary. All exit reasons include detailed context information to allow the hypervisor to emulate the operation, handle the event, or make decisions about guest execution.

Sources: src/exit.rs(L66 - L210) 

Exit Categories and Types

Memory Operations

Memory-related exits occur when the guest attempts operations that require hypervisor intervention:

Exit TypePurposeKey Data
MmioReadGuest reads from memory-mapped I/OAddress, access width, target register
MmioWriteGuest writes to memory-mapped I/OAddress, access width, data value
NestedPageFaultPage fault in nested pagingFault address, access flags

MMIO Operations

Page Faults

  • NestedPageFault src/exit.rs(L153 - L161)  occurs during EPT violations on x86 or stage-2 faults on ARM
  • Includes MappingFlags to indicate the type of access attempted

System Control Operations

System-level control operations that require hypervisor mediation:

Exit TypePurposeArchitecture Scope
SysRegReadRead system registersMSRs (x86), CSRs (RISC-V), System regs (ARM)
SysRegWriteWrite system registersMSRs (x86), CSRs (RISC-V), System regs (ARM)
HypercallGuest-initiated hypervisor callsAll architectures

System Register Access

  • src/exit.rs(L98 - L125)  defines both read and write operations
  • Address encoding varies by architecture (ESR_EL2.ISS format for ARM64)
  • Register index specified for read operations to identify target GPR

Hypercalls

  • src/exit.rs(L71 - L77)  includes hypercall number and up to 6 arguments
  • Provides standardized interface for guest-hypervisor communication

I/O Operations (x86-specific)

Direct I/O port access operations, specific to x86 architecture:

Interrupt and Exception Handling

Events that interrupt normal guest execution:

Power Management

CPU and system power state changes:

Exit TypePurposeData Fields
CpuUpBring up secondary CPUTarget CPU ID, entry point, argument
CpuDownPower down CPUPower state (currently unused)
SystemDownPower down entire systemNone

Multi-CPU Management

  • CpuUp src/exit.rs(L164 - L186)  handles architecture-specific CPU startup (PSCI/SIPI/SBI)
  • Target CPU identification varies by architecture (MPIDR/APIC ID/hartid)

Error and Special Conditions

Exceptional conditions and edge cases:

Sources: src/exit.rs(L68 - L210) 

Access Width Specifications

The AccessWidth enum src/exit.rs(L6 - L61)  provides precise specification of memory and I/O operation sizes:

flowchart TD
subgraph subGraph2["Bit Ranges"]
    R1["0..8 bits"]
    R2["0..16 bits"]
    R3["0..32 bits"]
    R4["0..64 bits"]
end
subgraph subGraph1["Conversion Methods"]
    B1["1 byte"]
    B2["2 bytes"]
    B4["4 bytes"]
    B8["8 bytes"]
end
subgraph subGraph0["AccessWidth Enum"]
    AW["AccessWidth"]
    Byte["Byte (8-bit)"]
    Word["Word (16-bit)"]
    Dword["Dword (32-bit)"]
    Qword["Qword (64-bit)"]
end

AW --> Byte
AW --> Dword
AW --> Qword
AW --> Word
B1 --> R1
B2 --> R2
B4 --> R3
B8 --> R4
Byte --> B1
Dword --> B4
Qword --> B8
Word --> B2

AccessWidth Conversion and Utility Methods

Sources: src/exit.rs(L6 - L61) 

Exit Reason Classification

flowchart TD
subgraph subGraph6["Error Conditions"]
    Nothing["Nothing"]
    FailEntry["FailEntry"]
end
subgraph subGraph5["Power Management"]
    CpuUp["CpuUp"]
    CpuDown["CpuDown"]
    SystemDown["SystemDown"]
end
subgraph subGraph4["Interrupts & Faults"]
    ExternalInterrupt["ExternalInterrupt"]
    Halt["Halt"]
end
subgraph subGraph3["I/O Operations"]
    IoRead["IoRead (x86 only)"]
    IoWrite["IoWrite (x86 only)"]
end
subgraph subGraph2["System Control"]
    SysRegRead["SysRegRead"]
    SysRegWrite["SysRegWrite"]
    Hypercall["Hypercall"]
end
subgraph subGraph1["Memory Operations"]
    MmioRead["MmioRead"]
    MmioWrite["MmioWrite"]
    NestedPageFault["NestedPageFault"]
end
subgraph subGraph0["AxVCpuExitReason Categories"]
    MEM["Memory Operations"]
    SYS["System Control"]
    IO["I/O Operations"]
    INT["Interrupts & Faults"]
    PWR["Power Management"]
    ERR["Error Conditions"]
end

ERR --> FailEntry
ERR --> Nothing
INT --> ExternalInterrupt
INT --> Halt
IO --> IoRead
IO --> IoWrite
MEM --> MmioRead
MEM --> MmioWrite
MEM --> NestedPageFault
PWR --> CpuDown
PWR --> CpuUp
PWR --> SystemDown
SYS --> Hypercall
SYS --> SysRegRead
SYS --> SysRegWrite

This diagram shows the logical grouping of exit reasons by their primary function and the specific enum variants that fall into each category.

Sources: src/exit.rs(L68 - L210) 

Exit Processing Flow

sequenceDiagram
    participant GuestExecution as "Guest Execution"
    participant AxVCpu as "AxVCpu"
    participant AxVCpuExitReason as "AxVCpuExitReason"
    participant HypervisorHandler as "Hypervisor Handler"

    GuestExecution ->> AxVCpu: "Trigger exit event"
    AxVCpu ->> AxVCpuExitReason: "Create specific exit reason"
    alt Memory Operation
        AxVCpuExitReason ->> AxVCpuExitReason: "MmioRead/MmioWrite + AccessWidth"
    else System Control
        AxVCpuExitReason ->> AxVCpuExitReason: "SysRegRead/SysRegWrite/Hypercall"
    else I/O Operation (x86)
        AxVCpuExitReason ->> AxVCpuExitReason: "IoRead/IoWrite + Port"
    else Power Management
        AxVCpuExitReason ->> AxVCpuExitReason: "CpuUp/CpuDown/SystemDown"
    else Interrupt/Fault
        AxVCpuExitReason ->> AxVCpuExitReason: "ExternalInterrupt/Halt/NestedPageFault"
    else Error/Special
        AxVCpuExitReason ->> AxVCpuExitReason: "Nothing/FailEntry"
    end
    AxVCpuExitReason ->> HypervisorHandler: "Return exit reason with context data"
    HypervisorHandler ->> HypervisorHandler: "Process based on exit type"
    HypervisorHandler ->> AxVCpu: "Resume or handle appropriately"

This sequence diagram illustrates how different types of guest operations result in specific exit reasons being created and returned to the hypervisor for processing.

Sources: src/exit.rs(L66 - L210) 

Memory Access and I/O Operations

Relevant source files

This document covers how memory access and I/O operations are handled within the axvcpu exit system. It details the mechanisms for memory-mapped I/O (MMIO), system register access, I/O port operations, and memory access violations that trigger VM exits. The focus is on the data structures and workflows that enable the hypervisor to intercept and emulate these operations.

For information about the broader exit handling categories and system, see Exit Reasons and Categories. For details on VCPU lifecycle management, see Core VCPU Management.

Access Width Specification

The AccessWidth enum provides a standardized way to specify the size of memory and I/O operations across different architectures. It supports 8-bit through 64-bit accesses with explicit bit range calculations.

Access TypeSize (bytes)Bit RangeUse Case
Byte10..8Character data, byte operations
Word20..1616-bit integers, x86 word operations
Dword40..3232-bit integers, most common size
Qword80..6464-bit integers, pointer operations
flowchart TD
AccessWidth["AccessWidth Enum"]
Byte["Byte (1 byte)0..8 bits"]
Word["Word (2 bytes)0..16 bits"]
Dword["Dword (4 bytes)0..32 bits"]
Qword["Qword (8 bytes)0..64 bits"]
ByteOps["Character I/OByte registers"]
WordOps["x86 16-bit operationsLegacy I/O"]
DwordOps["32-bit registersMost MMIO operations"]
QwordOps["64-bit pointersLarge data transfers"]

AccessWidth --> Byte
AccessWidth --> Dword
AccessWidth --> Qword
AccessWidth --> Word
Byte --> ByteOps
Dword --> DwordOps
Qword --> QwordOps
Word --> WordOps

The AccessWidth implementation provides conversion methods between enum values and byte sizes, enabling precise operation handling across architectures.

Sources: src/exit.rs(L6 - L61) 

Memory-Mapped I/O (MMIO) Operations

MMIO operations represent the most common form of device interaction in virtualized environments. When a guest attempts to access memory-mapped device registers, the hardware triggers a VM exit that the hypervisor must handle through device emulation.

MMIO Read Operations

MMIO read exits occur when the guest CPU attempts to read from a memory-mapped device register. The exit provides the physical address, access width, and register information needed for emulation.

sequenceDiagram
    participant GuestOS as "Guest OS"
    participant GuestCPU as "Guest CPU"
    participant Hypervisor as "Hypervisor"
    participant EmulatedDevice as "Emulated Device"

    GuestOS ->> GuestCPU: "ldr x0, [device_base + offset]"
    GuestCPU ->> Hypervisor: "MmioRead exit"
    Note over Hypervisor: "addr: GuestPhysAddr<br>width: AccessWidth<br>reg: usize<br>reg_width: AccessWidth"
    Hypervisor ->> EmulatedDevice: "Read device register"
    EmulatedDevice ->> Hypervisor: "Register value"
    Hypervisor ->> GuestCPU: "Set guest register"
    GuestCPU ->> GuestOS: "Continue execution"

The MmioRead variant includes both the memory access width and the target register width, allowing for proper handling of sub-register operations and sign extension.

MMIO Write Operations

MMIO write exits are triggered when the guest writes to memory-mapped device registers. The exit provides the target address, data value, and access width for the hypervisor to emulate the write operation.

flowchart TD
MmioWrite["MmioWrite Exit"]
addr["addr: GuestPhysAddrDevice register address"]
width["width: AccessWidthOperation size"]
data["data: u64Value to write"]
DeviceEmu["Device Emulation"]
RegUpdate["Update Device State"]
SideEffects["Trigger Side Effects"]
Interrupts["Generate Interrupts"]

DeviceEmu --> Interrupts
DeviceEmu --> RegUpdate
DeviceEmu --> SideEffects
MmioWrite --> addr
MmioWrite --> data
MmioWrite --> width
addr --> DeviceEmu
data --> DeviceEmu
width --> DeviceEmu

Sources: src/exit.rs(L78 - L97) 

System Register Access

System register operations provide a unified interface for accessing architecture-specific control and status registers. The implementation handles MSRs (x86), CSRs (RISC-V), and system registers (ARM64) through a common exit mechanism.

Register Address Encoding

Different architectures use distinct addressing schemes for system registers:

ArchitectureRegister TypeAddress Format
x86_64MSRDirect MSR number
RISC-VCSRDirect CSR address
ARM64System RegisterESR_EL2.ISS format:000000

System Register Read/Write Flow

flowchart TD
SysReg["System Register Access"]
SysRegRead["SysRegRead"]
SysRegWrite["SysRegWrite"]
ReadAddr["addr: usizeRegister address"]
ReadReg["reg: usizeTarget GPR index"]
WriteAddr["addr: usizeRegister address"]
WriteValue["value: u64Data to write"]
ArchHandler["Architecture-Specific Handler"]
x86MSR["x86: MSR Access"]
RISCVCSR["RISC-V: CSR Access"]
ARMSysReg["ARM64: System Register"]

ArchHandler --> ARMSysReg
ArchHandler --> RISCVCSR
ArchHandler --> x86MSR
ReadAddr --> ArchHandler
ReadReg --> ArchHandler
SysReg --> SysRegRead
SysReg --> SysRegWrite
SysRegRead --> ReadAddr
SysRegRead --> ReadReg
SysRegWrite --> WriteAddr
SysRegWrite --> WriteValue
WriteAddr --> ArchHandler
WriteValue --> ArchHandler

Sources: src/exit.rs(L98 - L125) 

I/O Port Operations

I/O port operations are specific to x86 architectures and handle legacy port-based device communication. These operations use 16-bit port addresses and support the standard x86 I/O instruction set.

Port I/O Characteristics

  • Port Range: 16-bit port numbers (0-65535)
  • Register Usage: Fixed to al/ax/eax registers
  • Architecture: x86-specific feature
  • Access Widths: Byte, Word, Dword operations
flowchart TD
IoOps["I/O Port Operations"]
IoRead["IoRead"]
IoWrite["IoWrite"]
ReadPort["port: u16Port number"]
ReadWidth["width: AccessWidthData size"]
WritePort["port: u16Port number"]
WriteWidth["width: AccessWidthData size"]
WriteData["data: u64Output value"]
PortEmulation["Port Device Emulation"]
LegacyDevices["Legacy PC Devices"]
DebugPorts["Debug Interfaces"]
ControlPorts["System Control"]

IoOps --> IoRead
IoOps --> IoWrite
IoRead --> ReadPort
IoRead --> ReadWidth
IoWrite --> WriteData
IoWrite --> WritePort
IoWrite --> WriteWidth
PortEmulation --> ControlPorts
PortEmulation --> DebugPorts
PortEmulation --> LegacyDevices
ReadPort --> PortEmulation
ReadWidth --> PortEmulation
WriteData --> PortEmulation
WritePort --> PortEmulation
WriteWidth --> PortEmulation

Sources: src/exit.rs(L126 - L145) 

Memory Access Violations

Nested page faults occur when guest memory accesses violate the host's memory protection policies. These exits enable the hypervisor to implement memory management features like demand paging, copy-on-write, and access control.

Nested Page Fault Handling

flowchart TD
GuestAccess["Guest Memory Access"]
PageTableWalk["Hardware Page Table Walk"]
ViolationCheck["Access Violation?"]
DirectAccess["Direct Memory Access"]
NestedPageFault["NestedPageFault Exit"]
FaultAddr["addr: GuestPhysAddrFaulting address"]
AccessFlags["access_flags: MappingFlagsAttempted operation"]
HypervisorHandler["Hypervisor Memory Manager"]
DemandPaging["Demand Paging"]
COW["Copy-on-Write"]
AccessControl["Access Control"]
SwapHandling["Swap Management"]

AccessFlags --> HypervisorHandler
FaultAddr --> HypervisorHandler
GuestAccess --> PageTableWalk
HypervisorHandler --> AccessControl
HypervisorHandler --> COW
HypervisorHandler --> DemandPaging
HypervisorHandler --> SwapHandling
NestedPageFault --> AccessFlags
NestedPageFault --> FaultAddr
PageTableWalk --> ViolationCheck
ViolationCheck --> DirectAccess
ViolationCheck --> NestedPageFault

The NestedPageFault exit provides the faulting guest physical address and the access flags that describe the type of operation that caused the fault (read, write, execute).

Sources: src/exit.rs(L153 - L161) 

Integration with Exit Handling System

All memory access and I/O operations are unified under the AxVCpuExitReason enum, providing a consistent interface for the hypervisor to handle different types of virtualization exits.

Exit Type Classification

flowchart TD
AxVCpuExitReason["AxVCpuExitReason"]
MemoryOps["Memory Operations"]
IOOps["I/O Operations"]
SystemOps["System Operations"]
MmioRead["MmioRead"]
MmioWrite["MmioWrite"]
NestedPageFault["NestedPageFault"]
IoRead["IoRead (x86)"]
IoWrite["IoWrite (x86)"]
SysRegRead["SysRegRead"]
SysRegWrite["SysRegWrite"]
AccessWidth["AccessWidth Support"]
ArchSpecific["Architecture-Specific Handling"]

AxVCpuExitReason --> IOOps
AxVCpuExitReason --> MemoryOps
AxVCpuExitReason --> SystemOps
IOOps --> IoRead
IOOps --> IoWrite
IoRead --> AccessWidth
IoWrite --> AccessWidth
MemoryOps --> MmioRead
MemoryOps --> MmioWrite
MemoryOps --> NestedPageFault
MmioRead --> AccessWidth
MmioWrite --> AccessWidth
SysRegRead --> ArchSpecific
SysRegWrite --> ArchSpecific
SystemOps --> SysRegRead
SystemOps --> SysRegWrite

This unified approach enables the hypervisor to implement comprehensive device emulation, memory management, and system control through a single exit handling interface.

Sources: src/exit.rs(L68 - L210) 

Implementation Details

Relevant source files

This document covers the core implementation systems that enable VCPU functionality in the axvcpu crate, focusing on the supporting infrastructure for virtualization state management and hardware abstraction. This includes the per-CPU virtualization state management system and the hardware abstraction layer that interfaces with the underlying host system.

For detailed information about VCPU lifecycle management and architecture abstraction, see Core VCPU Management. For specific exit handling mechanisms, see Exit Handling System.

Implementation Architecture Overview

The axvcpu implementation is built on two primary supporting systems that work together to provide a foundation for virtualization:

flowchart TD
subgraph subGraph2["Host System Integration"]
    ADDRSPACE["axaddrspaceAddress Space Management"]
    ERRNO["axerrnoError Handling"]
    PERCPU_LIB["percpuPer-CPU Variables"]
end
subgraph subGraph1["Implementation Infrastructure"]
    PERCPU["AxPerCpuPer-CPU State Manager"]
    HAL["AxVCpuHalHardware Abstraction"]
    ARCH_PERCPU["AxArchPerCpuArchitecture Per-CPU Trait"]
end
subgraph subGraph0["VCPU Management Layer"]
    VCPU["AxVCpuVCPU Manager"]
    EXIT["AxVCpuExitReasonExit Handler"]
end

HAL --> ADDRSPACE
PERCPU --> ARCH_PERCPU
PERCPU --> ERRNO
PERCPU --> PERCPU_LIB
VCPU --> HAL
VCPU --> PERCPU

Implementation Infrastructure Architecture

The implementation consists of two main abstraction layers:

ComponentPurposeKey Functions
AxPerCpuManages per-CPU virtualization stateinit(),hardware_enable(),hardware_disable()
AxVCpuHalHardware abstraction for memory and interruptsalloc_frame(),phys_to_virt(),irq_fetch()
AxArchPerCpuArchitecture-specific per-CPU operationsnew(),is_enabled(), hardware control

Sources: src/percpu.rs(L1 - L104)  src/hal.rs(L1 - L55) 

Per-CPU State Management System

The per-CPU state management system provides a generic wrapper around architecture-specific virtualization state, ensuring proper initialization and cleanup across different CPU architectures:

flowchart TD
subgraph subGraph1["Architecture Implementation"]
    ARCH_NEW["A::new(cpu_id)"]
    ARCH_ENABLE["A::hardware_enable()"]
    ARCH_DISABLE["A::hardware_disable()"]
    ARCH_CHECK["A::is_enabled()"]
end
subgraph subGraph0["Per-CPU State Lifecycle"]
    UNINIT["AxPerCpu::new_uninit()Uninitialized State"]
    INIT["init(cpu_id)Initialize Architecture State"]
    ENABLED["hardware_enable()Virtualization Active"]
    DISABLED["hardware_disable()Cleanup on Drop"]
end

DISABLED --> ARCH_DISABLE
ENABLED --> ARCH_CHECK
ENABLED --> ARCH_ENABLE
ENABLED --> DISABLED
INIT --> ARCH_NEW
INIT --> ENABLED
UNINIT --> INIT

Per-CPU State Management Flow

The AxPerCpu<A> struct provides a type-safe wrapper that:

Sources: src/percpu.rs(L40 - L95) 

Hardware Abstraction Layer

The AxVCpuHal trait defines the interface between the VCPU system and the underlying host system, abstracting memory management and interrupt handling:

flowchart TD
subgraph subGraph1["Host System Services"]
    FRAME_ALLOC["Frame Allocator"]
    ADDR_TRANS["Address Translation"]
    IRQ_SYSTEM["Interrupt Controller"]
end
subgraph subGraph0["AxVCpuHal Interface"]
    ALLOC["alloc_frame()→ Option"]
    DEALLOC["dealloc_frame(paddr)Frame Cleanup"]
    P2V["phys_to_virt(paddr)→ HostVirtAddr"]
    V2P["virt_to_phys(vaddr)→ HostPhysAddr"]
    IRQ_FETCH["irq_fetch()→ usize"]
    IRQ_HANDLER["irq_handler()Dispatch to Host"]
end

ALLOC --> FRAME_ALLOC
DEALLOC --> FRAME_ALLOC
IRQ_FETCH --> IRQ_SYSTEM
IRQ_HANDLER --> IRQ_SYSTEM
P2V --> ADDR_TRANS
V2P --> ADDR_TRANS

Hardware Abstraction Layer Interface

The HAL provides essential services for VCPU operation:

FunctionReturn TypePurpose
alloc_frame()OptionAllocate physical memory frame
dealloc_frame(paddr)()Release physical memory frame
phys_to_virt(paddr)HostVirtAddrConvert physical to virtual address
virt_to_phys(vaddr)HostPhysAddrConvert virtual to physical address
irq_fetch()usizeGet current interrupt number
irq_handler()()Dispatch interrupt to host

Sources: src/hal.rs(L4 - L54) 

Integration and Usage Patterns

The implementation components work together through well-defined integration patterns:

sequenceDiagram
    participant Hypervisor as "Hypervisor"
    participant AxPerCpuA as "AxPerCpu<A>"
    participant AxArchPerCpuImpl as "AxArchPerCpu Impl"
    participant AxVCpuHal as "AxVCpuHal"
    participant HostSystem as "Host System"

    Note over Hypervisor,HostSystem: Initialization Phase
    Hypervisor ->> AxPerCpuA: new_uninit()
    Hypervisor ->> AxPerCpuA: init(cpu_id)
    AxPerCpuA ->> AxArchPerCpuImpl: new(cpu_id)
    AxArchPerCpuImpl ->> AxVCpuHal: alloc_frame() (if needed)
    AxVCpuHal ->> HostSystem: allocate memory
    AxPerCpuA ->> AxArchPerCpuImpl: hardware_enable()
    Note over Hypervisor,HostSystem: Runtime Phase
    Hypervisor ->> AxPerCpuA: is_enabled()
    AxPerCpuA ->> AxArchPerCpuImpl: is_enabled()
    Hypervisor ->> AxVCpuHal: phys_to_virt(addr)
    AxVCpuHal ->> HostSystem: address translation
    Note over Hypervisor,HostSystem: Cleanup Phase
    Hypervisor ->> AxPerCpuA: Drop
    AxPerCpuA ->> AxArchPerCpuImpl: hardware_disable()
    AxArchPerCpuImpl ->> AxVCpuHal: dealloc_frame() (if needed)
    AxVCpuHal ->> HostSystem: release memory

Implementation Integration Sequence

The recommended usage pattern involves:

  1. Static Definition: Using the percpu crate to define per-CPU state src/percpu.rs(L27 - L28) 
  2. Initialization: Calling init() and hardware_enable() during hypervisor startup src/percpu.rs(L34 - L38) 
  3. Runtime Access: Using arch_checked() and arch_checked_mut() for safe access src/percpu.rs(L68 - L79) 
  4. Automatic Cleanup: Relying on the Drop implementation for proper shutdown src/percpu.rs(L97 - L103) 

Sources: src/percpu.rs(L21 - L39)  src/hal.rs(L1 - L55) 

Per-CPU Virtualization State

Relevant source files

This document covers the per-CPU virtualization state management system in axvcpu, which provides a safe abstraction for initializing, enabling, and managing hardware virtualization features on each CPU core. This system ensures that virtualization capabilities are properly configured across all CPUs in the hypervisor and provides architecture-independent lifecycle management with architecture-specific implementations.

For information about the overall VCPU management and state machine, see VCPU State Machine and Lifecycle. For details about the hardware abstraction layer, see Hardware Abstraction Layer.

Architecture Abstraction

The per-CPU virtualization state system is built around the AxArchPerCpu trait, which defines the interface that architecture-specific implementations must provide. This trait abstracts the hardware-specific operations needed to manage virtualization on a per-CPU basis.

AxArchPerCpu Trait Interface

The AxArchPerCpu trait defines four core operations for managing per-CPU virtualization state:

MethodPurposeReturn Type
new(cpu_id: usize)Create new per-CPU state for specified CPUAxResult
is_enabled(&self)Check if hardware virtualization is enabledbool
hardware_enable(&mut self)Enable hardware virtualization on current CPUAxResult
hardware_disable(&mut self)Disable hardware virtualization on current CPUAxResult

Each architecture (x86_64, ARM64, RISC-V) provides its own implementation of this trait to handle platform-specific virtualization setup and control.

AxArchPerCpu Trait Architecture

flowchart TD
subgraph subGraph3["RISC-V Implementation"]
    RISCV_IMPL["RiscVPerCpu"]
    RISCV_H["H Extension"]
    RISCV_CSR["CSR Configuration"]
end
subgraph subGraph2["ARM64 Implementation"]
    ARM_IMPL["ArmPerCpu"]
    ARM_EL2["EL2 Setup"]
    ARM_HYP["Hypervisor Mode"]
end
subgraph subGraph1["x86_64 Implementation"]
    X86_IMPL["X86PerCpu"]
    X86_VMX["VMX Setup"]
    X86_MSR["MSR Configuration"]
end
subgraph subGraph0["Architecture Abstraction"]
    TRAIT["AxArchPerCpu"]
    TRAIT_NEW["new(cpu_id: usize)"]
    TRAIT_ENABLED["is_enabled()"]
    TRAIT_ENABLE["hardware_enable()"]
    TRAIT_DISABLE["hardware_disable()"]
end

ARM_IMPL --> ARM_EL2
ARM_IMPL --> ARM_HYP
RISCV_IMPL --> RISCV_CSR
RISCV_IMPL --> RISCV_H
TRAIT --> TRAIT_DISABLE
TRAIT --> TRAIT_ENABLE
TRAIT --> TRAIT_ENABLED
TRAIT --> TRAIT_NEW
TRAIT_NEW --> ARM_IMPL
TRAIT_NEW --> RISCV_IMPL
TRAIT_NEW --> X86_IMPL
X86_IMPL --> X86_MSR
X86_IMPL --> X86_VMX

Sources: src/percpu.rs(L5 - L19) 

Per-CPU State Container

The AxPerCpu<A> struct serves as a safe wrapper around architecture-specific per-CPU state, providing initialization checking, lifecycle management, and automatic cleanup.

Structure and Fields

The AxPerCpu struct contains two key fields:

  • cpu_id: Option<usize> - Tracks the CPU ID and serves as an initialization flag
  • arch: MaybeUninit<A> - Stores the architecture-specific state in an uninitialized container

This design ensures that the architecture-specific state is only accessed after proper initialization and provides compile-time safety through the type system.

Initialization and Lifecycle

The per-CPU state follows a strict initialization pattern:

  1. Creation: new_uninit() creates an uninitialized state
  2. Initialization: init(cpu_id) initializes the architecture-specific state
  3. Usage: Methods check initialization before accessing architecture state
  4. Cleanup: Drop implementation automatically disables virtualization

AxPerCpu Lifecycle State Machine


Sources: src/percpu.rs(L40 - L95) 

Safety and Error Handling

The AxPerCpu implementation provides several safety mechanisms:

Initialization Checking

All methods that access the architecture-specific state use arch_checked() and arch_checked_mut(), which verify that initialization has occurred before accessing the underlying state:

  • Panics if cpu_id is None (not initialized)
  • Uses unsafe code only after verification that initialization occurred
  • Provides both immutable and mutable access patterns

Automatic Cleanup

The Drop implementation ensures that hardware virtualization is properly disabled when the per-CPU state is destroyed, preventing resource leaks and ensuring system stability.

Error Propagation

Methods return AxResult to propagate architecture-specific errors up to the hypervisor, allowing for proper error handling and recovery.

Sources: src/percpu.rs(L67 - L79)  src/percpu.rs(L97 - L103) 

Usage Patterns

The documentation provides a recommended usage pattern for integrating per-CPU state into a hypervisor:

Static Per-CPU Declaration

#[percpu::def_percpu]
pub static AXVM_PER_CPU: AxPerCpu<MyArchPerCpuImpl> = AxPerCpu::new_uninit();

Initialization and Enablement

let percpu = unsafe { AXVM_PER_CPU.current_ref_mut_raw() };
percpu.init(0).expect("Failed to initialize percpu state");
percpu.hardware_enable().expect("Failed to enable virtualization");

This pattern leverages the percpu crate to manage per-CPU variables and ensures that each CPU core has its own isolated virtualization state.

Per-CPU Integration Pattern

flowchart TD
subgraph subGraph2["Lifecycle Operations"]
    INIT_CALL["init(cpu_id)"]
    ENABLE_CALL["hardware_enable()"]
    RUNTIME["Runtime Operations"]
    DISABLE_CALL["hardware_disable()"]
end
subgraph subGraph1["Per-CPU Variables"]
    STATIC_VAR["AXVM_PER_CPU"]
    PERCPU_LIB["percpu crate"]
    CPU0["CPU 0 Instance"]
    CPU1["CPU 1 Instance"]
    CPUN["CPU N Instance"]
end
subgraph subGraph0["Hypervisor Initialization"]
    BOOT["Boot Process"]
    CPU_ENUM["CPU Enumeration"]
    PERCPU_INIT["Per-CPU Init"]
end

BOOT --> CPU_ENUM
CPU0 --> INIT_CALL
CPU_ENUM --> PERCPU_INIT
ENABLE_CALL --> RUNTIME
INIT_CALL --> ENABLE_CALL
PERCPU_INIT --> STATIC_VAR
PERCPU_LIB --> CPU0
PERCPU_LIB --> CPU1
PERCPU_LIB --> CPUN
RUNTIME --> DISABLE_CALL
STATIC_VAR --> PERCPU_LIB

Sources: src/percpu.rs(L23 - L39) 

Integration with VCPU System

The per-CPU virtualization state system provides the foundation for VCPU operations by ensuring that each physical CPU has the necessary hardware virtualization features enabled. This state is checked and managed independently of individual VCPU instances, allowing multiple VCPUs to share the same physical CPU infrastructure while maintaining isolation.

The is_enabled() method provides a quick check for whether a CPU is ready to run VCPUs, while the enable/disable methods allow for dynamic power management and system reconfiguration.

Sources: src/percpu.rs(L81 - L94) 

Hardware Abstraction Layer

Relevant source files

Purpose and Scope

The Hardware Abstraction Layer (HAL) provides a standardized interface for the VCPU system to interact with the underlying host environment, whether it's a kernel or hypervisor. The AxVCpuHal trait abstracts essential operations including memory management, address translation, and interrupt handling, enabling the VCPU implementation to remain portable across different host systems.

For information about architecture-specific VCPU implementations, see Architecture Abstraction Layer. For details about per-CPU state management, see Per-CPU Virtualization State.

HAL Interface Overview

The AxVCpuHal trait defines the contract between the VCPU system and the host environment:

flowchart TD
subgraph subGraph2["Host Environment"]
    KERNEL["Host Kernel/Hypervisor"]
    FRAME["Frame Allocator"]
    MMU["MMU/Page Tables"]
    INTC["Interrupt Controller"]
end
subgraph subGraph1["AxVCpuHal Interface"]
    HAL["AxVCpuHal trait"]
    MEM["Memory Operations"]
    ADDR["Address Translation"]
    IRQ["Interrupt Handling"]
end
subgraph subGraph0["VCPU Core System"]
    VCPU["AxVCpu"]
    ARCH["AxArchVCpu implementations"]
end

ADDR --> MMU
ARCH --> HAL
FRAME --> KERNEL
HAL --> ADDR
HAL --> IRQ
HAL --> MEM
INTC --> KERNEL
IRQ --> INTC
MEM --> FRAME
MMU --> KERNEL
VCPU --> HAL

HAL Trait Methods and Host System Integration

Sources: src/hal.rs(L1 - L54) 

Memory Management Functions

The HAL provides frame allocation and deallocation services required by the VCPU system for managing guest memory and virtualization structures:

FunctionPurposeParametersReturns
alloc_frameAllocates a physical memory frameNoneOption
dealloc_frameDeallocates a physical memory framepaddr: HostPhysAddrNone

The alloc_frame function returns None when allocation fails, allowing the VCPU system to handle memory pressure gracefully. These functions work with HostPhysAddr types from the axaddrspace crate to maintain type safety for physical address operations.

sequenceDiagram
    participant VCPUSystem as "VCPU System"
    participant AxVCpuHal as "AxVCpuHal"
    participant HostKernel as "Host Kernel"

    VCPUSystem ->> AxVCpuHal: "alloc_frame()"
    AxVCpuHal ->> HostKernel: "allocate physical frame"
    HostKernel -->> AxVCpuHal: "physical address or error"
    AxVCpuHal -->> VCPUSystem: "Option<HostPhysAddr>"
    Note over VCPUSystem: "Use allocated frame for guest memory"
    VCPUSystem ->> AxVCpuHal: "dealloc_frame(paddr)"
    AxVCpuHal ->> HostKernel: "free physical frame"
    HostKernel -->> AxVCpuHal: "completion"
    AxVCpuHal -->> VCPUSystem: "return"

Memory Frame Allocation and Deallocation Flow

Sources: src/hal.rs(L5 - L17) 

Address Translation Functions

Address translation is essential for converting between host physical and virtual addresses, supporting operations like accessing virtualization control structures and guest memory:

FunctionPurposeParametersReturns
phys_to_virtConvert physical to virtual addresspaddr: HostPhysAddrHostVirtAddr
virt_to_physConvert virtual to physical addressvaddr: HostVirtAddrHostPhysAddr

These functions enable the VCPU system to work with both physical addresses (for hardware configuration) and virtual addresses (for software access to data structures). The implementation relies on the host system's memory management unit and page table configuration.

Sources: src/hal.rs(L19 - L39) 

Interrupt Handling Functions

The HAL provides basic interrupt handling capabilities, though the current implementation includes placeholder functionality:

FunctionPurposeDefault BehaviorStatus
irq_fetchGet current IRQ numberReturns0Default implementation
irq_hanlderDispatch IRQ to hostunimplemented!()Requires implementation

The irq_fetch function provides a default implementation returning 0, while irq_hanlder (note the typo in the function name) requires implementation by the host system. This design allows basic interrupt functionality while requiring host-specific interrupt dispatch logic.

Sources: src/hal.rs(L41 - L53) 

Integration with Core Systems

The HAL integrates with several key components of the VCPU system and external dependencies:

flowchart TD
subgraph subGraph2["VCPU Core Components"]
    VCPU["AxVCpu"]
    ARCH["Architecture implementations"]
    PERCPU["Per-CPU state"]
end
subgraph subGraph1["AxVCpuHal Implementation"]
    TRAIT["AxVCpuHal trait"]
    ALLOC["alloc_frame()"]
    DEALLOC["dealloc_frame()"]
    P2V["phys_to_virt()"]
    V2P["virt_to_phys()"]
    FETCH["irq_fetch()"]
    HANDLER["irq_hanlder()"]
end
subgraph subGraph0["External Dependencies"]
    AXADDR["axaddrspace"]
    HOSTPHYS["HostPhysAddr"]
    HOSTVIRT["HostVirtAddr"]
end

ARCH --> TRAIT
AXADDR --> HOSTPHYS
AXADDR --> HOSTVIRT
HOSTPHYS --> ALLOC
HOSTPHYS --> DEALLOC
HOSTPHYS --> P2V
HOSTVIRT --> V2P
PERCPU --> TRAIT
TRAIT --> ALLOC
TRAIT --> DEALLOC
TRAIT --> FETCH
TRAIT --> HANDLER
TRAIT --> P2V
TRAIT --> V2P
VCPU --> TRAIT

HAL Dependencies and Component Integration

The HAL serves as the bridge between architecture-agnostic VCPU operations and host-specific system services. By implementing the AxVCpuHal trait, different host environments can provide their own memory management and interrupt handling strategies while maintaining compatibility with the VCPU abstraction layer.

Sources: src/hal.rs(L1 - L4)  src/hal.rs(L46 - L53) 

Development and Build System

Relevant source files

This document covers the build system, dependency management, continuous integration pipeline, and development workflow for the axvcpu crate. It provides guidance for developers on how to build, test, and contribute to the project.

For information about the crate's external dependencies and integration with the ArceOS ecosystem, see Dependencies and Integration. For details about the testing strategy and CI/CD pipeline, see Testing and Continuous Integration.

Build System Overview

The axvcpu crate uses Cargo as its build system with a standard Rust library configuration. The project targets multiple architectures and runtime environments, from hosted Linux environments to bare-metal embedded systems.

Package Configuration

The crate is configured as a standard Rust library with Rust Edition 2024 support. The package metadata is defined in Cargo.toml(L1 - L5)  with the package name axvcpu and version 0.1.0.

Build System Architecture

flowchart TD
subgraph subGraph2["Target Architectures"]
    X86_64_LINUX["x86_64-unknown-linux-gnu"]
    X86_64_NONE["x86_64-unknown-none"]
    RISCV64["riscv64gc-unknown-none-elf"]
    AARCH64["aarch64-unknown-none-softfloat"]
end
subgraph Dependencies["Dependencies"]
    AXERRNO["axerrno 0.1.0"]
    MEMADDR["memory_addr 0.3.1"]
    PERCPU["percpu 0.2.0"]
    AXADDRSPACE["axaddrspace (git)"]
end
subgraph subGraph0["Build System"]
    CARGO["cargo build"]
    TOML["Cargo.toml"]
    SRC["src/"]
end

CARGO --> AARCH64
CARGO --> RISCV64
CARGO --> TOML
CARGO --> X86_64_LINUX
CARGO --> X86_64_NONE
SRC --> CARGO
TOML --> AXADDRSPACE
TOML --> AXERRNO
TOML --> MEMADDR
TOML --> PERCPU

Sources: Cargo.toml(L1 - L12)  .github/workflows/ci.yml(L12) 

Dependency Management

The crate has minimal external dependencies, focusing on core virtualization functionality:

DependencyVersionPurpose
axerrno0.1.0Error code definitions and handling
memory_addr0.3.1Memory address abstractions
percpu0.2.0Per-CPU variable management
axaddrspacegitAddress space management (ArceOS ecosystem)

The axaddrspace dependency is pulled from the ArceOS hypervisor ecosystem via Git, indicating tight integration with the broader hypervisor framework Cargo.toml(L12) 

Sources: Cargo.toml(L7 - L12) 

Continuous Integration Pipeline

The CI system is implemented using GitHub Actions and supports multiple Rust toolchains and target architectures to ensure compatibility across different deployment scenarios.

CI/CD Pipeline Structure

flowchart TD
subgraph subGraph3["Documentation Job"]
    DOCBUILD["cargo doc --no-deps"]
    DEPLOY["JamesIves/github-pages-deploy-action@v4"]
end
subgraph subGraph2["CI Steps"]
    CHECKOUT["actions/checkout@v4"]
    RUSTUP["dtolnay/rust-toolchain@nightly"]
    FORMAT["cargo fmt --check"]
    CLIPPY["cargo clippy"]
    BUILD["cargo build"]
    TEST["cargo test"]
end
subgraph subGraph1["CI Job Matrix"]
    TOOLCHAIN1["nightly-2024-12-25"]
    TOOLCHAIN2["nightly"]
    TARGET1["x86_64-unknown-linux-gnu"]
    TARGET2["x86_64-unknown-none"]
    TARGET3["riscv64gc-unknown-none-elf"]
    TARGET4["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["Trigger Events"]
    PUSH["push"]
    PR["pull_request"]
end

BUILD --> TEST
CHECKOUT --> RUSTUP
CLIPPY --> BUILD
DOCBUILD --> DEPLOY
FORMAT --> CLIPPY
PR --> TOOLCHAIN1
PR --> TOOLCHAIN2
PUSH --> DOCBUILD
PUSH --> TOOLCHAIN1
PUSH --> TOOLCHAIN2
RUSTUP --> FORMAT
TARGET1 --> CHECKOUT
TOOLCHAIN1 --> TARGET1
TOOLCHAIN1 --> TARGET2
TOOLCHAIN1 --> TARGET3
TOOLCHAIN1 --> TARGET4

Sources: .github/workflows/ci.yml(L1 - L58) 

Multi-Architecture Testing

The CI pipeline tests across four target architectures:

  • x86_64-unknown-linux-gnu: Hosted Linux environment for development and unit testing
  • x86_64-unknown-none: Bare-metal x86_64 for hypervisor deployments
  • riscv64gc-unknown-none-elf: Bare-metal RISC-V with GC extensions
  • aarch64-unknown-none-softfloat: Bare-metal ARM64 with software floating point

Unit tests are only executed on the x86_64-unknown-linux-gnu target .github/workflows/ci.yml(L29 - L30)  since it's the only hosted environment that supports standard library features.

Code Quality Enforcement

The CI pipeline enforces code quality through multiple checks:

Sources: .github/workflows/ci.yml(L22 - L30) 

Documentation Generation

The project includes automated documentation generation and deployment:

Documentation Build Process

Documentation is built using cargo doc --no-deps --all-features .github/workflows/ci.yml(L49)  with strict link checking enabled through RUSTDOCFLAGS .github/workflows/ci.yml(L40)  The build process:

  1. Generates API documentation for all public interfaces
  2. Creates an index redirect page pointing to the main crate documentation
  3. Validates all intra-document links are functional
  4. Deploys to GitHub Pages on the default branch

GitHub Pages Deployment

Documentation is automatically deployed to GitHub Pages using the JamesIves/github-pages-deploy-action@v4 action .github/workflows/ci.yml(L53)  The deployment occurs only on pushes to the default branch, ensuring stable documentation reflects the latest released version.

Sources: .github/workflows/ci.yml(L32 - L57) 

Development Workflow

Local Development Setup

Developers should use Rust nightly toolchain nightly-2024-12-25 or later with the following components:

  • rust-src: For bare-metal target compilation
  • clippy: For linting
  • rustfmt: For code formatting

Build Commands

Standard development commands:

# Format code
cargo fmt --all

# Run linting
cargo clippy --all-features

# Build for specific target
cargo build --target x86_64-unknown-none --all-features

# Run tests (hosted target only)
cargo test --target x86_64-unknown-linux-gnu

Version Control

The project maintains a .gitignore configuration .gitignore(L1 - L18)  that excludes:

  • Build artifacts (/target, *.asm, *.img, *.bin, *.elf)
  • IDE settings (/.vscode)
  • Platform-specific files (.DS_Store)
  • Cargo.lock since axvcpu is a library crate

Sources: .github/workflows/ci.yml(L15 - L27)  .gitignore(L1 - L18) 

Dependencies and Integration

Relevant source files

This document explains the axvcpu crate's external dependencies and how it integrates with the ArceOS hypervisor ecosystem. It covers the four core dependencies (axerrno, memory_addr, percpu, axaddrspace) and describes the integration patterns that enable axvcpu to function as a virtualization abstraction layer.

For information about the actual VCPU management functionality, see Core VCPU Management. For details about testing and CI processes, see Testing and Continuous Integration.

Dependency Architecture

The axvcpu crate relies on four core dependencies that provide foundational abstractions for error handling, memory management, per-CPU operations, and address space management.

Dependency Graph

flowchart TD
subgraph subGraph1["axvcpu Core Modules"]
    lib_rs["lib.rs"]
    vcpu_rs["vcpu.rs"]
    exit_rs["exit.rs"]
    arch_vcpu_rs["arch_vcpu.rs"]
    hal_rs["hal.rs"]
    percpu_rs["percpu.rs"]
end
subgraph subGraph0["External Crates"]
    axerrno["axerrnov0.1.0"]
    memory_addr["memory_addrv0.3.1"]
    percpu_crate["percpuv0.2.0"]
    axaddrspace["axaddrspacegit dependency"]
end

axaddrspace --> lib_rs
axerrno --> lib_rs
hal_rs --> axaddrspace
lib_rs --> arch_vcpu_rs
lib_rs --> exit_rs
lib_rs --> hal_rs
lib_rs --> percpu_rs
lib_rs --> vcpu_rs
memory_addr --> lib_rs
percpu_crate --> lib_rs
percpu_rs --> percpu_crate
vcpu_rs --> arch_vcpu_rs
vcpu_rs --> exit_rs

Sources: Cargo.toml(L7 - L12) 

Core Dependencies Analysis

Error Handling Foundation - axerrno

The axerrno crate provides standardized error handling across the ArceOS ecosystem. axvcpu uses this for:

ComponentUsage PatternError Types
VCPU OperationsState transition failuresAxError::InvalidArg
Exit ProcessingHardware abstraction errorsAxError::Unsupported
Memory ManagementAddress space operationsAxError::NoMemory
Architecture InterfacePlatform-specific failuresAxError::BadState

Memory Abstractions - memory_addr

The memory_addr crate provides type-safe memory address abstractions used throughout the VCPU management system:

flowchart TD
subgraph subGraph1["axvcpu Usage"]
    exit_handling["Exit Handling"]
    mmio_ops["MMIO Operations"]
    guest_memory["Guest Memory"]
    host_memory["Host Memory"]
end
subgraph subGraph0["memory_addr Types"]
    VirtAddr["VirtAddr"]
    PhysAddr["PhysAddr"]
    Page["Page"]
    Frame["Frame"]
end

Frame --> host_memory
Page --> guest_memory
PhysAddr --> guest_memory
PhysAddr --> host_memory
VirtAddr --> exit_handling
VirtAddr --> mmio_ops

Sources: Cargo.toml(L9) 

Per-CPU State Management - percpu

The percpu crate enables efficient per-CPU variable management, critical for hypervisor operations where each physical CPU core needs isolated virtualization state:

Per-CPU ComponentPurposeAccess Pattern
VCPU Binding StateTrack which VCPU runs on which CPURead/Write during scheduling
Hardware ContextStore virtualization hardware stateSave/Restore on context switch
Exit StatisticsPerformance monitoring dataIncrement on VM exits

Address Space Integration - axaddrspace

The axaddrspace dependency is a git-based dependency providing address space management abstractions specifically designed for the ArceOS hypervisor:

flowchart TD
subgraph subGraph1["axvcpu Integration Points"]
    AxVCpuHal["AxVCpuHal trait"]
    hal_impl["hal.rs implementation"]
    guest_mappings["Guest Memory Mappings"]
    host_mappings["Host Memory Mappings"]
end
subgraph subGraph0["axaddrspace Abstractions"]
    AddrSpace["AddrSpace"]
    Mapping["Mapping"]
    Permission["Permission"]
    MemBackend["MemBackend"]
end

AddrSpace --> AxVCpuHal
AxVCpuHal --> hal_impl
Mapping --> hal_impl
MemBackend --> host_mappings
Permission --> guest_mappings
hal_impl --> guest_mappings
hal_impl --> host_mappings

Sources: Cargo.toml(L12) 

Integration Architecture

Trait-Based Integration Pattern

axvcpu uses trait-based abstractions to integrate with its dependencies while maintaining architectural independence:

flowchart TD
subgraph subGraph2["Implementation Layer"]
    arch_impl["Architecture implementations"]
    hal_impl["HAL implementations"]
    percpu_impl["Per-CPU implementations"]
end
subgraph subGraph1["axvcpu Trait Definitions"]
    AxArchVCpu["AxArchVCpu trait"]
    AxVCpuHal["AxVCpuHal trait"]
    AxPerCpu["AxPerCpu trait"]
end
subgraph subGraph0["Dependency Traits"]
    ErrorTrait["axerrno::AxResult"]
    AddrTrait["memory_addr::VirtAddr"]
    PerCpuTrait["percpu::PerCpu"]
    SpaceTrait["axaddrspace::AddrSpace"]
end

AddrTrait --> AxArchVCpu
AxArchVCpu --> arch_impl
AxPerCpu --> percpu_impl
AxVCpuHal --> hal_impl
ErrorTrait --> AxArchVCpu
ErrorTrait --> AxVCpuHal
PerCpuTrait --> AxPerCpu
SpaceTrait --> AxVCpuHal

Sources: Cargo.toml(L7 - L12) 

Build System Integration

The Cargo.toml configuration establishes the integration foundation:

Dependency TypeVersion StrategyIntegration Method
axerrnoSemantic versioning (0.1.0)Standard crates.io dependency
memory_addrSemantic versioning (0.3.1)Standard crates.io dependency
percpuSemantic versioning (0.2.0)Standard crates.io dependency
axaddrspaceGit dependencyArceOS ecosystem integration

The git dependency on axaddrspace indicates tight coupling with the ArceOS hypervisor ecosystem, while the semantic versioned dependencies provide stable foundations.

Ecosystem Integration Patterns

ArceOS Hypervisor Integration

flowchart TD
subgraph subGraph2["Supporting Ecosystem"]
    axaddrspace_sys["axaddrspace system"]
    error_system["axerrno error system"]
    percpu_system["percpu management"]
end
subgraph subGraph1["axvcpu Interface"]
    AxVCpu["AxVCpu"]
    AxVCpuExitReason["AxVCpuExitReason"]
    vcpu_state["VCpu state machine"]
end
subgraph subGraph0["ArceOS Hypervisor"]
    hypervisor["Hypervisor Core"]
    vm_manager["VM Manager"]
    scheduler["VCPU Scheduler"]
end

AxVCpu --> AxVCpuExitReason
AxVCpu --> axaddrspace_sys
AxVCpu --> error_system
AxVCpu --> percpu_system
AxVCpu --> vcpu_state
hypervisor --> AxVCpu
scheduler --> AxVCpu
vm_manager --> AxVCpu

Multi-Architecture Support Integration

The dependency structure enables cross-platform virtualization support:

ArchitectureIntegration PatternDependency Usage
x86_64VMX/VT-x hardware abstractionmemory_addrfor guest physical addresses
ARM64EL2 virtualizationpercpufor exception level state
RISC-VH-extension supportaxaddrspacefor guest address translation

Sources: Cargo.toml(L1 - L12) 

Version Management and Compatibility

The dependency version strategy ensures compatibility across the ArceOS ecosystem:

  • Stable dependencies: axerrno, memory_addr, percpu use semantic versioning for API stability
  • Ecosystem dependency: axaddrspace uses git dependency for coordinated development
  • Edition alignment: All components target Rust 2024 edition for language feature consistency

This integration architecture enables axvcpu to provide a stable virtualization abstraction while leveraging specialized components for error handling, memory management, and per-CPU operations within the broader ArceOS hypervisor ecosystem.

Sources: Cargo.toml(L1 - L12) 

Testing and Continuous Integration

Relevant source files

This page documents the comprehensive testing and continuous integration strategy for the axvcpu crate, including the CI/CD pipeline architecture, multi-architecture testing matrix, documentation generation, and development workflows. The testing framework ensures code quality and compatibility across multiple CPU architectures and Rust toolchain versions.

For information about build dependencies and external integrations, see Dependencies and Integration.

CI/CD Pipeline Overview

The axvcpu crate employs a GitHub Actions-based CI/CD pipeline that validates code quality, builds across multiple target architectures, and automatically generates and deploys documentation. The pipeline is configured to run on both push events and pull requests, ensuring comprehensive validation of all code changes.

CI Pipeline Architecture

flowchart TD
A["GitHub Push/PR"]
B["CI Job Matrix"]
C["Documentation Job"]
D["rust-toolchain Matrix"]
E["targets Matrix"]
F["nightly-2024-12-25"]
G["nightly"]
H["x86_64-unknown-linux-gnu"]
I["x86_64-unknown-none"]
J["riscv64gc-unknown-none-elf"]
K["aarch64-unknown-none-softfloat"]
L["Code Quality Checks"]
M["cargo fmt --check"]
N["cargo clippy"]
O["cargo build"]
P["cargo test"]
Q["Unit Tests Enabled"]
R["Build Only"]
S["cargo doc --no-deps"]
T["GitHub Pages Deploy"]
U["target/doc Output"]

A --> B
A --> C
B --> D
B --> E
C --> S
C --> T
D --> F
D --> G
E --> H
E --> I
E --> J
E --> K
F --> L
G --> L
H --> Q
I --> R
J --> R
K --> R
L --> M
L --> N
L --> O
L --> P
O --> R
P --> Q
S --> U
U --> T

Sources: .github/workflows/ci.yml(L1 - L58) 

The pipeline consists of two primary jobs that run in parallel, ensuring both functional correctness and documentation quality.

Multi-Architecture Testing Matrix

The CI system validates axvcpu across multiple CPU architectures and Rust toolchain versions to ensure broad compatibility with the ArceOS hypervisor ecosystem.

Target Architecture Configuration

Target ArchitecturePurposeTesting Level
x86_64-unknown-linux-gnuHost development and unit testingFull testing including unit tests
x86_64-unknown-noneBare-metal x86_64 hypervisorBuild validation and static analysis
riscv64gc-unknown-none-elfRISC-V hypervisor supportBuild validation and static analysis
aarch64-unknown-none-softfloatARM64 hypervisor supportBuild validation and static analysis

Rust Toolchain Matrix

The pipeline tests against two Rust toolchain configurations:

  • nightly-2024-12-25: Pinned stable nightly for reproducible builds
  • nightly: Latest nightly for early detection of compatibility issues

Both toolchains include essential components: rust-src, clippy, and rustfmt.

Sources: .github/workflows/ci.yml(L11 - L12)  .github/workflows/ci.yml(L16 - L19) 

Code Quality and Validation Pipeline

Static Analysis and Formatting

flowchart TD
A["Source Code"]
B["rustfmt Check"]
C["clippy Analysis"]
D["Compilation"]
E["Unit Tests"]
F["Format Validation"]
G["Lint Analysis"]
H["Multi-Arch Builds"]
I["Test Execution"]
J["CI Pass/Fail"]

A --> B
B --> C
B --> F
C --> D
C --> G
D --> E
D --> H
E --> I
F --> J
G --> J
H --> J
I --> J

Sources: .github/workflows/ci.yml(L22 - L30) 

The validation pipeline enforces code quality through multiple stages:

Format Checking

  • Command: cargo fmt --all -- --check
  • Purpose: Ensures consistent code formatting across the entire codebase
  • Failure Mode: Pipeline fails if any files are not properly formatted

Static Analysis

  • Command: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
  • Configuration: Allows new_without_default lint to accommodate API design patterns
  • Scope: Runs across all target architectures with full feature enablement

Build Validation

  • Command: cargo build --target ${{ matrix.targets }} --all-features
  • Coverage: All target architectures in the matrix
  • Features: Full feature set enabled to validate complete functionality

Unit Testing

  • Command: cargo test --target ${{ matrix.targets }} -- --nocapture
  • Restriction: Only runs on x86_64-unknown-linux-gnu target
  • Output: Detailed test output with --nocapture flag

Sources: .github/workflows/ci.yml(L23 - L30) 

Documentation Generation and Deployment

Documentation Pipeline

flowchart TD
A["Documentation Job"]
B["Rust Toolchain Setup"]
C["cargo doc Build"]
D["Index Page Generation"]
E["Branch Check"]
F["Default Branch?"]
G["Deploy to gh-pages"]
H["Build Only"]
I["RUSTDOCFLAGS Validation"]
J["Broken Links Check"]
K["Missing Docs Check"]
L["GitHub Pages Site"]
M["Pipeline Failure"]

A --> B
B --> C
C --> D
C --> I
D --> E
E --> F
F --> G
F --> H
G --> L
I --> J
I --> K
J --> M
K --> M

Sources: .github/workflows/ci.yml(L32 - L58) 

Documentation Configuration

The documentation build process includes strict validation and automatic deployment:

Rustdoc Validation

  • Environment: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
  • Validation: Treats broken internal links and missing documentation as errors
  • Purpose: Ensures comprehensive and accurate API documentation

Build Process

  • Command: cargo doc --no-deps --all-features
  • Scope: Generates documentation without external dependencies
  • Features: Includes all feature flags for complete API coverage

Index Generation

printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html

This automatically creates a redirect from the root documentation page to the main crate documentation.

Deployment Strategy

  • Target: GitHub Pages via gh-pages branch
  • Trigger: Only on default branch pushes
  • Method: Single-commit deployment using JamesIves/github-pages-deploy-action@v4
  • Source: target/doc directory

Sources: .github/workflows/ci.yml(L40)  .github/workflows/ci.yml(L48 - L57) 

Development Environment and Build Artifacts

Ignored Build Artifacts

The development environment excludes specific build artifacts from version control:

Artifact TypePatternPurpose
Build Output/targetRust compilation artifacts
Assembly Files*.asmGenerated assembly code
Binary Images.img,.bin,*.elfBootable images and binaries
Test Outputactual.out,qemu.logRuntime test artifacts
Lock FilesCargo.lockLibrary crates exclude lock files
IDE Settings/.vscode,.DS_StoreDevelopment environment files

Sources: .gitignore(L2 - L18) 

Testing Strategy and Coverage

The testing approach balances comprehensive validation with practical constraints:

Architecture-Specific Testing

  • Full Testing: Limited to x86_64-unknown-linux-gnu for unit test execution
  • Build Validation: All target architectures undergo compilation and static analysis
  • Rationale: Bare-metal targets cannot execute standard unit tests

Error Handling and Validation

  • Continue on Error: Documentation builds continue on non-default branches
  • Fail Fast: CI jobs use fail-fast: false to complete all matrix combinations
  • Branch Protection: Only default branch builds trigger documentation deployment

The testing framework ensures that axvcpu maintains compatibility across the supported architectures while providing comprehensive validation of core functionality through the Linux-based test environment.

Sources: .github/workflows/ci.yml(L8 - L9)  .github/workflows/ci.yml(L29 - L30)  .github/workflows/ci.yml(L47) 

Licensing and Legal

Relevant source files

This document outlines the multiple licensing options available for the axvcpu crate and provides guidance on legal compliance requirements for users, contributors, and distributors. The axvcpu crate implements a unique multi-license approach that offers flexibility for different use cases and jurisdictions.

For information about building and dependency management, see Dependencies and Integration. For details about the CI/CD pipeline and testing procedures, see Testing and Continuous Integration.

Multi-License Framework

The axvcpu crate is distributed under four different open-source licenses, allowing users to choose the most appropriate licensing terms for their specific use case. This approach maximizes compatibility with different projects and jurisdictions while maintaining open-source principles.

License Structure Overview

flowchart TD
subgraph subGraph1["License Categories"]
    PERMISSIVE["Permissive Licenses"]
    COPYLEFT["Copyleft Licenses"]
    REGIONAL["Regional Variants"]
end
subgraph subGraph0["AxVCpu Multi-License Options"]
    AXVCPU["axvcpu Crate"]
    APACHE["Apache License 2.0LICENSE.Apache2"]
    GPL["GNU GPL v3LICENSE.GPLv3"]
    MULAN_PSL["Mulan PSL v2LICENSE.MulanPSL2"]
    MULAN_PUB["Mulan PubL v2LICENSE.MulanPubL2"]
end

APACHE --> PERMISSIVE
AXVCPU --> APACHE
AXVCPU --> GPL
AXVCPU --> MULAN_PSL
AXVCPU --> MULAN_PUB
GPL --> COPYLEFT
MULAN_PSL --> PERMISSIVE
MULAN_PSL --> REGIONAL
MULAN_PUB --> COPYLEFT
MULAN_PUB --> REGIONAL

Sources: LICENSE.Apache2(L1 - L202)  LICENSE.GPLv3(L1 - L675)  LICENSE.MulanPSL2(L1 - L128)  LICENSE.MulanPubL2(L1 - L184) 

License Selection Matrix

LicenseTypePatent GrantAttribution RequiredSource DisclosureNetwork Copyleft
Apache 2.0PermissiveYesYesNoNo
GPL v3CopyleftYesYesYesNo
Mulan PSL v2PermissiveYesYesNoNo
Mulan PubL v2Network CopyleftYesYesYesYes

Individual License Summaries

Apache License 2.0

The Apache License 2.0 is a permissive license that allows broad usage rights while requiring attribution and patent grant protections.

Key Requirements:

  • Retain copyright notices and license text
  • Include copy of license with distributions
  • State changes made to files
  • Provide patent grant termination on patent litigation

Key Permissions:

  • Commercial use, distribution, modification
  • Private use, patent use
  • Sublicensing under different terms

Sources: LICENSE.Apache2(L66 - L88)  LICENSE.Apache2(L189 - L201) 

GNU General Public License v3

GPL v3 is a strong copyleft license ensuring derivative works remain open source.

Key Requirements:

  • Provide source code for distributed binaries
  • License entire work under GPL v3
  • Include installation information for user products
  • No additional restrictions on recipients

Key Features:

  • Anti-tivoization provisions
  • Patent retaliation clauses
  • International scope considerations

Sources: LICENSE.GPLv3(L154 - L177)  LICENSE.GPLv3(L245 - L295) 

Mulan Permissive Software License v2

Mulan PSL v2 is a permissive license designed for Chinese jurisdiction with bilingual terms.

Key Features:

  • Perpetual, worldwide, royalty-free rights
  • Patent grant with defensive termination
  • No trademark license granted
  • Chinese/English bilingual text with Chinese precedence

Network Service Provision: Unlike traditional software distribution, includes provisions for providing services over networks.

Sources: LICENSE.MulanPSL2(L84 - L90)  LICENSE.MulanPSL2(L104 - L106) 

Mulan Public License v2

Mulan PubL v2 extends copyleft requirements to network services, similar to AGPL v3.

Key Requirements:

  • Source code provision for network services
  • Copyleft applies to derivative works
  • AGPLv3 compatibility provisions
  • Chinese jurisdiction optimizations

Distribution Mechanisms:

  • Direct source code provision
  • Download link (minimum 3 years)
  • Written offer for source code

Sources: LICENSE.MulanPubL2(L118 - L143)  LICENSE.MulanPubL2(L154 - L156) 

License Selection Guidance

Decision Flow for License Choice

flowchart TD
START["Choosing License for AxVCpu Usage"]
JURISDICTION["PrimaryJurisdiction?"]
COPYLEFT["RequireCopyleft?"]
NETWORK["NetworkServices?"]
PATENT["PatentConcerns?"]
MULAN_REGION["Consider Mulan Licenses"]
WESTERN_REGION["Consider Apache/GPL"]
PERMISSIVE_CHOICE["Apache 2.0 or Mulan PSL v2"]
MULAN_PUB["Mulan PubL v2"]
GPL["GPL v3"]
APACHE["Apache 2.0(Explicit Patent Grant)"]
EITHER_PERMISSIVE["Apache 2.0 orMulan PSL v2"]

COPYLEFT --> NETWORK
COPYLEFT --> PERMISSIVE_CHOICE
JURISDICTION --> MULAN_REGION
JURISDICTION --> WESTERN_REGION
MULAN_REGION --> COPYLEFT
NETWORK --> GPL
NETWORK --> MULAN_PUB
PATENT --> APACHE
PATENT --> EITHER_PERMISSIVE
PERMISSIVE_CHOICE --> PATENT
START --> JURISDICTION
WESTERN_REGION --> COPYLEFT

Sources: LICENSE.Apache2(L73 - L87)  LICENSE.GPLv3(L471 - L490)  LICENSE.MulanPSL2(L88 - L90)  LICENSE.MulanPubL2(L122 - L124) 

Use Case Recommendations

Use CaseRecommended LicenseRationale
Commercial hypervisor productsApache 2.0Permissive, well-understood patent grants
Open source hypervisor projectsGPL v3Ensures derivative works remain open
Cloud service platformsMulan PubL v2Network copyleft prevents service exploitation
Research and academiaApache 2.0 or Mulan PSL v2Maximum flexibility for derivative research
Chinese market deploymentMulan PSL v2 or Mulan PubL v2Jurisdiction-optimized terms

Compliance Requirements

Attribution and Notice Requirements

All licenses require preservation of copyright notices and license attribution:

flowchart TD
subgraph subGraph1["Distribution Formats"]
    SOURCE["Source Distribution"]
    BINARY["Binary Distribution"]
    SERVICE["Network Service"]
end
subgraph subGraph0["Required Attribution Elements"]
    COPYRIGHT["Copyright Notices"]
    LICENSE_TEXT["License Text Copy"]
    CHANGES["Modification Notices"]
    PATENT_NOTICE["Patent Disclaimers"]
    TRADEMARK["Trademark Disclaimers"]
end

CHANGES --> SOURCE
COPYRIGHT --> BINARY
COPYRIGHT --> SERVICE
COPYRIGHT --> SOURCE
LICENSE_TEXT --> BINARY
LICENSE_TEXT --> SOURCE
PATENT_NOTICE --> BINARY
TRADEMARK --> SERVICE

Sources: LICENSE.Apache2(L94 - L105)  LICENSE.GPLv3(L197 - L203)  LICENSE.MulanPSL2(L98)  LICENSE.MulanPubL2(L134 - L135) 

Source Code Disclosure Requirements

For copyleft licenses (GPL v3, Mulan PubL v2), source code must be provided:

ScenarioGPL v3 RequirementMulan PubL v2 Requirement
Binary distributionCorresponding sourceCorresponding source code
Network servicesNo requirementSource for service platform
Derivative worksFull source under GPLSource under Mulan PubL
Installation infoUser products onlyNot specified
DurationAs long as distributedMinimum 3 years

Sources: LICENSE.GPLv3(L245 - L295)  LICENSE.MulanPubL2(L136 - L142) 

Warranty Disclaimers

All four licenses disclaim warranties and limit liability:

Common Disclaimers:

  • No warranties of any kind (express or implied)
  • No guarantee of fitness for particular purpose
  • No liability for damages from use or inability to use
  • "AS IS" basis provision

Patent Litigation Termination: All licenses include patent retaliation clauses that terminate patent grants if the licensee initiates patent litigation against contributors.

Compliance Monitoring

flowchart TD
subgraph subGraph0["Compliance Checklist"]
    CHECK_COPYRIGHT["✓ Copyright notices preserved"]
    CHECK_LICENSE["✓ License text included"]
    CHECK_CHANGES["✓ Changes documented"]
    CHECK_SOURCE["✓ Source available (if required)"]
    CHECK_PATENTS["✓ Patent grants understood"]
end
USAGE["AxVCpu Usage"]
LICENSE_CHOICE["Select Applicable License"]
TRACK_CHANGES["Track Modifications"]
ATTRIBUTION["Maintain Attribution"]
SOURCE_MGMT["Source Code Management"]

ATTRIBUTION --> SOURCE_MGMT
LICENSE_CHOICE --> TRACK_CHANGES
SOURCE_MGMT --> CHECK_CHANGES
SOURCE_MGMT --> CHECK_COPYRIGHT
SOURCE_MGMT --> CHECK_LICENSE
SOURCE_MGMT --> CHECK_PATENTS
SOURCE_MGMT --> CHECK_SOURCE
TRACK_CHANGES --> ATTRIBUTION
USAGE --> LICENSE_CHOICE

Sources: LICENSE.Apache2(L143 - L152)  LICENSE.GPLv3(L591 - L598)  LICENSE.MulanPSL2(L100 - L102)  LICENSE.MulanPubL2(L158 - L160) 

International Considerations

The inclusion of Mulan licenses provides specific advantages for users in Chinese jurisdiction, as these licenses are designed with Chinese legal framework considerations and include authoritative Chinese text alongside English translations.

Language Precedence:

  • Mulan licenses: Chinese version takes precedence in case of conflicts
  • Apache/GPL: English versions are authoritative

Sources: LICENSE.MulanPSL2(L104 - L106)  LICENSE.MulanPubL2(L162 - L164) 

Overview

Relevant source files

The axaddrspace crate provides guest virtual machine address space management capabilities for the ArceOS-Hypervisor system. This module handles the translation between guest physical addresses and host physical addresses through nested page tables, supports multiple memory mapping strategies, and provides a hardware abstraction layer for cross-platform virtualization support.

The crate enables hypervisors to manage guest VM memory efficiently while maintaining isolation between guest and host address spaces. For specific details about nested page table implementations, see Nested Page Tables. For information about memory mapping strategies, see Memory Mapping Backends.

Purpose and Scope

The axaddrspace crate serves as the core memory management component for guest VMs in the ArceOS-Hypervisor ecosystem. It abstracts the complexities of address translation, page table management, and platform-specific virtualization extensions into a unified API.

Core Responsibilities:

  • Guest-to-host address translation through nested page tables
  • Memory region management with configurable mapping backends
  • Hardware abstraction for memory operations across architectures
  • Page fault handling for dynamic memory allocation
  • Device address space management

Sources: Cargo.toml(L1 - L25)  src/lib.rs(L1 - L5) 

System Architecture

The following diagram illustrates the high-level architecture and key code entities within the axaddrspace crate:

System Component Overview

flowchart TD
subgraph subGraph5["Device Support"]
    Device["device/mod.rsdevice_addr.rs"]
end
subgraph subGraph4["Hardware Abstraction"]
    HAL["hal.rsAxMmHal"]
    Frame["frame.rsPhysFrame<H>"]
end
subgraph subGraph3["Nested Page Tables"]
    NptMod["npt/mod.rsNestedPageTable<H>"]
    ArchSel["npt/arch/mod.rs"]
    X86NPT["npt/arch/x86_64.rsExtendedPageTable"]
    ArmNPT["npt/arch/aarch64.rsNestedPageTable"]
    RiscvNPT["npt/arch/riscv.rsNestedPageTable"]
end
subgraph subGraph2["Memory Backends"]
    Backend["backend/mod.rsBackend<H>"]
    Linear["backend/linear.rsLinear"]
    Alloc["backend/alloc.rsAlloc"]
end
subgraph subGraph1["Address Management"]
    AddrMod["addr.rsGuestPhysAddrHostPhysAddrHostVirtAddr"]
    AddrSpace["address_space/AddrSpace<H>MemorySet"]
end
subgraph subGraph0["Core API Layer"]
    LibRS["lib.rsNestedPageFaultInfoAxMmHalPhysFrame"]
end

AddrSpace --> Backend
AddrSpace --> NptMod
Alloc --> HAL
ArchSel --> ArmNPT
ArchSel --> RiscvNPT
ArchSel --> X86NPT
Backend --> Alloc
Backend --> Linear
Frame --> HAL
LibRS --> AddrMod
LibRS --> AddrSpace
LibRS --> Device
LibRS --> Frame
LibRS --> HAL
NptMod --> ArchSel

Sources: src/lib.rs(L10 - L22)  Cargo.toml(L1 - L25) 

Key Components

Address Translation System

The crate implements a two-stage address translation system where guest virtual addresses are first translated to guest physical addresses by the guest OS, then guest physical addresses are translated to host physical addresses by the hypervisor's nested page tables.

Address Type Hierarchy

flowchart TD
subgraph subGraph2["Translation Mechanisms"]
    GuestPT["Guest Page Tables(Guest OS managed)"]
    NPT["NestedPageTable<H>(Hypervisor managed)"]
    HAL["AxMmHalphys_to_virt()virt_to_phys()"]
end
subgraph subGraph1["Host Address Space"]
    HPA["HostPhysAddr"]
    HVA["HostVirtAddr"]
end
subgraph subGraph0["Guest Address Space"]
    GVA["Guest Virtual Address"]
    GPA["GuestPhysAddr"]
end

GPA --> NPT
GVA --> GuestPT
GuestPT --> GPA
HAL --> HVA
HPA --> HAL
NPT --> HPA

Sources: src/lib.rs(L17)  src/lib.rs(L26 - L33) 

Memory Management Framework

The AddrSpace<H> struct serves as the central coordinator for memory management operations, utilizing different backend strategies and maintaining nested page table state.

ComponentTypePurpose
AddrSpaceMain structCoordinates memory operations and page table management
MemorySet<Backend>Memory regionsManages collections of mapped memory areas
BackendStrategy enumSelects between Linear and Alloc mapping strategies
NestedPageTablePage tableArchitecture-specific guest-to-host translation
NestedPageFaultInfoFault handlerProvides context for page fault resolution

Sources: src/lib.rs(L18)  src/lib.rs(L26 - L33) 

Hardware Abstraction Layer

The AxMmHal trait provides platform-independent memory operations that must be implemented by the underlying hypervisor platform. The PhysFrame<H> type implements RAII semantics for physical memory frame management.

Sources: src/lib.rs(L20 - L21) 

Error Handling

The crate integrates with the ArceOS error handling system through the mapping_err_to_ax_err function, which converts MappingError types from the memory_set crate into AxError types.

Sources: src/lib.rs(L23 - L24)  src/lib.rs(L35 - L42) 

Dependencies and Integration

The crate builds upon several foundational ArceOS modules:

  • memory_addr: Provides base address type definitions
  • memory_set: Supplies memory region management primitives
  • page_table_entry: Offers page table entry abstractions
  • page_table_multiarch: Enables multi-architecture page table support
  • axerrno: Provides standardized error types

Sources: Cargo.toml(L19 - L24) 

Feature Configuration

The crate supports architecture-specific features through Cargo feature flags:

  • arm-el2: Enables ARM EL2 (hypervisor) mode support (default)

Sources: Cargo.toml(L7 - L9) 

Core Architecture

Relevant source files

This document explains the fundamental architectural components of the axaddrspace crate and how they work together to provide address space management for guest VMs in ArceOS-Hypervisor. The core architecture handles the translation between guest and host address spaces, manages memory mappings with different backend strategies, and provides hardware-abstracted nested page table support.

For detailed information about specific address types and their usage, see Address Types and Spaces. For implementation details of nested page tables, see Nested Page Tables. For memory mapping strategies, see Memory Mapping Backends.

System Overview

The axaddrspace crate is organized around several key architectural components that work together to provide comprehensive guest address space management:

Core System Architecture

flowchart TD
subgraph subGraph4["Memory Backends"]
    Backend["Backend implementations"]
end
subgraph subGraph3["Nested Page Tables"]
    NPT["npt/"]
end
subgraph subGraph2["Hardware Abstraction"]
    HAL["hal.rs (AxMmHal)"]
    Frame["frame.rs (PhysFrame)"]
end
subgraph subGraph1["Address Management"]
    AddrMod["addr.rs"]
    AddrSpace["address_space/"]
end
subgraph subGraph0["Core Exports (lib.rs)"]
    LibCore["lib.rs"]
    NPTFault["NestedPageFaultInfo"]
end
subgraph subGraph5["Device Support"]
    Device["device/"]
end

AddrMod --> AddrSpace
AddrMod --> NPT
AddrSpace --> Backend
AddrSpace --> NPT
Backend --> HAL
Frame --> HAL
LibCore --> AddrMod
LibCore --> AddrSpace
LibCore --> Frame
LibCore --> HAL
LibCore --> NPTFault

Sources: src/lib.rs(L1 - L43) 

Core Components

Address Type System

The foundation of the architecture is a comprehensive address type system that distinguishes between different address spaces and contexts:

Address TypePurposeDefinition
HostVirtAddrHost virtual addressesType alias toVirtAddr
HostPhysAddrHost physical addressesType alias toPhysAddr
GuestVirtAddrGuest virtual addressesCustom usize-based type
GuestPhysAddrGuest physical addressesCustom usize-based type

Address Type Relationships

flowchart TD
subgraph subGraph2["Address Ranges"]
    GVAR["GuestVirtAddrRange"]
    GPAR["GuestPhysAddrRange"]
end
subgraph subGraph1["Guest Address Space"]
    GVA["GuestVirtAddr"]
    GPA["GuestPhysAddr"]
end
subgraph subGraph0["Host Address Space"]
    HVA["HostVirtAddr"]
    HPA["HostPhysAddr"]
end

GPA --> GPAR
GPA --> HPA
GVA --> GPA
GVA --> GVAR
HVA --> HPA

Sources: src/addr.rs(L1 - L31) 

Hardware Abstraction Layer

The AxMmHal trait provides a hardware-abstracted interface for memory management operations, enabling platform-independent code while supporting architecture-specific optimizations.

HAL and Frame Management Integration

flowchart TD
subgraph subGraph2["Backend Integration"]
    AllocBackend["Alloc Backend"]
    LinearBackend["Linear Backend"]
end
subgraph subGraph1["PhysFrame RAII Management"]
    Frame["PhysFrame<H>"]
    FrameAlloc["alloc() / alloc_zero()"]
    FrameDrop["Drop implementation"]
end
subgraph subGraph0["AxMmHal Trait Interface"]
    HAL["AxMmHal<H>"]
    AllocFrame["alloc_frame()"]
    DeallocFrame["dealloc_frame()"]
    PhysToVirt["phys_to_virt()"]
    VirtToPhys["virt_to_phys()"]
end

AllocBackend --> Frame
Frame --> FrameAlloc
Frame --> FrameDrop
FrameAlloc --> AllocFrame
FrameDrop --> DeallocFrame
HAL --> AllocFrame
HAL --> DeallocFrame
HAL --> PhysToVirt
HAL --> VirtToPhys
LinearBackend --> PhysToVirt

Sources: src/lib.rs(L20 - L21)  src/hal.rs src/frame.rs

Nested Page Fault Handling

The architecture includes a structured approach to handling nested page faults that occur during guest memory access:

pub struct NestedPageFaultInfo {
    pub access_flags: MappingFlags,
    pub fault_guest_paddr: GuestPhysAddr,
}

This structure captures the essential information needed to resolve page faults, including the type of memory access that caused the fault and the guest physical address that was being accessed.

Sources: src/lib.rs(L26 - L33) 

Component Interaction Flow

The core architecture supports a layered interaction model where each component has clearly defined responsibilities:

Memory Management Flow

flowchart TD
subgraph subGraph4["Fault Handling"]
    PageFault["NestedPageFaultInfo"]
    FaultHandler["Backend-specific Handler"]
end
subgraph subGraph3["Hardware Layer"]
    HAL["AxMmHal<H>"]
    Frame["PhysFrame<H>"]
    HPA["HostPhysAddr"]
end
subgraph subGraph2["Page Table Layer"]
    NPT["NestedPageTable"]
    Translation["GPA → HPA Translation"]
end
subgraph subGraph1["Address Space Layer"]
    AddrSpace["AddrSpace<H>"]
    Backend["Backend Strategy"]
end
subgraph subGraph0["Guest VM Access"]
    GuestAccess["Guest Memory Access"]
    GPA["GuestPhysAddr"]
end

AddrSpace --> Backend
AddrSpace --> NPT
Backend --> Frame
Backend --> HAL
FaultHandler --> Backend
GPA --> AddrSpace
GuestAccess --> GPA
GuestAccess --> PageFault
HAL --> HPA
NPT --> Translation
PageFault --> FaultHandler
Translation --> HPA

Sources: src/lib.rs(L26 - L33)  src/address_space/ src/npt/

Error Handling Architecture

The system includes a centralized error mapping mechanism that converts internal mapping errors to standardized AxError types:

#![allow(unused)]
fn main() {
fn mapping_err_to_ax_err(err: MappingError) -> AxError {
    match err {
        MappingError::InvalidParam => AxError::InvalidInput,
        MappingError::AlreadyExists => AxError::AlreadyExists,
        MappingError::BadState => AxError::BadState,
    }
}
}

This provides consistent error handling across all address space operations while maintaining compatibility with the broader ArceOS error handling framework.

Sources: src/lib.rs(L35 - L42) 

Module Organization

The crate is organized into focused modules that each handle specific aspects of address space management:

ModulePrimary ResponsibilityKey Types
addrAddress type definitionsGuestPhysAddr,HostPhysAddr, address ranges
address_spaceVirtual memory managementAddrSpace, memory mapping strategies
halHardware abstractionAxMmHaltrait
framePhysical frame managementPhysFrameRAII wrapper
nptNested page tablesArchitecture-specific page table implementations
deviceDevice supportDevice address abstractions

Sources: src/lib.rs(L10 - L22) 

The architecture provides a clean separation of concerns while enabling efficient guest-to-host address translation and memory management for hypervisor use cases.

Address Types and Spaces

Relevant source files

This document explains the fundamental address types and memory spaces used throughout the axaddrspace crate. These types form the foundation for all virtual memory management and address translation operations in the ArceOS-Hypervisor system.

The address type system distinguishes between guest and host address spaces, and between virtual and physical addressing within each space. For information about how these addresses are used in page table implementations, see Architecture-Specific Implementations. For details on how address spaces are managed at the system level, see Address Space Management.

Address Type Hierarchy

The codebase defines four primary address types that represent different addressing contexts within the hypervisor environment:


Address Type Definitions

TypeDefinitionPurposeSource
HostVirtAddrVirtAddraliasHost kernel virtual addressessrc/addr.rs4
HostPhysAddrPhysAddraliasHost machine physical addressessrc/addr.rs6
GuestVirtAddrCustomusizetypeGuest virtual addresses (GVA)src/addr.rs10
GuestPhysAddrCustomusizetypeGuest physical addresses (GPA)src/addr.rs12

Sources: src/addr.rs(L1 - L31) 

Guest vs Host Address Spaces

The hypervisor operates with two distinct address spaces, each serving different roles in the virtualization stack:

flowchart TD
subgraph subGraph2["Translation Mechanisms"]
    GPT["Guest Page Tables(GVA → GPA)"]
    NPT["Nested Page Tables(GPA → HPA)"]
    HMM["Host MMU(HVA → HPA)"]
end
subgraph subGraph1["Host Hypervisor Perspective"]
    HVA["Host VirtualAddress Space(HostVirtAddr)"]
    HPA["Host PhysicalAddress Space(HostPhysAddr)"]
end
subgraph subGraph0["Guest VM Perspective"]
    GVA["Guest VirtualAddress Space(GuestVirtAddr)"]
    GPA["Guest PhysicalAddress Space(GuestPhysAddr)"]
end

GPA --> NPT
GPT --> GPA
GVA --> GPT
HMM --> HPA
HVA --> HMM
NPT --> HPA

Address Space Characteristics

  • Guest Address Space: Managed by the guest operating system, providing the illusion of direct hardware access
  • Host Address Space: Managed by the hypervisor, representing actual physical hardware resources
  • Translation Chain: Guest virtual → Guest physical → Host physical addresses

Sources: src/addr.rs(L8 - L13) 

Address Type Implementation Details

The guest address types are implemented using specialized macros that provide type safety and debugging support:

flowchart TD
subgraph subGraph2["Display Formatting"]
    GVAFmt["GVA:{} format"]
    GPAFmt["GPA:{} format"]
end
subgraph subGraph1["Generated Address Types"]
    GuestVirtImpl["GuestVirtAddrstruct"]
    GuestPhysImpl["GuestPhysAddrstruct"]
end
subgraph subGraph0["Macro-Generated Types"]
    DefAddr["def_usize_addr!macro"]
    DefFmt["def_usize_addr_formatter!macro"]
end

DefAddr --> GuestPhysImpl
DefAddr --> GuestVirtImpl
DefFmt --> GPAFmt
DefFmt --> GVAFmt
GuestPhysImpl --> GPAFmt
GuestVirtImpl --> GVAFmt

Type Generation and Formatting

The def_usize_addr! macro creates strongly-typed wrappers around usize values, while def_usize_addr_formatter! provides custom debug formatting:

  • GuestVirtAddr displays as "GVA:{address}"
  • GuestPhysAddr displays as "GPA:{address}"

This approach ensures type safety while maintaining efficient runtime representation as raw pointer-sized integers.

Sources: src/addr.rs(L8 - L18) 

Address Range Abstractions

The system provides range types for representing contiguous memory regions within guest address spaces:

flowchart TD
subgraph subGraph1["Usage Contexts"]
    MemRegion["Memory RegionDescriptors"]
    MapOps["Mapping OperationsBatch Processing"]
    Validation["Address RangeValidation"]
end
subgraph subGraph0["Range Type Definitions"]
    AddrRange["AddrRange(from memory_addr crate)"]
    GVARange["GuestVirtAddrRangetype alias"]
    GPARange["GuestPhysAddrRangetype alias"]
end

AddrRange --> GPARange
AddrRange --> GVARange
GPARange --> MapOps
GPARange --> MemRegion
GPARange --> Validation
GVARange --> MapOps
GVARange --> MemRegion
GVARange --> Validation

Range Operations

Address ranges support standard operations for memory region management:

  • Start and end address access
  • Size calculation
  • Overlap detection
  • Alignment validation

Sources: src/addr.rs(L20 - L23) 

Architecture-Specific Integration

The address types integrate with architecture-specific page table implementations through trait implementations:

flowchart TD
subgraph subGraph2["Target Architectures"]
    RISCV32["riscv32"]
    RISCV64["riscv64"]
end
subgraph Implementation["Implementation"]
    GPAImpl["GuestPhysAddrSvVirtAddr impl"]
    TODOFlush["todo!() placeholder"]
end
subgraph subGraph0["Architecture Traits"]
    SvVirtAddr["SvVirtAddr trait(RISC-V specific)"]
    FlushTLB["flush_tlb() method"]
end

FlushTLB --> TODOFlush
GPAImpl --> SvVirtAddr
RISCV32 --> GPAImpl
RISCV64 --> GPAImpl
SvVirtAddr --> FlushTLB

RISC-V Integration

For RISC-V architectures, GuestPhysAddr implements the SvVirtAddr trait required by the page table implementation. The flush_tlb() method is currently unimplemented, indicating this functionality is still under development.

Sources: src/addr.rs(L25 - L30) 

Address Space Management

Relevant source files

This document covers the AddrSpace<H> system that manages guest virtual memory spaces in the axaddrspace crate. The address space manager coordinates memory regions, page tables, and mapping backends to provide virtualized memory management for guest systems.

For information about the underlying nested page table implementations, see Nested Page Tables. For details about the hardware abstraction layer, see Hardware Abstraction Layer. For specific backend implementations, see Memory Mapping Backends.

Purpose and Core Structure

The AddrSpace<H> struct serves as the central coordinator for guest virtual memory management. It maintains a collection of memory areas with different mapping strategies and uses nested page tables for address translation.

AddrSpace Components

The following diagram shows the core components of the address space management system:

flowchart TD
subgraph subGraph2["Backend Types"]
    Linear["Linear { pa_va_offset: usize }"]
    Alloc["Alloc { populate: bool }"]
end
subgraph subGraph1["MemorySet Structure"]
    MS["MemorySet>"]
    MA["MemoryArea"]
    BE["Backend"]
end
subgraph AddrSpace<H>["AddrSpace"]
    AS["AddrSpace"]
    VARange["va_range: GuestPhysAddrRange"]
    Areas["areas: MemorySet>"]
    PT["pt: PageTable"]
end

AS --> Areas
AS --> PT
AS --> VARange
Areas --> MS
BE --> Alloc
BE --> Linear
MA --> BE
MS --> MA

Sources: src/address_space/mod.rs(L17 - L22)  src/address_space/backend/mod.rs(L19 - L41) 

Memory Mapping Workflow

The address space manager supports two primary mapping operations through different backends:

Mapping Operations Flow

flowchart TD
subgraph subGraph0["Backend Dispatch"]
    LinearMap["Linear: map_linear()"]
    AllocMap["Alloc: map_alloc()"]
end
MapReq["map_linear() or map_alloc()"]
Validate["Validate address range and alignment"]
CreateArea["Create MemoryArea with Backend"]
MapToPageTable["areas.map(area, &mut pt, false)"]
BackendMap["Backend.map() implementation"]

BackendMap --> AllocMap
BackendMap --> LinearMap
CreateArea --> MapToPageTable
MapReq --> Validate
MapToPageTable --> BackendMap
Validate --> CreateArea

Sources: src/address_space/mod.rs(L70 - L89)  src/address_space/mod.rs(L97 - L119)  src/address_space/backend/mod.rs(L60 - L79) 

Address Space Operations

The AddrSpace<H> provides the following key operations:

OperationMethodPurpose
Linear Mappingmap_linear()Creates fixed-offset mappings between guest and host addresses
Dynamic Mappingmap_alloc()Creates dynamically allocated mappings with optional population
Unmappingunmap()Removes existing mappings from specified ranges
Translationtranslate()Converts guest virtual addresses to host physical addresses
Page Fault Handlinghandle_page_fault()Processes page faults for lazy allocation scenarios

Sources: src/address_space/mod.rs(L70 - L161) 

Backend Strategy Selection

The Backend<H> enum implements the MappingBackend trait to provide different memory mapping strategies:

Backend Implementation Details

flowchart TD
subgraph subGraph2["Implementation Strategy"]
    LinearImpl["map_linear() / unmap_linear()"]
    AllocImpl["map_alloc() / unmap_alloc()"]
end
subgraph subGraph1["MappingBackend Trait"]
    MapMethod["map(start, size, flags, pt)"]
    UnmapMethod["unmap(start, size, pt)"]
    ProtectMethod["protect(start, size, flags, pt)"]
end
subgraph subGraph0["Backend Enum"]
    Backend["Backend"]
    LinearVariant["Linear { pa_va_offset }"]
    AllocVariant["Alloc { populate, _phantom }"]
end

Backend --> AllocVariant
Backend --> LinearVariant
Backend --> MapMethod
Backend --> ProtectMethod
Backend --> UnmapMethod
MapMethod --> AllocImpl
MapMethod --> LinearImpl

Sources: src/address_space/backend/mod.rs(L55 - L90) 

Backend Characteristics

Backend TypeAddress TranslationMemory AllocationUse Case
LinearFixed offset (vaddr - pa_va_offset)Pre-allocated contiguous framesDevice memory, kernel mappings
AllocDynamic allocationGlobal allocator (eager/lazy)General purpose, user memory

Sources: src/address_space/backend/mod.rs(L19 - L41) 

Page Fault Management

The address space manager handles page faults through a coordinated approach between the memory areas and their backends:

Page Fault Handling Flow

sequenceDiagram
    participant Caller as Caller
    participant AddrSpaceH as "AddrSpace<H>"
    participant MemorySet as "MemorySet"
    participant BackendH as "Backend<H>"
    participant PageTableH as "PageTable<H>"

    Caller ->> AddrSpaceH: handle_page_fault(vaddr, access_flags)
    AddrSpaceH ->> AddrSpaceH: Check va_range.contains(vaddr)
    AddrSpaceH ->> MemorySet: find(vaddr)
    MemorySet -->> AddrSpaceH: Some(area) or None
    alt Area found
        AddrSpaceH ->> AddrSpaceH: Check orig_flags.contains(access_flags)
    alt Flags compatible
        AddrSpaceH ->> BackendH: handle_page_fault(vaddr, orig_flags, pt)
        BackendH ->> BackendH: Match backend type
    alt Linear backend
    BackendH -->> AddrSpaceH: false (no page faults)
    else Alloc backend
    BackendH ->> PageTableH: Allocate and map frame
    BackendH -->> AddrSpaceH: true (fault handled)
    end
    else Flags incompatible
        AddrSpaceH -->> Caller: false
    end
    else Area not found
        AddrSpaceH -->> Caller: false
    end

Sources: src/address_space/mod.rs(L147 - L161)  src/address_space/backend/mod.rs(L93 - L106) 

Address Translation Services

The address space provides multiple translation interfaces for different use cases:

Translation Methods

MethodReturn TypeUse Case
translate()OptionSimple address translation
translate_and_get_limit()Option<(PhysAddr, usize)>Translation with area size information
translated_byte_buffer()Option<Vec<&'static mut [u8]>>Direct memory access through page table

Translation Implementation

flowchart TD
TranslateReq["translate(vaddr)"]
CheckRange["Check va_range.contains(vaddr)"]
PageTableQuery["pt.query(vaddr)"]
ReturnResult["Return (phys_addr, flags, page_size)"]
ReturnNone["Return None"]

CheckRange --> PageTableQuery
CheckRange --> ReturnNone
PageTableQuery --> ReturnResult
TranslateReq --> CheckRange

Sources: src/address_space/mod.rs(L166 - L177)  src/address_space/mod.rs(L234 - L246) 

Lifecycle Management

The AddrSpace<H> implements proper resource cleanup through the Drop trait:

  • Automatic cleanup: The drop() implementation calls clear() to remove all mappings
  • Explicit cleanup: The clear() method removes all memory areas and their page table entries
  • Memory safety: Physical frames are automatically deallocated through the backend implementations

Sources: src/address_space/mod.rs(L259 - L263)  src/address_space/mod.rs(L137 - L139) 

Hardware Abstraction Layer

Relevant source files

Purpose and Scope

The Hardware Abstraction Layer (HAL) provides a platform-independent interface for memory management operations within the axaddrspace crate. It abstracts low-level memory allocation, deallocation, and address translation functions that are specific to the underlying hypervisor or operating system implementation.

The HAL consists of two primary components: the AxMmHal trait that defines the interface for hardware-specific operations, and the PhysFrame struct that provides RAII-based physical memory frame management. For information about how address types are defined and used throughout the system, see Address Types and Spaces. For details on how the HAL integrates with memory mapping strategies, see Linear Backend and Allocation Backend.

AxMmHal Trait Interface

The AxMmHal trait defines the core hardware abstraction interface for memory management operations. This trait must be implemented by the host system to provide concrete implementations of memory allocation and address translation.

AxMmHal Trait Methods

flowchart TD
subgraph subGraph1["Input/Output Types"]
    hpa["HostPhysAddr"]
    hva["HostVirtAddr"]
    opt["Option<HostPhysAddr>"]
end
subgraph subGraph0["AxMmHal Trait Interface"]
    trait["AxMmHal"]
    alloc["alloc_frame()"]
    dealloc["dealloc_frame(paddr)"]
    p2v["phys_to_virt(paddr)"]
    v2p["virt_to_phys(vaddr)"]
end

alloc --> opt
dealloc --> hpa
p2v --> hpa
p2v --> hva
trait --> alloc
trait --> dealloc
trait --> p2v
trait --> v2p
v2p --> hpa
v2p --> hva

The trait provides four essential operations:

MethodPurposeParametersReturn Type
alloc_frame()Allocates a physical memory frameNoneOption
dealloc_frame()Deallocates a physical memory framepaddr: HostPhysAddr()
phys_to_virt()Converts physical to virtual addresspaddr: HostPhysAddrHostVirtAddr
virt_to_phys()Converts virtual to physical addressvaddr: HostVirtAddrHostPhysAddr

Sources: src/hal.rs(L4 - L40) 

PhysFrame RAII Management

The PhysFrame<H: AxMmHal> struct provides automatic memory management for physical memory frames using the RAII (Resource Acquisition Is Initialization) pattern. It ensures that allocated frames are automatically deallocated when the frame goes out of scope.

PhysFrame Lifecycle

flowchart TD
subgraph Cleanup["Cleanup"]
    drop["Drop::drop()"]
    hal_dealloc["H::dealloc_frame()"]
end
subgraph Operations["Operations"]
    start["start_paddr()"]
    ptr["as_mut_ptr()"]
    fill["fill(byte)"]
end
subgraph subGraph1["PhysFrame State"]
    frame["PhysFrame<H>"]
    paddr["start_paddr: Option<HostPhysAddr>"]
    marker["_marker: PhantomData<H>"]
end
subgraph subGraph0["Allocation Methods"]
    alloc["PhysFrame::alloc()"]
    alloc_zero["PhysFrame::alloc_zero()"]
    uninit["PhysFrame::uninit()"]
end

alloc --> frame
alloc_zero --> frame
drop --> hal_dealloc
frame --> drop
frame --> fill
frame --> marker
frame --> paddr
frame --> ptr
frame --> start
uninit --> frame

Frame Allocation and Management

The PhysFrame struct provides several allocation methods:

  • alloc(): Allocates a new frame using H::alloc_frame() and validates the returned address is non-zero
  • alloc_zero(): Allocates a frame and fills it with zeros using the fill() method
  • uninit(): Creates an uninitialized frame for placeholder use (unsafe)

Frame operations include:

  • start_paddr(): Returns the starting physical address of the frame
  • as_mut_ptr(): Provides a mutable pointer to the frame content via H::phys_to_virt()
  • fill(byte): Fills the entire frame with a specified byte value (assumes 4KiB frame size)

The automatic cleanup is handled by the Drop implementation, which calls H::dealloc_frame() when the frame is dropped.

Sources: src/frame.rs(L14 - L74) 

Integration with System Components

The HAL serves as the foundation for memory management throughout the axaddrspace system, providing the interface between high-level address space management and low-level hardware operations.

HAL Integration Architecture

flowchart TD
subgraph subGraph3["Address Types"]
    host_phys["HostPhysAddr"]
    host_virt["HostVirtAddr"]
end
subgraph subGraph2["Host Implementation"]
    host_impl["Host-specific Implementation"]
    frame_alloc["Frame Allocator"]
    addr_trans["Address Translation"]
end
subgraph subGraph1["Hardware Abstraction Layer"]
    hal_trait["AxMmHal Trait"]
    physframe["PhysFrame<H>"]
end
subgraph subGraph0["Memory Backends"]
    alloc_backend["Alloc Backend"]
    linear_backend["Linear Backend"]
end

alloc_backend --> physframe
hal_trait --> host_impl
hal_trait --> host_phys
hal_trait --> host_virt
host_impl --> addr_trans
host_impl --> frame_alloc
linear_backend --> host_phys
physframe --> hal_trait

Key Integration Points

  1. Allocation Backend: The allocation backend (see Allocation Backend) uses PhysFrame for dynamic memory allocation with lazy population strategies.
  2. Address Translation: Both physical-to-virtual and virtual-to-physical address translation operations are abstracted through the HAL, enabling consistent address handling across different host environments.
  3. Frame Size Abstraction: The HAL abstracts frame size details, though the current implementation assumes 4KiB frames as defined by PAGE_SIZE_4K from the memory_addr crate.
  4. Error Handling: Frame allocation failures are handled gracefully through the Option<HostPhysAddr> return type, with higher-level components responsible for error propagation.

Sources: src/frame.rs(L1 - L7)  src/hal.rs(L1 - L2) 

The Hardware Abstraction Layer ensures that the axaddrspace crate can operate independently of specific hypervisor implementations while maintaining efficient memory management through RAII patterns and clear separation of concerns between platform-specific and platform-independent code.

Nested Page Tables

Relevant source files

Purpose and Scope

The Nested Page Tables system provides architecture-specific two-stage address translation capabilities for hypervisor virtualization. This system enables guest operating systems to manage their own virtual memory while the hypervisor transparently translates guest physical addresses to host physical addresses through hardware-assisted virtualization features.

This document covers the generic nested page table interface and architecture selection mechanism. For detailed implementation specifics, see Architecture Selection, AArch64 Implementation, x86_64 Implementation, and RISC-V Implementation. For broader address space management context, see Address Space Management.

Architecture Overview

The nested page table system uses a layered architecture that provides a unified interface while supporting multiple hardware virtualization technologies. The system leverages conditional compilation to select the appropriate implementation based on the target architecture.

Nested Page Table Architecture

flowchart TD
subgraph subGraph4["RISC-V Implementation"]
    RISCVNPT["NestedPageTable<H>"]
    SV39["SV39 Support"]
end
subgraph subGraph3["AArch64 Implementation"]
    AArch64NPT["NestedPageTable<H>"]
    ARMStage2["ARM Stage 2 Translation"]
end
subgraph subGraph2["x86_64 Implementation"]
    EPT["ExtendedPageTable<H>"]
    IntelEPT["Intel EPT Support"]
end
subgraph subGraph1["Architecture Selection"]
    CfgIf["cfg_if! Conditional Compilation"]
    ArchMod["arch/mod.rs"]
end
subgraph subGraph0["Generic Interface Layer"]
    NPT["NestedPageTable<H>"]
    TypeAlias["Type Alias System"]
end

AArch64NPT --> ARMStage2
ArchMod --> AArch64NPT
ArchMod --> EPT
ArchMod --> RISCVNPT
CfgIf --> ArchMod
EPT --> IntelEPT
NPT --> TypeAlias
RISCVNPT --> SV39
TypeAlias --> CfgIf

Sources: src/npt/mod.rs(L1 - L15)  src/npt/arch/mod.rs(L1 - L15) 

Type System and Abstraction

The nested page table system uses Rust's conditional compilation features to provide a single NestedPageTable<H> type that resolves to different concrete implementations depending on the target architecture. This approach ensures type safety while maintaining architecture-specific optimizations.

Type Alias Resolution System

flowchart TD
subgraph subGraph2["Concrete Types"]
    EPTType["arch::ExtendedPageTable<H>"]
    AArch64Type["arch::NestedPageTable<H>"]
    RISCVType["arch::NestedPageTable<H>"]
end
subgraph subGraph1["Type Alias Definitions"]
    NPTAlias["NestedPageTable<H>"]
end
subgraph subGraph0["Compile Time Selection"]
    CfgCheck["cfg_if! Macro"]
    TargetArch["target_arch Detection"]
end

CfgCheck --> TargetArch
NPTAlias --> AArch64Type
NPTAlias --> EPTType
NPTAlias --> RISCVType
TargetArch --> NPTAlias

The type resolution follows this pattern in src/npt/mod.rs(L1 - L12) :

Target ArchitectureType Alias ResolutionHardware Feature
x86_64arch::ExtendedPageTableIntel EPT
aarch64arch::NestedPageTableARM Stage 2 Translation
riscv32/riscv64arch::NestedPageTableRISC-V SV39

Sources: src/npt/mod.rs(L1 - L12) 

Architecture Module System

The architecture selection mechanism uses a two-tier conditional compilation system. The top-level module defines type aliases, while the architecture module handles implementation selection and re-exports.

Module Selection Flow

flowchart TD
subgraph subGraph2["Implementation Modules"]
    X86Mod["x86_64.rs"]
    AArch64Mod["aarch64.rs"]
    RISCVMod["riscv.rs"]
end
subgraph npt/arch/mod.rs["npt/arch/mod.rs"]
    ArchMod["cfg_if! Module Selection"]
    ModImport["Module Import & Re-export"]
end
subgraph npt/mod.rs["npt/mod.rs"]
    MainMod["cfg_if! Type Alias Selection"]
    NPTDef["NestedPageTable<H> Definition"]
end

AArch64Mod --> ArchMod
ArchMod --> ModImport
MainMod --> NPTDef
ModImport --> AArch64Mod
ModImport --> RISCVMod
ModImport --> X86Mod
NPTDef --> ArchMod
RISCVMod --> ArchMod
X86Mod --> ArchMod

The architecture module in src/npt/arch/mod.rs(L3 - L14)  uses pattern matching on target_arch to:

  1. Import the appropriate architecture-specific module
  2. Re-export all public items using wildcard imports
  3. Provide a unified interface for the parent module

Sources: src/npt/arch/mod.rs(L3 - L14) 

Integration Points

The nested page table system integrates with several other components in the axaddrspace crate:

  • Address Space Management: Used by AddrSpace<H> for guest-to-host address translation
  • Hardware Abstraction: Depends on AxMmHal trait for memory frame allocation
  • Address Types: Operates on GuestPhysAddr and HostPhysAddr types
  • Memory Backends: Provides translation services for both linear and allocation backends

The generic parameter H represents the hardware abstraction layer implementation, ensuring type safety across the entire address space management stack.

Sources: src/npt/mod.rs(L1 - L15)  src/npt/arch/mod.rs(L1 - L15) 

Architecture Selection

Relevant source files

Purpose and Scope

This page documents the architecture selection mechanism within the nested page table (NPT) system. It explains how axaddrspace uses conditional compilation to select appropriate page table implementations based on the target CPU architecture during build time.

The architecture selection system provides a unified interface while delegating to architecture-specific implementations. For detailed information about individual architecture implementations, see AArch64 Implementation, x86_64 Implementation, and RISC-V Implementation.

Conditional Compilation Strategy

The architecture selection system uses the cfg_if crate to perform conditional compilation based on the target architecture. This approach allows the codebase to include only the relevant architecture-specific code during compilation, reducing binary size and eliminating unused code paths.

Architecture Module Selection

The primary selection mechanism is implemented in src/npt/arch/mod.rs(L3 - L14)  which uses cfg_if to conditionally include and re-export architecture-specific modules:


Diagram: Architecture Module Selection Flow

Sources: src/npt/arch/mod.rs(L3 - L14) 

Type Alias System

The type alias system in src/npt/mod.rs(L1 - L12)  provides a uniform NestedPageTable<H> interface that resolves to different concrete types depending on the target architecture:

Target ArchitectureType Alias Resolves ToImplementation File
x86_64arch::ExtendedPageTablearch/x86_64.rs
aarch64arch::NestedPageTablearch/aarch64.rs
riscv32,riscv64arch::NestedPageTablearch/riscv.rs

Type Resolution Flow

flowchart TD
subgraph subGraph1["Architecture Implementations"]
    X86STRUCT["ExtendedPageTable struct"]
    ARMSTRUCT["NestedPageTable struct"]
    RISCVSTRUCT["NestedPageTable struct"]
end
subgraph subGraph0["Compilation Time Resolution"]
    ALIAS["NestedPageTable<H>"]
    X86TYPE["arch::ExtendedPageTable<H>"]
    ARMTYPE["arch::NestedPageTable<H>"]
    RISCVTYPE["arch::NestedPageTable<H>"]
end
CLIENT["Client Code"]

ALIAS --> ARMTYPE
ALIAS --> RISCVTYPE
ALIAS --> X86TYPE
ARMTYPE --> ARMSTRUCT
CLIENT --> ALIAS
RISCVTYPE --> RISCVSTRUCT
X86TYPE --> X86STRUCT

Diagram: Type Alias Resolution at Compile Time

Sources: src/npt/mod.rs(L1 - L12) 

Architecture Support Matrix

The system currently supports three major CPU architectures with their respective virtualization technologies:

ArchitectureConditional Compilation TargetVirtualization TechnologyType Name
Intel/AMD x86_64target_arch = "x86_64"Extended Page Tables (EPT)ExtendedPageTable
ARM AArch64target_arch = "aarch64"Stage 2 TranslationNestedPageTable
RISC-V 32/64-bittarget_arch = "riscv32"ortarget_arch = "riscv64"H-extensionNestedPageTable

Module Structure and Dependencies

The architecture selection system creates a clear separation between the generic interface and platform-specific implementations:


Diagram: Architecture Selection Module Structure

Sources: src/npt/mod.rs(L1 - L15)  src/npt/arch/mod.rs(L1 - L15) 

Build-Time Behavior

The architecture selection occurs entirely at compile time through Rust's conditional compilation features. When building for a specific target:

  1. The cfg_if macro in arch/mod.rs evaluates the target architecture
  2. Only the matching module is included in compilation
  3. The appropriate symbols are re-exported via pub use
  4. The type alias in mod.rs resolves to the selected implementation
  5. Dead code elimination removes unused architecture implementations

This approach ensures that the final binary contains only the code necessary for the target platform, avoiding runtime overhead and reducing memory footprint.

Sources: src/npt/arch/mod.rs(L3 - L14)  src/npt/mod.rs(L1 - L12) 

AArch64 Implementation

Relevant source files

This document covers the AArch64-specific implementation of nested page tables within the axaddrspace crate. The AArch64 implementation provides VMSAv8-64 Stage 2 translation support for hypervisor environments, enabling guest physical address to host physical address translation on ARM 64-bit architectures.

This implementation specifically handles ARM's hypervisor-mode address translation. For information about other architecture implementations, see x86_64 Implementation and RISC-V Implementation. For the generic nested page table interface, see Architecture Selection.

Architecture Overview

The AArch64 implementation follows ARM's VMSAv8-64 specification for Stage 2 translation, which is used in hypervisor environments to translate Intermediate Physical Addresses (IPA) from guest virtual machines to Host Physical Addresses (HPA).

flowchart TD
subgraph subGraph3["Address Translation"]
    GPA["GuestPhysAddr"]
    HPA["HostPhysAddr"]
    IPARange["40-bit IPA Space"]
    PARange["48-bit PA Space"]
end
subgraph subGraph2["Memory Management"]
    MemType["MemType"]
    MappingFlags["MappingFlags"]
    TLBFlush["TLB Flush Operations"]
end
subgraph subGraph1["Core Components"]
    A64PTEHV["A64PTEHV"]
    A64Meta["A64HVPagingMetaData"]
    DescAttr["DescriptorAttr"]
end
subgraph subGraph0["AArch64 NPT Implementation"]
    NPT["NestedPageTable<H>"]
    A64PT["PageTable64<A64HVPagingMetaData, A64PTEHV, H>"]
end

A64Meta --> GPA
A64Meta --> IPARange
A64Meta --> PARange
A64Meta --> TLBFlush
A64PT --> A64Meta
A64PT --> A64PTEHV
A64PTEHV --> DescAttr
A64PTEHV --> HPA
A64PTEHV --> MappingFlags
DescAttr --> MemType
NPT --> A64PT

Sources: src/npt/arch/aarch64.rs(L1 - L253) 

The implementation provides a 3-level page table structure with:

  • 40-bit Intermediate Physical Address (IPA) space for guest addresses
  • 48-bit Physical Address (PA) space for host addresses
  • VMSAv8-64 Stage 2 translation table format

Page Table Entry Implementation

The A64PTEHV struct represents a single VMSAv8-64 translation table descriptor, implementing the GenericPTE trait to integrate with the generic page table framework.

flowchart TD
subgraph subGraph3["Attribute Management"]
    DescriptorAttr["DescriptorAttr"]
    ValidBit["VALID"]
    NonBlock["NON_BLOCK"]
    AccessFlag["AF"]
end
subgraph subGraph2["Physical Address Handling"]
    PhysMask["PHYS_ADDR_MASK0x0000_ffff_ffff_f000"]
    AddrBits["bits 12..48"]
end
subgraph subGraph1["GenericPTE Implementation"]
    NewPage["new_page()"]
    NewTable["new_table()"]
    GetPAddr["paddr()"]
    GetFlags["flags()"]
    SetPAddr["set_paddr()"]
    SetFlags["set_flags()"]
    IsPresent["is_present()"]
    IsHuge["is_huge()"]
end
subgraph subGraph0["A64PTEHV Structure"]
    PTE["A64PTEHV(u64)"]
    PTEBits["Raw 64-bit Value"]
end

DescriptorAttr --> AccessFlag
DescriptorAttr --> NonBlock
DescriptorAttr --> ValidBit
GetFlags --> DescriptorAttr
GetPAddr --> PhysMask
IsHuge --> NonBlock
IsPresent --> ValidBit
NewPage --> DescriptorAttr
NewTable --> DescriptorAttr
PTE --> GetFlags
PTE --> GetPAddr
PTE --> IsHuge
PTE --> IsPresent
PTE --> NewPage
PTE --> NewTable
PTE --> PTEBits
PTE --> SetFlags
PTE --> SetPAddr
PhysMask --> AddrBits
SetFlags --> DescriptorAttr
SetPAddr --> PhysMask

Sources: src/npt/arch/aarch64.rs(L144 - L211) 

Key Features

FeatureImplementationPurpose
Physical Address Mask0x0000_ffff_ffff_f000Extracts bits 12-48 for physical addressing
Page vs BlockNON_BLOCKbitDistinguishes between 4KB pages and large blocks
Access FlagAFbitRequired for all valid entries in ARM architecture
Valid BitVALIDbitIndicates if the descriptor is valid for translation

Memory Attribute Management

The AArch64 implementation uses the DescriptorAttr bitflags structure to manage VMSAv8-64 memory attributes and access permissions.

flowchart TD
subgraph subGraph2["Conversion Logic"]
    ToMapping["to MappingFlags"]
    FromMapping["from MappingFlags"]
    MemTypeConv["Memory Type Conversion"]
end
subgraph subGraph1["Memory Types"]
    Device["Device Memory"]
    Normal["Normal Memory"]
    NormalNC["Normal Non-Cacheable"]
end
subgraph subGraph0["DescriptorAttr Bitflags"]
    DescAttr["DescriptorAttr"]
    ValidFlag["VALID (bit 0)"]
    NonBlockFlag["NON_BLOCK (bit 1)"]
    AttrIndex["ATTR (bits 2-5)"]
    S2AP_RO["S2AP_RO (bit 6)"]
    S2AP_WO["S2AP_WO (bit 7)"]
    Shareable["SHAREABLE (bit 9)"]
    AF["AF (bit 10)"]
    XN["XN (bit 54)"]
end

AttrIndex --> Device
AttrIndex --> Normal
AttrIndex --> NormalNC
DescAttr --> AF
DescAttr --> AttrIndex
DescAttr --> FromMapping
DescAttr --> MemTypeConv
DescAttr --> NonBlockFlag
DescAttr --> S2AP_RO
DescAttr --> S2AP_WO
DescAttr --> Shareable
DescAttr --> ToMapping
DescAttr --> ValidFlag
DescAttr --> XN
Device --> MemTypeConv
Normal --> MemTypeConv
NormalNC --> MemTypeConv

Sources: src/npt/arch/aarch64.rs(L8 - L137) 

Memory Type Encoding

The implementation supports three memory types with specific attribute encodings:

Memory TypeEncodingCharacteristics
Device0b0000Device memory with shareability
Normal0b1111Write-back cacheable with shareability
NormalNonCache0b0111Inner cacheable, outer non-cacheable

Permission Mapping

The AArch64 Stage 2 access permissions are mapped to generic MappingFlags:

  • Read: Controlled by VALID bit
  • Write: Controlled by absence of S2AP_WO bit
  • Execute: Controlled by absence of XN bit
  • Device: Determined by memory attribute index

Paging Metadata Configuration

The A64HVPagingMetaData struct provides architecture-specific configuration for the AArch64 hypervisor page tables.

flowchart TD
subgraph subGraph2["EL2 vs EL1 Operations"]
    EL2Ops["EL2 Operationsvae2is, alle2is"]
    EL1Ops["EL1 Operationsvaae1is, vmalle1"]
    ArmEL2Feature["arm-el2 feature"]
end
subgraph subGraph1["TLB Management"]
    FlushTLB["flush_tlb()"]
    SingleFlush["Single Address Flush"]
    FullFlush["Full TLB Flush"]
end
subgraph A64HVPagingMetaData["A64HVPagingMetaData"]
    Levels["LEVELS = 3"]
    PABits["PA_MAX_BITS = 48"]
    VABits["VA_MAX_BITS = 40"]
    VirtAddr["VirtAddr = GuestPhysAddr"]
end

ArmEL2Feature --> EL1Ops
ArmEL2Feature --> EL2Ops
FlushTLB --> FullFlush
FlushTLB --> SingleFlush
FullFlush --> EL1Ops
FullFlush --> EL2Ops
Levels --> FlushTLB
PABits --> FlushTLB
SingleFlush --> EL1Ops
SingleFlush --> EL2Ops
VABits --> FlushTLB
VirtAddr --> FlushTLB

Sources: src/npt/arch/aarch64.rs(L213 - L253) 

TLB Flush Implementation

The TLB flush implementation supports both Exception Level 1 (EL1) and Exception Level 2 (EL2) operations:

EL2 Mode (with arm-el2 feature):

  • Single address: tlbi vae2is - Invalidate by VA in EL2
  • Full flush: tlbi alle2is - Invalidate all EL2 TLB entries

EL1 Mode (default):

  • Single address: tlbi vaae1is - Invalidate by VA, all ASID in EL1
  • Full flush: tlbi vmalle1 - Invalidate all EL1 TLB entries

All TLB operations include data synchronization barriers (dsb sy) and instruction synchronization barriers (isb) to ensure proper ordering.

Integration with Generic Framework

The AArch64 implementation integrates with the generic page table framework through type aliases and trait implementations.

ComponentType DefinitionPurpose
NestedPageTablePageTable64<A64HVPagingMetaData, A64PTEHV, H>Main page table type for AArch64
Page Table EntryA64PTEHVVMSAv8-64 descriptor implementation
MetadataA64HVPagingMetaDataArchitecture-specific configuration
Virtual AddressGuestPhysAddrGuest physical address type

Sources: src/npt/arch/aarch64.rs(L252 - L253) 

The implementation ensures compatibility with the generic nested page table interface while providing ARM-specific optimizations for hypervisor workloads, including proper Stage 2 translation semantics and efficient TLB management for virtualized environments.

x86_64 Implementation

Relevant source files

This document covers the Intel x86_64 Extended Page Table (EPT) implementation for nested page tables in the axaddrspace crate. The EPT system provides hardware-assisted virtualization support for Intel VMX environments, enabling efficient guest-to-host physical address translation. For general nested page table concepts and architecture selection, see Architecture Selection. For implementations on other architectures, see AArch64 Implementation and RISC-V Implementation.

EPT Overview and Intel Virtualization Context

The x86_64 implementation leverages Intel's Extended Page Table technology, which is part of the VMX (Virtual Machine Extensions) feature set. EPT provides hardware-accelerated second-level address translation, mapping guest physical addresses to host physical addresses without requiring software intervention for most memory accesses.

EPT System Architecture

flowchart TD
subgraph subGraph3["Generic Framework Integration"]
    GenericPTE["GenericPTE trait"]
    PagingMetaData["PagingMetaData trait"]
    MappingFlags["MappingFlags"]
end
subgraph subGraph2["EPT Entry Components"]
    EPTEntry["EPTEntry"]
    EPTFlags["EPTFlags"]
    EPTMemType["EPTMemType"]
end
subgraph subGraph1["axaddrspace EPT Implementation"]
    ExtPageTable["ExtendedPageTable<H>"]
    EPTMeta["ExtendedPageTableMetadata"]
    PageTable64["PageTable64<ExtendedPageTableMetadata, EPTEntry, H>"]
end
subgraph subGraph0["Intel VMX Environment"]
    VMCS["VMCS (VM Control Structure)"]
    EPTP["EPTP (EPT Pointer)"]
    EPT_BASE["EPT Base Address"]
end

EPTEntry --> EPTFlags
EPTEntry --> EPTMemType
EPTEntry --> GenericPTE
EPTFlags --> MappingFlags
EPTMeta --> PagingMetaData
EPTP --> EPT_BASE
EPT_BASE --> ExtPageTable
ExtPageTable --> PageTable64
PageTable64 --> EPTEntry
PageTable64 --> EPTMeta
VMCS --> EPTP

Sources: src/npt/arch/x86_64.rs(L182 - L184) 

EPT Entry Structure and Flags

The EPTEntry structure represents a 64-bit EPT page table entry that follows Intel's EPT specification. Each entry contains physical address bits, access permissions, and memory type information.

EPT Entry Bit Layout and Components

flowchart TD
subgraph subGraph2["Memory Type Values"]
    Uncached["Uncached (0)"]
    WriteCombining["WriteCombining (1)"]
    WriteThrough["WriteThrough (4)"]
    WriteProtected["WriteProtected (5)"]
    WriteBack["WriteBack (6)"]
end
subgraph subGraph1["EPTFlags Components"]
    READ["READ (bit 0)Read access permission"]
    WRITE["WRITE (bit 1)Write access permission"]
    EXECUTE["EXECUTE (bit 2)Execute access permission"]
    MEM_TYPE_MASK["MEM_TYPE_MASK (bits 5:3)Memory type specification"]
    IGNORE_PAT["IGNORE_PAT (bit 6)Ignore PAT memory type"]
    HUGE_PAGE["HUGE_PAGE (bit 7)Large page indicator"]
    ACCESSED["ACCESSED (bit 8)Page accessed flag"]
    DIRTY["DIRTY (bit 9)Page dirty flag"]
    EXECUTE_FOR_USER["EXECUTE_FOR_USER (bit 10)User-mode execute access"]
end
subgraph subGraph0["EPTEntry 64-bit Structure"]
    RawBits["u64 raw value (self.0)"]
    PhysAddr["Physical Address [51:12]PHYS_ADDR_MASK: 0x000f_ffff_ffff_f000"]
    FlagsBits["Flags and Properties [11:0]"]
end

FlagsBits --> ACCESSED
FlagsBits --> DIRTY
FlagsBits --> EXECUTE
FlagsBits --> EXECUTE_FOR_USER
FlagsBits --> HUGE_PAGE
FlagsBits --> IGNORE_PAT
FlagsBits --> MEM_TYPE_MASK
FlagsBits --> READ
FlagsBits --> WRITE
MEM_TYPE_MASK --> Uncached
MEM_TYPE_MASK --> WriteBack
MEM_TYPE_MASK --> WriteCombining
MEM_TYPE_MASK --> WriteProtected
MEM_TYPE_MASK --> WriteThrough
RawBits --> FlagsBits
RawBits --> PhysAddr

Sources: src/npt/arch/x86_64.rs(L9 - L32)  src/npt/arch/x86_64.rs(L34 - L45)  src/npt/arch/x86_64.rs(L99 - L107) 

EPT Flags Implementation

The EPTFlags structure uses the bitflags! macro to provide type-safe manipulation of EPT entry flags. Key access permissions follow Intel's VMX specification:

FlagBit PositionPurpose
READ0Enables read access to the page
WRITE1Enables write access to the page
EXECUTE2Enables instruction execution from the page
MEM_TYPE_MASK5:3Specifies memory type for terminate pages
HUGE_PAGE7Indicates 2MB or 1GB page size
ACCESSED8Hardware-set when page is accessed
DIRTY9Hardware-set when page is written

Sources: src/npt/arch/x86_64.rs(L9 - L32) 

Memory Type Management

EPT supports Intel's memory type system for controlling caching behavior and memory ordering. The EPTMemType enum defines the available memory types that can be encoded in EPT entries.

Memory Type Configuration Flow

flowchart TD
subgraph subGraph2["EPTFlags Methods"]
    SetMemType["EPTFlags::set_mem_type()"]
    GetMemType["EPTFlags::mem_type()"]
    BitsManip["BitField operationsbits.set_bits(3..6, type)"]
end
subgraph subGraph1["EPT Memory Type Selection"]
    MemTypeCheck["Device memory?"]
    WriteBackType["EPTMemType::WriteBack(normal memory)"]
    UncachedType["EPTMemType::Uncached(device memory)"]
end
subgraph subGraph0["MappingFlags Input"]
    DeviceFlag["MappingFlags::DEVICE"]
    StandardFlags["Standard R/W/X flags"]
end

BitsManip --> GetMemType
DeviceFlag --> MemTypeCheck
MemTypeCheck --> UncachedType
MemTypeCheck --> WriteBackType
SetMemType --> BitsManip
StandardFlags --> MemTypeCheck
UncachedType --> SetMemType
WriteBackType --> SetMemType

Sources: src/npt/arch/x86_64.rs(L47 - L56)  src/npt/arch/x86_64.rs(L58 - L78)  src/npt/arch/x86_64.rs(L80 - L97) 

The memory type conversion logic ensures that:

  • Normal memory regions use WriteBack caching for optimal performance
  • Device memory regions use Uncached type to prevent caching side effects
  • The conversion is bidirectional between MappingFlags and EPTFlags

Generic Page Table Framework Integration

The EPTEntry implements the GenericPTE trait, enabling integration with the architecture-independent page table framework. This abstraction allows the same high-level page table operations to work across different architectures.

GenericPTE Implementation for EPTEntry

flowchart TD
subgraph subGraph2["Address Space Integration"]
    HostPhysAddr["HostPhysAddr type"]
    GuestPhysAddr["GuestPhysAddr type"]
    PageTable64Framework["PageTable64<Meta, PTE, H>"]
end
subgraph subGraph1["EPTEntry Implementation Details"]
    PhysAddrMask["PHYS_ADDR_MASK0x000f_ffff_ffff_f000"]
    FlagsConversion["EPTFlags ↔ MappingFlagsbidirectional conversion"]
    PresenceCheck["RWX bits != 0determines presence"]
    HugePageBit["HUGE_PAGE flagindicates large pages"]
end
subgraph subGraph0["GenericPTE Trait Methods"]
    NewPage["new_page(paddr, flags, is_huge)"]
    NewTable["new_table(paddr)"]
    GetPaddr["paddr() → HostPhysAddr"]
    GetFlags["flags() → MappingFlags"]
    SetPaddr["set_paddr(paddr)"]
    SetFlags["set_flags(flags, is_huge)"]
    IsPresent["is_present() → bool"]
    IsHuge["is_huge() → bool"]
    IsUnused["is_unused() → bool"]
    Clear["clear()"]
end

GetFlags --> FlagsConversion
GetPaddr --> HostPhysAddr
IsHuge --> HugePageBit
IsPresent --> PresenceCheck
NewPage --> FlagsConversion
NewPage --> PhysAddrMask
NewTable --> PhysAddrMask
PageTable64Framework --> GetPaddr
PageTable64Framework --> IsPresent
PageTable64Framework --> NewPage
SetFlags --> FlagsConversion
SetFlags --> HugePageBit
SetPaddr --> PhysAddrMask

Sources: src/npt/arch/x86_64.rs(L109 - L154) 

Key Implementation Details

The EPTEntry provides several critical implementation details:

MethodImplementationPurpose
is_present()self.0 & 0x7 != 0Checks if any of R/W/X bits are set
paddr()(self.0 & PHYS_ADDR_MASK)Extracts 40-bit physical address
new_table()Sets R+W+X for intermediate tablesCreates non-leaf EPT entries
set_flags()ConvertsMappingFlagstoEPTFlagsHandles huge page flag setting

Sources: src/npt/arch/x86_64.rs(L117 - L149) 

Extended Page Table Metadata

The ExtendedPageTableMetadata structure defines the architectural parameters for Intel EPT, implementing the PagingMetaData trait to provide page table configuration to the generic framework.

EPT Configuration Parameters

flowchart TD
subgraph subGraph1["Address Type Mapping"]
    VirtAddrType["type VirtAddr = GuestPhysAddr"]
    GuestContext["Guest Physical Address SpaceWhat guests see as 'physical'"]
end
subgraph subGraph0["ExtendedPageTableMetadata Constants"]
    LEVELS["LEVELS = 44-level page table"]
    PA_MAX_BITS["PA_MAX_BITS = 5252-bit physical addresses"]
    VA_MAX_BITS["VA_MAX_BITS = 4848-bit guest addresses"]
end
subgraph subGraph2["TLB Management"]
    FlushTlb["flush_tlb(vaddr)TODO: implement"]
    HardwareFlush["Hardware TLB invalidationINVEPT instruction"]
end

FlushTlb --> HardwareFlush
LEVELS --> VirtAddrType
PA_MAX_BITS --> VirtAddrType
VA_MAX_BITS --> VirtAddrType
VirtAddrType --> GuestContext

Sources: src/npt/arch/x86_64.rs(L167 - L180) 

The metadata configuration aligns with Intel's EPT specification:

  • 4-level hierarchy: EPT4, EPT3, EPT2, EPT1 tables providing 48-bit guest address translation
  • 52-bit physical addresses: Supporting Intel's maximum physical address width
  • Guest physical address context: EPT translates what guests consider "physical" addresses

Type Alias and Framework Integration

The final component ties together all EPT-specific implementations with the generic page table framework through a type alias:

pub type ExtendedPageTable<H> = PageTable64<ExtendedPageTableMetadata, EPTEntry, H>;

This type alias creates the main EPT page table type that:

  • Uses ExtendedPageTableMetadata for architectural parameters
  • Uses EPTEntry for page table entries with Intel-specific features
  • Accepts a generic hardware abstraction layer H
  • Inherits all functionality from the PageTable64 generic implementation

Sources: src/npt/arch/x86_64.rs(L182 - L184) 

The resulting ExtendedPageTable<H> type provides a complete EPT implementation that integrates seamlessly with the broader address space management system while leveraging Intel's hardware virtualization features for optimal performance.

RISC-V Implementation

Relevant source files

This document covers the RISC-V-specific implementation of nested page tables within the axaddrspace crate. The RISC-V implementation provides support for the SV39 virtual memory scheme, enabling guest-to-host address translation in RISC-V virtualization environments.

For information about the overall nested page table architecture selection, see Architecture Selection. For details about other architecture implementations, see AArch64 Implementation and x86_64 Implementation.

Architecture Overview

The RISC-V implementation follows the same architectural pattern as other platform-specific nested page table implementations. It combines generic page table functionality with RISC-V-specific components to provide hardware-appropriate virtualization support.

flowchart TD
subgraph subGraph2["Address Translation"]
    GPA["GuestPhysAddrGuest Physical Address"]
    SV39["SV39 Virtual Memory39-bit Address Space"]
end
subgraph subGraph1["External Dependencies"]
    ENTRY_LIB["page_table_entry::riscv::Rv64PTE"]
    MULTI_LIB["page_table_multiarch::PageTable64"]
    META_LIB["page_table_multiarch::riscv::Sv39MetaData"]
end
subgraph subGraph0["RISC-V NPT Architecture"]
    NPT["NestedPageTable<H>Type Alias"]
    PT64["PageTable64Generic Framework"]
    META["Sv39MetaData<GuestPhysAddr>RISC-V Metadata"]
    PTE["Rv64PTE64-bit Page Table Entry"]
    HAL["H: AxMmHalHardware Abstraction"]
end

META --> GPA
META --> META_LIB
META --> SV39
NPT --> HAL
NPT --> META
NPT --> PT64
NPT --> PTE
PT64 --> MULTI_LIB
PTE --> ENTRY_LIB

Sources: src/npt/arch/riscv.rs(L1 - L6) 

Type Definition and Components

The RISC-V nested page table implementation is defined as a single type alias that combines specialized RISC-V components with the generic PageTable64 framework:

pub type NestedPageTable<H> = PageTable64<Sv39MetaData<GuestPhysAddr>, Rv64PTE, H>;

This type alias instantiates the generic page table with three key parameters:

ParameterTypePurpose
MetadataSv39MetaDataRISC-V SV39 paging scheme configuration
Entry TypeRv64PTE64-bit RISC-V page table entry format
HAL TypeH: AxMmHalHardware abstraction layer implementation

Sources: src/npt/arch/riscv.rs(L6) 

SV39 Virtual Memory Support

The implementation leverages the Sv39MetaData type to provide support for RISC-V's SV39 virtual memory scheme. SV39 is a 39-bit virtual address translation scheme commonly used in RISC-V systems, providing:

  • 39-bit virtual address space
  • Three-level page table hierarchy
  • 4 KiB page size support
  • Integration with GuestPhysAddr for guest physical addressing
flowchart TD
subgraph subGraph1["RISC-V Components"]
    META["Sv39MetaData<GuestPhysAddr>"]
    PTE["Rv64PTE"]
end
subgraph subGraph0["SV39 Address Translation"]
    VA["39-bit Virtual Address"]
    L2["Level 2Page Directory"]
    L1["Level 1Page Directory"]
    L0["Level 0Page Table"]
    PA["Physical Address"]
end

L0 --> PA
L1 --> L0
L2 --> L1
META --> L0
META --> L1
META --> L2
PTE --> L0
VA --> L2

Sources: src/npt/arch/riscv.rs(L2 - L6) 

Integration with Generic Framework

The RISC-V implementation demonstrates the modular design of the nested page table system. It relies entirely on external crates for the actual implementation details:

  • page_table_entry: Provides the Rv64PTE type for RISC-V page table entries
  • page_table_multiarch: Supplies both the generic PageTable64 framework and RISC-V-specific Sv39MetaData

This approach ensures that:

  • Architecture-specific logic is maintained in specialized crates
  • The axaddrspace crate focuses on integration and abstraction
  • Updates to RISC-V specifications can be handled in dedicated libraries
  • The implementation remains consistent with other architectures
flowchart TD
subgraph Result["Result"]
    TYPE["NestedPageTable<H>RISC-V Type Alias"]
end
subgraph subGraph1["Crate Dependencies"]
    ENTRY["page_table_entry::riscv"]
    MULTI["page_table_multiarch"]
end
subgraph subGraph0["Integration Pattern"]
    IMPL["riscv.rsArchitecture Integration"]
    GENERIC["PageTable64Generic Implementation"]
    SPECIFIC["RISC-V Specific Types"]
end

GENERIC --> MULTI
IMPL --> GENERIC
IMPL --> SPECIFIC
IMPL --> TYPE
SPECIFIC --> ENTRY
SPECIFIC --> MULTI

Sources: src/npt/arch/riscv.rs(L1 - L6) 

Memory Mapping Backends

Relevant source files

This document explains the different strategies for mapping guest physical memory to host physical memory in the axaddrspace crate. Memory mapping backends provide the core abstraction for how virtual memory regions are translated to physical memory, supporting both static linear mappings and dynamic allocation-based mappings.

For detailed implementation of individual backends, see Linear Backend and Allocation Backend. For information about how backends integrate with address space management, see Address Space Management.

Backend Architecture Overview

The memory mapping backend system is built around a unified Backend<H> enum that provides different strategies for memory mapping. Each backend implements the MappingBackend trait to provide consistent interfaces for memory operations while allowing different underlying implementation strategies.

Backend Strategy Selection

flowchart TD
subgraph subGraph2["Memory Characteristics"]
    LinearMem["Contiguous Physical MemoryKnown at Creation Time"]
    AllocMem["Dynamic Physical MemoryFrom Global Allocator"]
end
subgraph subGraph1["Implementation Strategy"]
    LinearImpl["Fixed Offset Translation"]
    AllocEager["Eager Allocation (populate=true)"]
    AllocLazy["Lazy Allocation (populate=false)"]
end
subgraph subGraph0["Backend Selection"]
    BackendEnum["Backend<H> enum"]
    LinearVariant["Linear { pa_va_offset: usize }"]
    AllocVariant["Alloc { populate: bool }"]
end

AllocEager --> AllocMem
AllocLazy --> AllocMem
AllocVariant --> AllocEager
AllocVariant --> AllocLazy
BackendEnum --> AllocVariant
BackendEnum --> LinearVariant
LinearImpl --> LinearMem
LinearVariant --> LinearImpl

Sources: src/address_space/backend/mod.rs(L11 - L41) 

Backend Trait Implementation

Each backend variant implements the MappingBackend trait, which defines the core memory management operations. The trait provides a uniform interface while allowing backend-specific implementations for mapping, unmapping, and protection operations.

MappingBackend Trait Implementation

flowchart TD
subgraph subGraph2["Page Table Operations"]
    PageTableOps["PageTable<H> operations"]
    MappingFlags["MappingFlags"]
    GuestPhysAddr["GuestPhysAddr"]
end
subgraph subGraph1["Backend Dispatch"]
    BackendMatch["Backend<H> match"]
    LinearDispatch["map_linear / unmap_linear"]
    AllocDispatch["map_alloc / unmap_alloc"]
end
subgraph subGraph0["MappingBackend Trait"]
    MapMethod["map(start, size, flags, pt)"]
    UnmapMethod["unmap(start, size, pt)"]
    ProtectMethod["protect(start, size, new_flags, pt)"]
end

AllocDispatch --> PageTableOps
BackendMatch --> AllocDispatch
BackendMatch --> LinearDispatch
LinearDispatch --> PageTableOps
MapMethod --> BackendMatch
PageTableOps --> GuestPhysAddr
PageTableOps --> MappingFlags
UnmapMethod --> BackendMatch

Sources: src/address_space/backend/mod.rs(L55 - L90) 

Memory Mapping Strategies

The system supports two fundamental mapping strategies that serve different use cases in virtualization scenarios:

Backend TypeStrategyPhysical MemoryPage FaultsUse Case
LinearFixed offset translationContiguous, pre-allocatedNot supportedDevice memory, firmware regions
Alloc(populate=true)Eager allocationDynamic, immediateNot triggeredHigh-performance guest RAM
Alloc(populate=false)Lazy allocationDynamic, on-demandHandled on accessLarge sparse memory regions

The Linear backend uses a simple arithmetic translation where guest physical address gpa maps to host physical address gpa - pa_va_offset. This provides zero-overhead translation for contiguous memory regions.

The Alloc backend obtains physical frames from the global allocator through the hardware abstraction layer. When populate is true, all frames are allocated during mapping creation. When false, frames are allocated on-demand through page fault handling.

Sources: src/address_space/backend/mod.rs(L13 - L40) 

Page Fault Handling

Page fault handling behavior differs significantly between backend types. The handle_page_fault method provides backend-specific fault resolution:

Page Fault Resolution Flow

flowchart TD
subgraph subGraph3["Alloc Backend Response"]
    AllocHandler["handle_page_fault_alloc()"]
    PopulateCheck["Check populate flag"]
    FrameAllocation["Allocate physical frame"]
    PageTableUpdate["Update page table entry"]
end
subgraph subGraph2["Linear Backend Response"]
    LinearReturn["return false"]
    NoFaultExpected["Linear mappings should not fault"]
end
subgraph subGraph1["Backend Dispatch"]
    HandlePageFault["handle_page_fault()"]
    BackendCheck["Backend variant check"]
end
subgraph subGraph0["Page Fault Trigger"]
    PageFaultEvent["Guest Memory Access"]
    FaultInfo["vaddr, orig_flags, page_table"]
end

AllocHandler --> PopulateCheck
BackendCheck --> AllocHandler
BackendCheck --> LinearReturn
FaultInfo --> HandlePageFault
FrameAllocation --> PageTableUpdate
HandlePageFault --> BackendCheck
LinearReturn --> NoFaultExpected
PageFaultEvent --> FaultInfo
PopulateCheck --> FrameAllocation

Linear backends return false from handle_page_fault because all mappings should be established at creation time. Page faults on linear mappings indicate configuration errors or hardware issues.

Allocation backends with populate=false use page fault handling to implement lazy allocation. When a page fault occurs, the handler allocates a new physical frame and updates the page table entry to resolve the fault.

Sources: src/address_space/backend/mod.rs(L92 - L106) 

Backend Integration

The Backend<H> enum integrates with the broader address space management system through generic type parameter H, which must implement PagingHandler. This allows backends to work with different nested page table implementations across architectures.

System Integration Points

flowchart TD
subgraph subGraph3["Hardware Abstraction"]
    AxMmHal["AxMmHal trait"]
    PhysFrame["PhysFrame<H>"]
end
subgraph subGraph2["Page Table Layer"]
    NestedPageTable["NestedPageTable<H>"]
    PagingHandler["PagingHandler trait"]
end
subgraph subGraph1["Backend Layer"]
    Backend["Backend<H>"]
    MappingBackendTrait["MappingBackend trait"]
end
subgraph subGraph0["Address Space Layer"]
    AddrSpace["AddrSpace<H>"]
    MemorySet["MemorySet<Backend<H>>"]
end

AddrSpace --> MemorySet
Backend --> AxMmHal
Backend --> MappingBackendTrait
Backend --> PhysFrame
MappingBackendTrait --> NestedPageTable
MemorySet --> Backend
NestedPageTable --> PagingHandler

The type system ensures that backends, page tables, and hardware abstraction layers are all compatible through the shared H: PagingHandler constraint. This enables compile-time verification of architectural compatibility while maintaining runtime flexibility in backend selection.

Sources: src/address_space/backend/mod.rs(L3 - L6)  src/address_space/backend/mod.rs(L55 - L59) 

Linear Backend

Relevant source files

Purpose and Scope

The Linear Backend implements a fixed-offset memory mapping strategy for guest address spaces in the ArceOS-Hypervisor. This backend provides a simple, predictable translation mechanism between guest physical addresses and host physical addresses using a constant offset value.

For information about dynamic allocation strategies, see Allocation Backend. For the broader memory mapping system architecture, see Memory Mapping Backends.

Overview

The Linear Backend uses a straightforward address translation model where guest physical addresses are mapped to host physical addresses by subtracting a fixed offset (pa_va_offset). This creates a linear, one-to-one mapping relationship that is efficient for scenarios where the guest memory layout can be predetermined and doesn't require dynamic allocation.

Linear Mapping Address Translation

flowchart TD
GPA["GuestPhysAddrGuest Physical Address"]
Offset["pa_va_offsetFixed Offset Value"]
HPA["HostPhysAddrHost Physical Address"]
Translation["Address Translationhost_addr = guest_addr - offset"]
Mapping["Page Table Mappingpt.map_region()"]
NPT["NestedPageTableHardware Page Table"]

GPA --> Translation
Mapping --> NPT
Offset --> Translation
Translation --> HPA
Translation --> Mapping

Sources: src/address_space/backend/linear.rs(L21 - L32) 

Implementation Structure

The Linear Backend is implemented as a variant within the Backend<H> enum and provides three core operations through associated methods.

Backend Construction and Core Methods


Sources: src/address_space/backend/linear.rs(L7 - L11)  src/address_space/backend/linear.rs(L13 - L51) 

Core Operations

Backend Creation

The new_linear method creates a Linear Backend instance with a specified physical-to-virtual address offset:

MethodParametersReturn TypePurpose
new_linearpa_va_offset: usizeBackendCreates Linear variant with fixed offset

The method is marked as const, allowing compile-time initialization of linear mapping configurations.

Sources: src/address_space/backend/linear.rs(L8 - L11) 

Memory Mapping Operations

The Linear Backend provides two primary mapping operations that work with the nested page table system:

Map Operation

The map_linear method establishes mappings in the page table using the linear translation strategy:

sequenceDiagram
    participant Caller as Caller
    participant Backendmap_linear as "Backend::map_linear"
    participant AddressTranslation as "Address Translation"
    participant PageTablemap_region as "PageTable::map_region"

    Caller ->> Backendmap_linear: map_linear(start, size, flags, pt, offset)
    Backendmap_linear ->> AddressTranslation: Calculate host_addr = guest_addr - offset
    AddressTranslation -->> Backendmap_linear: PhysAddr
    Backendmap_linear ->> PageTablemap_region: map_region(start, translation_fn, size, flags)
    PageTablemap_region -->> Backendmap_linear: Result<()>
    Backendmap_linear -->> Caller: bool (success/failure)
    Note over Backendmap_linear,PageTablemap_region: Translation function:<br>|va| PhysAddr::from(va - offset)

Sources: src/address_space/backend/linear.rs(L13 - L39) 

Unmap Operation

The unmap_linear method removes existing mappings from the page table:

ParameterTypeDescription
startGuestPhysAddrStarting guest physical address
sizeusizeSize of region to unmap
pt&mut PageTableMutable reference to page table
_pa_va_offsetusizeUnused in unmap operation

Sources: src/address_space/backend/linear.rs(L41 - L50) 

Address Translation Mechanism

The Linear Backend implements address translation through a closure passed to the page table's map_region method. This translation function converts guest virtual addresses to host physical addresses by subtracting the configured offset:

flowchart TD
subgraph subGraph1["Page Table Integration"]
    MapRegion["pt.map_region()"]
    TransFn["Translation Function|va| PhysAddr::from(va - offset)"]
end
subgraph subGraph0["Linear Translation Process"]
    GVA["Guest Virtual Address(va.as_usize())"]
    Subtract["Subtraction Operationva - pa_va_offset"]
    HPA["Host Physical AddressPhysAddr::from()"]
end

GVA --> Subtract
MapRegion --> TransFn
Subtract --> HPA
TransFn --> Subtract

The translation occurs at src/address_space/backend/linear.rs(L32)  where the closure |va| PhysAddr::from(va.as_usize() - pa_va_offset) is passed to the page table mapping function.

Sources: src/address_space/backend/linear.rs(L30 - L37) 

Integration with Nested Page Tables

The Linear Backend operates through the NestedPageTable<H> type (aliased as PageTable<H>), which provides hardware-abstracted page table operations. The backend leverages the page table's map_region and unmap_region methods to establish the actual memory mappings.

Page Table Method Parameters

MethodKey ParametersLinear Backend Usage
map_regionstart,translate_fn,size,flagsProvides linear translation function
unmap_regionstart,size,flushAlways flushes TLB (true)

The Linear Backend sets specific parameters for page table operations:

  • Always uses false for both huge page flags in map_region
  • Always flushes the TLB during unmap_region operations
  • Provides a simple subtraction-based translation function

Sources: src/address_space/backend/linear.rs(L30 - L37)  src/address_space/backend/linear.rs(L49) 

Allocation Backend

Relevant source files

This document covers the dynamic allocation backend implementation for guest memory management in axaddrspace. The allocation backend provides flexible memory allocation strategies with support for both eager and lazy allocation patterns. For information about the alternative linear mapping strategy, see Linear Backend. For broader context on how backends fit into the address space management system, see Memory Mapping Backends and Address Space Management.

Overview

The allocation backend implements dynamic memory management for guest physical memory regions, where physical frames are allocated from the host system either eagerly at mapping time or lazily on first access. This backend is implemented as part of the Backend<H> enum and provides three core operations: mapping, unmapping, and page fault handling.

Allocation Backend Architecture

flowchart TD
subgraph subGraph3["Page Table Operations"]
    PT["PageTable<H>"]
    PT_MAP["pt.map()"]
    PT_UNMAP["pt.unmap()"]
    PT_REMAP["pt.remap()"]
    PT_MAP_REGION["pt.map_region()"]
end
subgraph subGraph2["Physical Memory Management"]
    HAL_ALLOC["H::alloc_frame()"]
    HAL_DEALLOC["H::dealloc_frame()"]
    FRAME["HostPhysAddr"]
end
subgraph subGraph1["Allocation Operations"]
    NEW["new_alloc(populate: bool)"]
    MAP["map_alloc()"]
    UNMAP["unmap_alloc()"]
    PF["handle_page_fault_alloc()"]
end
subgraph subGraph0["Backend Selection"]
    BE["Backend<H>"]
    ALLOC_VAR["Backend::Alloc { populate, _phantom }"]
end

ALLOC_VAR --> MAP
ALLOC_VAR --> NEW
ALLOC_VAR --> PF
ALLOC_VAR --> UNMAP
BE --> ALLOC_VAR
HAL_ALLOC --> FRAME
HAL_DEALLOC --> FRAME
MAP --> HAL_ALLOC
MAP --> PT_MAP
MAP --> PT_MAP_REGION
PF --> HAL_ALLOC
PF --> PT_REMAP
UNMAP --> HAL_DEALLOC
UNMAP --> PT_UNMAP

Sources: src/address_space/backend/alloc.rs(L7 - L14) 

Allocation Strategies

The allocation backend supports two distinct allocation strategies controlled by the populate boolean parameter:

StrategyDescriptionPhysical Frame AllocationPage Fault Behavior
Eager (populate=true)Physical frames allocated at mapping timeImmediate allocation duringmap_alloc()Should not occur
Lazy (populate=false)Physical frames allocated on first accessDeferred until page faultAllocates frame on demand

Eager Allocation Strategy

When populate is true, the backend performs immediate physical frame allocation for all pages in the requested range:

Eager Allocation Flow


Sources: src/address_space/backend/alloc.rs(L31 - L41) 

Lazy Allocation Strategy

When populate is false, the backend creates empty page table entries that will trigger page faults on first access:

Lazy Allocation Flow


Sources: src/address_space/backend/alloc.rs(L42 - L54)  src/address_space/backend/alloc.rs(L79 - L96) 

Implementation Details

Backend Creation

The allocation backend is created through the new_alloc() constructor method:

pub const fn new_alloc(populate: bool) -> Self {
    Self::Alloc {
        populate,
        _phantom: core::marker::PhantomData,
    }
}

The populate parameter determines the allocation strategy for the lifetime of this backend instance.

Sources: src/address_space/backend/alloc.rs(L8 - L14) 

Memory Mapping Process

The map_alloc() method handles the mapping process differently based on the populate flag:

Mapping Implementation Structure

flowchart TD
subgraph subGraph1["Lazy Branch"]
    MAP_REGION["pt.map_region(start, |_va| PhysAddr::from(0), size, empty_flags)"]
    SUCCESS_LAZY["Return pt.map_region().is_ok()"]
end
subgraph subGraph0["Populate Branch"]
    POPULATE_CHECK["populate == true?"]
    PAGE_ITER["PageIter4K::new(start, start + size)"]
    ALLOC_FRAME["H::alloc_frame()"]
    PT_MAP["pt.map(addr, frame, Size4K, flags)"]
    SUCCESS_EAGER["Return true"]
    FAIL_EAGER["Return false"]
end
MAP_ALLOC["map_alloc(start, size, flags, pt, populate)"]

ALLOC_FRAME --> PT_MAP
MAP_ALLOC --> POPULATE_CHECK
MAP_REGION --> SUCCESS_LAZY
PAGE_ITER --> ALLOC_FRAME
POPULATE_CHECK --> MAP_REGION
POPULATE_CHECK --> PAGE_ITER
PT_MAP --> FAIL_EAGER
PT_MAP --> SUCCESS_EAGER

Sources: src/address_space/backend/alloc.rs(L16 - L54) 

Memory Unmapping Process

The unmap_alloc() method handles cleanup by iterating through all pages and deallocating physical frames:

  • Uses PageIter4K to iterate through all 4KB pages in the range
  • Calls pt.unmap() to remove page table entries
  • Deallocates physical frames via H::dealloc_frame() when mappings exist
  • Gracefully handles pages that are not mapped
  • Rejects huge page mappings for safety

Sources: src/address_space/backend/alloc.rs(L56 - L77) 

Page Fault Handling

The allocation backend implements lazy allocation through page fault handling in handle_page_fault_alloc():

Page Fault Resolution Logic

flowchart TD
subgraph subGraph1["Lazy Allocation"]
    ALLOC_FRAME["H::alloc_frame()"]
    PT_REMAP["pt.remap(vaddr, frame, orig_flags)"]
    SUCCESS_LAZY["Return success"]
end
subgraph subGraph0["Eager Allocation Error"]
    ERROR_POPULATED["Return false"]
    ERROR_NOTE["Populated mappings should not fault"]
end
PF_START["handle_page_fault_alloc(vaddr, orig_flags, pt, populate)"]
POPULATE_CHECK["populate == true?"]

ALLOC_FRAME --> PT_REMAP
ERROR_POPULATED --> ERROR_NOTE
PF_START --> POPULATE_CHECK
POPULATE_CHECK --> ALLOC_FRAME
POPULATE_CHECK --> ERROR_POPULATED
PT_REMAP --> SUCCESS_LAZY

Key characteristics:

  • Eager allocation faults: Return false since populated mappings should never fault
  • Lazy allocation faults: Allocate physical frame and remap the faulting virtual address
  • Address alignment: The pt.remap() method automatically handles address alignment

Sources: src/address_space/backend/alloc.rs(L79 - L96) 

Memory Management Lifecycle

The allocation backend integrates with the hardware abstraction layer for physical memory management:

  1. Frame Allocation: Uses H::alloc_frame() to obtain HostPhysAddr values
  2. Page Table Integration: Works with PageTable<H> for virtual-to-physical mappings
  3. Frame Deallocation: Uses H::dealloc_frame() during unmapping operations
  4. Size Constraints: Currently supports only 4KB pages, rejecting huge page operations

The backend ensures proper resource management through RAII principles and explicit deallocation during unmapping operations.

Sources: src/address_space/backend/alloc.rs(L1 - L98) 

Device Support

Relevant source files

Purpose and Scope

The device support module provides abstractions for hardware device interaction within the axaddrspace crate. This system defines standardized address types and access patterns for different classes of devices, including memory-mapped devices, I/O ports, and system registers. The module establishes a trait-based architecture that allows uniform handling of diverse device addressing schemes while maintaining type safety.

For information about memory mapping strategies used with devices, see Memory Mapping Backends. For details about the underlying address space management, see Address Space Management.

Device Address Architecture

The device support system is built around two core traits that provide a foundation for device addressing abstractions.

Device Address Trait Hierarchy

flowchart TD
DeviceAddr["DeviceAddr traitCopy + Eq + Ord + Debug"]
DeviceAddrRange["DeviceAddrRange traitcontains() method"]
GuestPhysAddr["GuestPhysAddrPhysical memory addresses"]
Port["PortI/O port numbers (u16)"]
SysRegAddr["SysRegAddrSystem register addresses (usize)"]
AddrRange["AddrRange<GuestPhysAddr>Standard address range"]
SysRegAddrRange["SysRegAddrRangeInclusive register range"]
PortRange["PortRangeInclusive port range"]

DeviceAddr --> GuestPhysAddr
DeviceAddr --> Port
DeviceAddr --> SysRegAddr
DeviceAddrRange --> AddrRange
DeviceAddrRange --> PortRange
DeviceAddrRange --> SysRegAddrRange
GuestPhysAddr --> AddrRange
Port --> PortRange
SysRegAddr --> SysRegAddrRange

Sources: src/device/device_addr.rs(L9 - L10)  src/device/device_addr.rs(L12 - L19)  src/device/mod.rs(L66 - L132) 

The DeviceAddr trait defines the minimal requirements for types that can represent device addresses. All implementing types must be copyable, comparable, orderable, and debuggable. The DeviceAddrRange trait provides address containment checking for ranges of device addresses.

Device Address Implementations

Address TypeUnderlying TypePurposeRange Support
GuestPhysAddrGuest physical addressMemory-mapped devicesAddrRange
Portu16I/O port operationsPortRange
SysRegAddrusizeSystem register accessSysRegAddrRange

Sources: src/device/device_addr.rs(L21)  src/device/device_addr.rs(L31)  src/device/device_addr.rs(L65) 

Access Width Specifications

The system defines standardized access widths that correspond to different data transfer sizes during device operations.

AccessWidth Enumeration

flowchart TD
subgraph subGraph0["Conversion Methods"]
    TryFromUsize["try_from(usize)"]
    IntoUsize["into usize"]
    Size["size() -> usize"]
    BitsRange["bits_range() -> Range<usize>"]
end
AccessWidth["AccessWidth enum"]
Byte["Byte8-bit (1 byte)"]
Word["Word16-bit (2 bytes)"]
Dword["Dword32-bit (4 bytes)"]
Qword["Qword64-bit (8 bytes)"]

AccessWidth --> BitsRange
AccessWidth --> Byte
AccessWidth --> Dword
AccessWidth --> IntoUsize
AccessWidth --> Qword
AccessWidth --> Size
AccessWidth --> TryFromUsize
AccessWidth --> Word

Sources: src/device/mod.rs(L9 - L64) 

The AccessWidth enum provides bidirectional conversion between access sizes and byte counts, along with utility methods for determining bit ranges covered by each access type.

I/O Port Addressing

The Port type encapsulates 16-bit I/O port numbers used in x86-style port-mapped I/O operations.

Port Type Structure

The Port struct wraps a u16 value and provides formatting implementations for debugging and display purposes. Port numbers are used to identify specific I/O devices in architectures that support port-mapped I/O.

flowchart TD
subgraph subGraph2["Range Support"]
    PortRange["PortRangestart: Port, end: Port"]
    PortRangeNew["new(start: Port, end: Port)"]
    PortRangeContains["contains(addr: Port) -> bool"]
end
subgraph Formatting["Formatting"]
    LowerHex["LowerHexPort(#x)"]
    UpperHex["UpperHexPort(#X)"]
    Debug["DebugPort(decimal)"]
end
subgraph Creation["Creation"]
    NewMethod["new(port: u16) -> Port"]
    NumberMethod["number() -> u16"]
end
Port["Port(u16)"]

Port --> Debug
Port --> LowerHex
Port --> NewMethod
Port --> NumberMethod
Port --> PortRange
Port --> UpperHex
PortRange --> PortRangeContains
PortRange --> PortRangeNew

Sources: src/device/mod.rs(L66 - L98)  src/device/device_addr.rs(L67 - L97) 

The PortRange struct provides inclusive range checking for port numbers, supporting both formatted output and containment testing.

System Register Addressing

The SysRegAddr type represents addresses used to access architecture-specific system registers.

System Register Type Structure

System register addresses use usize to accommodate different architecture requirements while maintaining generality across platforms. The inclusive range type supports efficient range checking for register banks.

flowchart TD
subgraph subGraph2["Range Support"]
    SysRegAddrRange["SysRegAddrRangestart: SysRegAddr, end: SysRegAddr"]
    SysRangeNew["new(start: SysRegAddr, end: SysRegAddr)"]
    SysRangeContains["contains(addr: SysRegAddr) -> bool"]
    SysRangeFormat["LowerHex formatting#x..=#x"]
end
subgraph Formatting["Formatting"]
    SysLowerHex["LowerHexSysRegAddr(#x)"]
    SysUpperHex["UpperHexSysRegAddr(#X)"]
    SysDebug["DebugSysRegAddr(decimal)"]
end
subgraph Creation["Creation"]
    NewConst["const new(addr: usize) -> SysRegAddr"]
    AddrConst["const addr() -> usize"]
end
SysRegAddr["SysRegAddr(usize)"]

SysRegAddr --> AddrConst
SysRegAddr --> NewConst
SysRegAddr --> SysDebug
SysRegAddr --> SysLowerHex
SysRegAddr --> SysRegAddrRange
SysRegAddr --> SysUpperHex
SysRegAddrRange --> SysRangeContains
SysRegAddrRange --> SysRangeFormat
SysRegAddrRange --> SysRangeNew

Sources: src/device/mod.rs(L100 - L132)  src/device/device_addr.rs(L31 - L63) 

The SysRegAddrRange struct uses inclusive bounds on both ends, unlike standard Rust ranges, and provides specialized formatting for register address ranges.

Guest Physical Address Device Support

Guest physical addresses serve as device addresses for memory-mapped I/O operations within the guest address space.

GuestPhysAddr Integration

The integration leverages the existing memory_addr::AddrRange type to provide standard range operations while maintaining compatibility with the device address abstraction framework.

Sources: src/device/device_addr.rs(L21 - L29) 

The GuestPhysAddr implementation uses the standard AddrRange<GuestPhysAddr> for range operations, providing consistency with other address management components in the system.

Development Guide

Relevant source files

This document provides essential information for developers working on the axaddrspace crate, including environment setup, build procedures, testing, and contribution guidelines. For information about the codebase architecture and components, see Core Architecture. For usage examples and API documentation, see Overview.

Development Environment Setup

The axaddrspace crate requires a Rust nightly toolchain with specific components and target platforms for cross-architecture development.

Required Toolchain Components

The project requires the following Rust toolchain components as configured in the CI pipeline:

ComponentPurpose
rust-srcSource code for standard library cross-compilation
clippyLinting and static analysis
rustfmtCode formatting

Supported Target Platforms

The codebase supports multiple target architectures for hypervisor deployment:

flowchart TD
subgraph subGraph2["Target Categories"]
    hosted["Hosted Environment(Linux userspace)"]
    bare_metal["Bare Metal(No OS)"]
end
subgraph subGraph1["Supported Build Targets"]
    x86_hosted["x86_64-unknown-linux-gnu"]
    x86_bare["x86_64-unknown-none"]
    riscv_bare["riscv64gc-unknown-none-elf"]
    aarch64_bare["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["Rust Toolchain Configuration"]
    toolchain["nightly toolchain"]
end

aarch64_bare --> bare_metal
riscv_bare --> bare_metal
toolchain --> aarch64_bare
toolchain --> riscv_bare
toolchain --> x86_bare
toolchain --> x86_hosted
x86_bare --> bare_metal
x86_hosted --> hosted

Target Platform Configuration Sources: .github/workflows/ci.yml(L12) 

Build System and CI Pipeline

The project uses GitHub Actions for continuous integration with a matrix build strategy covering all supported architectures.

CI Pipeline Architecture

flowchart TD
subgraph subGraph3["Documentation Job Steps"]
    doc_build["cargo doc --no-deps --all-features"]
    doc_deploy["JamesIves/github-pages-deploy-action@v4"]
end
subgraph subGraph2["CI Job Steps"]
    checkout["actions/checkout@v4"]
    toolchain["dtolnay/rust-toolchain@nightly"]
    version_check["rustc --version --verbose"]
    format_check["cargo fmt --all -- --check"]
    clippy_check["cargo clippy --target TARGET --all-features"]
    build_step["cargo build --target TARGET --all-features"]
    test_step["cargo test --target x86_64-unknown-linux-gnu"]
end
subgraph subGraph1["Build Matrix Jobs"]
    ci_job["ci job"]
    doc_job["doc job"]
end
subgraph subGraph0["GitHub Actions Workflow"]
    trigger["Push/PR Trigger"]
    matrix["Matrix Strategy"]
end

build_step --> test_step
checkout --> toolchain
ci_job --> checkout
clippy_check --> build_step
doc_build --> doc_deploy
doc_job --> doc_build
format_check --> clippy_check
matrix --> ci_job
matrix --> doc_job
toolchain --> version_check
trigger --> matrix
version_check --> format_check

CI Workflow Configuration Sources: .github/workflows/ci.yml(L1 - L31) 

Build Commands

The CI pipeline executes the following build and verification commands:

CommandPurposeTarget Scope
cargo fmt --all -- --checkCode formatting verificationAll files
cargo clippy --target $TARGET --all-featuresStatic analysisPer target
cargo build --target $TARGET --all-featuresCompilationPer target
cargo test --target x86_64-unknown-linux-gnuUnit testingHosted target only

Sources: .github/workflows/ci.yml(L23 - L30) 

Testing Procedures

Unit Testing

Unit tests are executed only on the x86_64-unknown-linux-gnu target, which provides a hosted environment with full standard library support. Tests run with the --nocapture flag to display all output.

The testing is configured to run conditionally:

if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}

This approach ensures tests have access to necessary host OS features while maintaining cross-platform build verification.

Sources: .github/workflows/ci.yml(L28 - L30) 

Code Quality Checks

The CI pipeline enforces code quality through automated checks:

  1. Format Verification: Ensures consistent code formatting using rustfmt
  2. Linting: Uses clippy with custom configuration allowing new_without_default warnings
  3. Build Verification: Compiles all targets with full feature sets enabled

Sources: .github/workflows/ci.yml(L22 - L27) 

Documentation Generation

The project maintains automated documentation generation and deployment through GitHub Pages.

Documentation Build Process

flowchart TD
subgraph subGraph2["Output Structure"]
    target_doc["target/doc/"]
    crate_docs["Crate Documentation"]
    index_redirect["index.html(auto-redirect)"]
end
subgraph subGraph1["Documentation Flags"]
    rustdoc_flags["RUSTDOCFLAGS"]
    broken_links["-D rustdoc::broken_intra_doc_links"]
    missing_docs["-D missing-docs"]
end
subgraph subGraph0["Documentation Generation"]
    doc_trigger["Push to Default Branch"]
    doc_build["cargo doc --no-deps --all-features"]
    index_gen["Generate index.html redirect"]
    doc_deploy["Deploy to gh-pages"]
end

broken_links --> doc_build
doc_build --> index_gen
doc_build --> target_doc
doc_trigger --> doc_build
index_gen --> doc_deploy
missing_docs --> doc_build
rustdoc_flags --> broken_links
rustdoc_flags --> missing_docs
target_doc --> crate_docs
target_doc --> index_redirect

Documentation Configuration Sources: .github/workflows/ci.yml(L32 - L55) 

Documentation Standards

The documentation build process enforces strict standards:

  • Broken Link Detection: Fails on broken intra-doc links using -D rustdoc::broken_intra_doc_links
  • Missing Documentation: Requires documentation for all public items using -D missing-docs
  • Auto-deployment: Automatically deploys to GitHub Pages on pushes to the default branch

The documentation includes an auto-generated index redirect that points to the main crate documentation.

Sources: .github/workflows/ci.yml(L40 - L48) 

Code Quality Standards

Linting Configuration

The project uses clippy for static analysis with the following configuration:

  • Allows clippy::new_without_default warnings using -A clippy::new_without_default
  • Runs with --all-features to check all conditional compilation paths
  • Executes on all supported target platforms

Format Standards

Code formatting is enforced using rustfmt with the --check flag to ensure consistent style across the codebase. All files must pass formatting checks before merge.

Sources: .github/workflows/ci.yml(L23 - L25) 

Development Workflow

  1. Environment Setup: Install Rust nightly with required components and targets
  2. Local Development: Use standard cargo commands for building and testing
  3. Pre-commit Checks: Run cargo fmt, cargo clippy, and cargo test locally
  4. CI Validation: All checks must pass in the automated CI pipeline
  5. Documentation: Ensure all public APIs are documented to meet -D missing-docs requirements

The automated CI pipeline provides comprehensive validation across all supported architectures, ensuring code quality and cross-platform compatibility.

Sources: .github/workflows/ci.yml(L1 - L55)  .gitignore(L1 - L5) 

Overview

Relevant source files

Purpose and Scope

This document provides an overview of the riscv_vcpu codebase, which implements a RISC-V virtual CPU (VCPU) system designed for hypervisor environments. The system provides virtualization capabilities for RISC-V guest virtual machines running within the ArceOS hypervisor framework.

The riscv_vcpu crate serves as a foundational layer that abstracts RISC-V hardware virtualization features, implements the Supervisor Binary Interface (SBI), and manages virtual CPU lifecycle operations. It requires RISC-V H-extension hardware support to enable efficient guest VM execution with hardware-assisted virtualization.

For detailed information about the core VCPU implementation and execution flow, see Core VCPU Implementation. For system architecture and supporting components, see System Architecture. For low-level assembly and hardware interface details, see Low-Level Implementation.

System Architecture Overview

The RISC-V VCPU system follows a layered architecture that integrates with the ArceOS hypervisor framework while providing hardware-accelerated virtualization for RISC-V guest VMs.

RISC-V VCPU System Architecture

flowchart TD
subgraph subGraph4["Hardware Layer"]
    riscv_h_ext["RISC-V H-ExtensionHardware virtualization support"]
    riscv_core["RISC-V CoreBase ISA + Extensions"]
end
subgraph subGraph3["Low-Level Systems"]
    trap_s["trap.SAssembly trap handlersGuest entry/exit"]
    regs_rs["regs.rsVmCpuRegistersRegister state management"]
    csr_def["def.rsCSR definitionsHardware register interface"]
end
subgraph subGraph2["Core Implementation Layer"]
    vcpu_rs["vcpu.rsRISCVVCpu structVM execution & SBI handling"]
    percpu_rs["percpu.rsRISCVPerCpu structPer-CPU state management"]
    detect_rs["detect.rsdetect_h_extension()Hardware capability detection"]
end
subgraph subGraph1["riscv_vcpu Public Interface"]
    lib_rs["lib.rsRISCVVCpu, RISCVPerCpuhas_hardware_support()"]
    create_config["RISCVVCpuCreateConfighart_id, dtb_addr"]
    eid_hvc["EID_HVCHypercall Extension ID"]
end
subgraph subGraph0["ArceOS Framework Layer"]
    axvcpu["axvcpuVCPU Abstraction"]
    axaddrspace["axaddrspaceAddress Space Management"]
end

axaddrspace --> lib_rs
axvcpu --> lib_rs
create_config --> vcpu_rs
csr_def --> riscv_core
detect_rs --> csr_def
lib_rs --> detect_rs
lib_rs --> percpu_rs
lib_rs --> vcpu_rs
percpu_rs --> csr_def
regs_rs --> csr_def
trap_s --> riscv_h_ext
vcpu_rs --> csr_def
vcpu_rs --> regs_rs
vcpu_rs --> trap_s

Sources: src/lib.rs(L1 - L46)  Cargo.toml(L1 - L26) 

Key Components and Data Flow

The system operates through well-defined interfaces that manage the complete lifecycle of virtual CPU operations, from hardware detection through guest execution.

Component Interaction and Data Flow

flowchart TD
subgraph subGraph3["External Interface"]
    guest_vm["Guest Virtual Machine"]
    host_hypervisor["Host Hypervisor"]
    rustsbi["RustSBI Framework"]
end
subgraph subGraph2["State Management"]
    vm_cpu_regs["VmCpuRegistersregs.rs"]
    csr_setup["setup_csrspercpu.rs"]
    register_sync["Register synchronizationGeneral purpose & CSRs"]
end
subgraph subGraph1["Runtime Execution"]
    guest_entry["_run_guesttrap.S assembly"]
    vm_exit_handler["vmexit_handlervcpu.rs"]
    sbi_processing["SBI call handlingvcpu.rs"]
end
subgraph subGraph0["Initialization Flow"]
    hw_detect["has_hardware_support()detect.rs"]
    percpu_init["RISCVPerCpu::new()percpu.rs"]
    vcpu_create["RISCVVCpu::new()vcpu.rs"]
end

csr_setup --> vm_cpu_regs
guest_entry --> register_sync
guest_entry --> vm_exit_handler
guest_vm --> guest_entry
hw_detect --> percpu_init
percpu_init --> vcpu_create
rustsbi --> guest_vm
sbi_processing --> guest_entry
sbi_processing --> rustsbi
vcpu_create --> csr_setup
vm_exit_handler --> host_hypervisor
vm_exit_handler --> register_sync
vm_exit_handler --> sbi_processing

Sources: src/lib.rs(L18 - L20)  src/vcpu.rs src/percpu.rs src/detect.rs

Hardware Requirements and Dependencies

The RISC-V VCPU system requires specific hardware and software dependencies to function correctly.

ComponentRequirementDetection MethodPurpose
RISC-V H-ExtensionHardware support requireddetect_h_extension()Hardware-assisted virtualization
Base RISC-V ISARV64 or RV32Compile-time configurationCore instruction set
SBI ImplementationRustSBI frameworkRuntime dependencyGuest-host interface
Address Space Managementaxaddrspace crateFramework integrationMemory virtualization

The system performs runtime hardware detection using a trap-based mechanism implemented in detect.rs to verify H-extension availability before initializing virtualization capabilities.

Hardware Detection and Initialization Sequence

flowchart TD
start["System Boot"]
hw_check["has_hardware_support()calls detect_h_extension()"]
percpu_new["RISCVPerCpu::new()Initialize per-CPU state"]
hw_fail["Hardware not supportedReturn error"]
csr_init["setup_csrs()Configure hypervisor CSRs"]
vcpu_ready["System ready forVCPU creation"]
vcpu_create["RISCVVCpu::new()with RISCVVCpuCreateConfig"]
guest_ready["Guest VM readyfor execution"]
end_fail["Virtualization unavailable"]
end_success["VCPU operational"]

csr_init --> vcpu_ready
guest_ready --> end_success
hw_check --> hw_fail
hw_check --> percpu_new
hw_fail --> end_fail
percpu_new --> csr_init
start --> hw_check
vcpu_create --> guest_ready
vcpu_ready --> vcpu_create

Sources: src/detect.rs src/lib.rs(L20)  src/percpu.rs

Integration with ArceOS Framework

The riscv_vcpu crate integrates seamlessly with the broader ArceOS hypervisor ecosystem through well-defined interfaces and shared abstractions.

ArceOS ComponentIntegration PointPurpose
axvcpuTrait implementationGeneric VCPU abstraction
axaddrspaceGuestPhysAddrtypeGuest physical address management
RustSBISBI call forwardingGuest system call interface
riscv crateHardware intrinsicsLow-level RISC-V operations

The crate exports three primary interfaces through lib.rs:

  • RISCVVCpu<H>: The main virtual CPU implementation
  • RISCVPerCpu<H>: Per-CPU state management
  • has_hardware_support(): Hardware capability detection function

These components work together to provide a complete RISC-V virtualization solution that abstracts hardware complexity while maintaining high performance through hardware-assisted virtualization features.

Sources: src/lib.rs(L1 - L46)  Cargo.toml(L25 - L26) 

Core VCPU Implementation

Relevant source files

Purpose and Scope

This document covers the core virtual CPU implementation centered around the RISCVVCpu struct and its fundamental operations. The RISCVVCpu serves as the primary abstraction for executing RISC-V guest virtual machines within the ArceOS hypervisor framework, handling register state management, guest execution control, and hypervisor interactions.

For detailed information about VCPU creation and lifecycle management, see VCPU Lifecycle and Management. For SBI call processing and hypercall handling, see SBI Interface and Hypercalls. For VM exit event processing, see VM Exit Processing.

Core Data Structures

The VCPU implementation is built around several key data structures that manage virtual CPU state and execution context:

RISCVVCpu Structure

The main RISCVVCpu<H: AxVCpuHal> struct src/vcpu.rs(L23 - L27)  encapsulates all virtual CPU state:

FieldTypePurpose
regsVmCpuRegistersComplete guest register state including GPRs, CSRs, and trap context
sbiRISCVVCpuSbiSBI interface implementation for handling supervisor binary interface calls
_markerPhantomDataType marker for hardware abstraction layer integration

SBI Integration Structure

The RISCVVCpuSbi struct src/vcpu.rs(L29 - L33)  implements the RustSBI interface using the #[derive(RustSBI)] macro, providing automatic forwarding for standard SBI extensions including console, PMU, fence, reset, info, and HSM operations.

Configuration Structures

The system defines two configuration types:

Architecture Overview

The following diagram illustrates the core VCPU architecture and its relationship to key code entities:

flowchart TD
subgraph subGraph3["External Assembly Interface"]
    RunGuest["_run_guest()Assembly Entry Point"]
    TrapHandler["Trap Handlingtrap.S"]
end
subgraph subGraph2["Internal Implementation Methods"]
    VmexitHandler["vmexit_handler()VM Exit Processing"]
    AdvancePc["advance_pc()PC Manipulation"]
    GetGpr["get_gpr()Register Access"]
    SetGpr["set_gpr_from_gpr_index()Register Modification"]
end
subgraph subGraph1["AxArchVCpu Trait Implementation"]
    NewMethod["new()VCPU Creation"]
    SetupMethod["setup()CSR Configuration"]
    RunMethod["run()Guest Execution"]
    BindMethod["bind()Memory Binding"]
    SetEntryMethod["set_entry()Entry Point Setup"]
    SetEptRootMethod["set_ept_root()Page Table Root"]
end
subgraph subGraph0["RISCVVCpu Core Structure"]
    RISCVVCpu["RISCVVCpu<H>Main VCPU Implementation"]
    VmCpuRegisters["VmCpuRegistersComplete Register State"]
    RISCVVCpuSbi["RISCVVCpuSbiSBI Interface Handler"]
end

RISCVVCpu --> BindMethod
RISCVVCpu --> NewMethod
RISCVVCpu --> RISCVVCpuSbi
RISCVVCpu --> RunMethod
RISCVVCpu --> SetEntryMethod
RISCVVCpu --> SetEptRootMethod
RISCVVCpu --> SetupMethod
RISCVVCpu --> VmCpuRegisters
RunGuest --> TrapHandler
RunMethod --> RunGuest
RunMethod --> VmexitHandler
TrapHandler --> VmexitHandler
VmexitHandler --> AdvancePc
VmexitHandler --> GetGpr
VmexitHandler --> SetGpr

Sources: src/vcpu.rs(L23 - L164) 

VCPU State Management

The VCPU maintains comprehensive state through the VmCpuRegisters structure, which includes guest general-purpose registers, control and status registers, and trap context information.

Register Access Interface

The VCPU provides controlled access to guest registers through dedicated methods:

flowchart TD
subgraph subGraph1["Internal Storage"]
    GuestRegs["guest_regs.gprsGeneralPurposeRegisters"]
    VirtualCSRs["virtual_hs_csrsHypervisor CSRs"]
    TrapCSRs["trap_csrsTrap Context"]
end
subgraph subGraph0["Register Access Methods"]
    GetGpr["get_gpr(GprIndex)Read Guest Register"]
    SetGprIndex["set_gpr_from_gpr_index()Write by GprIndex"]
    SetGprOffset["set_gpr(usize, usize)Write by Offset"]
end

GetGpr --> GuestRegs
GuestRegs --> TrapCSRs
GuestRegs --> VirtualCSRs
SetGprIndex --> GuestRegs
SetGprOffset --> GuestRegs

Sources: src/vcpu.rs(L129 - L153) 

Execution Control Flow

The VCPU execution follows a structured pattern that integrates with the hypervisor's execution model:

Core Execution Cycle

sequenceDiagram
    participant HostHypervisor as "Host/Hypervisor"
    participant RISCVVCpurun as "RISCVVCpu::run()"
    participant _run_guest as "_run_guest()"
    participant vmexit_handler as "vmexit_handler()"

    HostHypervisor ->> RISCVVCpurun: Call run()
    RISCVVCpurun ->> RISCVVCpurun: Setup interrupts (sie)
    RISCVVCpurun ->> _run_guest: _run_guest(&mut regs)
    _run_guest ->> _run_guest: Execute guest code
    _run_guest ->> vmexit_handler: VM Exit occurs
    vmexit_handler ->> vmexit_handler: Read trap CSRs
    vmexit_handler ->> vmexit_handler: Process exit reason
    vmexit_handler ->> RISCVVCpurun: Return AxVCpuExitReason
    RISCVVCpurun ->> RISCVVCpurun: Cleanup interrupts
    RISCVVCpurun ->> HostHypervisor: Return exit reason

Sources: src/vcpu.rs(L92 - L111)  src/vcpu.rs(L167 - L308) 

Integration with ArceOS Framework

The VCPU implements the AxArchVCpu trait src/vcpu.rs(L42 - L142)  providing standardized interfaces for:

MethodPurposeKey Operations
new()VCPU creationInitialize registers, set hart ID and DTB address
setup()ConfigurationConfiguresstatusandhstatusCSRs
set_entry()Entry pointSet guest program counter (sepc)
set_ept_root()Memory setupConfigure hypervisor guest address translation (hgatp)
bind()Memory bindingActivate guest page tables and invalidate TLB
run()ExecutionExecute guest code and handle VM exits

Hardware Abstraction Layer Integration

The VCPU uses a generic type parameter H: AxVCpuHal src/vcpu.rs(L23)  to integrate with hardware abstraction layers, enabling platform-specific optimizations while maintaining a consistent interface.

Sources: src/vcpu.rs(L42 - L142)  src/lib.rs(L18 - L20) 

Memory Management Integration

The VCPU integrates with the ArceOS address space management through several key mechanisms:

Extended Page Table Support

The set_ept_root() method src/vcpu.rs(L87 - L90)  configures the hgatp CSR with an Sv48 page table format (mode 8) and the physical page number of the root page table, enabling two-stage address translation for guest memory access.

Memory Binding Operations

The bind() and unbind() methods src/vcpu.rs(L113 - L126)  manage the activation and deactivation of guest address spaces, including TLB invalidation through hfence_gvma_all() to ensure memory consistency.

Sources: src/vcpu.rs(L87 - L90)  src/vcpu.rs(L113 - L126) 

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) 

SBI Interface and Hypercalls

Relevant source files

This page covers the Supervisor Binary Interface (SBI) implementation and hypercall handling within the RISC-V VCPU system. It details how the hypervisor processes SBI calls from guest virtual machines, including legacy extensions, modern extensions like Hart State Management (HSM), and custom hypercalls. For information about VM exit processing in general, see 2.3. For details about the overall VCPU lifecycle, see 2.1.

SBI Architecture Overview

The SBI interface serves as the critical communication layer between guest operating systems and the hypervisor. The RISC-V VCPU implements a comprehensive SBI handler that supports both legacy and modern SBI extensions while providing a custom hypercall interface.

flowchart TD
subgraph subGraph2["Host/Hypervisor Layer"]
    HostSBI["Host SBI Implementation"]
    RustSBI["RustSBI Framework"]
    HostHardware["Host Hardware"]
end
subgraph subGraph1["RISC-V VCPU SBI Handler"]
    VMExit["vmexit_handler"]
    SBIRouter["SBI Call Router"]
    LegacySBI["Legacy SBI Handler"]
    ModernSBI["Modern SBI Handler"]
    HypercallHandler["Hypercall Handler"]
    ForwardSBI["RISCVVCpuSbi::handle_ecall"]
end
subgraph subGraph0["Guest Virtual Machine"]
    GuestOS["Guest OS"]
    GuestEcall["ecall instruction"]
    GuestRegs["Guest Registersa0-a7"]
end

ForwardSBI --> GuestOS
ForwardSBI --> RustSBI
GuestEcall --> GuestRegs
GuestOS --> GuestEcall
GuestRegs --> VMExit
HostSBI --> HostHardware
HypercallHandler --> GuestOS
LegacySBI --> GuestOS
LegacySBI --> HostSBI
ModernSBI --> GuestOS
RustSBI --> HostSBI
SBIRouter --> ForwardSBI
SBIRouter --> HypercallHandler
SBIRouter --> LegacySBI
SBIRouter --> ModernSBI
VMExit --> SBIRouter

Sources: src/vcpu.rs(L184 - L308) 

RISCVVCpuSbi Implementation

The RISCVVCpuSbi struct provides the core SBI handling infrastructure using the RustSBI framework. It implements a forwarding mechanism for standard SBI extensions while allowing custom handling of specific calls.

classDiagram
note for RISCVVCpuSbi "Derived using #[derive(RustSBI)]Automatically implements SBI traits"
note for RISCVVCpuSbi "Forwards calls to host SBIimplementation"
note for Forward "Forwards calls to host SBIimplementation"
class RISCVVCpuSbi {
    +Forward forward
    +handle_ecall(eid, fid, param) SbiResult
}

class RISCVVCpu {
    +VmCpuRegisters regs
    +RISCVVCpuSbi sbi
    +vmexit_handler() AxVCpuExitReason
}

class Forward {
    +console: ConsoleExtension
    +pmu: PmuExtension
    +fence: FenceExtension
    +reset: ResetExtension
    +info: InfoExtension
    +hsm: HsmExtension
    
}

RISCVVCpu  *--  RISCVVCpuSbi
RISCVVCpu  *--  Forward

The struct uses the #[derive(RustSBI)] macro to automatically implement the necessary SBI traits, with the forward field handling console, PMU, fence, reset, info, and HSM extensions through delegation to the host SBI implementation.

Sources: src/vcpu.rs(L29 - L40) 

Legacy SBI Extensions

The hypervisor provides backward compatibility by implementing legacy SBI extensions that were defined in earlier RISC-V SBI specifications. These extensions use function-based calling conventions rather than the modern extension-based approach.

Extension IDFunctionImplementationReturn Behavior
LEGACY_SET_TIMERSet timer interruptCallssbi_rt::set_timer(), clearsvstipSetsa0to 0
LEGACY_CONSOLE_PUTCHAROutput characterForwards to host viasbi_call_legacy_1Direct forwarding
LEGACY_CONSOLE_GETCHARInput characterForwards to host viasbi_call_legacy_0Returns char ina0
LEGACY_SHUTDOWNSystem shutdownTriggers system shutdownReturnsSystemDownexit
sequenceDiagram
    participant GuestOS as "Guest OS"
    participant RISCVVCpu as "RISCVVCpu"
    participant HostSBI as "Host SBI"

    GuestOS ->> RISCVVCpu: "ecall with legacy EID"
    RISCVVCpu ->> RISCVVCpu: "Extract a0-a7 registers"
    alt LEGACY_SET_TIMER
        RISCVVCpu ->> HostSBI: "sbi_rt::set_timer(param[0])"
        RISCVVCpu ->> RISCVVCpu: "hvip::clear_vstip()"
        RISCVVCpu ->> GuestOS: "Set a0=0, advance PC"
    else LEGACY_CONSOLE_PUTCHAR
        RISCVVCpu ->> HostSBI: "sbi_call_legacy_1(EID, char)"
        RISCVVCpu ->> GuestOS: "Advance PC"
    else LEGACY_CONSOLE_GETCHAR
        RISCVVCpu ->> HostSBI: "sbi_call_legacy_0(EID)"
        HostSBI ->> RISCVVCpu: "Return character"
        RISCVVCpu ->> GuestOS: "Set a0=char, advance PC"
    else LEGACY_SHUTDOWN
        RISCVVCpu ->> RISCVVCpu: "Return SystemDown exit"
    end

Sources: src/vcpu.rs(L192 - L220)  src/vcpu.rs(L312 - L335) 

Modern SBI Extensions - HSM

The Hart State Management (HSM) extension provides standardized CPU lifecycle management operations. The hypervisor implements key HSM functions to enable guest operating systems to manage multiple CPU cores.

HSM Function Implementation

flowchart TD
subgraph Parameters["Parameters"]
    StartParams["hartid: target CPUstart_addr: entry pointopaque: argument"]
    SuspendParams["suspend_type: suspend moderesume_addr: resume pointopaque: context"]
end
subgraph subGraph1["VCPU Exit Reasons"]
    CpuUp["AxVCpuExitReason::CpuUp"]
    CpuDown["AxVCpuExitReason::CpuDown"]
    Halt["AxVCpuExitReason::Halt"]
end
subgraph subGraph0["HSM Functions"]
    HartStart["HART_STARTfid=0x0"]
    HartStop["HART_STOPfid=0x1"]
    HartSuspend["HART_SUSPENDfid=0x3"]
end

HartStart --> CpuUp
HartStart --> StartParams
HartStop --> CpuDown
HartSuspend --> Halt
HartSuspend --> SuspendParams

The HSM implementation extracts parameters from guest registers and translates them into appropriate VCPU exit reasons that the hypervisor can handle at a higher level.

Sources: src/vcpu.rs(L222 - L245) 

Hypercall Interface

The hypervisor provides a custom hypercall interface using the EID_HVC extension ID, allowing direct communication between guest software and the hypervisor for performance-critical operations or hypervisor-specific functionality.

Hypercall Processing Flow

flowchart TD
subgraph subGraph2["Exit Reason Structure"]
    HypercallExit["AxVCpuExitReason::Hypercall {nr: function_id,args: [param0..param5]}"]
end
subgraph subGraph1["VCPU Processing"]
    CheckEID["Check extension_id == EID_HVC"]
    ExtractParams["Extract function_id from a6Extract param[0-5] from a0-a5"]
    CreateExit["Create AxVCpuExitReason::Hypercall"]
    AdvancePC["Advance PC by 4 bytes"]
end
subgraph subGraph0["Guest Hypercall"]
    GuestCall["Guest calls ecall"]
    SetRegs["Set a7=EID_HVCSet a6=function_idSet a0-a5=parameters"]
end

AdvancePC --> HypercallExit
CheckEID --> ExtractParams
CreateExit --> AdvancePC
ExtractParams --> CreateExit
GuestCall --> SetRegs
SetRegs --> CheckEID

The hypercall mechanism provides a standardized way for guests to request hypervisor services while maintaining the SBI calling convention. The hypervisor returns control to the calling hypervisor framework through the Hypercall exit reason.

Sources: src/vcpu.rs(L246 - L260) 

SBI Call Processing Flow

The complete SBI call processing occurs within the vmexit_handler function when a VirtualSupervisorEnvCall exception is detected. The handler examines register contents to determine the appropriate processing path.

flowchart TD
VMExit["VM Exit: VirtualSupervisorEnvCall"]
ExtractRegs["Extract a0-a7 registersextension_id = a[7]function_id = a[6]param = a[0..5]"]
Router["Route by extension_id"]
LegacyPath["Legacy SBI Processing"]
HSMPath["HSM Extension Processing"]
HypercallPath["Hypercall Processing"]
ForwardPath["Forward to RustSBI"]
SetRegs1["Set return values in a0/a1"]
ExitReason1["Return VCPU exit reason"]
ExitReason2["Return VCPU exit reason"]
SBIHandle["sbi.handle_ecall(eid, fid, param)"]
CheckError["ret.is_err()?"]
LogWarn["Log warning message"]
SetRegs2["Set a0=ret.errorSet a1=ret.value"]
AdvancePC1["Advance PC by 4"]
AdvancePC2["Advance PC by 4"]
ReturnNothing1["Return Nothing"]
ReturnNothing2["Return Nothing"]
HostHandler["Return to host handler"]
GuestResume["Resume guest execution"]

AdvancePC1 --> ReturnNothing1
AdvancePC2 --> ReturnNothing2
CheckError --> LogWarn
CheckError --> SetRegs2
ExitReason1 --> HostHandler
ExitReason2 --> HostHandler
ExtractRegs --> Router
ForwardPath --> SBIHandle
HSMPath --> ExitReason1
HypercallPath --> ExitReason2
LegacyPath --> SetRegs1
LogWarn --> SetRegs2
ReturnNothing1 --> GuestResume
ReturnNothing2 --> GuestResume
Router --> ForwardPath
Router --> HSMPath
Router --> HypercallPath
Router --> LegacyPath
SBIHandle --> CheckError
SetRegs1 --> AdvancePC1
SetRegs2 --> AdvancePC2
VMExit --> ExtractRegs

The processing flow ensures that all SBI calls either result in immediate guest resumption (for calls handled within the hypervisor) or appropriate exit reasons for calls requiring host-level processing.

Sources: src/vcpu.rs(L184 - L278) 

Register Convention and Error Handling

The SBI implementation follows the standard RISC-V calling convention where a7 contains the extension ID, a6 contains the function ID, and a0-a5 contain parameters. Return values are placed in a0 (error code) and a1 (return value).

For forwarded calls that fail, the system logs detailed error information including the extension ID, function ID, parameters, error code, and return value to aid in debugging SBI-related issues.

Sources: src/vcpu.rs(L264 - L273) 

VM Exit Processing

Relevant source files

Purpose and Scope

This document covers the VM exit processing mechanism in the RISC-V VCPU hypervisor system. VM exits occur when guest execution needs to be interrupted and control transferred back to the hypervisor for handling specific events such as privileged instructions, interrupts, or memory access violations.

The VM exit processing is primarily implemented in the vmexit_handler function and encompasses the detection of exit reasons, processing of different exit types, and the coordination of return mechanisms back to guest execution. For information about the overall VCPU lifecycle, see VCPU Lifecycle and Management. For details about SBI interface implementation, see SBI Interface and Hypercalls.

VM Exit Flow Overview

The VM exit processing follows a structured flow from guest execution interruption through exit reason determination to appropriate handling and guest resumption.

High-Level Exit Processing Flow

flowchart TD
GuestRun["Guest Execution_run_guest()"]
VMExit["VM Exit Occurs"]
CSRRead["Read Exit Informationscause, stval, htval, htinst"]
ExitDecode["Decode Exit Reasonvmexit_handler()"]
SBICall["scause == VirtualSupervisorEnvCall"]
TimerInt["scause == SupervisorTimer"]
ExtInt["scause == SupervisorExternal"]
PageFault["Load/Store GuestPageFault"]
UnhandledTrap["Unhandled Trappanic!"]
ProcessSBI["Process SBI CallExtension/Function ID"]
HandleTimer["Handle Timer InterruptSet VSTIP"]
HandleExternal["Handle External Interrupt"]
HandlePageFault["Handle Nested Page Fault"]
ReturnNothing["Return Nothing"]
ReturnCpuUp["Return CpuUp"]
ReturnCpuDown["Return CpuDown"]
ReturnHalt["Return Halt"]
ReturnHypercall["Return Hypercall"]
ReturnSystemDown["Return SystemDown"]
ReturnExtInt["Return ExternalInterrupt"]
ReturnPageFault["Return NestedPageFault"]
ResumeGuest["Resume Guest Execution"]
HostAction["Host Action Required"]
EventualResume["Eventually Resume Guest"]

CSRRead --> ExitDecode
EventualResume --> GuestRun
ExitDecode --> ExtInt
ExitDecode --> PageFault
ExitDecode --> SBICall
ExitDecode --> TimerInt
ExitDecode --> UnhandledTrap
ExtInt --> HandleExternal
GuestRun --> VMExit
HandleExternal --> ReturnExtInt
HandlePageFault --> ReturnPageFault
HandleTimer --> ReturnNothing
HostAction --> EventualResume
PageFault --> HandlePageFault
ProcessSBI --> ReturnCpuDown
ProcessSBI --> ReturnCpuUp
ProcessSBI --> ReturnHalt
ProcessSBI --> ReturnHypercall
ProcessSBI --> ReturnNothing
ProcessSBI --> ReturnSystemDown
ResumeGuest --> GuestRun
ReturnCpuDown --> HostAction
ReturnCpuUp --> HostAction
ReturnExtInt --> HostAction
ReturnHalt --> HostAction
ReturnHypercall --> HostAction
ReturnNothing --> ResumeGuest
ReturnPageFault --> HostAction
ReturnSystemDown --> HostAction
SBICall --> ProcessSBI
TimerInt --> HandleTimer
VMExit --> CSRRead

Sources: src/vcpu.rs(L92 - L111)  src/vcpu.rs(L167 - L308) 

Exit Reason Detection and CSR Reading

The VM exit processing begins by capturing the CPU state and trap information from various Control and Status Registers (CSRs) to determine the cause and context of the exit.

CSR State Capture Process

flowchart TD
VMExit["VM Exit Event"]
ReadCSRs["Read Trap CSRs"]
SCAUSE["scauseTrap Cause"]
STVAL["stvalTrap Value"]
HTVAL["htvalHypervisor Trap Value"]
HTINST["htinstHypervisor Trap Instruction"]
DecodeType["Decode Trap TypeException vs Interrupt"]
FaultAddr["Calculate Fault Address"]
GuestPageAddr["Guest Page Information"]
InstInfo["Instruction Information"]
ProcessExit["Route to Specific Handler"]

DecodeType --> ProcessExit
FaultAddr --> ProcessExit
GuestPageAddr --> ProcessExit
HTINST --> InstInfo
HTVAL --> GuestPageAddr
InstInfo --> ProcessExit
ReadCSRs --> HTINST
ReadCSRs --> HTVAL
ReadCSRs --> SCAUSE
ReadCSRs --> STVAL
SCAUSE --> DecodeType
STVAL --> FaultAddr
VMExit --> ReadCSRs

The vmexit_handler function captures the following CSR state immediately upon entry:

CSRPurposeUsage in Exit Processing
scauseTrap cause identificationDetermines if exit was due to exception or interrupt
stvalTrap value/addressContains faulting address for memory exceptions
htvalHypervisor trap valueGuest physical page number for guest page faults
htinstHypervisor trap instructionInstruction bits for trapped instruction

Sources: src/vcpu.rs(L167 - L181) 

SBI Call Processing

Virtual Supervisor Environment Calls represent the largest category of VM exits, handling communication between the guest operating system and the hypervisor through the Supervisor Binary Interface.

SBI Call Processing Flow

flowchart TD
VSEnvCall["VirtualSupervisorEnvCallException"]
ExtractArgs["Extract Argumentsa0-a7 registers"]
ExtID["extension_id = a[7]"]
FuncID["function_id = a[6]"]
Params["param[0-5] = a[0-5]"]
CheckLegacy["Legacy SBI?LEGACY_SET_TIMER..LEGACY_SHUTDOWN"]
CheckHSM["HSM Extension?EID_HSM"]
CheckHVC["Hypercall?EID_HVC"]
ForwardSBI["Forward to RustSBI"]
ProcessLegacy["Process Legacy SBITimer/Console/Shutdown"]
ProcessHSM["Process HSMHart Management"]
ProcessHVC["Process Hypercall"]
SetTimer["LEGACY_SET_TIMERsbi_rt::set_timer()"]
ConsolePut["LEGACY_CONSOLE_PUTCHAR"]
ConsoleGet["LEGACY_CONSOLE_GETCHAR"]
Shutdown["LEGACY_SHUTDOWNReturn SystemDown"]
HartStart["HART_STARTReturn CpuUp"]
HartStop["HART_STOPReturn CpuDown"]
HartSuspend["HART_SUSPENDReturn Halt"]
ReturnHypercall["Return Hypercallwith nr and args"]
RustSBIHandle["sbi.handle_ecall()Forward to RustSBI"]
AdvancePC["Advance PC by 4"]
ReturnResult["Return Appropriate Result"]

AdvancePC --> ReturnResult
CheckHSM --> ProcessHSM
CheckHVC --> ProcessHVC
CheckLegacy --> ProcessLegacy
ConsoleGet --> AdvancePC
ConsolePut --> AdvancePC
ExtID --> CheckHSM
ExtID --> CheckHVC
ExtID --> CheckLegacy
ExtID --> ForwardSBI
ExtractArgs --> ExtID
ExtractArgs --> FuncID
ExtractArgs --> Params
ForwardSBI --> RustSBIHandle
HartStart --> AdvancePC
ProcessHSM --> HartStart
ProcessHSM --> HartStop
ProcessHSM --> HartSuspend
ProcessHVC --> AdvancePC
ProcessHVC --> ReturnHypercall
ProcessLegacy --> ConsoleGet
ProcessLegacy --> ConsolePut
ProcessLegacy --> SetTimer
ProcessLegacy --> Shutdown
RustSBIHandle --> AdvancePC
SetTimer --> AdvancePC
VSEnvCall --> ExtractArgs

SBI Extension Categories

The system processes SBI calls in several categories:

Extension TypeExtension ID RangeProcessing MethodReturn Behavior
Legacy SBI0x00-0x08Direct implementationContinue guest execution
HSM Extension0x48534DReturn to host for CPU managementHost handles CPU lifecycle
HypercallsEID_HVCReturn to host with call detailsHost processes hypercall
Standard ExtensionsOther valuesForward to RustSBI implementationContinue guest execution

Sources: src/vcpu.rs(L184 - L278)  src/vcpu.rs(L311 - L335) 

Interrupt Handling

The hypervisor handles two primary categories of interrupts that cause VM exits: timer interrupts and external interrupts.

Interrupt Processing Mechanisms

flowchart TD
IntTrap["Interrupt Trap"]
CheckType["Interrupt Type"]
TimerInt["SupervisorTimerInterrupt"]
ExtInt["SupervisorExternalInterrupt"]
EnableVSTIP["Enable Guest Timerhvip::set_vstip()"]
EnableSTimer["Enable S-mode Timersie::set_stimer()"]
ReturnNothing1["Return NothingResume Guest"]
ReturnExternal["Return ExternalInterruptvector: 0"]
GuestResume1["Guest Continueswith Timer Available"]
HostHandle["Host HandlesExternal Interrupt"]

CheckType --> ExtInt
CheckType --> TimerInt
EnableSTimer --> ReturnNothing1
EnableVSTIP --> ReturnNothing1
ExtInt --> ReturnExternal
IntTrap --> CheckType
ReturnExternal --> HostHandle
ReturnNothing1 --> GuestResume1
TimerInt --> EnableSTimer
TimerInt --> EnableVSTIP

Timer Interrupt Processing

Timer interrupts are handled by enabling the virtual supervisor timer interrupt (VSTIP) for the guest and allowing it to continue execution. This delegation allows the guest to handle its own timer events while maintaining hypervisor control.

External Interrupt Processing

External interrupts are returned to the host system for processing, as they typically require hypervisor-level handling and may affect multiple guests or system-wide state.

Sources: src/vcpu.rs(L279 - L290) 

Page Fault Processing

Guest page faults represent memory access violations that require hypervisor intervention, typically for memory management, protection, or virtualization purposes.

Guest Page Fault Analysis

flowchart TD
PageFaultTrap["Guest Page Fault"]
FaultType["Fault Type"]
LoadFault["LoadGuestPageFaultException"]
StoreFault["StoreGuestPageFaultException"]
CalcAddr["Calculate Fault Address"]
AddrCalc["fault_addr = htval << 2 | stval & 0x3"]
CreateResult["Create NestedPageFault Result"]
ReturnFault["Return NestedPageFaultaddr: GuestPhysAddraccess_flags: empty"]
HostEPT["Host HandlesExtended Page Tables"]

AddrCalc --> CreateResult
CalcAddr --> AddrCalc
CreateResult --> ReturnFault
FaultType --> LoadFault
FaultType --> StoreFault
LoadFault --> CalcAddr
PageFaultTrap --> FaultType
ReturnFault --> HostEPT
StoreFault --> CalcAddr

Fault Address Calculation

The system calculates the precise fault address by combining information from two CSRs:

  • htval: Contains the guest physical page number (shifted left by 2)
  • stval: Contains the page offset (lower 2 bits)

The combination htval << 2 | stval & 0x3 provides the complete fault address for the host to resolve through extended page table management.

Sources: src/vcpu.rs(L291 - L298) 

Return Mechanisms and Exit Reasons

The VM exit processing concludes by returning specific exit reasons that inform the host system about the required action and whether guest execution should resume immediately or requires host intervention.

Exit Reason Categories

flowchart TD
ExitReasons["AxVCpuExitReason"]
ImmediateResume["Immediate Resume"]
HostAction["Host Action Required"]
Nothing["NothingContinue guest execution"]
CpuMgmt["CPU Management"]
SystemMgmt["System Management"]
MemoryMgmt["Memory Management"]
CallMgmt["Call Management"]
CpuUp["CpuUpStart additional CPU"]
CpuDown["CpuDownStop current CPU"]
Halt["HaltSuspend CPU"]
SystemDown["SystemDownShutdown system"]
ExternalInterrupt["ExternalInterruptHandle interrupt"]
NestedPageFault["NestedPageFaultResolve memory fault"]
Hypercall["HypercallProcess hypercall"]

CallMgmt --> Hypercall
CpuMgmt --> CpuDown
CpuMgmt --> CpuUp
CpuMgmt --> Halt
ExitReasons --> HostAction
ExitReasons --> ImmediateResume
HostAction --> CallMgmt
HostAction --> CpuMgmt
HostAction --> MemoryMgmt
HostAction --> SystemMgmt
ImmediateResume --> Nothing
MemoryMgmt --> NestedPageFault
SystemMgmt --> ExternalInterrupt
SystemMgmt --> SystemDown

Exit Reason Impact on Execution Flow

Exit ReasonHost ActionGuest StateExecution Resumption
NothingNonePC advancedImmediate
CpuUpStart target CPUPC advancedAfter CPU start
CpuDownHandle CPU stopUnchangedNever (CPU stopped)
HaltSuspend handlingUnchangedOn resume event
SystemDownShutdown sequenceUnchangedNever (system down)
ExternalInterruptProcess interruptUnchangedAfter interrupt handling
NestedPageFaultResolve page faultUnchangedAfter page table update
HypercallProcess hypercallPC advancedAfter hypercall completion

Sources: src/vcpu.rs(L212)  src/vcpu.rs(L228 - L232)  src/vcpu.rs(L235)  src/vcpu.rs(L242)  src/vcpu.rs(L249 - L259)  src/vcpu.rs(L277)  src/vcpu.rs(L286)  src/vcpu.rs(L289)  src/vcpu.rs(L294 - L297) 

System Architecture

Relevant source files

This document covers the supporting infrastructure and architectural components that enable the RISC-V VCPU system to function. It focuses on the foundational systems including per-CPU management, hardware detection, and the overall system initialization flow. For detailed coverage of the core VCPU implementation and VM execution lifecycle, see Core VCPU Implementation. For low-level assembly implementations and hardware register definitions, see Low-Level Implementation.

System Initialization and Component Integration

The RISC-V VCPU system follows a layered architecture where hardware detection validates platform capabilities before initializing per-CPU state and enabling hypervisor functionality.

System Startup Flow

flowchart TD
Start["System Startup"]
LibInit["lib.rs Module Loading"]
HWDetect["detect_h_extension()"]
HWCheck["Hardware Support?"]
PerCPUInit["RISCVPerCpu::new()"]
Failed["AxError::Unsupported"]
CSRSetup["setup_csrs()"]
HedelegConfig["hedeleg::Hedeleg::write()"]
HidelegConfig["hideleg::Hideleg::write()"]
InterruptClear["hvip::clear_*()"]
SIEConfig["sie::set_*()"]
VCPUReady["VCPU System Ready"]
VCPUCreate["RISCVVCpu Creation"]
VMExecution["VM Execution Loop"]
End["System Unavailable"]

CSRSetup --> HedelegConfig
CSRSetup --> HidelegConfig
CSRSetup --> InterruptClear
CSRSetup --> SIEConfig
Failed --> End
HWCheck --> Failed
HWCheck --> PerCPUInit
HWDetect --> HWCheck
HedelegConfig --> VCPUReady
HidelegConfig --> VCPUReady
InterruptClear --> VCPUReady
LibInit --> HWDetect
PerCPUInit --> CSRSetup
SIEConfig --> VCPUReady
Start --> LibInit
VCPUCreate --> VMExecution
VCPUReady --> VCPUCreate

Sources: src/lib.rs(L1 - L46)  src/percpu.rs(L15 - L41)  src/detect.rs(L15 - L25) 

Public Interface Architecture

The system exposes its functionality through a clean public API defined in the main library module:

ComponentTypePurpose
RISCVPerCpuPublic StructPer-CPU state management
RISCVVCpuPublic StructVirtual CPU implementation
has_hardware_support()FunctionHardware capability detection
RISCVVCpuCreateConfigConfigurationVCPU creation parameters
EID_HVCConstantHypercall extension identifier

The library uses feature gates for no-std operation and RISC-V specific intrinsics, enabling bare-metal hypervisor deployment.

Sources: src/lib.rs(L1 - L46) 

Per-CPU State Management Architecture

The RISCVPerCpu<H> component implements the AxArchPerCpu trait to provide hardware-specific per-CPU functionality within the ArceOS framework.

Per-CPU Component Structure

flowchart TD
subgraph subGraph3["Hardware Detection"]
    HasHWSupport["has_hardware_support()"]
    DetectHExt["detect_h_extension()"]
end
subgraph subGraph2["CSR Management"]
    SetupCSRs["setup_csrs()"]
    HedelegSetup["hedeleg::Hedeleg"]
    HidelegSetup["hideleg::Hideleg"]
    HVIPClear["hvip::clear_*()"]
    SIESet["sie::set_*()"]
end
subgraph subGraph1["RISCVPerCpu Implementation"]
    RISCVPerCpu["RISCVPerCpu"]
    NewMethod["new(cpu_id: usize)"]
    HardwareEnable["hardware_enable()"]
    IsEnabled["is_enabled()"]
    HardwareDisable["hardware_disable()"]
end
subgraph subGraph0["ArceOS Framework Interface"]
    AxArchPerCpu["AxArchPerCpu Trait"]
    AxVCpuHal["AxVCpuHal Trait"]
end

AxArchPerCpu --> RISCVPerCpu
AxVCpuHal --> RISCVPerCpu
HardwareEnable --> HasHWSupport
HasHWSupport --> DetectHExt
NewMethod --> SetupCSRs
SetupCSRs --> HVIPClear
SetupCSRs --> HedelegSetup
SetupCSRs --> HidelegSetup
SetupCSRs --> SIESet

Sources: src/percpu.rs(L1 - L82) 

CSR Initialization Process

The setup_csrs() function configures critical Control and Status Registers for hypervisor operation:

Exception Delegation Configuration:

  • Delegates specific synchronous exceptions via hedeleg::Hedeleg::write()
  • Includes instruction address misalignment, breakpoints, environment calls, and page faults
  • Enables guest operating systems to handle these exceptions directly

Interrupt Delegation Configuration:

  • Delegates virtual supervisor interrupts through hideleg::Hideleg::write()
  • Covers timer, external, and software interrupts for guest VMs
  • Allows efficient interrupt handling without hypervisor intervention

Interrupt State Management:

  • Clears all pending virtual supervisor interrupts using hvip::clear_*() functions
  • Configures supervisor interrupt enable via sie::set_*() functions
  • Establishes clean interrupt state for VM execution

Sources: src/percpu.rs(L43 - L81)  src/consts.rs(L1 - L50) 

Hardware Detection System

The hardware detection subsystem uses a trap-and-emulate approach to safely probe RISC-V H-extension availability without causing system instability.

Detection Mechanism Architecture

flowchart TD
subgraph subGraph3["CSR Access Test"]
    CSRRInstr["asm!(csrr {}, 0x680)"]
    HGATPRead["hgatp CSR Read"]
end
subgraph subGraph2["Trap Handler Components"]
    OnDetectTrap["on_detect_trap()"]
    RustDetectTrap["rust_detect_trap()"]
    TrapFrame["TrapFrame struct"]
end
subgraph subGraph1["Trap Detection Framework"]
    WithDetectTrap["with_detect_trap()"]
    InitDetectTrap["init_detect_trap()"]
    RestoreDetectTrap["restore_detect_trap()"]
end
subgraph subGraph0["Detection Interface"]
    DetectHExt["detect_h_extension()"]
    HasHWSupport["has_hardware_support()"]
end

CSRRInstr --> HGATPRead
DetectHExt --> WithDetectTrap
InitDetectTrap --> OnDetectTrap
OnDetectTrap --> RustDetectTrap
RustDetectTrap --> TrapFrame
WithDetectTrap --> CSRRInstr
WithDetectTrap --> InitDetectTrap
WithDetectTrap --> RestoreDetectTrap

Sources: src/detect.rs(L1 - L218) 

Detection Process Flow

The hardware detection operates through controlled exception handling:

  1. Environment Setup: init_detect_trap() disables interrupts and installs custom trap handler
  2. Probe Execution: Attempts to read the hgatp CSR (register 0x680) using inline assembly
  3. Exception Analysis: rust_detect_trap() examines scause to determine if illegal instruction occurred
  4. State Restoration: restore_detect_trap() restores original interrupt and trap configuration

The system returns success (true) if the CSR read completes without illegal instruction exception, indicating H-extension support.

Trap Frame Structure

FieldPurpose
ra,tp,a0-a7,t0-t6General-purpose register preservation
sstatusSupervisor status register state
sepcException program counter
scauseException cause identification
stvalException value (instruction bits for illegal instruction)

The trap handler uses instruction length analysis via riscv_illegal_insn_bits() to correctly advance sepc past the illegal instruction, ensuring proper execution continuation.

Sources: src/detect.rs(L120 - L144)  src/detect.rs(L40 - L77) 

Component Integration and Data Flow

The system architecture enables seamless integration between hardware detection, per-CPU management, and the broader ArceOS framework.

Module Dependency Graph

flowchart TD
subgraph subGraph1["Internal Modules"]
    LibRS["lib.rs"]
    PerCPU["percpu.rs"]
    Detect["detect.rs"]
    Consts["consts.rs"]
    Regs["regs.rs"]
    Trap["trap.rs"]
    VCPU["vcpu.rs"]
end
subgraph subGraph0["External Dependencies"]
    ArceOS["axvcpu::AxArchPerCpu"]
    AddrSpace["axaddrspace::GuestPhysAddr"]
    RISCVRegs["riscv::register::*"]
    Log["log crate"]
end

AddrSpace --> LibRS
ArceOS --> PerCPU
Detect --> Consts
LibRS --> Detect
LibRS --> PerCPU
LibRS --> VCPU
Log --> LibRS
PerCPU --> Consts
PerCPU --> Detect
RISCVRegs --> Detect
RISCVRegs --> PerCPU
VCPU --> Regs
VCPU --> Trap

Sources: src/lib.rs(L7 - L20) 

Configuration and Error Handling

The system provides robust configuration through RISCVVCpuCreateConfig:

pub struct RISCVVCpuCreateConfig {
    pub hart_id: usize,                              // CPU core identifier
    pub dtb_addr: axaddrspace::GuestPhysAddr,        // Device tree blob location
}

Error handling follows ArceOS conventions using AxResult<T> and AxError types. Hardware capability failures return AxError::Unsupported, enabling graceful degradation when H-extension is unavailable.

The hypercall extension identifier EID_HVC (0x485643, "HVC" in ASCII) provides a standardized interface for guest-hypervisor communication outside the standard SBI specification.

Sources: src/lib.rs(L22 - L45) 

Per-CPU Management

Relevant source files

Purpose and Scope

This document describes the per-CPU state management system in the RISC-V VCPU hypervisor, focusing on how each CPU core is initialized and configured for virtualization. The per-CPU management system handles Control and Status Register (CSR) initialization, hardware feature enablement, and interrupt/exception delegation setup required for RISC-V hypervisor extensions.

For information about overall VCPU lifecycle management, see VCPU Lifecycle and Management. For details about hardware feature detection, see Hardware Detection. For CSR register definitions and bitfield details, see CSR Definitions and Hardware Registers.

Per-CPU Architecture Overview

The per-CPU management system provides a hardware abstraction layer that initializes each CPU core for hypervisor operation. It bridges the gap between the ArceOS framework's generic per-CPU interface and RISC-V-specific hypervisor initialization requirements.

flowchart TD
subgraph subGraph2["Hardware Layer"]
    hedeleg["HEDELEG CSR"]
    hideleg["HIDELEG CSR"]
    hvip["HVIP CSR"]
    hcounteren["HCOUNTEREN CSR"]
    sie["SIE CSR"]
end
subgraph subGraph1["RISC-V Per-CPU Implementation"]
    RISCVPerCpu["RISCVPerCpu<H>"]
    setup_csrs["setup_csrs()"]
    hardware_enable["hardware_enable()"]
    has_hardware_support["has_hardware_support()"]
end
subgraph subGraph0["ArceOS Framework"]
    AxArchPerCpu["AxArchPerCpu Trait"]
    AxVCpuHal["AxVCpuHal Interface"]
end

AxArchPerCpu --> RISCVPerCpu
AxVCpuHal --> RISCVPerCpu
RISCVPerCpu --> hardware_enable
RISCVPerCpu --> setup_csrs
hardware_enable --> has_hardware_support
setup_csrs --> hcounteren
setup_csrs --> hedeleg
setup_csrs --> hideleg
setup_csrs --> hvip
setup_csrs --> sie

Sources: src/percpu.rs(L1 - L82) 

RISCVPerCpu Structure and Lifecycle

The RISCVPerCpu struct serves as the primary per-CPU state container, implementing the AxArchPerCpu trait to integrate with the ArceOS hypervisor framework.

Structure Definition


The structure uses a PhantomData marker to maintain the generic AxVCpuHal type parameter while storing no actual data, as the per-CPU state is maintained entirely in hardware CSRs.

Sources: src/percpu.rs(L10 - L13)  src/percpu.rs(L15 - L41) 

Initialization Flow

The per-CPU initialization follows a specific sequence that prepares each CPU core for hypervisor operation:

flowchart TD
subgraph subGraph0["setup_csrs() Details"]
    exception_delegate["Configure Exception Delegation (HEDELEG)"]
    interrupt_delegate["Configure Interrupt Delegation (HIDELEG)"]
    clear_interrupts["Clear Virtual Interrupts (HVIP)"]
    inst_misalign["INST_ADDR_MISALIGN"]
    breakpoint["BREAKPOINT"]
    ecall["ENV_CALL_FROM_U_OR_VU"]
    page_faults["Page Fault Exceptions"]
    vs_timer["VIRTUAL_SUPERVISOR_TIMER"]
    vs_external["VIRTUAL_SUPERVISOR_EXTERNAL"]
    vs_soft["VIRTUAL_SUPERVISOR_SOFT"]
    clear_vssip["hvip::clear_vssip()"]
    clear_vstip["hvip::clear_vstip()"]
    clear_vseip["hvip::clear_vseip()"]
end
Start["RISCVPerCpu::new(cpu_id)"]
setup_csrs_call["Call setup_csrs()"]
setup_counters["Setup Counter Access (HCOUNTEREN)"]
enable_interrupts["Enable Supervisor Interrupts (SIE)"]
create_struct["Create RISCVPerCpu Instance"]
End["Return AxResult"]

Start --> setup_csrs_call
clear_interrupts --> clear_vseip
clear_interrupts --> clear_vssip
clear_interrupts --> clear_vstip
clear_interrupts --> setup_counters
create_struct --> End
enable_interrupts --> create_struct
exception_delegate --> breakpoint
exception_delegate --> ecall
exception_delegate --> inst_misalign
exception_delegate --> interrupt_delegate
exception_delegate --> page_faults
interrupt_delegate --> clear_interrupts
interrupt_delegate --> vs_external
interrupt_delegate --> vs_soft
interrupt_delegate --> vs_timer
setup_counters --> enable_interrupts
setup_csrs_call --> exception_delegate

Sources: src/percpu.rs(L16 - L24)  src/percpu.rs(L44 - L81) 

CSR Initialization Process

The setup_csrs() function performs critical hypervisor-level Control and Status Register initialization. This unsafe function configures the hardware delegation and interrupt management necessary for virtualization.

Exception Delegation Configuration

The hypervisor exception delegation register (HEDELEG) is configured to automatically forward specific exceptions from guest execution to the guest's supervisor mode rather than trapping to the hypervisor:

Exception TypeConstantPurpose
Instruction Address MisalignedINST_ADDR_MISALIGNMemory alignment errors
BreakpointBREAKPOINTDebug breakpoint traps
Environment CallENV_CALL_FROM_U_OR_VUSystem calls from user/virtual-user mode
Instruction Page FaultINST_PAGE_FAULTInstruction fetch page faults
Load Page FaultLOAD_PAGE_FAULTLoad operation page faults
Store Page FaultSTORE_PAGE_FAULTStore operation page faults
Illegal InstructionILLEGAL_INSTInvalid instruction execution

Sources: src/percpu.rs(L47 - L56)  src/consts.rs

Interrupt Delegation Setup

The hypervisor interrupt delegation register (HIDELEG) forwards virtual supervisor-level interrupts directly to the guest:

flowchart TD
subgraph subGraph2["Guest Handling"]
    GuestISR["Guest Interrupt Handlers"]
end
subgraph subGraph1["HIDELEG Configuration"]
    VSTIMER_BIT["VIRTUAL_SUPERVISOR_TIMER"]
    VSEXT_BIT["VIRTUAL_SUPERVISOR_EXTERNAL"]
    VSSOFT_BIT["VIRTUAL_SUPERVISOR_SOFT"]
end
subgraph subGraph0["Interrupt Sources"]
    VSTimer["Virtual Supervisor Timer"]
    VSExternal["Virtual Supervisor External"]
    VSSoft["Virtual Supervisor Software"]
end

VSEXT_BIT --> GuestISR
VSExternal --> VSEXT_BIT
VSSOFT_BIT --> GuestISR
VSSoft --> VSSOFT_BIT
VSTIMER_BIT --> GuestISR
VSTimer --> VSTIMER_BIT

Sources: src/percpu.rs(L59 - L64)  src/consts.rs

Counter and Interrupt Enablement

The initialization process also configures:

  1. Counter Access (HCOUNTEREN): Enables guest access to performance counters by setting all bits to 1
  2. Virtual Interrupt Clearing (HVIP): Clears all pending virtual supervisor interrupts
  3. Supervisor Interrupt Enable (SIE): Enables external, software, and timer interrupts at supervisor level

Sources: src/percpu.rs(L67 - L79) 

Hardware Enablement Interface

The per-CPU system provides hardware enablement control through the AxArchPerCpu trait implementation:

stateDiagram-v2
[*] --> Uninitialized
Uninitialized --> Initialized : new(cpu_id)
Initialized --> HardwareEnabled : hardware_enable()
HardwareEnabled --> HardwareDisabled : hardware_disable()
HardwareDisabled --> HardwareEnabled : hardware_enable()
note left of HardwareEnabled : ['has_hardware_support() == true']
note left of HardwareDisabled : ['Hardware virtualization disabled']
note left of Uninitialized : ['Per-CPU state not created']

Hardware Support Validation

The hardware_enable() method validates that the underlying hardware supports RISC-V hypervisor extensions before enabling virtualization features:

  • Success Path: Returns Ok(()) when has_hardware_support() returns true
  • Failure Path: Returns AxError::Unsupported when hardware lacks H-extension support

Sources: src/percpu.rs(L30 - L36) 

Unimplemented Features

The current implementation includes placeholder methods for future functionality:

  • is_enabled(): Returns whether hardware virtualization is currently active (unimplemented)
  • hardware_disable(): Disables hardware virtualization features (unimplemented)

Sources: src/percpu.rs(L26 - L28)  src/percpu.rs(L38 - L40) 

Integration with System Architecture

The per-CPU management system integrates with the broader RISC-V hypervisor architecture through several key interfaces:

flowchart TD
subgraph subGraph3["VCPU Management"]
    VCPUCreation["VCPU Creation"]
    VCPUExecution["VCPU Execution"]
end
subgraph subGraph2["Hardware Detection"]
    has_hardware_support_func["has_hardware_support()"]
    detect_h_extension["detect_h_extension()"]
end
subgraph subGraph1["Per-CPU Layer"]
    RISCVPerCpu_new["RISCVPerCpu::new()"]
    setup_csrs_func["setup_csrs()"]
    hardware_enable_func["hardware_enable()"]
end
subgraph subGraph0["System Initialization"]
    SystemBoot["System Boot"]
    CPUEnumeration["CPU Enumeration"]
    PerCPUCreation["Per-CPU Creation"]
end

CPUEnumeration --> PerCPUCreation
PerCPUCreation --> RISCVPerCpu_new
RISCVPerCpu_new --> hardware_enable_func
RISCVPerCpu_new --> setup_csrs_func
SystemBoot --> CPUEnumeration
VCPUCreation --> VCPUExecution
hardware_enable_func --> has_hardware_support_func
has_hardware_support_func --> detect_h_extension
setup_csrs_func --> VCPUCreation

This per-CPU initialization must complete successfully before any VCPU instances can be created or executed on the respective CPU core, ensuring that the hardware virtualization environment is properly configured.

Sources: src/percpu.rs(L1 - L82)  src/detect.rs

Hardware Detection

Relevant source files

This page documents the trap-based hardware feature detection system used to identify RISC-V H-extension support at runtime. The detection mechanism safely probes for hypervisor extension availability by attempting to access hypervisor-specific Control Status Registers (CSRs) and catching any resulting illegal instruction exceptions.

For information about per-CPU management and CSR initialization, see Per-CPU Management. For details about trap handling in the main VCPU system, see VM Exit Processing.

Purpose and Scope

The hardware detection system provides runtime identification of RISC-V hypervisor extension support through a controlled trap-and-return procedure. The system disables interrupts, attempts to access hypervisor CSRs, and uses exception handling to determine whether the hardware supports the required extensions.

Sources: src/detect.rs(L1 - L6) 

Detection Process Overview

The hardware detection system uses a trap-based approach to safely probe for hardware features. The process attempts to read the hgatp (Hypervisor Guest Address Translation and Protection) register, which is only available when the H-extension is present.

Detection Flow Diagram

flowchart TD
Start["detect_h_extension()"]
Setup["with_detect_trap()"]
DisableInt["Disable S-level interrupts"]
SetTrap["Set custom trap handler"]
AttemptCSR["Attempt to read hgatp CSR (0x680)"]
Success["CSR read successful"]
IllegalInst["Illegal instruction exception"]
RestoreOrig["Restore original state"]
TrapHandler["on_detect_trap assembly handler"]
RustHandler["rust_detect_trap()"]
SkipInst["Skip illegal instruction"]
RestoreAfterTrap["Restore original state"]
ReturnTrue["Return true (H-extension present)"]
ReturnFalse["Return false (H-extension absent)"]
End["Hardware support confirmed"]

AttemptCSR --> IllegalInst
AttemptCSR --> Success
DisableInt --> SetTrap
IllegalInst --> TrapHandler
RestoreAfterTrap --> ReturnFalse
RestoreOrig --> ReturnTrue
ReturnFalse --> End
ReturnTrue --> End
RustHandler --> SkipInst
SetTrap --> AttemptCSR
Setup --> DisableInt
SkipInst --> RestoreAfterTrap
Start --> Setup
Success --> RestoreOrig
TrapHandler --> RustHandler

Sources: src/detect.rs(L18 - L25)  src/detect.rs(L32 - L39) 

Implementation Architecture

The detection system consists of several interconnected components that work together to safely probe hardware features and handle any resulting exceptions.

Component Relationships

flowchart TD
subgraph subGraph3["Hardware Interface"]
    CSRAccess["CSR Access (hgatp)"]
    InsnDecoding["riscv_illegal_insn_bits()"]
end
subgraph subGraph2["Trap Handling System"]
    AsmHandler["on_detect_trap() (naked asm)"]
    RustHandler["rust_detect_trap()"]
    TrapFrameStruct["TrapFrame struct"]
end
subgraph subGraph1["Core Detection Logic"]
    WithTrap["with_detect_trap()"]
    InitTrap["init_detect_trap()"]
    RestoreTrap["restore_detect_trap()"]
end
subgraph subGraph0["Public Interface"]
    DetectFunc["detect_h_extension()"]
    PublicAPI["has_hardware_support (lib.rs)"]
end

AsmHandler --> RustHandler
CSRAccess --> AsmHandler
DetectFunc --> WithTrap
InitTrap --> AsmHandler
PublicAPI --> DetectFunc
RestoreTrap --> WithTrap
RustHandler --> InsnDecoding
RustHandler --> TrapFrameStruct
WithTrap --> CSRAccess
WithTrap --> InitTrap
WithTrap --> RestoreTrap

Sources: src/lib.rs(L20)  src/detect.rs(L18 - L25)  src/detect.rs(L32 - L39)  src/detect.rs(L155 - L217) 

Detection Process Implementation

The detect_h_extension() function serves as the primary entry point for hardware detection. It uses a controlled environment to attempt accessing hypervisor-specific CSRs.

Core Detection Function

The detection process attempts to read the hgatp CSR (register 0x680) within a controlled trap environment:

// From detect.rs lines 18-25
pub fn detect_h_extension() -> bool {
    let ans = with_detect_trap(0, || unsafe {
        asm!("csrr  {}, 0x680", out(reg) _, options(nomem, nostack)); // 0x680 => hgatp
    });
    ans != 2  // 0 => success, 2 => failed (illegal instruction)
}

The function returns true if the CSR access succeeds (indicating H-extension presence) or false if an illegal instruction exception occurs (indicating absence).

Sources: src/detect.rs(L18 - L25) 

Trap Environment Setup

The with_detect_trap() function creates a controlled environment for safe hardware probing:

PhaseFunctionPurpose
Setupinit_detect_trap()Disable interrupts, set custom trap handler
ExecutionClosure parameterExecute the probing instruction
Cleanuprestore_detect_trap()Restore original state, return result

The setup process preserves the original system state while creating a minimal trap handling environment.

Sources: src/detect.rs(L32 - L39)  src/detect.rs(L81 - L101)  src/detect.rs(L105 - L118) 

Trap Handling Mechanism

When an illegal instruction exception occurs during hardware probing, the system uses a specialized trap handling mechanism to safely recover and continue execution.

Trap Handler Flow

sequenceDiagram
    participant CPUCore as "CPU Core"
    participant on_detect_trapasm as "on_detect_trap() (asm)"
    participant rust_detect_trap as "rust_detect_trap()"
    participant TrapFrame as "TrapFrame"

    CPUCore ->> on_detect_trapasm: "Illegal instruction exception"
    on_detect_trapasm ->> TrapFrame: "Save all registers to TrapFrame"
    on_detect_trapasm ->> rust_detect_trap: "Call with TrapFrame pointer"
    rust_detect_trap ->> TrapFrame: "Read scause, stval, sepc"
    rust_detect_trap ->> rust_detect_trap: "Decode instruction length"
    rust_detect_trap ->> TrapFrame: "Update sepc to skip instruction"
    rust_detect_trap ->> TrapFrame: "Set tp register with exception code"
    rust_detect_trap ->> on_detect_trapasm: "Return to assembly handler"
    on_detect_trapasm ->> TrapFrame: "Restore all registers"
    on_detect_trapasm ->> CPUCore: "sret (return from trap)"

Sources: src/detect.rs(L155 - L217)  src/detect.rs(L42 - L60) 

TrapFrame Structure

The trap handler uses a specialized TrapFrame structure to preserve processor state during exception handling:

// Key fields from TrapFrame (lines 122-144)
struct TrapFrame {
    ra: usize,     // Return address
    tp: usize,     // Thread pointer (used for return value)
    a0-a7: usize,  // Argument registers
    t0-t6: usize,  // Temporary registers
    sstatus: usize, // Supervisor status
    sepc: usize,   // Exception program counter
    scause: Scause,// Exception cause
    stval: usize,  // Trap value
}

The tp register serves dual purposes: passing parameters into the detection routine and returning the exception code.

Sources: src/detect.rs(L121 - L144) 

Exception Processing and Recovery

The trap handler analyzes exceptions and implements appropriate recovery mechanisms based on the exception type.

Exception Analysis Process

flowchart TD
TrapEntry["rust_detect_trap() entry"]
ReadCause["Read scause register"]
CheckType["Exception type?"]
ProcessIllegal["Process illegal instruction"]
Unreachable1["unreachable!()"]
Unreachable2["unreachable!() (filtered)"]
ReadStval["Read stval for instruction bits"]
CheckStval["stval[0..16] == 0?"]
ReadMemory["Read instruction from sepc"]
DecodeStval["Decode from stval"]
DecodeLength["riscv_illegal_insn_bits()"]
UpdateSepc["sepc += instruction_length"]
SetReturnCode["tp = scause.bits()"]
Return["Return to assembly handler"]

CheckStval --> DecodeStval
CheckStval --> ReadMemory
CheckType --> ProcessIllegal
CheckType --> Unreachable1
CheckType --> Unreachable2
DecodeLength --> UpdateSepc
DecodeStval --> DecodeLength
ProcessIllegal --> ReadStval
ReadCause --> CheckType
ReadMemory --> DecodeLength
ReadStval --> CheckStval
SetReturnCode --> Return
TrapEntry --> ReadCause
UpdateSepc --> SetReturnCode

Sources: src/detect.rs(L42 - L60)  src/detect.rs(L64 - L77) 

Instruction Length Decoding

The system includes logic to determine RISC-V instruction lengths for proper exception recovery:

Instruction PatternLengthEncoding
xxxxxxxxxxxxxxxx11(bits [1:0] != 11)16-bitCompressed
xxxxxxxxxxxxxxxnnn11(bits [4:2] != 111)32-bitStandard
xxxxxxxxxxxxxxx11111(bits [4:2] == 111)≥48-bitExtended

The riscv_illegal_insn_bits() function implements this decoding logic to ensure the exception handler skips the correct number of bytes.

Sources: src/detect.rs(L64 - L77) 

Integration with System Architecture

The hardware detection system integrates with the broader RISC-V VCPU architecture through the public API and initialization processes.

Public Interface Integration

The detection functionality is exposed through the main library interface:

// From lib.rs line 20
pub use detect::detect_h_extension as has_hardware_support;

This allows other system components to query hardware capabilities before attempting to use hypervisor features.

Sources: src/lib.rs(L20) 

Error Handling Strategy

The detection system uses a fail-safe approach where:

  • Successful CSR access indicates H-extension support
  • Any exception (particularly illegal instruction) indicates lack of support
  • The system gracefully recovers from all exception scenarios
  • No system state is permanently modified during detection

This approach ensures that hardware detection can safely run on any RISC-V system, regardless of hypervisor extension availability.

Sources: src/detect.rs(L18 - L25)  src/detect.rs(L42 - L60) 

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 CategoryRegistersPurpose
Argument registersA0-A7Function arguments and return values
Saved registersS0-S11Callee-saved registers
Temporary registersT0-T6Caller-saved temporary values
Special registersZero, RA, SP, GP, TPZero, 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

StructureUsage ContextSave/Restore Timing
HypervisorCpuStateHost execution stateSaved on VM entry, restored on VM exit
GuestCpuStateGuest execution stateRestored on VM entry, saved on VM exit
GuestVsCsrsVS-mode virtualizationActive when V=1, context switched between VMs
GuestVirtualHsCsrsEmulated hypervisorMaintained per-VM for nested virtualization
VmCpuTrapStateTrap informationWritten 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.

Low-Level Implementation

Relevant source files

This page covers the assembly-level implementations, hardware register definitions, and low-level system constants that form the foundation of the RISC-V VCPU system. This includes the critical assembly trap handlers, Control and Status Register (CSR) bitfield definitions, and system-wide constants for interrupts and exceptions.

For higher-level VCPU management and lifecycle operations, see Core VCPU Implementation. For register state management data structures, see Register Management.

Assembly Trap Handler Implementation

The core of the hypervisor's guest-host transition mechanism is implemented in assembly language through the trap handler system. This provides the critical low-level routines for entering and exiting guest virtual machines.

Guest Entry and Exit Assembly Functions

The system implements two primary assembly functions that handle the critical guest-hypervisor transitions:

Guest Entry Flow (_run_guest)

flowchart TD
start["_run_guest"]
save_hyp["Save Hypervisor GPRs"]
swap_csr["Swap in Guest CSRs"]
set_stvec["Set stvec to _guest_exit"]
save_sscratch["Save sscratch, set to GuestInfo"]
restore_guest["Restore Guest GPRs"]
sret["sret - Enter Guest"]
guest_exec["Guest Execution"]
trap["VM Exit/Trap"]
guest_exit["_guest_exit"]

guest_exec --> trap
restore_guest --> sret
save_hyp --> swap_csr
save_sscratch --> restore_guest
set_stvec --> save_sscratch
sret --> guest_exec
start --> save_hyp
swap_csr --> set_stvec
trap --> guest_exit

Guest Exit Flow (_guest_exit)

flowchart TD
guest_exit["_guest_exit"]
swap_a0["Swap GuestInfo from sscratch"]
save_guest["Save Guest GPRs"]
save_guest_a0["Save Guest a0 from sscratch"]
restore_csr["Swap in Hypervisor CSRs"]
save_sepc["Save Guest EPC"]
restore_hyp["Restore Hypervisor GPRs"]
ret_hyp["ret - Return to Hypervisor"]

guest_exit --> swap_a0
restore_csr --> save_sepc
restore_hyp --> ret_hyp
save_guest --> save_guest_a0
save_guest_a0 --> restore_csr
save_sepc --> restore_hyp
swap_a0 --> save_guest

The assembly implementation uses precise register offset calculations to access the VmCpuRegisters structure fields. The offsets are computed using compile-time constants generated by the hyp_gpr_offset and guest_gpr_offset functions in src/trap.rs(L8 - L19) 

Sources: src/trap.S(L1 - L183)  src/trap.rs(L1 - L103) 

Register Context Switching Mechanism

The trap handler performs comprehensive context switching between hypervisor and guest register states:

flowchart TD
subgraph subGraph1["Memory Layout"]
    VmCpuRegs["VmCpuRegisters"]
    HypState["HypervisorCpuStatehyp_regs"]
    GuestState["GuestCpuStateguest_regs"]
end
subgraph subGraph0["Register Categories"]
    GPRs["General Purpose Registersra, gp, tp, s0-s11, a1-a7, sp"]
    CSRs["Control Status Registerssstatus, hstatus, scounterenstvec, sscratch, sepc"]
    Special["Special Handlinga0 (via sscratch)t0-t6 (guest only)"]
end

CSRs --> GuestState
CSRs --> HypState
GPRs --> GuestState
GPRs --> HypState
VmCpuRegs --> GuestState
VmCpuRegs --> HypState

The offset calculation macros hyp_csr_offset! and guest_csr_offset! at src/trap.rs(L22 - L33)  enable the assembly code to access specific CSR fields within the register structure using compile-time computed offsets.

Sources: src/trap.rs(L35 - L102)  src/trap.S(L32 - L156) 

CSR Register Definitions and Hardware Interfaces

The system provides comprehensive definitions for RISC-V Control and Status Registers using the tock_registers framework, enabling type-safe bitfield manipulation of hardware registers.

Hypervisor Extension CSR Definitions

Key hypervisor-specific CSRs are defined with detailed bitfield structures:

CSR NameAddressPurpose
hstatus0x600Hypervisor status register
hedeleg0x602Hypervisor exception delegation
hideleg0x603Hypervisor interrupt delegation
hie0x604Hypervisor interrupt enable
hcounteren0x606Hypervisor counter enable
hvip0x645Hypervisor virtual interrupt pending

HSTATUS Register Bitfield Layout

The hstatus register contains critical hypervisor state control bits:

flowchart TD
subgraph subGraph0["hstatus Register Fields"]
    VSBE["vsbe[6]Virtual SupervisorBig Endian"]
    GVA["gva[6]Guest VirtualAddress"]
    SPV["spv[7]Supervisor PreviousVirtualization Mode"]
    SPVP["spvp[8]Supervisor PreviousVirtual Privilege"]
    HU["hu[9]Hypervisor inUser Mode"]
    VGEIN["vgein[17:12]Virtual GuestExternal Interrupt"]
    VTVM["vtvm[20]Virtual TLBManagement"]
    VTW["vtw[21]Virtual TimeoutWait"]
    VTSR["vtsr[22]Virtual TimeoutState Reporting"]
    VSXL["vsxl[33:32]Virtual SupervisorXLEN"]
end

The SPV field uses enumerated values for privilege modes:

  • User = 0 - User mode virtualization
  • Supervisor = 1 - Supervisor mode virtualization

Sources: def.rs(L37 - L1284) 

Exception and Interrupt Delegation Registers

The hedeleg register controls which exceptions are delegated to VS-mode:

flowchart TD
subgraph subGraph0["Exception Types (hedeleg)"]
    MISALIGN["instr_misaligned[0]Instruction Address Misaligned"]
    IFAULT["instr_fault[1]Instruction Access Fault"]
    ILLEGAL["illegal_instr[2]Illegal Instruction"]
    BREAK["breakpoint[3]Breakpoint"]
    LMISALIGN["load_misaligned[4]Load Address Misaligned"]
    LFAULT["load_fault[5]Load Access Fault"]
    SMISALIGN["store_misaligned[6]Store Address Misaligned"]
    SFAULT["store_fault[7]Store Access Fault"]
    UECALL["u_ecall[8]User Environment Call"]
    IPFAULT["instr_page_fault[12]Instruction Page Fault"]
    LPFAULT["load_page_fault[13]Load Page Fault"]
    SPFAULT["store_page_fault[15]Store Page Fault"]
end

The hideleg register controls interrupt delegation with fields for:

  • vssoft[2] - VS-mode software interrupt
  • vstimer[6] - VS-mode timer interrupt
  • vsext[10] - VS-mode external interrupt

Sources: def.rs(L55 - L1420) 

System Constants and Trap Definitions

The system defines comprehensive constants for interrupt handling, exception types, and trap processing organized into logical modules.

Interrupt Type Constants

The interrupt constants are organized by interrupt source and privilege level:

flowchart TD
subgraph subGraph0["Interrupt Constants (consts::traps::interrupt)"]
    USER_SOFT["USER_SOFT = 1<<0"]
    SUPERVISOR_SOFT["SUPERVISOR_SOFT = 1<<1"]
    VIRTUAL_SUPERVISOR_SOFT["VIRTUAL_SUPERVISOR_SOFT = 1<<2"]
    MACHINE_SOFT["MACHINE_SOFT = 1<<3"]
    USER_TIMER["USER_TIMER = 1<<4"]
    SUPERVISOR_TIMER["SUPERVISOR_TIMER = 1<<5"]
    VIRTUAL_SUPERVISOR_TIMER["VIRTUAL_SUPERVISOR_TIMER = 1<<6"]
    MACHINE_TIMER["MACHINE_TIMER = 1<<7"]
    USER_EXTERNAL["USER_EXTERNAL = 1<<8"]
    SUPERVISOR_EXTERNAL["SUPERVISOR_EXTERNAL = 1<<9"]
    VIRTUAL_SUPERVISOR_EXTERNAL["VIRTUAL_SUPERVISOR_EXTERNAL = 1<<10"]
    MACHINEL_EXTERNAL["MACHINEL_EXTERNAL = 1<<11"]
    SUPERVISOR_GUEST_EXTERNEL["SUPERVISOR_GUEST_EXTERNEL = 1<<12"]
end

Exception Type Constants

Exception constants define the various fault and trap conditions:

Exception TypeConstant ValueDescription
INST_ADDR_MISALIGN1<<0Instruction address misaligned
INST_ACCESSS_FAULT1<<1Instruction access fault
ILLEGAL_INST1<<2Illegal instruction
BREAKPOINT1<<3Breakpoint
LOAD_ADDR_MISALIGNED1<<4Load address misaligned
LOAD_ACCESS_FAULT1<<5Load access fault
STORE_ADDR_MISALIGNED1<<6Store address misaligned
STORE_ACCESS_FAULT1<<7Store access fault
ENV_CALL_FROM_U_OR_VU1<<8Environment call from U-mode or VU-mode
ENV_CALL_FROM_HS1<<9Environment call from HS-mode
ENV_CALL_FROM_VS1<<10Environment call from VS-mode
ENV_CALL_FROM_M1<<11Environment call from M-mode

IRQ Processing Constants

The IRQ module provides constants for interrupt processing and cause register interpretation:

flowchart TD
subgraph subGraph0["IRQ Constants (consts::traps::irq)"]
    INTC_BASE["INTC_IRQ_BASE1 << (usize::BITS - 1)Interrupt Bit Mask"]
    S_SOFT["S_SOFTINTC_IRQ_BASE + 1Supervisor Software IRQ"]
    S_TIMER["S_TIMERINTC_IRQ_BASE + 5Supervisor Timer IRQ"]
    S_EXT["S_EXTINTC_IRQ_BASE + 9Supervisor External IRQ"]
    MAX_IRQ["MAX_IRQ_COUNT = 1024Maximum IRQ Number"]
    TIMER_IRQ["TIMER_IRQ_NUM = S_TIMERTimer IRQ Identifier"]
end

The INTC_IRQ_BASE constant at src/consts.rs(L79)  provides the base value for distinguishing interrupts from exceptions in the scause register, with interrupts having the most significant bit set.

Sources: src/consts.rs(L1 - L92) 

Trap Handler Implementation

Relevant source files

This document covers the low-level assembly language trap handlers that manage guest virtual machine entry and exit. These handlers perform the critical register context switching between hypervisor and guest execution contexts, enabling secure isolation and efficient virtualization on RISC-V systems with H-extension support.

For higher-level trap processing and VM exit handling, see VM Exit Processing. For register data structure definitions, see Register Management.

Overview

The trap handler implementation consists of two primary assembly language routines that manage the transition between hypervisor and guest execution contexts:

  • Guest Entry (_run_guest) - Saves hypervisor state, loads guest state, and transfers control to the guest VM
  • Guest Exit (_guest_exit) - Captures guest state upon VM exit and restores hypervisor execution context

These handlers work in conjunction with carefully designed data structures in src/regs.rs to ensure complete CPU state preservation during virtualization transitions.

Architecture and Data Flow

The trap handler operates through a precisely choreographed sequence of register and CSR manipulations:

flowchart TD
subgraph subGraph0["VmCpuRegisters Data Structure"]
    HypRegs["hyp_regs: HypervisorCpuState"]
    GuestRegs["guest_regs: GuestCpuState"]
    VSCSRs["vs_csrs: GuestVsCsrs"]
    TrapCSRs["trap_csrs: VmCpuTrapState"]
end
HypCall["Hypervisor calls _run_guest()"]
SaveHyp["Save Hypervisor State"]
LoadGuest["Load Guest State"]
SetTrap["Set trap vector to _guest_exit"]
SRET["Execute sret to enter guest"]
GuestExec["Guest Execution"]
TrapOccurs["Guest Trap/Exit Occurs"]
SaveGuest["Save Guest State"]
RestoreHyp["Restore Hypervisor State"]
Return["Return to Hypervisor"]

GuestExec --> TrapOccurs
HypCall --> SaveHyp
LoadGuest --> GuestRegs
LoadGuest --> SetTrap
RestoreHyp --> HypRegs
RestoreHyp --> Return
SRET --> GuestExec
SaveGuest --> GuestRegs
SaveGuest --> RestoreHyp
SaveHyp --> HypRegs
SaveHyp --> LoadGuest
SetTrap --> SRET
TrapOccurs --> SaveGuest

Sources: src/trap.S(L1 - L183)  src/regs.rs(L116 - L196) 

Register Context Data Structures

The trap handlers operate on the VmCpuRegisters structure, which contains separate state containers for hypervisor and guest contexts:

StructurePurposeKey Fields
HypervisorCpuStateHypervisor execution stateGPRs, sstatus, hstatus, scounteren, stvec, sscratch
GuestCpuStateGuest execution stateGPRs, sstatus, hstatus, scounteren, sepc
GeneralPurposeRegisters32 RISC-V registersx0-x31 (zero, ra, sp, gp, tp, t0-t6, s0-s11, a0-a7)

The assembly code accesses these structures through computed offsets provided by the Rust compilation system:

flowchart TD
subgraph subGraph2["Offset Calculation Functions"]
    VmCpuRegs["VmCpuRegisters"]
    HypState["hyp_regs"]
    GuestState["guest_regs"]
    HypGPRs["HypervisorCpuState.gprs"]
    GuestGPRs["GuestCpuState.gprs"]
    HypGPROffset["hyp_gpr_offset()"]
    GuestGPROffset["guest_gpr_offset()"]
    HypCSROffset["hyp_csr_offset!()"]
    GuestCSROffset["guest_csr_offset!()"]
end
subgraph subGraph1["Assembly Constants"]
    HypRA["hyp_ra offset"]
    HypSP["hyp_sp offset"]
    GuestRA["guest_ra offset"]
    GuestSP["guest_sp offset"]
    HypSSTATUS["hyp_sstatus offset"]
    GuestSSTATUS["guest_sstatus offset"]
end
subgraph subGraph0["Rust Data Structure"]
    VmCpuRegs["VmCpuRegisters"]
    HypState["hyp_regs"]
    GuestState["guest_regs"]
    HypGPRs["HypervisorCpuState.gprs"]
    GuestGPRs["GuestCpuState.gprs"]
    HypGPROffset["hyp_gpr_offset()"]
    GuestGPROffset["guest_gpr_offset()"]
end

GuestCSROffset --> GuestSSTATUS
GuestGPROffset --> GuestRA
GuestGPROffset --> GuestSP
GuestState --> GuestGPRs
HypCSROffset --> HypSSTATUS
HypGPROffset --> HypRA
HypGPROffset --> HypSP
HypState --> HypGPRs
VmCpuRegs --> GuestState
VmCpuRegs --> HypState

Sources: src/trap.rs(L7 - L33)  src/regs.rs(L1 - L114)  src/regs.rs(L116 - L138) 

Guest Entry Sequence (_run_guest)

The _run_guest function implements the critical transition from hypervisor to guest execution:

Hypervisor State Preservation

The handler first saves the hypervisor's execution context, excluding temporary registers and the a0 register which contains the VmCpuRegisters pointer:

  • General-purpose registers (ra, gp, tp, s0-s11, a1-a7, sp)
  • Critical CSRs (sstatus, hstatus, scounteren, stvec, sscratch)

Guest State Loading

The handler then configures the CPU for guest execution:

  • Swaps hypervisor CSRs with guest CSRs using csrrw instructions
  • Loads guest program counter into sepc
  • Sets trap vector (stvec) to _guest_exit for handling guest traps
  • Stores VmCpuRegisters pointer in sscratch for quick access during exit
  • Restores all guest general-purpose registers

Context Switch Completion

The sequence concludes with an sret instruction that:

  • Switches to guest privilege level
  • Jumps to the guest program counter (sepc)
  • Enables guest execution with appropriate privilege and virtualization settings

Sources: src/trap.S(L3 - L91)  src/trap.rs(L35 - L102) 

Guest Exit Sequence (_guest_exit)

When the guest encounters a trap, interrupt, or exception, control transfers to _guest_exit:

Guest State Capture

The exit handler immediately preserves the guest's execution state:

  • Retrieves VmCpuRegisters pointer from sscratch
  • Saves all guest general-purpose registers to the guest state structure
  • Captures guest's a0 register value from sscratch
  • Records guest program counter from sepc

Hypervisor State Restoration

The handler restores the hypervisor execution environment:

  • Swaps guest CSRs back to hypervisor CSRs using csrrw
  • Restores hypervisor trap vector (stvec)
  • Restores hypervisor scratch register (sscratch)
  • Reloads all hypervisor general-purpose registers

Return to Hypervisor

The handler concludes with a ret instruction, returning control to the hypervisor code that called _run_guest.

sequenceDiagram
    participant Hypervisor as Hypervisor
    participant Guest as Guest
    participant _run_guest as _run_guest
    participant _guest_exit as _guest_exit

    Hypervisor ->> _run_guest: Call with VmCpuRegisters*
    _run_guest ->> _run_guest: Save hypervisor GPRs
    _run_guest ->> _run_guest: Save hypervisor CSRs
    _run_guest ->> _run_guest: Load guest CSRs
    _run_guest ->> _run_guest: Set stvec to _guest_exit
    _run_guest ->> _run_guest: Load guest GPRs
    _run_guest ->> Guest: sret (enter guest)
    Guest ->> Guest: Execute guest code
    Guest ->> _guest_exit: Trap/Exception occurs
    _guest_exit ->> _guest_exit: Save guest GPRs
    _guest_exit ->> _guest_exit: Save guest CSRs
    _guest_exit ->> _guest_exit: Restore hypervisor CSRs
    _guest_exit ->> _guest_exit: Restore hypervisor GPRs
    _guest_exit ->> Hypervisor: ret (return to hypervisor)

Sources: src/trap.S(L92 - L183) 

Assembly-Rust Interface

The integration between assembly and Rust code relies on computed memory offsets and the global_asm! macro:

Offset Calculation

The Rust code provides compile-time offset calculations for accessing structure fields:

  • hyp_gpr_offset() computes offsets into hypervisor register arrays
  • guest_gpr_offset() computes offsets into guest register arrays
  • hyp_csr_offset!() and guest_csr_offset!() macros compute CSR field offsets

Assembly Integration

The core::arch::global_asm! macro embeds the assembly code with substituted constants:

core::arch::global_asm!(
    include_str!("trap.S"),
    hyp_ra = const hyp_gpr_offset(GprIndex::RA),
    guest_ra = const guest_gpr_offset(GprIndex::RA),
    hyp_sstatus = const hyp_csr_offset!(sstatus),
    // ... additional offset constants
);

This approach ensures type safety and automatic offset calculation while maintaining the performance benefits of hand-optimized assembly code.

Sources: src/trap.rs(L1 - L102)  src/regs.rs(L5 - L86) 

Special Register Handling

sscratch Register Usage

The sscratch CSR serves a dual purpose in the trap handling mechanism:

  • During guest execution: Contains the VmCpuRegisters pointer for fast access
  • During hypervisor execution: Contains the hypervisor's original sscratch value

Register A0 Special Case

The a0 register receives special handling because it carries the VmCpuRegisters pointer:

  • Not saved during initial hypervisor state preservation
  • Swapped with guest a0 via sscratch during guest entry
  • Guest's a0 value recovered from sscratch during exit

Zero Register Enforcement

The GeneralPurposeRegisters::set_reg() method enforces RISC-V architectural requirements by preventing writes to the zero register (x0), ensuring compliance with the RISC-V specification.

Sources: src/trap.S(L53 - L55)  src/trap.S(L94 - L95)  src/trap.S(L129 - L131)  src/regs.rs(L96 - L102) 

CSR Definitions and Hardware Registers

Relevant source files

This document provides comprehensive reference material for the Control and Status Register (CSR) definitions and hardware register interfaces used throughout the RISC-V VCPU hypervisor system. It covers the register bitfield definitions, CSR initialization procedures, and the hierarchical organization of supervisor, virtual supervisor, and hypervisor-level registers.

For information about trap handling and exception processing that utilizes these CSRs, see Trap Handler Implementation. For system constants and trap definitions, see System Constants and Trap Definitions.

CSR Architecture Overview

The RISC-V hypervisor extension introduces a three-level privilege hierarchy that requires careful management of CSRs across supervisor, virtual supervisor, and hypervisor modes. The system implements comprehensive CSR definitions using the tock_registers framework to provide type-safe register access.

Register Hierarchy and Privilege Levels

flowchart TD
subgraph subGraph2["Virtual Supervisor Mode (VS-level)"]
    VSSTATUS["VSSTATUSVirtual Supervisor Status"]
    VSIE["VSIEVirtual Supervisor IE"]
    VSTVEC["VSTVECVirtual Trap Vector"]
    VSSCRATCH["VSSCRATCHVirtual Scratch"]
    VSEPC["VSEPCVirtual Exception PC"]
    VSCAUSE["VSCAUSEVirtual Exception Cause"]
    VSTVAL["VSTVALVirtual Trap Value"]
    VSATP["VSATPVirtual Address Translation"]
end
subgraph subGraph1["Supervisor Mode (S-level)"]
    SSTATUS["SSTATUSSupervisor Status"]
    SIE["SIESupervisor Interrupt Enable"]
    STVEC["STVECTrap Vector"]
    SSCRATCH["SSCRATCHScratch Register"]
    SEPC["SEPCException PC"]
    SCAUSE["SCAUSEException Cause"]
    STVAL["STVALTrap Value"]
    SATP["SATPAddress Translation"]
end
subgraph subGraph0["Hypervisor Mode (HS-level)"]
    HSTATUS["HSTATUSHypervisor Status"]
    HEDELEG["HEDELEGException Delegation"]
    HIDELEG["HIDELEGInterrupt Delegation"]
    HIE["HIEHypervisor Interrupt Enable"]
    HVIP["HVIPVirtual Interrupt Pending"]
    HCOUNTEREN["HCOUNTERENCounter Enable"]
    HGATP["HGATPGuest Address Translation"]
end

HEDELEG --> SCAUSE
HIDELEG --> SIE
HSTATUS --> VSSTATUS
HVIP --> VSIE

Sources: def.rs(L1 - L52)  src/percpu.rs(L44 - L81) 

CSR Address Space Organization

flowchart TD
subgraph Implementation_Modules["Implementation_Modules"]
    SIE_Impl["sie moduleSupervisor Interrupt Enable"]
    HSTATUS_Impl["hstatus moduleHypervisor Status"]
    HEDELEG_Impl["hedeleg moduleException Delegation"]
    HIDELEG_Impl["hideleg moduleInterrupt Delegation"]
    HIE_Impl["hie moduleHypervisor Interrupt Enable"]
    HCOUNTEREN_Impl["hcounteren moduleCounter Enable"]
    HVIP_Impl["hvip moduleVirtual Interrupt Pending"]
end
subgraph CSR_Address_Space["CSR_Address_Space"]
    S_Range["0x100-0x1FFSupervisor CSRs"]
    VS_Range["0x200-0x2FFVirtual Supervisor CSRs"]
    H_Range["0x600-0x6FFHypervisor CSRs"]
    M_Range["0x300-0x3FFMachine CSRs"]
end

H_Range --> HCOUNTEREN_Impl
H_Range --> HEDELEG_Impl
H_Range --> HIDELEG_Impl
H_Range --> HIE_Impl
H_Range --> HSTATUS_Impl
H_Range --> HVIP_Impl
S_Range --> SIE_Impl

Sources: def.rs(L4 - L52)  def.rs(L551 - L684)  def.rs(L687 - L1284) 

CSR Initialization Process

The system initializes CSRs through the setup_csrs() function called during per-CPU initialization. This process configures exception delegation, interrupt delegation, and enables necessary hardware features.

CSR Setup Flow

flowchart TD
subgraph Exception_Types["Exception_Types"]
    LOAD_PAGE_FAULT["LOAD_PAGE_FAULT"]
    STORE_PAGE_FAULT["STORE_PAGE_FAULT"]
    ILLEGAL_INST["ILLEGAL_INST"]
    Start["setup_csrs()"]
    subgraph Interrupt_Types["Interrupt_Types"]
        VIRTUAL_SUPERVISOR_TIMER["VIRTUAL_SUPERVISOR_TIMER"]
        VIRTUAL_SUPERVISOR_EXTERNAL["VIRTUAL_SUPERVISOR_EXTERNAL"]
        VIRTUAL_SUPERVISOR_SOFT["VIRTUAL_SUPERVISOR_SOFT"]
        INST_ADDR_MISALIGN["INST_ADDR_MISALIGN"]
        BREAKPOINT["BREAKPOINT"]
        ENV_CALL_FROM_U_OR_VU["ENV_CALL_FROM_U_OR_VU"]
        INST_PAGE_FAULT["INST_PAGE_FAULT"]
    end
end
HEDELEG_Setup["Configure HEDELEGException Delegation"]
HIDELEG_Setup["Configure HIDELEGInterrupt Delegation"]
HVIP_Clear["Clear HVIPVirtual Interrupts"]
HCOUNTEREN_Set["Set HCOUNTERENCounter Access"]
SIE_Enable["Enable SIESupervisor Interrupts"]
Complete["CSR Setup Complete"]

HCOUNTEREN_Set --> SIE_Enable
HEDELEG_Setup --> HIDELEG_Setup
HIDELEG_Setup --> HVIP_Clear
HVIP_Clear --> HCOUNTEREN_Set
SIE_Enable --> Complete
Start --> HEDELEG_Setup

Sources: src/percpu.rs(L44 - L81) 

Key CSR Register Definitions

Hypervisor Exception Delegation Register (HEDELEG)

The hedeleg module provides bitfield definitions for configuring which exceptions are delegated to lower privilege levels. Each bit corresponds to a specific exception type defined in the trap constants.

FieldBit PositionDescription
instr_misaligned0Instruction address misaligned
instr_fault1Instruction access fault
illegal_instr2Illegal instruction
breakpoint3Breakpoint
load_misaligned4Load address misaligned
load_fault5Load access fault
store_misaligned6Store address misaligned
store_fault7Store access fault
u_ecall8User environment call
instr_page_fault12Instruction page fault
load_page_fault13Load page fault
store_page_fault15Store page fault

Sources: def.rs(L55 - L547) 

Hypervisor Status Register (HSTATUS)

The hstatus module defines the hypervisor status register with fields controlling virtualization behavior and guest state management.

FieldBit PositionWidthDescription
vsbe61Virtual supervisor big-endian
gva61Guest virtual address
spv71Supervisor previous virtualization mode
spvp81Supervisor previous privilege
hu91Hypervisor in user mode
vgein12-176Virtual guest external interrupt number
vtvm201Virtual TVM
vtw211Virtual timeout wait
vtsr221Virtual trap SRET
vsxl32-332Virtual supervisor XLEN

Sources: def.rs(L687 - L1284) 

Supervisor Interrupt Enable Register (SIE)

The sie module provides definitions for enabling specific interrupt types at the supervisor level.

FieldBit PositionDescription
ssoft1Supervisor software interrupt
stimer5Supervisor timer interrupt
sext9Supervisor external interrupt

Sources: def.rs(L551 - L684) 

Virtual Interrupt Management

The system provides comprehensive virtual interrupt management through several CSR modules:

flowchart TD
subgraph Control_Operations["Control_Operations"]
    Delegate["hideleg.write()Delegate interrupts"]
    Enable["hie.write()Enable interrupts"]
    Inject["hvip.set_*()Inject interrupts"]
    Clear["hvip.clear_*()Clear interrupts"]
end
subgraph Interrupt_Types["Interrupt_Types"]
    VSSOFT["vssoftBit 2VS-mode software"]
    VSTIMER["vstimerBit 6VS-mode timer"]
    VSEXT["vsextBit 10VS-mode external"]
    SGEXT["sgextBit 12Supervisor guest external"]
end
subgraph Virtual_Interrupt_Control["Virtual_Interrupt_Control"]
    HIDELEG_CSR["hideleg CSR0x603"]
    HIE_CSR["hie CSR0x604"]
    HVIP_CSR["hvip CSR0x645"]
end

Delegate --> VSEXT
Delegate --> VSSOFT
Delegate --> VSTIMER
Enable --> SGEXT
HIDELEG_CSR --> Delegate
HIE_CSR --> Enable
HVIP_CSR --> Clear
HVIP_CSR --> Inject

Sources: def.rs(L1287 - L1596)  def.rs(L1422 - L1596)  def.rs(L1774 - L1908) 

CSR Usage in System Initialization

The RISCVPerCpu implementation demonstrates practical CSR usage during system initialization. The setup_csrs() function configures the hypervisor environment for guest execution.

Exception Delegation Configuration

flowchart TD
subgraph Delegated_Exceptions["Delegated_Exceptions"]
    INST_ADDR_MISALIGN_BIT["INST_ADDR_MISALIGN"]
    BREAKPOINT_BIT["BREAKPOINT"]
    ENV_CALL_BIT["ENV_CALL_FROM_U_OR_VU"]
    INST_PAGE_FAULT_BIT["INST_PAGE_FAULT"]
    LOAD_PAGE_FAULT_BIT["LOAD_PAGE_FAULT"]
    STORE_PAGE_FAULT_BIT["STORE_PAGE_FAULT"]
    ILLEGAL_INST_BIT["ILLEGAL_INST"]
end
subgraph setup_csrs_function["setup_csrs_function"]
    HEDELEG_Config["hedeleg::Hedeleg::from_bits()"]
    Exception_Mask["traps::exception bitmask"]
    HEDELEG_Write["hedeleg.write()"]
end

BREAKPOINT_BIT --> Exception_Mask
ENV_CALL_BIT --> Exception_Mask
Exception_Mask --> HEDELEG_Config
HEDELEG_Config --> HEDELEG_Write
ILLEGAL_INST_BIT --> Exception_Mask
INST_ADDR_MISALIGN_BIT --> Exception_Mask
INST_PAGE_FAULT_BIT --> Exception_Mask
LOAD_PAGE_FAULT_BIT --> Exception_Mask
STORE_PAGE_FAULT_BIT --> Exception_Mask

Counter Enable Configuration

The system includes a workaround for the hcounteren CSR due to an error in the riscv crate. Direct assembly is used to write the correct CSR address:

// Direct CSR write due to riscv crate error
core::arch::asm!("csrw {csr}, {rs}", csr = const 0x606, rs = in(reg) -1);

Sources: src/percpu.rs(L72 - L74) 

Register Access Patterns

The CSR definitions use the tock_registers framework to provide type-safe register access with clear field semantics. Each register module includes:

  • Field definitions with bit positions and widths
  • Value enumerations for multi-bit fields
  • SET/CLEAR constants for single-bit fields
  • Type-safe conversion functions between values and register representations

Field Value Operations

flowchart TD
subgraph Usage_Examples["Usage_Examples"]
    HSTATUS_SPV["hstatus::spv::Supervisor"]
    SIE_SEXT["sie::sext::SET"]
    HVIP_CLEAR["hvip::vstimer::CLEAR"]
end
subgraph Register_Field_Operations["Register_Field_Operations"]
    FieldValue["FieldValue"]
    SET_Const["Field::SET constant"]
    CLEAR_Const["Field::CLEAR constant"]
    Custom_Value["Custom value creation"]
end

CLEAR_Const --> HVIP_CLEAR
Custom_Value --> HSTATUS_SPV
FieldValue --> CLEAR_Const
FieldValue --> Custom_Value
FieldValue --> SET_Const
SET_Const --> SIE_SEXT

Sources: def.rs(L783 - L881)  def.rs(L578 - L603)  def.rs(L853 - L867) 

System Constants and Trap Definitions

Relevant source files

This document covers the system constants, interrupt types, exception definitions, and trap-related constants used throughout the RISC-V VCPU hypervisor system. These definitions provide the foundation for trap handling, interrupt management, and VM exit processing.

For detailed information about the trap handler assembly implementation, see Trap Handler Implementation. For CSR register definitions used in conjunction with these constants, see CSR Definitions and Hardware Registers.

Trap Constants Architecture

The system defines comprehensive constants for all RISC-V trap types, organized into three main categories: interrupts, exceptions, and IRQ handling. These constants are used throughout the hypervisor for identifying and processing different trap conditions.

Trap Type Hierarchy

flowchart TD
subgraph subGraph3["Trap Constants (consts.rs)"]
    TrapMod["traps module"]
    subgraph subGraph2["IRQ Constants"]
        IrqMod["irq module"]
        IntcBase["INTC_IRQ_BASE = 1<<(usize::BITS-1)"]
        SSoft["S_SOFT = INTC_IRQ_BASE + 1"]
        STimer["S_TIMER = INTC_IRQ_BASE + 5"]
        SExt["S_EXT = INTC_IRQ_BASE + 9"]
        MaxIrq["MAX_IRQ_COUNT = 1024"]
        TimerIrq["TIMER_IRQ_NUM = S_TIMER"]
    end
    subgraph subGraph1["Exception Constants"]
        ExcMod["exception module"]
        InstMisalign["INST_ADDR_MISALIGN = 1<<0"]
        InstFault["INST_ACCESSS_FAULT = 1<<1"]
        IllegalInst["ILLEGAL_INST = 1<<2"]
        Breakpoint["BREAKPOINT = 1<<3"]
        LoadMisalign["LOAD_ADDR_MISALIGNED = 1<<4"]
        LoadFault["LOAD_ACCESS_FAULT = 1<<5"]
        StoreMisalign["STORE_ADDR_MISALIGNED = 1<<6"]
        StoreFault["STORE_ACCESS_FAULT = 1<<7"]
        EnvCallU["ENV_CALL_FROM_U_OR_VU = 1<<8"]
        EnvCallHS["ENV_CALL_FROM_HS = 1<<9"]
        EnvCallVS["ENV_CALL_FROM_VS = 1<<10"]
        EnvCallM["ENV_CALL_FROM_M = 1<<11"]
        InstPageFault["INST_PAGE_FAULT = 1<<12"]
        LoadPageFault["LOAD_PAGE_FAULT = 1<<13"]
        StorePageFault["STORE_PAGE_FAULT = 1<<15"]
        InstGuestFault["INST_GUEST_PAGE_FAULT = 1<<20"]
        LoadGuestFault["LOAD_GUEST_PAGE_FAULT = 1<<21"]
        VirtInst["VIRTUAL_INST = 1<<22"]
        StoreGuestFault["STORE_GUEST_PAGE_FAULT = 1<<23"]
    end
    subgraph subGraph0["Interrupt Constants"]
        IntMod["interrupt module"]
        UserSoft["USER_SOFT = 1<<0"]
        SuperSoft["SUPERVISOR_SOFT = 1<<1"]
        VirtSupSoft["VIRTUAL_SUPERVISOR_SOFT = 1<<2"]
        MachineSoft["MACHINE_SOFT = 1<<3"]
        UserTimer["USER_TIMER = 1<<4"]
        SuperTimer["SUPERVISOR_TIMER = 1<<5"]
        VirtSupTimer["VIRTUAL_SUPERVISOR_TIMER = 1<<6"]
        MachineTimer["MACHINE_TIMER = 1<<7"]
        UserExt["USER_EXTERNAL = 1<<8"]
        SuperExt["SUPERVISOR_EXTERNAL = 1<<9"]
        VirtSupExt["VIRTUAL_SUPERVISOR_EXTERNAL = 1<<10"]
        MachineExt["MACHINEL_EXTERNAL = 1<<11"]
        SuperGuestExt["SUPERVISOR_GUEST_EXTERNEL = 1<<12"]
    end
end

ExcMod --> Breakpoint
ExcMod --> EnvCallHS
ExcMod --> EnvCallM
ExcMod --> EnvCallU
ExcMod --> EnvCallVS
ExcMod --> IllegalInst
ExcMod --> InstFault
ExcMod --> InstGuestFault
ExcMod --> InstMisalign
ExcMod --> InstPageFault
ExcMod --> LoadFault
ExcMod --> LoadGuestFault
ExcMod --> LoadMisalign
ExcMod --> LoadPageFault
ExcMod --> StoreFault
ExcMod --> StoreGuestFault
ExcMod --> StoreMisalign
ExcMod --> StorePageFault
ExcMod --> VirtInst
IntMod --> MachineExt
IntMod --> MachineSoft
IntMod --> MachineTimer
IntMod --> SuperExt
IntMod --> SuperGuestExt
IntMod --> SuperSoft
IntMod --> SuperTimer
IntMod --> UserExt
IntMod --> UserSoft
IntMod --> UserTimer
IntMod --> VirtSupExt
IntMod --> VirtSupSoft
IntMod --> VirtSupTimer
IrqMod --> IntcBase
IrqMod --> MaxIrq
IrqMod --> SExt
IrqMod --> SSoft
IrqMod --> STimer
IrqMod --> TimerIrq
TrapMod --> ExcMod
TrapMod --> IntMod
TrapMod --> IrqMod

Sources: src/consts.rs(L1 - L92) 

Interrupt Type Constants

The system defines interrupt constants as bit flags representing different interrupt sources in the RISC-V architecture. These constants are organized by privilege level and interrupt type.

Software Interrupts

ConstantValueDescription
USER_SOFT1 << 0User software interrupt
SUPERVISOR_SOFT1 << 1Supervisor software interrupt
VIRTUAL_SUPERVISOR_SOFT1 << 2Virtual supervisor software interrupt
MACHINE_SOFT1 << 3Machine software interrupt

Timer Interrupts

ConstantValueDescription
USER_TIMER1 << 4User timer interrupt
SUPERVISOR_TIMER1 << 5Supervisor timer interrupt
VIRTUAL_SUPERVISOR_TIMER1 << 6Virtual supervisor timer interrupt
MACHINE_TIMER1 << 7Machine timer interrupt

External Interrupts

ConstantValueDescription
USER_EXTERNAL1 << 8User external interrupt
SUPERVISOR_EXTERNAL1 << 9Supervisor external interrupt
VIRTUAL_SUPERVISOR_EXTERNAL1 << 10Virtual supervisor external interrupt
MACHINEL_EXTERNAL1 << 11Machine external interrupt
SUPERVISOR_GUEST_EXTERNEL1 << 12Supervisor guest external interrupt

Sources: src/consts.rs(L4 - L32) 

Exception Type Constants

Exception constants define the various fault and trap conditions that can occur during instruction execution. These are used to identify the specific cause of a trap.

Memory Access Exceptions

ConstantValueDescription
INST_ADDR_MISALIGN1 << 0Instruction address misaligned
INST_ACCESSS_FAULT1 << 1Instruction access fault
LOAD_ADDR_MISALIGNED1 << 4Load address misaligned
LOAD_ACCESS_FAULT1 << 5Load access fault
STORE_ADDR_MISALIGNED1 << 6Store address misaligned
STORE_ACCESS_FAULT1 << 7Store access fault

Control Flow Exceptions

ConstantValueDescription
ILLEGAL_INST1 << 2Illegal instruction
BREAKPOINT1 << 3Breakpoint exception

Environment Call Exceptions

ConstantValueDescription
ENV_CALL_FROM_U_OR_VU1 << 8Environment call from U-mode or VU-mode
ENV_CALL_FROM_HS1 << 9Environment call from HS-mode
ENV_CALL_FROM_VS1 << 10Environment call from VS-mode
ENV_CALL_FROM_M1 << 11Environment call from M-mode

Page Fault Exceptions

ConstantValueDescription
INST_PAGE_FAULT1 << 12Instruction page fault
LOAD_PAGE_FAULT1 << 13Load page fault
STORE_PAGE_FAULT1 << 15Store page fault
INST_GUEST_PAGE_FAULT1 << 20Instruction guest page fault
LOAD_GUEST_PAGE_FAULT1 << 21Load guest page fault
STORE_GUEST_PAGE_FAULT1 << 23Store guest page fault

Virtualization Exceptions

ConstantValueDescription
VIRTUAL_INST1 << 22Virtual instruction exception

Sources: src/consts.rs(L34 - L74) 

IRQ Processing Constants

The IRQ constants provide standardized values for interrupt request processing and scause register interpretation.

Base IRQ Constants

ConstantValueDescription
INTC_IRQ_BASE1 << (usize::BITS - 1)Interrupt bit inscauseregister
S_SOFTINTC_IRQ_BASE + 1Supervisor software interrupt inscause
S_TIMERINTC_IRQ_BASE + 5Supervisor timer interrupt inscause
S_EXTINTC_IRQ_BASE + 9Supervisor external interrupt inscause

System Limits

ConstantValueDescription
MAX_IRQ_COUNT1024Maximum number of IRQs supported
TIMER_IRQ_NUMS_TIMERTimer IRQ number alias

Sources: src/consts.rs(L76 - L91) 

Trap Handling Integration

The trap constants are integrated with the VCPU's VM exit handler to provide comprehensive trap processing. The system uses the RISC-V scause register to determine trap types and dispatch to appropriate handlers.

VM Exit Handler Trap Processing

flowchart TD
subgraph subGraph4["Return Values"]
    ExitNothing["AxVCpuExitReason::Nothing"]
    ExitPageFault["AxVCpuExitReason::NestedPageFault"]
    ExitExtInt["AxVCpuExitReason::ExternalInterrupt"]
    ExitHypercall["AxVCpuExitReason::Hypercall"]
    ExitCpuUp["AxVCpuExitReason::CpuUp"]
    ExitSystemDown["AxVCpuExitReason::SystemDown"]
end
subgraph subGraph3["vmexit_handler Function"]
    VmExit["vmexit_handler()"]
    ReadScause["scause::read()"]
    TrapMatch["match scause.cause()"]
    subgraph subGraph2["CSR State Capture"]
        CaptureScause["trap_csrs.scause = scause::read().bits()"]
        CaptureStval["trap_csrs.stval = stval::read()"]
        CaptureHtval["trap_csrs.htval = htval::read()"]
        CaptureHtinst["trap_csrs.htinst = htinst::read()"]
    end
    subgraph subGraph1["Interrupt Handling"]
        SupervisorTimer["SupervisorTimer"]
        SupervisorExternal["SupervisorExternal"]
        TimerHandler["Timer Interrupt Processing"]
        ExtHandler["External Interrupt Processing"]
    end
    subgraph subGraph0["Exception Handling"]
        VSEnvCall["VirtualSupervisorEnvCall"]
        LoadGuestFault["LoadGuestPageFault"]
        StoreGuestFault["StoreGuestPageFault"]
        SBICall["SBI Call Processing"]
        PageFaultHandler["Nested Page Fault"]
    end
end

ExtHandler --> ExitExtInt
LoadGuestFault --> PageFaultHandler
PageFaultHandler --> ExitPageFault
ReadScause --> TrapMatch
SBICall --> ExitCpuUp
SBICall --> ExitHypercall
SBICall --> ExitNothing
SBICall --> ExitSystemDown
StoreGuestFault --> PageFaultHandler
SupervisorExternal --> ExtHandler
SupervisorTimer --> TimerHandler
TimerHandler --> ExitNothing
TrapMatch --> LoadGuestFault
TrapMatch --> StoreGuestFault
TrapMatch --> SupervisorExternal
TrapMatch --> SupervisorTimer
TrapMatch --> VSEnvCall
VSEnvCall --> SBICall
VmExit --> CaptureHtinst
VmExit --> CaptureHtval
VmExit --> CaptureScause
VmExit --> CaptureStval
VmExit --> ReadScause

Sources: src/vcpu.rs(L167 - L308) 

Specific Trap Handling Cases

The VCPU system handles several critical trap scenarios using these constants:

SBI Environment Calls

When a VirtualSupervisorEnvCall exception occurs, the system processes SBI (Supervisor Binary Interface) calls by examining the extension ID and function ID in guest registers. This includes:

  • Legacy SBI calls: Timer, console I/O, and system shutdown
  • HSM extension: Hart start, stop, and suspend operations
  • Custom hypercalls: Application-specific hypercall processing
  • Forwarded calls: Standard SBI extensions handled by RustSBI

Guest Page Faults

The system handles two types of guest page faults:

  • LoadGuestPageFault: Guest attempted to load from an unmapped or protected page
  • StoreGuestPageFault: Guest attempted to store to an unmapped or protected page

These faults result in AxVCpuExitReason::NestedPageFault to allow the hypervisor to handle guest memory management.

Timer and External Interrupts

  • Supervisor Timer Interrupts: Enable guest timer interrupts via hvip::set_vstip()
  • Supervisor External Interrupts: Forward to hypervisor as AxVCpuExitReason::ExternalInterrupt

Sources: src/vcpu.rs(L184 - L307) 

Constant Usage Patterns

The trap constants follow RISC-V specification patterns where:

  1. Bit Position Encoding: Most constants use 1 << N format for bit flag representation
  2. Privilege Level Organization: Constants are grouped by privilege levels (User, Supervisor, Machine)
  3. Interrupt vs Exception: Clear separation between interrupt (asynchronous) and exception (synchronous) constants
  4. IRQ Base Calculation: Uses architecture-specific bit width for interrupt base calculation

These patterns ensure compatibility with RISC-V hardware and provide efficient bit-mask operations for trap classification.

Sources: src/consts.rs(L1 - L92) 

Development and Tooling

Relevant source files

This document covers the development infrastructure, build configuration, testing framework, and tooling used to develop and maintain the RISC-V VCPU hypervisor system. It includes details on the CI/CD pipeline, dependency management, code generation tools, and documentation workflow.

For information about the core VCPU implementation, see Core VCPU Implementation. For details about system architecture components, see System Architecture.

Build System and Dependencies

The RISC-V VCPU system uses Cargo as its primary build system with a carefully curated set of dependencies that provide RISC-V architecture support, hypervisor functionality, and low-level register manipulation capabilities.

Package Configuration

The project is configured as a Rust library crate targeting the 2024 edition with the package name riscv_vcpu version 0.1.0. The build system is designed specifically for bare-metal RISC-V environments without standard library support.

Dependency Architecture

flowchart TD
subgraph subGraph4["riscv_vcpu Implementation"]
    vcpu_lib["riscv_vcpu Library"]
end
subgraph subGraph3["ArceOS Framework Dependencies"]
    axerrno["axerrno:0.1.0Error Handling"]
    page_table_entry["page_table_entry:0.5Page Table Management"]
    memory_addr["memory_addr:0.3.1Memory Addressing"]
    axaddrspace["axaddrspaceAddress Space Management"]
    axvcpu["axvcpuVCPU Abstractions"]
end
subgraph subGraph2["SBI and Hypervisor Support"]
    rustsbi["rustsbi:0.4.0SBI Implementation"]
    sbi_rt["sbi-rt:0.0.3SBI Runtime"]
    sbi_spec["sbi-spec:0.0.7SBI Specifications"]
end
subgraph subGraph1["RISC-V Architecture Support"]
    riscv_crate["riscvCore RISC-V Support"]
    riscv_decode["riscv-decodeInstruction Decoding"]
    tock_registers["tock-registers:0.8.1Register Abstractions"]
    memoffset["memoffsetMemory Layout"]
end
subgraph subGraph0["Core Dependencies"]
    log["log:0.4.19Logging Framework"]
    cfg_if["cfg-if:1.0Conditional Compilation"]
    bitflags["bitflags:2.2Flag Types"]
    bit_field["bit_field:0.10Bit Manipulation"]
    crate_interface["crate_interface:0.1Interface Definitions"]
end

axaddrspace --> vcpu_lib
axerrno --> vcpu_lib
axvcpu --> vcpu_lib
bit_field --> vcpu_lib
bitflags --> vcpu_lib
cfg_if --> vcpu_lib
crate_interface --> vcpu_lib
log --> vcpu_lib
memoffset --> vcpu_lib
memory_addr --> vcpu_lib
page_table_entry --> vcpu_lib
riscv_crate --> vcpu_lib
riscv_decode --> vcpu_lib
rustsbi --> vcpu_lib
sbi_rt --> vcpu_lib
sbi_spec --> vcpu_lib
tock_registers --> vcpu_lib

Sources: Cargo.toml(L6 - L26) 

Key Dependency Categories

CategoryCratesPurpose
RISC-V Coreriscv,riscv-decode,tock-registersProvide RISC-V instruction set support, instruction decoding, and register manipulation primitives
SBI Interfacerustsbi,sbi-rt,sbi-specImplement Supervisor Binary Interface for guest OS communication
ArceOS Integrationaxaddrspace,axvcpu,axerrnoInterface with the ArceOS hypervisor framework
Low-Level Utilitiesbitflags,bit_field,memoffsetProvide bit manipulation and memory layout tools

CI/CD Pipeline

The continuous integration pipeline ensures code quality, compatibility across Rust toolchains, and automated documentation deployment using GitHub Actions.

Pipeline Architecture

flowchart TD
subgraph subGraph3["Documentation Pipeline"]
    doc_build["cargo docDocumentation Generation"]
    doc_deploy["GitHub PagesDocumentation Deployment"]
end
subgraph subGraph2["Quality Checks"]
    fmt_check["cargo fmt --checkCode Formatting"]
    clippy_check["cargo clippyLinting Analysis"]
    build_check["cargo buildCompilation Test"]
    unit_test["cargo testUnit Testing"]
end
subgraph subGraph1["CI Job Matrix"]
    toolchain1["nightly-2024-12-25riscv64gc-unknown-none-elf"]
    toolchain2["nightlyriscv64gc-unknown-none-elf"]
end
subgraph subGraph0["Trigger Events"]
    push["Push Events"]
    pr["Pull Requests"]
end

doc_build --> doc_deploy
pr --> toolchain1
pr --> toolchain2
push --> toolchain1
push --> toolchain2
toolchain1 --> build_check
toolchain1 --> clippy_check
toolchain1 --> doc_build
toolchain1 --> fmt_check
toolchain1 --> unit_test
toolchain2 --> build_check
toolchain2 --> clippy_check
toolchain2 --> fmt_check
toolchain2 --> unit_test

Sources: .github/workflows/ci.yml(L1 - L65) 

CI Configuration Details

The pipeline runs two primary jobs: ci for code quality and testing, and doc for documentation generation and deployment.

Code Quality Matrix

The CI system tests against multiple Rust toolchain versions to ensure compatibility:

  • Primary Toolchain: nightly-2024-12-25 (stable reference)
  • Latest Toolchain: nightly (forward compatibility)
  • Target Architecture: riscv64gc-unknown-none-elf (bare-metal RISC-V 64-bit)

Quality Gates

StageCommandPurposeFailure Handling
Format Checkcargo fmt --all -- --checkEnforce consistent code formattingHard failure
Lintingcargo clippy --target riscv64gc-unknown-none-elf --all-featuresStatic analysis and best practicesContinue on error for nightly
Buildcargo build --target riscv64gc-unknown-none-elf --all-featuresCompilation verificationContinue on error for nightly
Unit Testscargo test --target x86_64-unknown-linux-gnuFunctional testingHard failure

Sources: .github/workflows/ci.yml(L20 - L32) 

Documentation Workflow

The documentation pipeline automatically generates and deploys API documentation to GitHub Pages for the default branch.

flowchart TD
subgraph subGraph1["Deployment Pipeline"]
    gh_pages["GitHub PagesBranch: gh-pages"]
    auto_deploy["JamesIves/github-pages-deploy-action"]
end
subgraph subGraph0["Documentation Generation"]
    source["Source Codewith Doc Comments"]
    cargo_doc["cargo doc--no-deps --all-features"]
    index_gen["Index Page Generation"]
end

auto_deploy --> gh_pages
cargo_doc --> index_gen
index_gen --> auto_deploy
source --> cargo_doc

Sources: .github/workflows/ci.yml(L34 - L64) 

The documentation build includes strict linting with RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs to ensure documentation completeness and link integrity.

Code Generation and Register Definitions

The system employs sophisticated code generation techniques to create type-safe register definitions for RISC-V Control and Status Registers (CSRs).

Register Definition Architecture

flowchart TD
subgraph subGraph2["Runtime Usage"]
    csr_access["CSR Read/WriteOperations"]
    type_safety["Compile-timeType Safety"]
    field_manipulation["BitfieldManipulation"]
end
subgraph subGraph1["Generated CSR Definitions"]
    csr_constants["CSR Address ConstantsCSR_HSTATUS: 0x600"]
    register_structs["Register Structshstatus::Register"]
    field_definitions["Field Definitionsspv, vsxl, vtvm"]
    field_values["Field Value EnumsUser, Supervisor"]
end
subgraph subGraph0["Register Definition Generator"]
    tock_registers["tock-registersMacro Framework"]
    register_bitfields["register_bitfields!Macro Invocation"]
end

csr_constants --> csr_access
field_definitions --> field_manipulation
field_values --> field_manipulation
register_bitfields --> csr_constants
register_bitfields --> field_definitions
register_bitfields --> field_values
register_bitfields --> register_structs
register_structs --> type_safety
tock_registers --> register_bitfields

Sources: def.rs(L1 - L1910) 

CSR Definition Structure

The register definitions follow a hierarchical pattern:

  1. CSR Address Constants: Hexadecimal addresses for each CSR register
  2. Register Modules: Type-safe wrappers for each register
  3. Field Definitions: Bit field specifications with masks and offsets
  4. Value Enumerations: Named constants for field values

Example Register Definition Pattern

The hstatus register demonstrates the complete pattern:

// CSR address constant
pub const CSR_HSTATUS: u16 = 0x600;

// Register module with fields
pub mod hstatus {
    pub struct Register;
    
    // Field definitions with bit positions
    pub const spv: Field<usize, Register> = Field::new(mask, offset);
    
    // Field value enumerations  
    pub mod spv {
        pub enum Value {
            User = 0,
            Supervisor = 1,
        }
    }
}

Sources: def.rs(L37 - L881) 

Generated Register Coverage

Register CategoryExamplesPurpose
Supervisor CSRsCSR_SSTATUS,CSR_SIE,CSR_SATPGuest supervisor mode state
Virtual Supervisor CSRsCSR_VSSTATUS,CSR_VSIE,CSR_VSATPVirtualized supervisor state
Hypervisor CSRsCSR_HSTATUS,CSR_HEDELEG,CSR_HIEHypervisor control and delegation

Testing Framework

The testing strategy combines unit testing for functional verification with CI-based integration testing across multiple toolchain versions.

Test Execution Environment

flowchart TD
subgraph subGraph1["Test Categories"]
    functional["Functional TestsCore Logic Verification"]
    compilation["Compilation TestsCross-platform Build"]
    linting["Static AnalysisClippy Checks"]
    formatting["Style Testsrustfmt Validation"]
end
subgraph subGraph0["Test Environments"]
    unit_tests["Unit Testsx86_64-unknown-linux-gnu"]
    integration_tests["Integration Testsriscv64gc-unknown-none-elf"]
end

integration_tests --> compilation
integration_tests --> formatting
integration_tests --> linting
unit_tests --> functional

Sources: .github/workflows/ci.yml(L30 - L32) 

Test Configuration

The test suite runs with specific configurations:

  • Unit Tests: Execute on x86_64-unknown-linux-gnu for host-based testing
  • Integration Tests: Build verification on riscv64gc-unknown-none-elf target
  • Test Output: Uses --nocapture flag for complete test output visibility

Development Workflow Integration

The development workflow integrates multiple tools and processes to ensure code quality and system reliability.

Development Tool Chain

flowchart TD
subgraph Deployment["Deployment"]
    crate_publish["Crate Publication"]
    gh_pages["GitHub PagesDocumentation"]
end
subgraph subGraph2["Automated Validation"]
    ci_pipeline["CI Pipeline"]
    quality_gates["Quality Gates"]
    doc_generation["Documentation"]
end
subgraph subGraph1["Version Control"]
    git_commit["Git Commit"]
    git_push["Git Push"]
    pull_request["Pull Request"]
end
subgraph subGraph0["Local Development"]
    rust_toolchain["Rust Nightly Toolchain"]
    cargo_fmt["cargo fmtCode Formatting"]
    cargo_clippy["cargo clippyLinting"]
    cargo_test["cargo testUnit Testing"]
end

cargo_clippy --> cargo_test
cargo_fmt --> cargo_clippy
cargo_test --> git_commit
ci_pipeline --> quality_gates
doc_generation --> gh_pages
git_commit --> git_push
git_push --> ci_pipeline
pull_request --> ci_pipeline
quality_gates --> crate_publish
quality_gates --> doc_generation
rust_toolchain --> cargo_fmt

Sources: .github/workflows/ci.yml(L1 - L65)  Cargo.toml(L1 - L26) 

Required Toolchain Components

The development environment requires specific Rust toolchain components:

  • Rust Source: rust-src for cross-compilation support
  • Clippy: Static analysis and linting tool
  • Rustfmt: Code formatting tool
  • Target Support: riscv64gc-unknown-none-elf compilation target

Sources: .github/workflows/ci.yml(L18 - L19) 

Overview

Relevant source files

Purpose and Scope

The axdevice crate provides virtual machine device emulation capabilities within the ArceOS hypervisor ecosystem. This document covers the core functionality of device configuration management and MMIO (Memory-Mapped I/O) operation handling for guest virtual machines.

For detailed information about system architecture patterns, see System Architecture. For implementation details of individual components, see Core Components. For integration with other ArceOS crates, see ArceOS Ecosystem Integration.

Device Emulation Framework

The axdevice crate serves as the central device management layer for the ArceOS hypervisor, enabling guest virtual machines to interact with emulated hardware devices through standardized MMIO operations. The crate abstracts device configuration and runtime behavior into two primary components: AxVmDeviceConfig for device setup and AxVmDevices for operational device management.

Core Entity Relationships

flowchart TD
AxVmDeviceConfig["AxVmDeviceConfig(Configuration Management)"]
AxVmDevices["AxVmDevices(Device Orchestrator)"]
EmulatedDeviceConfig["EmulatedDeviceConfig(Individual Device Setup)"]
BaseDeviceOps["BaseDeviceOps(Device Interface Trait)"]
GuestVM["Guest VM(MMIO Operations)"]
DeviceInstances["Device Instances(Console, VirtIO, etc.)"]

AxVmDeviceConfig --> AxVmDevices
AxVmDeviceConfig --> EmulatedDeviceConfig
AxVmDevices --> BaseDeviceOps
AxVmDevices --> DeviceInstances
DeviceInstances --> BaseDeviceOps
GuestVM --> AxVmDevices

Sources: src/lib.rs(L15 - L19)  Cargo.toml(L1 - L19) 

Main Components

The crate is organized into two fundamental modules that handle distinct aspects of device emulation:

ComponentModulePrimary Responsibility
AxVmDeviceConfigconfigDevice configuration management and initialization parameters
AxVmDevicesdeviceRuntime device operation handling and MMIO request routing

Component Interaction Flow

sequenceDiagram
    participant Initialization as "Initialization"
    participant AxVmDeviceConfig as "AxVmDeviceConfig"
    participant AxVmDevices as "AxVmDevices"
    participant GuestVM as "Guest VM"
    participant EmulatedDevice as "Emulated Device"

    Initialization ->> AxVmDeviceConfig: "Create configuration"
    AxVmDeviceConfig ->> AxVmDevices: "AxVmDevices::new(config)"
    AxVmDevices ->> EmulatedDevice: "Initialize device instances"
    GuestVM ->> AxVmDevices: "MMIO read/write operations"
    AxVmDevices ->> EmulatedDevice: "Route to appropriate device"
    EmulatedDevice ->> AxVmDevices: "Return operation result"
    AxVmDevices ->> GuestVM: "Complete MMIO operation"

Sources: src/lib.rs(L15 - L16)  src/lib.rs(L18 - L19) 

ArceOS Hypervisor Integration

The axdevice crate operates as a foundational component within the ArceOS hypervisor stack, integrating with several specialized crates to provide comprehensive device emulation capabilities:

Dependency Architecture

flowchart TD
subgraph subGraph2["External Dependencies"]
    log["log(Logging Framework)"]
    cfg_if["cfg-if(Conditional Compilation)"]
end
subgraph subGraph1["ArceOS System Dependencies"]
    axerrno["axerrno(Error Handling)"]
    memory_addr["memory_addr(Memory Addressing)"]
end
subgraph subGraph0["ArceOS Hypervisor Dependencies"]
    axvmconfig["axvmconfig(VM Configuration)"]
    axaddrspace["axaddrspace(Address Space Management)"]
    axdevice_base["axdevice_base(Device Abstraction Layer)"]
end
axdevice["axdevice(This Crate)"]

axdevice --> axaddrspace
axdevice --> axdevice_base
axdevice --> axerrno
axdevice --> axvmconfig
axdevice --> cfg_if
axdevice --> log
axdevice --> memory_addr

Sources: Cargo.toml(L8 - L18) 

Runtime Environment

The crate is designed for no_std environments, utilizing the alloc crate for dynamic memory allocation without requiring the full standard library. This design enables deployment in hypervisor contexts where minimal runtime overhead is critical.

The logging infrastructure is integrated throughout the system using the log crate, providing debugging and operational visibility into device emulation activities.

Sources: src/lib.rs(L1)  src/lib.rs(L11 - L13) 

Development Context

The axdevice crate follows the ArceOS project's modular architecture approach, where specialized functionality is separated into focused crates. The device emulation layer specifically handles the intersection between guest virtual machine memory operations and host-side device implementations.

For build and development procedures, see Development Guide. For detailed component implementation, see Configuration Management and Device Emulation.

Sources: Cargo.toml(L1 - L4)  src/lib.rs(L3 - L9) 

System Architecture

Relevant source files

Purpose and Scope

This document provides a comprehensive view of the axdevice system architecture, detailing how the configuration and device management components work together to provide virtual machine device emulation within the ArceOS hypervisor ecosystem. The architecture encompasses MMIO operation handling, device lifecycle management, and integration with external ArceOS components.

For detailed information about individual core components, see Core Components. For ArceOS ecosystem integration details, see ArceOS Ecosystem Integration.

High-Level System Architecture

The axdevice crate implements a two-layer architecture consisting of configuration management and device emulation orchestration:

Core Architecture Overview

flowchart TD
subgraph subGraph2["Guest VM Interface"]
    MMIO_Read["handle_mmio_read()"]
    MMIO_Write["handle_mmio_write()"]
    find_dev["find_dev()"]
end
subgraph subGraph1["External Dependencies"]
    EmulatedDeviceConfig["EmulatedDeviceConfig(from axvmconfig)"]
    BaseDeviceOps["BaseDeviceOps(from axdevice_base)"]
    GuestPhysAddr["GuestPhysAddr(from axaddrspace)"]
end
subgraph subGraph0["axdevice Core"]
    AxVmDeviceConfig["AxVmDeviceConfigConfiguration Container"]
    AxVmDevices["AxVmDevicesDevice Orchestrator"]
    emu_devices["emu_devices: Vec<Arc<dyn BaseDeviceOps>>Device Collection"]
end

AxVmDeviceConfig --> AxVmDevices
AxVmDevices --> MMIO_Read
AxVmDevices --> MMIO_Write
AxVmDevices --> emu_devices
AxVmDevices --> find_dev
BaseDeviceOps --> emu_devices
EmulatedDeviceConfig --> AxVmDeviceConfig
GuestPhysAddr --> MMIO_Read
GuestPhysAddr --> MMIO_Write
GuestPhysAddr --> find_dev

Sources: src/config.rs(L5 - L8)  src/device.rs(L12 - L16)  src/device.rs(L57 - L62)  src/device.rs(L65 - L75)  src/device.rs(L78 - L92) 

Component Interaction Flow

The system follows a clear initialization and runtime operation pattern:

Device Initialization and MMIO Handling Flow

sequenceDiagram
    participant AxVmDeviceConfig as "AxVmDeviceConfig"
    participant AxVmDevices as "AxVmDevices"
    participant emu_devices as "emu_devices"
    participant GuestVM as "Guest VM"
    participant BaseDeviceOps as "BaseDeviceOps"

    Note over AxVmDeviceConfig,BaseDeviceOps: Initialization Phase
    AxVmDeviceConfig ->> AxVmDevices: "new(config)"
    AxVmDevices ->> AxVmDevices: "init(&mut self, &emu_configs)"
    Note over AxVmDevices: "Currently stub implementation"
    Note over AxVmDeviceConfig,BaseDeviceOps: Runtime MMIO Operations
    GuestVM ->> AxVmDevices: "handle_mmio_read(addr, width)"
    AxVmDevices ->> AxVmDevices: "find_dev(addr)"
    AxVmDevices ->> emu_devices: "iter().find(|dev| dev.address_range().contains(addr))"
    emu_devices -->> AxVmDevices: "Some(Arc<dyn BaseDeviceOps>)"
    AxVmDevices ->> BaseDeviceOps: "handle_read(addr, width)"
    BaseDeviceOps -->> AxVmDevices: "AxResult<usize>"
    AxVmDevices -->> GuestVM: "Return value"
    GuestVM ->> AxVmDevices: "handle_mmio_write(addr, width, val)"
    AxVmDevices ->> AxVmDevices: "find_dev(addr)"
    AxVmDevices ->> BaseDeviceOps: "handle_write(addr, width, val)"
    BaseDeviceOps -->> AxVmDevices: "Operation complete"
    AxVmDevices -->> GuestVM: "Write acknowledged"

Sources: src/device.rs(L20 - L28)  src/device.rs(L30 - L54)  src/device.rs(L57 - L62)  src/device.rs(L65 - L75)  src/device.rs(L78 - L92) 

Core Data Structures and Relationships

Configuration and Device Management Structure

classDiagram
class AxVmDeviceConfig {
    +emu_configs: Vec~EmulatedDeviceConfig~
    +new(emu_configs: Vec~EmulatedDeviceConfig~) Self
}

class AxVmDevices {
    -emu_devices: Vec~Arc~dyn BaseDeviceOps~~
    +new(config: AxVmDeviceConfig) Self
    -init(&mut self, emu_configs: &Vec~EmulatedDeviceConfig~)
    +find_dev(ipa: GuestPhysAddr) Option~Arc~dyn BaseDeviceOps~~
    +handle_mmio_read(addr: GuestPhysAddr, width: usize) AxResult~usize~
    +handle_mmio_write(addr: GuestPhysAddr, width: usize, val: usize)
}

class EmulatedDeviceConfig {
    <<external>>
    +emu_type: field
    
}

class BaseDeviceOps {
    <<trait>>
    
    +address_range() Range
    +handle_read(addr: GuestPhysAddr, width: usize) AxResult~usize~
    +handle_write(addr: GuestPhysAddr, width: usize, val: usize)
}

AxVmDeviceConfig  *--  EmulatedDeviceConfig : "contains"
AxVmDevices  o--  AxVmDeviceConfig : "initialized from"
AxVmDevices  *--  BaseDeviceOps : "manages collection"

Sources: src/config.rs(L5 - L16)  src/device.rs(L12 - L16)  src/device.rs(L20 - L28)  src/device.rs(L57 - L62) 

MMIO Operation Handling Architecture

The system's core functionality centers around Memory-Mapped I/O (MMIO) operation routing:

MMIO Address Resolution and Device Routing

OperationMethodAddress ResolutionDevice Interaction
Readhandle_mmio_readfind_dev(addr)→address_range().contains(addr)device.handle_read(addr, width)
Writehandle_mmio_writefind_dev(addr)→address_range().contains(addr)device.handle_write(addr, width, val)

The address resolution process follows this pattern:

flowchart TD
MMIO_Request["MMIO Request(addr: GuestPhysAddr)"]
find_dev["find_dev(addr)"]
device_iter["emu_devices.iter()"]
address_check["dev.address_range().contains(addr)"]
device_found["Device FoundArc<dyn BaseDeviceOps>"]
device_missing["No Device Foundpanic!"]
handle_operation["handle_read() orhandle_write()"]

MMIO_Request --> find_dev
address_check --> device_found
address_check --> device_missing
device_found --> handle_operation
device_iter --> address_check
find_dev --> device_iter

Sources: src/device.rs(L57 - L62)  src/device.rs(L65 - L75)  src/device.rs(L78 - L92) 

Dependency Architecture and External Integration

The system integrates with multiple ArceOS ecosystem components:

External Dependencies and Integration Points

flowchart TD
subgraph subGraph3["Standard Dependencies"]
    log_crate["logLogging Macros"]
    cfg_if["cfg-ifConditional Compilation"]
end
subgraph subGraph2["ArceOS System Components"]
    axerrno["axerrnoAxResult"]
    memory_addr["memory_addrMemory Addressing"]
end
subgraph subGraph1["ArceOS Hypervisor Components"]
    axvmconfig["axvmconfigEmulatedDeviceConfig"]
    axaddrspace["axaddrspaceGuestPhysAddr"]
    axdevice_base["axdevice_baseBaseDeviceOps"]
end
subgraph subGraph0["axdevice Implementation"]
    Config["AxVmDeviceConfig"]
    Devices["AxVmDevices"]
end

axaddrspace --> Devices
axdevice_base --> Devices
axerrno --> Devices
axvmconfig --> Config
cfg_if --> Config
log_crate --> Devices
memory_addr --> Devices

Dependency Details

ComponentSourcePurposeUsage
EmulatedDeviceConfigaxvmconfigDevice configuration dataStored inAxVmDeviceConfig.emu_configs
GuestPhysAddraxaddrspaceGuest physical addressingMMIO operation parameters
BaseDeviceOpsaxdevice_baseDevice operation interfaceStored asArc
AxResultaxerrnoError handlingReturn type forhandle_mmio_read

Sources: Cargo.toml(L8 - L18)  src/config.rs(L2)  src/device.rs(L6 - L9)  src/lib.rs(L11 - L13) 

Current Implementation Status

The system architecture includes placeholder implementations for future device types:

Device Type Support (Planned Implementation)

The init method in AxVmDevices contains commented code indicating planned support for multiple device types:

  • Console devices (EmuDeviceTConsole)
  • VirtIO devices (EmuDeviceTVirtioBlk, EmuDeviceTVirtioNet, EmuDeviceTVirtioConsole)
  • Interrupt controllers (EmuDeviceTGicdV2, EmuDeviceTGICR)
  • IOMMU (EmuDeviceTIOMMU)
  • Other specialized devices (EmuDeviceTGPPT, EmuDeviceTICCSRE, EmuDeviceTSGIR, EmuDeviceTMeta)

Sources: src/device.rs(L30 - L54) 

Core Components

Relevant source files

This page provides an overview of the main functional components that make up the axdevice system. It introduces the two primary subsystems: configuration management through AxVmDeviceConfig and device emulation through AxVmDevices. For detailed information about configuration management, see Configuration Management. For detailed information about device emulation functionality, see Device Emulation.

Component Overview

The axdevice crate is structured around two core components that work together to provide virtual machine device emulation capabilities:

ComponentPrimary TypeResponsibilityFile Location
Configuration ManagementAxVmDeviceConfigStores and manages device configuration parameterssrc/config.rs
Device EmulationAxVmDevicesHandles MMIO operations and device lifecycle managementsrc/device.rs

Configuration Management

The AxVmDeviceConfig struct serves as the central configuration container for all emulated devices in a virtual machine. It maintains a vector of EmulatedDeviceConfig objects that define the parameters for individual devices.

Key characteristics:

Device Emulation

The AxVmDevices struct represents the runtime device management system for a virtual machine. It maintains collections of emulated devices and provides the interface for handling guest VM memory-mapped I/O operations.

Key characteristics:

Component Architecture

Core Component Structure

flowchart TD
subgraph subGraph1["External Dependencies"]
    EmulatedDeviceConfig["EmulatedDeviceConfigaxvmconfig"]
    BaseDeviceOps["BaseDeviceOpsaxdevice_base"]
    GuestPhysAddr["GuestPhysAddraxaddrspace"]
end
subgraph subGraph0["axdevice Core"]
    Config["AxVmDeviceConfigconfig.rs:5"]
    Devices["AxVmDevicesdevice.rs:12"]
    EmuConfigs["emu_configs: Vec<EmulatedDeviceConfig>config.rs:7"]
    EmuDevices["emu_devices: Vec<Arc<dyn BaseDeviceOps>>device.rs:14"]
end

Config --> Devices
Config --> EmuConfigs
Devices --> EmuDevices
Devices --> GuestPhysAddr
EmuConfigs --> EmulatedDeviceConfig
EmuDevices --> BaseDeviceOps

Sources: src/lib.rs(L18 - L19)  src/config.rs(L5 - L8)  src/device.rs(L12 - L16) 

Component Interaction Flow

Initialization and Runtime Flow


Sources: src/config.rs(L13 - L15)  src/device.rs(L21 - L28)  src/device.rs(L65 - L92) 

Public Interface

The axdevice crate exports both core components through its main library interface:

pub use config::AxVmDeviceConfig;
pub use device::AxVmDevices;

Key Public Methods:

ComponentMethodPurposeReturn Type
AxVmDeviceConfignew(emu_configs)Create configuration instanceSelf
AxVmDevicesnew(config)Initialize device managerSelf
AxVmDevicesfind_dev(ipa)Locate device by addressOption<Arc>
AxVmDeviceshandle_mmio_read(addr, width)Process guest read operationsAxResult
AxVmDeviceshandle_mmio_write(addr, width, val)Process guest write operations()

Sources: src/lib.rs(L18 - L19)  src/device.rs(L57 - L92) 

Implementation Status

The current implementation provides the foundational structure but has key areas marked for future development:

  • Device Initialization: The init() method contains commented-out code for device type dispatch src/device.rs(L31 - L54) 
  • Device Type Support: Placeholder comments indicate planned support for multiple device types including console, VirtIO, and interrupt controllers src/device.rs(L34 - L47) 
  • Passthrough Devices: Architecture anticipates future support for passthrough devices src/device.rs(L15) 

The MMIO handling functionality is fully implemented and operational, providing the runtime interface needed for guest VM device interactions.

Sources: src/device.rs(L31 - L54)  src/device.rs(L15) 

Configuration Management

Relevant source files

Purpose and Scope

This document covers the configuration management system in the axdevice crate, specifically focusing on how device configurations are defined, structured, and used to initialize emulated devices in the ArceOS hypervisor. The AxVmDeviceConfig structure serves as the primary interface for managing collections of emulated device configurations that will be instantiated by the device emulation system.

For information about the actual device emulation and MMIO operation handling, see Device Emulation. For broader system architecture context, see System Architecture.

Core Configuration Structure

The configuration management system is built around a simple but effective structure that aggregates individual device configurations into a manageable collection.

AxVmDeviceConfig Structure

The primary configuration container is AxVmDeviceConfig, which maintains a vector of EmulatedDeviceConfig objects imported from the axvmconfig crate.


Configuration Structure Analysis

ComponentTypePurposeLocation
AxVmDeviceConfigStructContainer for multiple device configurationssrc/config.rs5-8
emu_configsVecCollection of individual device configurationssrc/config.rs7
new()ConstructorCreates new configuration instancesrc/config.rs13-15

Sources: src/config.rs(L1 - L17) 

Configuration Data Flow

The configuration system follows a straightforward data flow pattern where external configuration data is aggregated and then passed to the device management layer.

Configuration Lifecycle

sequenceDiagram
    participant Client as Client
    participant AxVmDeviceConfig as AxVmDeviceConfig
    participant AxVmDevices as AxVmDevices
    participant EmulatedDeviceConfig as EmulatedDeviceConfig

    Client ->> AxVmDeviceConfig: "new(emu_configs)"
    Note over AxVmDeviceConfig: "Store Vec<EmulatedDeviceConfig>"
    AxVmDeviceConfig ->> Client: "return AxVmDeviceConfig instance"
    Client ->> AxVmDevices: "new(config)"
    AxVmDevices ->> AxVmDeviceConfig: "access emu_configs field"
    loop "For each EmulatedDeviceConfig"
        AxVmDevices ->> EmulatedDeviceConfig: "read device parameters"
        AxVmDevices ->> AxVmDevices: "initialize emulated device"
    end
    Note over AxVmDevices: "Devices ready for MMIO operations"

Configuration Flow Steps

  1. Construction: AxVmDeviceConfig::new() accepts a vector of EmulatedDeviceConfig objects
  2. Storage: The configuration vector is stored in the emu_configs field
  3. Consumption: Device management system accesses configurations during initialization
  4. Device Creation: Each configuration drives the creation of corresponding emulated devices

Sources: src/config.rs(L13 - L15)  src/lib.rs(L18) 

Module Integration

The configuration management integrates seamlessly with the broader axdevice crate structure and external dependencies.

Crate Integration Pattern

flowchart TD
subgraph subGraph4["Consumer Systems"]
    AxVmDevices["AxVmDevices (device management)"]
    ClientCode["Client Code"]
end
subgraph subGraph3["External Dependencies"]
    AxVmConfigCrate["axvmconfig::EmulatedDeviceConfig"]
    AllocCrate["alloc::vec::Vec"]
end
subgraph subGraph2["axdevice Crate"]
    subgraph src/config.rs["src/config.rs"]
        AxVmDeviceConfig["struct AxVmDeviceConfig"]
        NewMethod["impl AxVmDeviceConfig::new()"]
    end
    subgraph src/lib.rs["src/lib.rs"]
        LibExports["pub use config::AxVmDeviceConfig"]
        ConfigMod["mod config"]
    end
end
note1["File: src/lib.rs:15,18"]
note2["File: src/config.rs:2,7"]

AxVmDeviceConfig --> NewMethod
ConfigMod --> AxVmDeviceConfig
LibExports --> AxVmDeviceConfig
LibExports --> AxVmDevices
LibExports --> ClientCode
NewMethod --> AllocCrate
NewMethod --> AxVmConfigCrate

Integration Responsibilities

ComponentResponsibilityImplementation
Module DeclarationExpose config modulesrc/lib.rs15
Public ExportMakeAxVmDeviceConfigavailablesrc/lib.rs18
Dependency ImportAccessEmulatedDeviceConfigtypesrc/config.rs2
Memory ManagementUseVecfor dynamic collectionssrc/config.rs1

Sources: src/lib.rs(L15 - L19)  src/config.rs(L1 - L2) 

Configuration Usage Patterns

The design supports common configuration management patterns expected in a hypervisor device emulation system.

Typical Usage Workflow

flowchart TD
subgraph subGraph2["Device Management Handoff"]
    PassToDevices["Pass config to AxVmDevices"]
    AccessConfigs["AxVmDevices accesses emu_configs"]
    InitDevices["Initialize individual emulated devices"]
end
subgraph subGraph1["AxVmDeviceConfig Creation"]
    CallNew["AxVmDeviceConfig::new(emu_configs)"]
    StoreConfigs["Store configurations in emu_configs field"]
end
subgraph subGraph0["Configuration Preparation"]
    LoadConfigs["Load EmulatedDeviceConfig objects"]
    CreateVector["Create Vec"]
end
Start["System Initialization"]
End["Ready for MMIO Operations"]
note1["Constructor: src/config.rs:13-15"]
note2["Field access for device creation"]

AccessConfigs --> InitDevices
CallNew --> StoreConfigs
CreateVector --> CallNew
InitDevices --> End
LoadConfigs --> CreateVector
PassToDevices --> AccessConfigs
Start --> LoadConfigs
StoreConfigs --> PassToDevices

Configuration Management Characteristics

  • Simplicity: Minimal wrapper around configuration vector with single constructor
  • Flexibility: Accepts any number of device configurations through Vec collection
  • Integration: Seamless handoff to device management layer through public field access
  • Memory Safety: Uses alloc::vec::Vec for safe dynamic memory management in no_std environment

Sources: src/config.rs(L5 - L16)  src/lib.rs(L11 - L18) 

Error Handling and Validation

The current configuration management implementation follows a minimal approach with validation delegated to consuming systems and external configuration sources.

Validation Strategy

Validation LevelResponsibilityImplementation
Type SafetyRust compilerVectype constraints
Content ValidationaxvmconfigcrateEmulatedDeviceConfiginternal validation
Device CreationAxVmDevicesValidation during device instantiation
Runtime ChecksDevice implementationsMMIO operation validation

The simple structure allows higher-level systems to implement appropriate validation and error handling strategies while maintaining a clean separation of concerns.

Sources: src/config.rs(L1 - L17) 

Device Emulation

Relevant source files

Purpose and Scope

This document covers the device emulation system implemented by the AxVmDevices struct, which serves as the central orchestrator for all emulated devices within a virtual machine. The system handles Memory-Mapped I/O (MMIO) operations from guest VMs, manages device lifecycles, and routes operations to appropriate device implementations through the BaseDeviceOps trait interface.

For information about device configuration management, see Configuration Management. For broader system integration details, see ArceOS Ecosystem Integration.

AxVmDevices Architecture

The AxVmDevices struct acts as the primary interface between guest virtual machines and emulated devices. It maintains a collection of device implementations and provides unified access to MMIO operations.

Core Structure

flowchart TD
subgraph subGraph2["Configuration Input"]
    H["AxVmDeviceConfig"]
    I["emu_configs: Vec"]
end
subgraph Dependencies["Dependencies"]
    D["axaddrspace::GuestPhysAddr"]
    E["axdevice_base::BaseDeviceOps"]
    F["axerrno::AxResult"]
    G["axvmconfig::EmulatedDeviceConfig"]
end
subgraph subGraph0["AxVmDevices Structure"]
    A["AxVmDevices"]
    B["emu_devices: Vec>"]
    C["TODO: passthrough devices"]
end

A --> B
A --> C
A --> D
A --> E
A --> F
H --> A
H --> I
I --> A

Sources: src/device.rs(L1 - L16) 

The AxVmDevices struct currently focuses on emulated devices through the emu_devices field, with placeholders for future passthrough device support.

Device Collection Management

flowchart TD
subgraph subGraph1["Trait Interface"]
    E["BaseDeviceOps"]
    F["address_range()"]
    G["handle_read()"]
    H["handle_write()"]
end
subgraph subGraph0["Device Storage"]
    A["Vec>"]
    B["Arc"]
    C["Arc"]
    D["Arc"]
end

A --> B
A --> C
A --> D
B --> E
C --> E
D --> E
E --> F
E --> G
E --> H

Sources: src/device.rs(L12 - L16) 

Device Lifecycle Management

Initialization Process

The AxVmDevices initialization follows a two-phase approach: structure creation and device initialization.

sequenceDiagram
    participant AxVmDeviceConfig as "AxVmDeviceConfig"
    participant AxVmDevices as "AxVmDevices"
    participant EmulatedDeviceConfig as "EmulatedDeviceConfig"
    participant BaseDeviceOps as "BaseDeviceOps"

    Note over AxVmDeviceConfig,BaseDeviceOps: Initialization Phase
    AxVmDeviceConfig ->> AxVmDevices: "new(config)"
    AxVmDevices ->> AxVmDevices: "Create empty emu_devices Vec"
    AxVmDevices ->> AxVmDevices: "init(&mut self, &config.emu_configs)"
    Note over AxVmDevices,BaseDeviceOps: Device Creation (TODO)
    loop "For each EmulatedDeviceConfig"
        AxVmDevices ->> EmulatedDeviceConfig: "Read device configuration"
        EmulatedDeviceConfig ->> BaseDeviceOps: "Create device instance"
        BaseDeviceOps ->> AxVmDevices: "Add to emu_devices"
    end

Sources: src/device.rs(L19 - L28)  src/device.rs(L30 - L54) 

The new() method creates an empty device collection and delegates to the init() method for device-specific initialization. Currently, the init() method contains placeholder code with TODO comments indicating future device type handling.

Device Type Enumeration

The commented code in init() reveals the planned device type architecture:

Device TypeDescription
EmuDeviceTConsoleConsole device emulation
EmuDeviceTGicdV2GIC distributor v2
EmuDeviceTGPPTGeneral Purpose Physical Timer
EmuDeviceTVirtioBlkVirtIO block device
EmuDeviceTVirtioNetVirtIO network device
EmuDeviceTVirtioConsoleVirtIO console device
EmuDeviceTIOMMUI/O Memory Management Unit
EmuDeviceTICCSREInterrupt Controller System Register Enable
EmuDeviceTSGIRSoftware Generated Interrupt Register
EmuDeviceTGICRGIC redistributor
EmuDeviceTMetaMetadata device

Sources: src/device.rs(L34 - L46) 

MMIO Operation Handling

Device Lookup Mechanism

The find_dev() method provides address-based device resolution using the BaseDeviceOps trait's address_range() method.

flowchart TD
subgraph subGraph0["Device Lookup Process"]
    A["find_dev(ipa: GuestPhysAddr)"]
    B["Iterate emu_devices"]
    C["dev.address_range().contains(ipa)"]
    D["Address Match?"]
    E["Return Some(Arc)"]
    F["Continue iteration"]
    G["More devices?"]
    H["Return None"]
end

A --> B
B --> C
C --> D
D --> E
D --> F
F --> G
G --> B
G --> H

Sources: src/device.rs(L56 - L62) 

MMIO Read Operations

The handle_mmio_read() method processes guest VM read requests and returns data from the appropriate device.

sequenceDiagram
    participant GuestVM as "Guest VM"
    participant AxVmDevices as "AxVmDevices"
    participant Device as "Device"

    GuestVM ->> AxVmDevices: "handle_mmio_read(addr, width)"
    AxVmDevices ->> AxVmDevices: "find_dev(addr)"
    alt "Device found"
        AxVmDevices ->> AxVmDevices: "Log operation info"
        AxVmDevices ->> Device: "handle_read(addr, width)"
        Device ->> AxVmDevices: "Return AxResult<usize>"
        AxVmDevices ->> GuestVM: "Return value"
    else "No device found"
        AxVmDevices ->> AxVmDevices: "panic!(no emul handler)"
    end

Sources: src/device.rs(L64 - L75) 

The method includes comprehensive logging that outputs the device's address range and the specific IPA (Intermediate Physical Address) being accessed.

MMIO Write Operations

The handle_mmio_write() method processes guest VM write requests to device registers.

sequenceDiagram
    participant GuestVM as "Guest VM"
    participant AxVmDevices as "AxVmDevices"
    participant Device as "Device"

    GuestVM ->> AxVmDevices: "handle_mmio_write(addr, width, val)"
    AxVmDevices ->> AxVmDevices: "find_dev(addr)"
    alt "Device found"
        AxVmDevices ->> AxVmDevices: "Log operation info"
        AxVmDevices ->> Device: "handle_write(addr, width, val)"
        Device ->> AxVmDevices: "Operation complete"
        AxVmDevices ->> GuestVM: "Return"
    else "No device found"
        AxVmDevices ->> AxVmDevices: "panic!(no emul handler)"
    end

Sources: src/device.rs(L77 - L93) 

Both MMIO operations follow a similar pattern: device lookup, logging, delegation to the device implementation, and error handling via panic for unmapped addresses.

Address Space Integration

Guest Physical Address Handling

The system uses GuestPhysAddr from the axaddrspace crate to represent guest physical memory addresses, ensuring type safety in address operations.

flowchart TD
subgraph subGraph1["Device Address Ranges"]
    H["Device 1 Range"]
    I["Device 2 Range"]
    J["Device N Range"]
end
subgraph subGraph0["Address Resolution"]
    A["GuestPhysAddr"]
    B["Device Lookup"]
    C["BaseDeviceOps::address_range()"]
    D["AddressRange::contains()"]
    E["Match Found?"]
    F["Route to Device"]
    G["Panic: No Handler"]
end

A --> B
B --> C
C --> D
C --> H
C --> I
C --> J
D --> E
E --> F
E --> G

Sources: src/device.rs(L6)  src/device.rs(L57 - L62) 

Error Handling Strategy

The current implementation uses panic-based error handling for unmapped addresses, which provides immediate feedback during development but may require more graceful handling in production environments.

flowchart TD
A["MMIO Request"]
B["find_dev(addr)"]
C["Device Found?"]
D["Execute Operation"]
E["panic!(no emul handler)"]
F["Log Operation"]
G["Call BaseDeviceOps Method"]
H["Return Result"]

A --> B
B --> C
C --> D
C --> E
D --> F
F --> G
G --> H

Sources: src/device.rs(L74)  src/device.rs(L88 - L91) 

Integration with BaseDeviceOps

The AxVmDevices system relies entirely on the BaseDeviceOps trait for device-specific functionality, creating a clean separation between device management and device implementation.

Trait Method Usage

MethodPurposeUsage in AxVmDevices
address_range()Define device memory mappingUsed infind_dev()for address resolution
handle_read()Process read operationsCalled fromhandle_mmio_read()
handle_write()Process write operationsCalled fromhandle_mmio_write()

Sources: src/device.rs(L60)  src/device.rs(L72)  src/device.rs(L85) 

Device Abstraction Benefits

flowchart TD
subgraph subGraph2["Device Implementations"]
    G["Console Device"]
    H["VirtIO Devices"]
    I["Interrupt Controllers"]
    J["Custom Devices"]
end
subgraph subGraph1["BaseDeviceOps (Device Abstraction)"]
    D["address_range()"]
    E["handle_read()"]
    F["handle_write()"]
end
subgraph subGraph0["AxVmDevices (Device Manager)"]
    A["Unified MMIO Interface"]
    B["Address-based Routing"]
    C["Lifecycle Management"]
end

A --> D
B --> D
C --> D
D --> G
D --> H
D --> I
D --> J
E --> G
E --> H
E --> I
E --> J
F --> G
F --> H
F --> I
F --> J

Sources: src/device.rs(L7)  src/device.rs(L14) 

This architecture enables the AxVmDevices system to remain device-agnostic while providing consistent MMIO handling across all emulated device types.

ArceOS Ecosystem Integration

Relevant source files

Purpose and Scope

This document explains how the axdevice crate integrates with the broader ArceOS hypervisor ecosystem. It covers the modular architecture approach, dependency relationships with other ArceOS crates, and the layered design that enables extensible device emulation within virtual machines.

For information about the internal architecture of axdevice components, see System Architecture. For details about device configuration and emulation implementation, see Core Components.

ArceOS Hypervisor Ecosystem Structure

The axdevice crate operates within a carefully structured ecosystem of ArceOS hypervisor components, each serving specific responsibilities in the virtualization stack.

Ecosystem Dependency Graph

flowchart TD
subgraph subGraph4["External Dependencies"]
    log["logLogging Framework"]
    cfg_if["cfg-ifConditional Compilation"]
end
subgraph subGraph3["System Foundation Dependencies"]
    axerrno["axerrnoError Type Definitions"]
    memory_addr["memory_addrMemory Address Types"]
end
subgraph subGraph2["Device Abstraction Dependencies"]
    axdevice_base["axdevice_baseDevice Interface Traits"]
    axdevice_crates["axdevice_cratesDevice Implementation Framework"]
end
subgraph subGraph1["VM Management Dependencies"]
    axvmconfig["axvmconfigVM Configuration"]
    axaddrspace["axaddrspaceAddress Space Management"]
end
subgraph subGraph0["Device Management Layer"]
    axdevice["axdeviceDevice Orchestration"]
end

axaddrspace --> memory_addr
axdevice --> axaddrspace
axdevice --> axdevice_base
axdevice --> axerrno
axdevice --> axvmconfig
axdevice --> cfg_if
axdevice --> log
axdevice --> memory_addr
axdevice_base --> axdevice_crates
axvmconfig --> axerrno

Sources: Cargo.toml(L1 - L19) 

Dependency Architecture

ArceOS Hypervisor-Specific Dependencies

The axdevice crate depends on three key ArceOS hypervisor components, each maintained in separate Git repositories to support modular development:

DependencyRepositoryRole in axdevice
axvmconfiggithub.com/arceos-hypervisor/axvmconfig.gitProvides VM configuration structures that define device parameters
axaddrspacegithub.com/arceos-hypervisor/axaddrspace.gitManages guest physical address space mappings for device MMIO regions
axdevice_basegithub.com/arceos-hypervisor/axdevice_crates.gitDefinesBaseDeviceOpstrait and device abstraction interfaces

ArceOS System Foundation Dependencies

The crate relies on foundational ArceOS system components for basic functionality:

  • axerrno: Provides standardized error types (AxError, AxResult) used throughout the hypervisor ecosystem
  • memory_addr: Supplies memory address types (GuestPhysAddr, HostVirtAddr) for address space operations

External Ecosystem Dependencies

Standard Rust ecosystem crates provide utility functionality:

  • log: Enables structured logging for debugging and monitoring device operations
  • cfg-if: Supports conditional compilation for platform-specific device implementations

Sources: Cargo.toml(L8 - L18) 

Integration Patterns and Code Entities

Component Integration Flow

flowchart TD
subgraph subGraph4["Error Handling Integration"]
    AxError["AxError"]
    AxResult["AxResult"]
end
subgraph subGraph3["Address Space Integration"]
    GuestPhysAddr["GuestPhysAddr"]
    HostVirtAddr["HostVirtAddr"]
    address_mapping["Address Mapping"]
end
subgraph subGraph2["Device Implementation Phase"]
    BaseDeviceOps["BaseDeviceOps trait"]
    device_implementations["Device Implementations"]
end
subgraph subGraph1["Device Management Phase"]
    AxVmDevices["AxVmDevices"]
    find_dev["find_dev()"]
    handle_read["handle_read()"]
    handle_write["handle_write()"]
end
subgraph subGraph0["Configuration Phase"]
    AxVmDeviceConfig["AxVmDeviceConfig"]
    EmulatedDeviceConfig["EmulatedDeviceConfig"]
end

AxError --> AxResult
AxResult --> handle_read
AxResult --> handle_write
AxVmDeviceConfig --> EmulatedDeviceConfig
AxVmDevices --> find_dev
BaseDeviceOps --> device_implementations
EmulatedDeviceConfig --> AxVmDevices
GuestPhysAddr --> find_dev
HostVirtAddr --> address_mapping
address_mapping --> AxVmDevices
find_dev --> handle_read
find_dev --> handle_write
handle_read --> BaseDeviceOps
handle_write --> BaseDeviceOps

Trait and Type Integration Points

The ecosystem integration occurs through specific code entities that provide standardized interfaces:

Configuration Integration:

  • AxVmDeviceConfig structure coordinates with axvmconfig crate for VM-level device configuration
  • EmulatedDeviceConfig instances define individual device parameters within the VM configuration

Device Operation Integration:

  • BaseDeviceOps trait from axdevice_base defines the interface contract for all emulated devices
  • find_dev() method uses GuestPhysAddr from memory_addr crate for address-based device lookup
  • handle_read() and handle_write() methods return AxResult<T> using error types from axerrno

Address Space Integration:

  • GuestPhysAddr and HostVirtAddr types from memory_addr enable precise memory mapping
  • Integration with axaddrspace manages guest physical memory regions assigned to devices

Sources: Cargo.toml(L13 - L18) 

Modular Design Benefits

Repository Separation Strategy

The ArceOS hypervisor ecosystem uses a multi-repository approach that provides several architectural advantages:

Development Independence:

  • Each component (axvmconfig, axaddrspace, axdevice_base) can evolve independently
  • Version pinning through Git dependencies allows controlled integration of component updates
  • Teams can work on different aspects of the hypervisor without merge conflicts

Dependency Management:

  • axdevice specifies default-features = false for axvmconfig, enabling minimal dependency inclusion
  • Git-based dependencies allow using cutting-edge features before crates.io publication
  • Clear separation of concerns prevents circular dependencies between components

Extensibility Framework:

  • New device types can be added by implementing BaseDeviceOps without modifying core axdevice code
  • Additional VM configuration options can be added to axvmconfig without breaking existing device implementations
  • Address space management improvements in axaddrspace benefit all device emulation automatically

Integration Testing and Compatibility

The modular approach enables comprehensive integration testing:

  • Each component maintains its own test suite for isolated functionality verification
  • Integration tests can verify compatibility between specific versions of ecosystem components
  • CI/CD pipelines can test multiple version combinations to ensure ecosystem stability

Sources: Cargo.toml(L16 - L18) 

Development Guide

Relevant source files

This document provides comprehensive information for developers working with the axdevice crate, covering build processes, testing procedures, CI/CD workflows, and contribution guidelines. This guide focuses on the practical aspects of developing, building, and maintaining the device emulation components within the ArceOS hypervisor ecosystem.

For information about the system architecture and how components interact, see System Architecture. For details about specific device implementations and configuration management, see Core Components.

Development Environment Setup

The axdevice crate requires a Rust nightly toolchain with specific target support for cross-platform compatibility across multiple architectures supported by the ArceOS hypervisor.

Required Toolchain Components

flowchart TD
subgraph subGraph1["Target Architectures"]
    E["x86_64-unknown-linux-gnu"]
    F["x86_64-unknown-none"]
    G["riscv64gc-unknown-none-elf"]
    H["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["Rust Toolchain Requirements"]
    A["rust-toolchain: nightly"]
    B["rust-src component"]
    C["clippy component"]
    D["rustfmt component"]
end

A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
A --> H

Development Environment Architecture

The development environment supports both hosted Linux development and bare-metal hypervisor targets across multiple CPU architectures.

Sources: .github/workflows/ci.yml(L11 - L19) 

Target Platform Support

TargetPurposeUsage Context
x86_64-unknown-linux-gnuHost development and testingUnit tests and development builds
x86_64-unknown-noneBare-metal x86_64 hypervisorProduction hypervisor deployment
riscv64gc-unknown-none-elfRISC-V bare-metalRISC-V based hypervisor systems
aarch64-unknown-none-softfloatARM64 bare-metalARM-based hypervisor platforms

Sources: .github/workflows/ci.yml(L12) 

Build and Test Workflow

The development workflow follows a standardized process that ensures code quality and cross-platform compatibility through automated checks and builds.

Development Workflow Diagram

sequenceDiagram
    participant Developer as "Developer"
    participant LocalEnvironment as "Local Environment"
    participant GitHubActionsCI as "GitHub Actions CI"
    participant GitHubPages as "GitHub Pages"

    Developer ->> LocalEnvironment: "cargo fmt --all -- --check"
    LocalEnvironment -->> Developer: "Format validation"
    Developer ->> LocalEnvironment: "cargo clippy --all-features"
    LocalEnvironment -->> Developer: "Linting results"
    Developer ->> LocalEnvironment: "cargo build --all-features"
    LocalEnvironment -->> Developer: "Build success"
    Developer ->> LocalEnvironment: "cargo test"
    LocalEnvironment -->> Developer: "Test results"
    Developer ->> GitHubActionsCI: "git push"
    GitHubActionsCI ->> GitHubActionsCI: "Format check (cargo fmt)"
    GitHubActionsCI ->> GitHubActionsCI: "Clippy analysis"
    GitHubActionsCI ->> GitHubActionsCI: "Multi-target builds"
    GitHubActionsCI ->> GitHubActionsCI: "Unit tests (x86_64-linux)"
    GitHubActionsCI ->> GitHubActionsCI: "Documentation build"
    GitHubActionsCI ->> GitHubPages: "Deploy docs (main branch)"

Complete Development and CI Workflow

Sources: .github/workflows/ci.yml(L23 - L30)  .github/workflows/ci.yml(L44 - L55) 

Local Development Commands

The standard development cycle involves these key commands:

# Code formatting verification
cargo fmt --all -- --check

# Static analysis with custom flags
cargo clippy --target <TARGET> --all-features -- -A clippy::new_without_default

# Cross-platform builds
cargo build --target <TARGET> --all-features

# Unit testing (Linux host only)
cargo test --target x86_64-unknown-linux-gnu -- --nocapture

Sources: .github/workflows/ci.yml(L23 - L30) 

CI/CD Pipeline Architecture

The continuous integration system uses GitHub Actions with a matrix strategy to ensure compatibility across all supported target platforms.

CI Pipeline Flow

flowchart TD
subgraph subGraph3["Documentation Job"]
    K["cargo doc --no-deps --all-features"]
    L["RUSTDOCFLAGS validation"]
    M["GitHub Pages deployment"]
end
subgraph subGraph2["Validation Steps"]
    F["rustc --version --verbose"]
    G["cargo fmt --all -- --check"]
    H["cargo clippy (per target)"]
    I["cargo build (per target)"]
    J["cargo test (x86_64-linux only)"]
end
subgraph subGraph1["CI Job Matrix"]
    C["ubuntu-latest runner"]
    D["nightly toolchain"]
    E["4 target platforms"]
end
subgraph subGraph0["Trigger Events"]
    A["push events"]
    B["pull_request events"]
end

A --> C
B --> C
C --> D
C --> K
D --> E
E --> F
F --> G
G --> H
H --> I
I --> J
K --> L
L --> M

CI/CD Pipeline Architecture

Sources: .github/workflows/ci.yml(L1 - L31)  .github/workflows/ci.yml(L32 - L56) 

Matrix Strategy Configuration

The CI system uses a fail-fast: false strategy to ensure all target platforms are tested even if one fails:

strategy:
  fail-fast: false
  matrix:
    rust-toolchain: [nightly]
    targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]

Sources: .github/workflows/ci.yml(L8 - L12) 

Code Quality Standards

The project enforces strict code quality standards through automated tooling integrated into the CI pipeline.

Linting Configuration

The clippy configuration suppresses specific warnings that are acceptable for the hypervisor context:

cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default

The -A clippy::new_without_default flag allows new() methods without corresponding Default implementations, which is common in device emulation where default configurations may not be meaningful.

Sources: .github/workflows/ci.yml(L25) 

Documentation Standards

Documentation builds use strict flags to ensure comprehensive documentation quality:

RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs

These flags treat broken documentation links and missing documentation as compilation errors, ensuring complete and accurate documentation.

Sources: .github/workflows/ci.yml(L40) 

Documentation Generation and Deployment

The documentation system automatically generates and deploys API documentation to GitHub Pages for the main branch.

Documentation Build Process

flowchart TD
subgraph subGraph1["Deployment Conditions"]
    D["Branch == default branch"]
    E["JamesIves/github-pages-deploy-action@v4"]
    F["single-commit: true"]
    G["branch: gh-pages"]
end
subgraph subGraph0["Documentation Build"]
    A["cargo doc --no-deps --all-features"]
    B["Generate index.html redirect"]
    C["Extract crate name from cargo tree"]
end

A --> B
B --> C
C --> D
D --> E
E --> F
F --> G

Documentation Generation and Deployment Process

The documentation build creates a redirect page that automatically navigates to the main crate documentation:

printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html

Sources: .github/workflows/ci.yml(L44 - L55) 

Project Structure and Ignored Files

The development environment excludes specific files and directories that are generated during the build process or are environment-specific.

Ignored File Categories

CategoryFilesPurpose
Build Artifacts/target,.asm,.img,.bin,.elfCompiled binaries and assembly output
Runtime Filesactual.out,qemu.logTest execution and emulation logs
Development Toolsrusty-tags.vi,/.vscode,.DS_StoreEditor and IDE configurations
DependenciesCargo.lockLock file excluded because axdevice is a library

Sources: .gitignore(L1 - L18) 

Contributing Guidelines

Pre-submission Checklist

Before submitting code changes, ensure all of the following pass locally:

  1. Code Formatting: cargo fmt --all -- --check
  2. Static Analysis: cargo clippy --all-features -- -A clippy::new_without_default
  3. Cross-platform Builds: Test builds on all target platforms
  4. Unit Tests: cargo test --target x86_64-unknown-linux-gnu -- --nocapture
  5. Documentation: cargo doc --no-deps --all-features

Target Platform Testing

All changes must build successfully on the four supported target platforms. The CI system will verify this automatically, but local testing on critical platforms is recommended:

# Test core functionality on Linux host
cargo test --target x86_64-unknown-linux-gnu

# Verify bare-metal builds
cargo build --target x86_64-unknown-none --all-features
cargo build --target riscv64gc-unknown-none-elf --all-features
cargo build --target aarch64-unknown-none-softfloat --all-features

Sources: .github/workflows/ci.yml(L27 - L30) 

Overview

Relevant source files

Purpose and Scope

The x86_vcpu library provides a complete Intel VMX-based virtualization framework for x86_64 architectures. This library implements virtual CPU management, memory virtualization through Extended Page Tables (EPT), and hardware abstraction for building hypervisors.

The library focuses specifically on Intel VMX technology and provides the core virtualization primitives needed to create and manage virtual machines. For AMD SVM support, see the amd feature flag documented in Project Configuration. For build and development processes, see Development and Configuration.

Sources: src/lib.rs(L1 - L31)  README.md(L1 - L29)  Cargo.toml(L1 - L28) 

System Architecture

The x86_vcpu library is structured around several core subsystems that work together to provide complete virtualization capabilities:

Core System Components

flowchart TD
subgraph ExportedAPI["Public API"]
    VmxExports["VmxExitInfoVmxExitReasonVmxInterruptInfoVmxIoExitInfo"]
    GeneralExports["GeneralRegistersGuestPageWalkInfohas_hardware_support"]
end
subgraph ExternalInterfaces["External Interfaces"]
    PhysFrameIf["PhysFrameIf TraitHardware Abstraction"]
    Dependencies["External Dependenciesx86, x86_64, axvcpu"]
end
subgraph LibraryCore["x86_vcpu Library Core"]
    LibRS["lib.rsMain Entry Point"]
    subgraph MemoryMgmt["Memory Management"]
        EPTMod["ept.rsExtended Page Tables"]
        FrameMod["frame.rsPhysical Frame Management"]
        GuestPageWalk["GuestPageWalkInfoGuest Memory Virtualization"]
    end
end
subgraph SupportSystems["Supporting Systems"]
    RegsMod["regs.rsGeneralRegisters"]
    MsrMod["msr.rsMSR Access"]
    VmxDefs["vmx/definitions.rsVMX Types"]
    VmxInstr["vmx/instructions.rsVMX Instructions"]
    VmxVcpu["VmxArchVCpuVirtual CPU Implementation"]
    VmxPerCpu["VmxArchPerCpuStatePer-CPU VMX State"]
end
subgraph VMXEngine["VMX Virtualization Engine"]
    RegsMod["regs.rsGeneralRegisters"]
    MsrMod["msr.rsMSR Access"]
    VmxMod["vmx/mod.rsVMX Module"]
    VmxVcpu["VmxArchVCpuVirtual CPU Implementation"]
    VmxPerCpu["VmxArchPerCpuStatePer-CPU VMX State"]
end

EPTMod --> GuestPageWalk
FrameMod --> PhysFrameIf
PhysFrameIf --> Dependencies
VmxPerCpu --> VmxMod
VmxVcpu --> VmxMod

Sources: src/lib.rs(L18 - L31)  Cargo.toml(L6 - L22) 

VMX Feature Compilation Model

flowchart TD
subgraph PublicAPI["Exported Types"]
    VmxTypes["VmxExitInfoVmxExitReasonVmxInterruptInfoVmxIoExitInfo"]
    CommonTypes["GeneralRegistersGuestPageWalkInfohas_hardware_support"]
end
subgraph ConditionalCode["Conditional Implementation"]
    VmxImpl["VMX ImplementationVmxArchVCpuVmxArchPerCpuState"]
    VendorAlias["vender aliaspoints to active implementation"]
end
subgraph FeatureGates["Feature-Based Compilation"]
    DefaultFeature["default = ['vmx']"]
    VmxFeature["vmx feature"]
    AmdFeature["amd feature"]
end

DefaultFeature --> VmxFeature
VendorAlias --> VmxTypes
VmxFeature --> VmxImpl
VmxImpl --> CommonTypes
VmxImpl --> VendorAlias

Sources: src/lib.rs(L18 - L27)  Cargo.toml(L24 - L27) 

Key Components

VMX Virtualization Engine

The core of the library is the VMX virtualization engine, which provides complete Intel VMX support. The VmxArchVCpu structure implements the virtual CPU interface defined by the axvcpu crate, handling VM entry/exit, guest state management, and hardware virtualization features.

For detailed information about VMX components, see VMX Virtualization Engine.

Memory Management

The memory management subsystem handles both host physical memory allocation and guest memory virtualization. The PhysFrameIf trait provides hardware abstraction for physical frame allocation, while the EPT system manages guest virtual-to-physical memory translation.

For memory management details, see Memory Management.

Hardware Abstraction

The library defines clear interfaces for hardware abstraction through the PhysFrameIf trait, allowing integration with different underlying memory management systems.

Interface Requirements

PhysFrameIf Implementation

Users must implement the PhysFrameIf trait to provide physical memory management:

MethodPurpose
alloc_frame()Allocate a 4KB physical memory frame
dealloc_frame(paddr)Deallocate a physical memory frame
phys_to_virt(paddr)Convert physical address to virtual address

The implementation uses the crate_interface mechanism for dependency injection, as shown in README.md(L13 - L29) 

Sources: README.md(L7 - L29)  src/lib.rs(L1 - L6) 

Dependency Integration

The library integrates with several external crates:

Crate CategoryDependenciesPurpose
Low-level x86x86,x86_64,raw-cpuidHardware instruction and register access
Hypervisor Frameworkaxvcpu,axaddrspaceVirtual CPU and address space interfaces
Utilitybitflags,bit_field,memory_addrData structure manipulation

Sources: Cargo.toml(L6 - L22) 

Implementation Approach

The library uses conditional compilation to support different virtualization technologies. Currently, Intel VMX is the primary implementation, with the vmx feature enabled by default. The architecture allows for future AMD SVM support through the amd feature flag.

The modular design separates concerns into distinct subsystems:

  • VMX-specific code is isolated in the vmx module
  • Memory management provides hardware-agnostic abstractions
  • Supporting systems offer reusable components for register and MSR access

For specific implementation details of each subsystem, refer to the corresponding sections: Virtual CPU Management, Physical Frame Management, and Supporting Systems.

Sources: src/lib.rs(L18 - L31)  Cargo.toml(L24 - L27) 

VMX Virtualization Engine

Relevant source files

The VMX Virtualization Engine forms the core of the x86_vcpu hypervisor library, implementing Intel's VMX (Virtual Machine Extensions) technology to enable hardware-assisted virtualization. This system provides the fundamental infrastructure for creating, managing, and executing virtual CPUs using Intel's VT-x capabilities.

For detailed information about physical memory management, see Physical Frame Management. For register and MSR access patterns, see Supporting Systems.

Architecture Overview

The VMX Virtualization Engine consists of several interconnected components that work together to provide complete virtualization functionality. The system is built around the VmxVcpu structure, which represents a virtual CPU and manages the complete virtualization lifecycle.

flowchart TD
subgraph subGraph4["Guest State Management"]
    GuestRegs["GeneralRegisters"]
    XState["XStateExtended Processor State"]
    PendingEvents["VecDeque<pending_events>"]
end
subgraph subGraph3["Per-CPU State"]
    PerCpuState["VmxPerCpuState(vmx/percpu.rs)"]
    HwEnable["hardware_enable()"]
    HwDisable["hardware_disable()"]
end
subgraph subGraph2["VMX Data Management"]
    VmxRegion["VmxRegionVMCS Memory"]
    IOBitmap["IOBitmapPort Interception"]
    MsrBitmap["MsrBitmapMSR Control"]
    VmcsFields["VMCS Field Access(vmx/vmcs.rs)"]
end
subgraph subGraph1["Core Virtualization Engine"]
    VmxVcpu["VmxVcpu<H>(vmx/vcpu.rs)"]
    VmExecution["VM Executionvmx_launch/vmx_resume"]
    VmExitHandler["VM Exit Handlerbuiltin_vmexit_handler()"]
end
subgraph subGraph0["VMX Module Interface"]
    VmxMod["VmxModFacade(vmx/mod.rs)"]
    HwSupport["has_hardware_support()"]
    RevisionId["read_vmcs_revision_id()"]
end

PerCpuState --> HwDisable
PerCpuState --> HwEnable
VmExecution --> VmExitHandler
VmxMod --> PerCpuState
VmxMod --> VmxVcpu
VmxVcpu --> GuestRegs
VmxVcpu --> IOBitmap
VmxVcpu --> MsrBitmap
VmxVcpu --> PendingEvents
VmxVcpu --> VmcsFields
VmxVcpu --> VmxRegion
VmxVcpu --> XState

Sources: src/vmx/mod.rs(L1 - L36)  src/vmx/vcpu.rs(L143 - L159) 

Core Components and Interactions

The VMX engine operates through a collection of specialized components that handle different aspects of virtualization. The central VmxVcpu structure coordinates these components to provide a complete virtual machine implementation.

flowchart TD
subgraph subGraph3["VMCS Management"]
    VmcsSetup["setup_vmcs_host()setup_vmcs_guest()setup_vmcs_control()"]
    VmcsFields["VMCS Field AccessVmcsGuestNW::RIPVmcsControl32::PINBASED_EXEC_CONTROLSVmcsHost64::IA32_PAT"]
end
subgraph subGraph2["VM Exit Handling"]
    ExitInfo["exit_info() -> VmxExitInfo"]
    BuiltinHandler["builtin_vmexit_handler()"]
    ExitTypes["Exit Types- INTERRUPT_WINDOW- PREEMPTION_TIMER- XSETBV- CR_ACCESS- CPUID"]
end
subgraph subGraph1["VM Lifecycle Operations"]
    NewVcpu["new() -> AxResult<Self>"]
    SetupVcpu["setup(ept_root, entry)"]
    BindVcpu["bind_to_current_processor()"]
    RunVcpu["inner_run() -> Option<VmxExitInfo>"]
    UnbindVcpu["unbind_from_current_processor()"]
end
subgraph subGraph0["VmxVcpu Structure"]
    CoreFields["Core Fieldsguest_regs: GeneralRegistershost_stack_top: u64launched: bool"]
    DataStructs["Data Structuresvmcs: VmxRegion<H>io_bitmap: IOBitmap<H>msr_bitmap: MsrBitmap<H>"]
    StateFields["State Managementpending_events: VecDequexstate: XStateentry: Option<GuestPhysAddr>ept_root: Option<HostPhysAddr>"]
end

BuiltinHandler --> ExitTypes
CoreFields --> NewVcpu
DataStructs --> SetupVcpu
ExitInfo --> BuiltinHandler
RunVcpu --> ExitInfo
SetupVcpu --> VmcsSetup
StateFields --> RunVcpu
VmcsSetup --> VmcsFields

Sources: src/vmx/vcpu.rs(L144 - L159)  src/vmx/vcpu.rs(L238 - L280)  src/vmx/vcpu.rs(L890 - L906) 

VM Execution Lifecycle

The virtual machine execution follows a well-defined lifecycle that involves hardware initialization, VMCS configuration, guest execution, and VM exit handling. This process is managed through careful coordination between the hypervisor and VMX hardware.


Sources: src/vmx/vcpu.rs(L162 - L180)  src/vmx/vcpu.rs(L238 - L280)  src/vmx/vcpu.rs(L822 - L856) 

Hardware Support and Abstraction

The VMX engine provides hardware abstraction through well-defined interfaces that detect VMX capabilities and manage processor-specific features. The system validates hardware support and configures VMX features based on detected capabilities.

ComponentPurposeKey Functions
Hardware DetectionValidate VMX supporthas_hardware_support()
VMCS RevisionGet processor VMCS versionread_vmcs_revision_id()
Per-CPU StateManage processor VMX statehardware_enable(),hardware_disable()
Error HandlingConvert VMX errors to AxErroras_axerr()

The hardware abstraction layer ensures compatibility across different Intel processors while providing a uniform interface for VMX operations. The VmxBasic structure reads processor capabilities, and the system adapts its behavior based on detected features such as RDTSCP, INVPCID, and XSAVES support.

flowchart TD
subgraph subGraph1["VMX Configuration"]
    RevisionId["revision_id"]
    Controls["VMX Control MSRsIA32_VMX_PINBASED_CTLSIA32_VMX_PROCBASED_CTLSIA32_VMX_PROCBASED_CTLS2"]
    Features["Feature AdaptationRDTSCPINVPCIDXSAVES"]
end
subgraph subGraph0["Hardware Detection"]
    CpuId["raw_cpuid::CpuId"]
    FeatureCheck["get_feature_info().has_vmx()"]
    VmxBasic["VmxBasic::read()"]
end
subgraph subGraph2["Error Handling"]
    VmFail["x86::vmx::VmFail"]
    ErrorConv["as_axerr()"]
    AxError["axerrno::AxError"]
end

CpuId --> FeatureCheck
ErrorConv --> AxError
FeatureCheck --> Controls
RevisionId --> Features
VmFail --> ErrorConv
VmxBasic --> RevisionId

Sources: src/vmx/mod.rs(L16 - L27)  src/vmx/mod.rs(L29 - L35)  src/vmx/vcpu.rs(L606 - L663) 

Module Interface and Exports

The VMX module provides a clean interface through its mod.rs facade, exporting the essential types and functions needed by the broader hypervisor system. The module structure separates concerns between different aspects of VMX functionality while providing unified access through type aliases.

ExportSourcePurpose
VmxExitReasondefinitions::VmxExitReasonVM exit reason enumeration
VmxArchPerCpuStatepercpu::VmxPerCpuStatePer-CPU VMX state management
VmxArchVCpuvcpu::VmxVcpuMain virtual CPU implementation
VmxExitInfovmcs::VmxExitInfoVM exit information structure
VmxInterruptInfovmcs::VmxInterruptInfoInterrupt exit details
VmxIoExitInfovmcs::VmxIoExitInfoI/O instruction exit details

The module implements a layered architecture where higher-level abstractions build upon lower-level VMX primitives, providing both flexibility for advanced users and simplicity for common virtualization tasks.

Sources: src/vmx/mod.rs(L1 - L14)  src/vmx/mod.rs(L11 - L14) 

Virtual CPU Management

Relevant source files

This document covers the implementation and lifecycle management of virtual CPUs (vCPUs) in the x86_vcpu hypervisor library. It focuses on the VmxVcpu structure and its role in orchestrating VM execution, handling VM exits, and managing guest/host state transitions. For VMX data structures and memory management details, see VMX Data Structures and Physical Frame Management.

VmxVcpu Structure Overview

The VmxVcpu<H: AxVCpuHal> struct serves as the central abstraction for managing virtual CPU execution within the VMX virtualization framework. It encapsulates all necessary state and control structures required for guest execution.

flowchart TD
subgraph CoreMethods["Core VmxVcpu Methods"]
    RunMethod["inner_run()"]
    UnbindMethod["unbind_from_current_processor()"]
    VmcsRegion["vmcs: VmxRegion"]
    IOBitmap["io_bitmap: IOBitmap"]
    subgraph StateManagement["State Management"]
        XStateSwitch["XState::switch_to_guest/host()"]
        RegisterAccess["regs()/regs_mut()"]
        VmcsAccess["VMCS field access"]
        NewMethod["new()"]
        SetupMethod["setup()"]
        BindMethod["bind_to_current_processor()"]
        GuestRegs["guest_regs: GeneralRegisters"]
        HostStack["host_stack_top: u64"]
        LaunchState["launched: bool"]
    end
end
subgraph VmxVcpuStruct["VmxVcpu Structure"]
    XStateSwitch["XState::switch_to_guest/host()"]
    RegisterAccess["regs()/regs_mut()"]
    VmcsAccess["VMCS field access"]
    NewMethod["new()"]
    SetupMethod["setup()"]
    BindMethod["bind_to_current_processor()"]
    RunMethod["inner_run()"]
    UnbindMethod["unbind_from_current_processor()"]
    GuestRegs["guest_regs: GeneralRegisters"]
    HostStack["host_stack_top: u64"]
    LaunchState["launched: bool"]
    VmcsRegion["vmcs: VmxRegion"]
    IOBitmap["io_bitmap: IOBitmap"]
    MsrBitmap["msr_bitmap: MsrBitmap"]
    PendingEvents["pending_events: VecDeque<(u8, Option)>"]
    XStateData["xstate: XState"]
    EntryPoint["entry: Option"]
    EptRoot["ept_root: Option"]
end

Sources: src/vmx/vcpu.rs(L144 - L159) 

Key Components

ComponentTypePurpose
guest_regsGeneralRegistersGuest general-purpose register state
host_stack_topu64Host stack pointer for VM exit handling
vmcsVmxRegionVirtual Machine Control Structure memory region
io_bitmapIOBitmapI/O port access control bitmap
msr_bitmapMsrBitmapMSR access control bitmap
pending_eventsVecDeque<(u8, Option)>Queue for interrupt/exception injection
xstateXStateExtended processor state management

Sources: src/vmx/vcpu.rs(L144 - L159) 

Virtual CPU Lifecycle

The virtual CPU follows a well-defined lifecycle from creation through execution to teardown. Each phase involves specific initialization and state management operations.

sequenceDiagram
    participant ClientCode as "Client Code"
    participant VmxVcpuH as "VmxVcpu<H>"
    participant VMCSManager as "VMCS Manager"
    participant VMXHardware as "VMX Hardware"

    ClientCode ->> VmxVcpuH: "new()"
    VmxVcpuH ->> VmxVcpuH: "Initialize guest_regs, vmcs, bitmaps"
    VmxVcpuH ->> VmxVcpuH: "Create XState::new()"
    ClientCode ->> VmxVcpuH: "setup(ept_root, entry)"
    VmxVcpuH ->> VMCSManager: "setup_vmcs_guest()"
    VmxVcpuH ->> VMCSManager: "setup_vmcs_control()"
    ClientCode ->> VmxVcpuH: "bind_to_current_processor()"
    VmxVcpuH ->> VMXHardware: "vmx::vmptrld(vmcs.phys_addr())"
    VmxVcpuH ->> VMCSManager: "setup_vmcs_host()"
    loop "VM Execution"
        ClientCode ->> VmxVcpuH: "inner_run()"
        VmxVcpuH ->> VmxVcpuH: "inject_pending_events()"
        VmxVcpuH ->> VMXHardware: "vmlaunch/vmresume"
        VMXHardware -->> VmxVcpuH: "VM Exit"
        VmxVcpuH ->> VmxVcpuH: "builtin_vmexit_handler()"
    end
    ClientCode ->> VmxVcpuH: "unbind_from_current_processor()"
    VmxVcpuH ->> VMXHardware: "vmx::vmclear(vmcs.phys_addr())"

Sources: src/vmx/vcpu.rs(L162 - L217)  src/vmx/vcpu.rs(L238 - L280) 

Creation and Setup

The VmxVcpu::new() method initializes a new virtual CPU instance with default state:

  • Creates default GeneralRegisters and XState
  • Allocates VmxRegion with proper VMX revision ID
  • Initializes I/O and MSR bitmaps with passthrough configuration
  • Sets up pending events queue

Sources: src/vmx/vcpu.rs(L162 - L180) 

The setup() method configures the virtual CPU for a specific guest:

  • Calls setup_vmcs() to configure VMCS guest, host, and control fields
  • Sets guest entry point and EPT root page table address

Sources: src/vmx/vcpu.rs(L183 - L186) 

Processor Binding

Before execution, the virtual CPU must be bound to a physical processor using bind_to_current_processor():

  • Loads the VMCS using vmx::vmptrld()
  • Configures host state in VMCS via setup_vmcs_host()

Sources: src/vmx/vcpu.rs(L194 - L204) 

VM Execution Cycle

The core execution mechanism is implemented in inner_run(), which orchestrates the complete VM entry/exit cycle including guest execution and exit handling.


Sources: src/vmx/vcpu.rs(L238 - L280) 

VM Entry Mechanisms

The hypervisor uses naked assembly functions for precise control over VM entry:

  • vmx_launch(): Initial VM entry using vmlaunch instruction
  • vmx_resume(): Subsequent VM entries using vmresume instruction
  • Both functions save host state and restore guest state before entry

Sources: src/vmx/vcpu.rs(L822 - L840) 

VM Exit Handling

When a VM exit occurs, vmx_exit() handles the transition back to host mode:

  • Saves guest registers to the GeneralRegisters structure
  • Restores host stack pointer from host_stack_top
  • Returns control to inner_run() for exit processing

Sources: src/vmx/vcpu.rs(L842 - L856) 

Built-in Exit Handling

The VmxVcpu implements built-in handlers for common VM exit reasons that can be handled transparently without external intervention.

flowchart TD
VMExit["VM Exit"]
BuiltinHandler["builtin_vmexit_handler()"]
ExitReason["exit_reason"]
IntWindow["set_interrupt_window(false)"]
PreemptTimer["handle_vmx_preemption_timer()"]
XSetBV["handle_xsetbv()"]
CRAccess["handle_cr()"]
CPUID["handle_cpuid()"]
External["Return None - external handling"]
Success["Some(Ok(_))"]
None["None"]

BuiltinHandler --> ExitReason
CPUID --> Success
CRAccess --> Success
ExitReason --> CPUID
ExitReason --> CRAccess
ExitReason --> External
ExitReason --> IntWindow
ExitReason --> PreemptTimer
ExitReason --> XSetBV
External --> None
IntWindow --> Success
PreemptTimer --> Success
VMExit --> BuiltinHandler
XSetBV --> Success

Sources: src/vmx/vcpu.rs(L890 - L906) 

Exit Handler Implementations

Exit ReasonHandler MethodPurpose
INTERRUPT_WINDOWset_interrupt_window(false)Disable interrupt window exiting after pending interrupt injection
PREEMPTION_TIMERhandle_vmx_preemption_timer()Reset VMX preemption timer value
XSETBVhandle_xsetbv()Handle guest XCR0 modifications for extended state
CR_ACCESShandle_cr()Handle guest control register access (CR0, CR4)
CPUIDhandle_cpuid()Virtualize CPUID instruction with hypervisor-specific responses

Sources: src/vmx/vcpu.rs(L908 - L1042) 

CPUID Virtualization

The handle_cpuid() method provides comprehensive CPUID virtualization:

  • Masks VMX feature bit to hide hardware virtualization from guest
  • Sets hypervisor present bit to indicate virtualized environment
  • Provides custom hypervisor identification strings
  • Handles extended state enumeration with proper XState context switching

Sources: src/vmx/vcpu.rs(L955 - L1042) 

State Management

Extended Processor State (XState)

The XState structure manages extended processor state including XCR0 and IA32_XSS registers for both guest and host contexts.

flowchart TD
subgraph XStateStruct["XState Structure"]
    LoadGuest["load_guest_xstate()"]
    LoadHost["load_host_xstate()"]
    SwitchGuest["switch_to_guest()"]
    SwitchHost["switch_to_host()"]
    HostXCR0["host_xcr0: u64"]
    GuestXCR0["guest_xcr0: u64"]
    HostXSS["host_xss: u64"]
    GuestXSS["guest_xss: u64"]
    XSaveAvail["xsave_available: bool"]
    XSavesAvail["xsaves_available: bool"]
    subgraph VMExecution["VM Execution Integration"]
        subgraph StateSwitching["State Switching Methods"]
            LoadGuest["load_guest_xstate()"]
            LoadHost["load_host_xstate()"]
            SwitchGuest["switch_to_guest()"]
            SwitchHost["switch_to_host()"]
            HostXCR0["host_xcr0: u64"]
            GuestXCR0["guest_xcr0: u64"]
        end
    end
end

Sources: src/vmx/vcpu.rs(L31 - L138) 

Register Access Interface

The VmxVcpu provides multiple interfaces for accessing guest processor state:

  • regs() and regs_mut(): Access general-purpose registers
  • stack_pointer() and set_stack_pointer(): Guest RSP access
  • rip() and advance_rip(): Guest instruction pointer management
  • get_cpu_mode(): Determine current guest CPU mode (Real, Protected, Compatibility, Mode64)

Sources: src/vmx/vcpu.rs(L307 - L399) 

Event Injection

The pending events mechanism allows queueing interrupts and exceptions for injection:

  • queue_event(): Add interrupt/exception to pending queue
  • inject_pending_events(): Process queue before VM entry
  • set_interrupt_window(): Enable interrupt window exiting when guest interrupts are blocked

Sources: src/vmx/vcpu.rs(L402 - L404)  src/vmx/vcpu.rs(L871 - L888) 

Integration with AxArchVCpu Trait

The VmxVcpu implements the AxArchVCpu trait to provide a standardized interface for the broader hypervisor framework.

Trait MethodImplementationPurpose
new()VmxVcpu::new()Create new virtual CPU instance
set_entry()SetsentryfieldConfigure guest entry point
set_ept_root()Setsept_rootfieldConfigure EPT root page table
setup()Callssetup_vmcs()Initialize VMCS with entry and EPT root
run()Callsinner_run()and converts exit infoExecute guest and return standardized exit reasons
bind()/unbind()Processor binding/unbindingManage processor affinity

Sources: src/vmx/vcpu.rs(L1144 - L1255) 

This integration enables the VMX-specific virtual CPU implementation to work seamlessly with the broader ArceOS hypervisor architecture while maintaining VMX-specific optimizations and capabilities.

Sources: src/vmx/vcpu.rs(L1 - L1256) 

VMX Data Structures

Relevant source files

This document covers the fundamental VMX data structures that support Intel VMX virtualization operations. These structures include memory-backed control regions (VmxRegion), access control bitmaps (IOBitmap, MsrBitmap), hardware capability reporting (VmxBasic), and page table pointers (EPTPointer). These structures work together to configure and control virtual machine execution.

For information about how these structures are used in VCPU management, see Virtual CPU Management. For details about VMCS field access patterns, see VMCS Field Management.

Core VMX Memory Structures

The hypervisor manages three primary memory-backed VMX structures that control virtual machine behavior. Each structure is backed by one or more 4KB physical frames managed through the PhysFrame<H> abstraction.

VmxRegion

The VmxRegion<H: AxVCpuHal> struct represents VMCS/VMXON regions as defined in Intel SDM Vol. 3C, Section 24.2. These regions are 4KB memory areas that store VMX control structures.

flowchart TD
VmxRegion["VmxRegion<H>"]
PhysFrame["PhysFrame<H>"]
RevisionHeader["Revision Header (32-bit)"]
RevisionID["revision_id (bits 0-30)"]
ShadowIndicator["shadow_indicator (bit 31)"]

PhysFrame --> RevisionHeader
RevisionHeader --> RevisionID
RevisionHeader --> ShadowIndicator
VmxRegion --> PhysFrame

The constructor VmxRegion::new() initializes the region with a revision identifier and shadow indicator bit as required by VMX specifications. The phys_addr() method provides the physical address needed for VMX instructions.

IOBitmap

The IOBitmap<H: AxVCpuHal> controls I/O port access interception using two 4KB frames covering the complete 16-bit I/O port space as specified in Intel SDM Vol. 3C, Section 25.6.4.

FramePort RangeSize
io_bitmap_a_frame0x0000-0x7FFF4KB
io_bitmap_b_frame0x8000-0xFFFF4KB
flowchart TD
IOBitmap["IOBitmap<H>"]
BitmapA["io_bitmap_a_frame"]
BitmapB["io_bitmap_b_frame"]
PortsLow["Ports 0x0000-0x7FFF"]
PortsHigh["Ports 0x8000-0xFFFF"]
BitLogic["1 bit = intercept0 bit = passthrough"]

BitmapA --> PortsLow
BitmapB --> PortsHigh
IOBitmap --> BitmapA
IOBitmap --> BitmapB
PortsHigh --> BitLogic
PortsLow --> BitLogic

The set_intercept() method configures individual port interception by setting bits in the appropriate bitmap frame. The passthrough_all() constructor creates zero-filled bitmaps for unrestricted I/O access, while intercept_all() creates bitmaps with all bits set.

MsrBitmap

The MsrBitmap<H: AxVCpuHal> controls Model-Specific Register access using a single 4KB frame divided into four 1KB regions for different MSR ranges and access types.

OffsetRegionMSR RangeAccess Type
0x000Read Low0x0000_0000-0x0000_1FFFRead
0x400Read High0xC000_0000-0xC000_1FFFRead
0x800Write Low0x0000_0000-0x0000_1FFFWrite
0xC00Write High0xC000_0000-0xC000_1FFFWrite

The set_read_intercept() and set_write_intercept() methods configure MSR access control by manipulating bits in the appropriate region of the bitmap frame.

Sources: src/vmx/structs.rs(L13 - L163) 

Hardware Capability Structures

VmxBasic Capabilities

The VmxBasic struct provides access to the IA32_VMX_BASIC MSR, which reports fundamental VMX capabilities. This structure implements the MsrReadWrite trait for MSR access.

flowchart TD
VmxBasic["VmxBasic"]
MSR["IA32_VMX_BASIC MSR"]
RevisionID["revision_id (bits 0-30)"]
RegionSize["region_size (bits 32-44)"]
AddrWidth["is_32bit_address (bit 48)"]
MemType["mem_type (bits 50-53)"]
IOExitInfo["io_exit_info (bit 54)"]
FlexControls["vmx_flex_controls (bit 55)"]

VmxBasic --> AddrWidth
VmxBasic --> FlexControls
VmxBasic --> IOExitInfo
VmxBasic --> MSR
VmxBasic --> MemType
VmxBasic --> RegionSize
VmxBasic --> RevisionID

Key fields include:

  • revision_id: 31-bit VMCS revision identifier
  • region_size: Required allocation size for VMXON/VMCS regions
  • mem_type: Required memory type (typically VMX_MEMORY_TYPE_WRITE_BACK = 6)
  • vmx_flex_controls: Indicates flexible control support

FeatureControl Management

The FeatureControl struct manages the IA32_FEATURE_CONTROL MSR through FeatureControlFlags bitflags. This MSR controls VMX enablement at the processor level.

FlagPurpose
LOCKEDPrevents further MSR modification
VMXON_ENABLED_INSIDE_SMXEnables VMX inside SMX mode
VMXON_ENABLED_OUTSIDE_SMXEnables VMX outside SMX mode

The write() method preserves reserved bits while updating control flags, ensuring proper MSR manipulation.

Sources: src/vmx/structs.rs(L165 - L240) 

Extended Page Table Structures

EPTPointer Configuration

The EPTPointer bitflags struct configures Extended Page Table parameters as defined in Intel SDM Vol. 3C, Section 24.6.11. This structure combines page table root address with control flags.

flowchart TD
EPTPointer["EPTPointer"]
MemoryType["Memory Type (bits 0-2)"]
WalkLength["Page Walk Length (bits 3-5)"]
AccessedDirty["Enable Accessed/Dirty (bit 6)"]
TableAddr["Page Table Root Address (bits 12-51)"]
UC["MEM_TYPE_UC = 0"]
WB["MEM_TYPE_WB = 6"]
Length4["WALK_LENGTH_4 = 3"]
Enabled["ENABLE_ACCESSED_DIRTY"]

AccessedDirty --> Enabled
EPTPointer --> AccessedDirty
EPTPointer --> MemoryType
EPTPointer --> TableAddr
EPTPointer --> WalkLength
MemoryType --> UC
MemoryType --> WB
WalkLength --> Length4

The from_table_phys() constructor creates a properly configured EPT pointer from a physical page table root address, setting standard flags for write-back memory type, 4-level page walks, and accessed/dirty bit support.

Structure Relationships and Usage Patterns

The VMX data structures work together to establish virtual machine execution context:

flowchart TD
subgraph subGraph3["VM Execution"]
    VMCS["VMCS Fields"]
    VMEntry["VM Entry/Exit"]
end
subgraph subGraph2["Hardware Interface"]
    VmxBasic["VmxBasic"]
    FeatureControl["FeatureControl"]
    EPTPointer["EPTPointer"]
end
subgraph subGraph1["VMX Control Structures"]
    VmxRegion["VmxRegion"]
    IOBitmap["IOBitmap"]
    MsrBitmap["MsrBitmap"]
end
subgraph subGraph0["Physical Memory"]
    PhysFrames["PhysFrame<H> Allocations"]
end

EPTPointer --> VMCS
FeatureControl --> VMEntry
IOBitmap --> VMCS
MsrBitmap --> VMCS
PhysFrames --> IOBitmap
PhysFrames --> MsrBitmap
PhysFrames --> VmxRegion
VMCS --> VMEntry
VmxBasic --> VmxRegion
VmxRegion --> VMCS

These structures are typically initialized during VCPU setup and configured based on virtualization requirements. The physical frame allocation through AxVCpuHal ensures proper memory management across different hypervisor implementations.

Sources: src/vmx/structs.rs(L242 - L270) 

VMCS Field Management

Relevant source files

This document covers the Virtual Machine Control Structure (VMCS) field management system within the VMX virtualization engine. The VMCS is Intel's hardware structure that controls virtual machine behavior, and this module provides type-safe access patterns, control field manipulation algorithms, and VM exit information gathering mechanisms.

For Virtual CPU lifecycle management and VM execution flow, see Virtual CPU Management. For the underlying VMX data structures that contain VMCS regions, see VMX Data Structures.

VMCS Field Organization

The VMCS contains hundreds of fields organized into distinct categories based on their purpose and access patterns. The hypervisor provides type-safe enums for each category, ensuring correct field access and preventing runtime errors.

flowchart TD
subgraph subGraph5["Access Implementations"]
    ReadImpl["vmcs_read! macrogenerates .read() methods"]
    WriteImpl["vmcs_write! macrogenerates .write() methods"]
    ROFields["define_vmcs_fields_ro!read-only field traits"]
    RWFields["define_vmcs_fields_rw!read-write field traits"]
end
subgraph subGraph4["VMCS Field Categories"]
    subgraph subGraph3["Read-Only Data Fields"]
        ReadOnly32["VmcsReadOnly32EXIT_REASON, VM_INSTRUCTION_ERROR"]
        ReadOnly64["VmcsReadOnly64GUEST_PHYSICAL_ADDR"]
        ReadOnlyNW["VmcsReadOnlyNWEXIT_QUALIFICATION, GUEST_LINEAR_ADDR"]
    end
    subgraph subGraph2["Host State Fields"]
        Host16["VmcsHost16ES_SELECTOR, TR_SELECTOR"]
        Host32["VmcsHost32IA32_SYSENTER_CS"]
        Host64["VmcsHost64IA32_PAT, IA32_EFER"]
        HostNW["VmcsHostNWCR0, RSP, RIP"]
    end
    subgraph subGraph1["Guest State Fields"]
        Guest16["VmcsGuest16ES_SELECTOR, CS_SELECTOR"]
        Guest32["VmcsGuest32ES_LIMIT, INTERRUPTIBILITY_STATE"]
        Guest64["VmcsGuest64IA32_EFER, IA32_PAT"]
        GuestNW["VmcsGuestNWCR0, RSP, RIP, RFLAGS"]
    end
    subgraph subGraph0["Control Fields"]
        Control16["VmcsControl16VPID, EPTP_INDEX"]
        Control32["VmcsControl32EXEC_CONTROLS, EXCEPTION_BITMAP"]
        Control64["VmcsControl64IO_BITMAP_A_ADDR, MSR_BITMAPS_ADDR"]
        ControlNW["VmcsControlNWCR0_GUEST_HOST_MASK, CR4_READ_SHADOW"]
    end
end

Control16 --> RWFields
Control32 --> RWFields
Control64 --> RWFields
ControlNW --> RWFields
Guest16 --> RWFields
Guest32 --> RWFields
Guest64 --> RWFields
GuestNW --> RWFields
Host16 --> RWFields
Host32 --> RWFields
Host64 --> RWFields
HostNW --> RWFields
ROFields --> ReadImpl
RWFields --> ReadImpl
RWFields --> WriteImpl
ReadOnly32 --> ROFields
ReadOnly64 --> ROFields
ReadOnlyNW --> ROFields

Sources: src/vmx/vmcs.rs(L85 - L486) 

Field Access Patterns

The system uses code generation macros to create consistent, type-safe access patterns for all VMCS fields. These macros handle the underlying vmread and vmwrite instructions while providing error handling and architecture-specific adaptations.

Read/Write Macro Implementation

flowchart TD
subgraph subGraph3["Architecture Handling"]
    Arch64["64-bit: Direct access"]
    Arch32["32-bit: Split high/low"]
end
subgraph subGraph2["Hardware Instructions"]
    VmreadInstr["vmx::vmread()"]
    VmwriteInstr["vmx::vmwrite()"]
end
subgraph subGraph1["Generated Methods"]
    ReadMethod[".read() -> AxResult<T>"]
    WriteMethod[".write(value: T) -> AxResult"]
end
subgraph subGraph0["Field Access Macros"]
    VmcsRead["vmcs_read! macro"]
    VmcsWrite["vmcs_write! macro"]
    DefineRO["define_vmcs_fields_ro!"]
    DefineRW["define_vmcs_fields_rw!"]
end

DefineRO --> ReadMethod
DefineRW --> ReadMethod
DefineRW --> WriteMethod
ReadMethod --> VmreadInstr
VmcsRead --> ReadMethod
VmcsWrite --> WriteMethod
VmreadInstr --> Arch32
VmreadInstr --> Arch64
VmwriteInstr --> Arch32
VmwriteInstr --> Arch64
WriteMethod --> VmwriteInstr

The vmcs_read! and vmcs_write! macros generate implementations that automatically handle 32-bit vs 64-bit architecture differences. On 32-bit systems, 64-bit fields require two separate hardware accesses to read the high and low portions.

Sources: src/vmx/vmcs.rs(L19 - L83) 

VM Exit Information Gathering

When a VM exit occurs, the hypervisor must gather information about why the exit happened and the guest's state. The VMCS provides read-only fields containing this information, which the system abstracts into structured data types.

sequenceDiagram
    participant GuestVM as "Guest VM"
    participant VMXHardware as "VMX Hardware"
    participant VMCSFields as "VMCS Fields"
    participant exit_info as "exit_info()"
    participant ExitHandler as "Exit Handler"

    GuestVM ->> VMXHardware: "Instruction causes VM exit"
    VMXHardware ->> VMCSFields: "Populate exit fields"
    Note over VMCSFields: EXIT_REASON<br>EXIT_QUALIFICATION<br>VMEXIT_INSTRUCTION_LEN<br>Guest RIP
    ExitHandler ->> exit_info: "Call exit_info()"
    exit_info ->> VMCSFields: "VmcsReadOnly32::EXIT_REASON.read()"
    exit_info ->> VMCSFields: "VmcsReadOnly32::VMEXIT_INSTRUCTION_LEN.read()"
    exit_info ->> VMCSFields: "VmcsGuestNW::RIP.read()"
    exit_info ->> ExitHandler: "VmxExitInfo struct"
    ExitHandler ->> ExitHandler: "Process exit reason"

Exit Information Structures

The system defines several structured types for different categories of VM exit information:

StructurePurposeKey Fields
VmxExitInfoGeneral exit informationexit_reason,entry_failure,guest_rip
VmxInterruptInfoInterrupt/exception detailsvector,int_type,err_code
VmxIoExitInfoI/O instruction exitsport,access_size,is_in
CrAccessInfoControl register accesscr_number,access_type,gpr

Sources: src/vmx/vmcs.rs(L488 - L582)  src/vmx/vmcs.rs(L645 - L774) 

Control Field Management

Control fields determine VM behavior and require careful management to ensure compatibility with the underlying hardware. The set_control() function implements the Intel-specified algorithm for safely setting control bits while respecting hardware capabilities.

flowchart TD
subgraph subGraph1["Capability Check"]
    MSRRead["capability_msr.read()"]
    Allowed0["allowed0 = cap[31:0](must be 1)"]
    Allowed1["allowed1 = cap[63:32](may be 1)"]
    Flexible["flexible = !allowed0 & allowed1"]
end
subgraph subGraph0["Control Setting Algorithm"]
    Input["set_control(control, msr, old, set, clear)"]
    ReadMSR["Read capability MSR"]
    ExtractBits["Extract allowed0/allowed1"]
    ValidateConflict["Validate set & clear don't conflict"]
    ValidateAllowed1["Validate set bits allowed in allowed1"]
    ValidateAllowed0["Validate clear bits allowed in allowed0"]
    CalculateValue["Calculate final value:fixed1 | default | set"]
    WriteVMCS["Write to VMCS field"]
end

Allowed0 --> Flexible
Allowed1 --> Flexible
CalculateValue --> WriteVMCS
ExtractBits --> Allowed0
ExtractBits --> Allowed1
ExtractBits --> ValidateConflict
Flexible --> CalculateValue
Input --> ReadMSR
MSRRead --> ExtractBits
ReadMSR --> MSRRead
ValidateAllowed0 --> CalculateValue
ValidateAllowed1 --> ValidateAllowed0
ValidateConflict --> ValidateAllowed1

The algorithm follows Intel SDM Volume 3C, Section 31.5.1, Algorithm 3, ensuring that control bits are set correctly based on processor capabilities.

Sources: src/vmx/vmcs.rs(L589 - L631) 

Event Injection

The hypervisor can inject interrupts and exceptions into the guest using VMCS entry control fields. The inject_event() function handles the complex logic of setting up proper event injection based on the event type.

Event Injection Flow

flowchart TD
subgraph subGraph1["VMCS Fields Updated"]
    EntryErrCode["VMENTRY_EXCEPTION_ERR_CODE"]
    EntryInstrLen["VMENTRY_INSTRUCTION_LEN"]
    EntryIntInfo["VMENTRY_INTERRUPTION_INFO_FIELD"]
end
subgraph subGraph0["Event Injection Process"]
    InjectCall["inject_event(vector, err_code)"]
    DetermineType["Determine VmxInterruptionType from vector"]
    HandleError["Handle error code:- Required by vector type?- Use provided or exit code"]
    CreateInfo["Create VmxInterruptInfo"]
    WriteErrorCode["Write VMENTRY_EXCEPTION_ERR_CODE"]
    HandleSoft["For soft interrupts:Set VMENTRY_INSTRUCTION_LEN"]
    WriteIntInfo["Write VMENTRY_INTERRUPTION_INFO_FIELD"]
end

CreateInfo --> WriteErrorCode
DetermineType --> HandleError
HandleError --> CreateInfo
HandleSoft --> EntryInstrLen
HandleSoft --> WriteIntInfo
InjectCall --> DetermineType
WriteErrorCode --> EntryErrCode
WriteErrorCode --> HandleSoft
WriteIntInfo --> EntryIntInfo

The VmxInterruptInfo structure encodes the interrupt information according to Intel specifications, with specific bit fields for vector, interruption type, error code validity, and overall validity.

Sources: src/vmx/vmcs.rs(L677 - L694)  src/vmx/vmcs.rs(L515 - L534) 

Specialized Information Extraction

The system provides specialized functions for extracting detailed information from different types of VM exits:

I/O Exit Information

  • Function: io_exit_info()
  • Purpose: Extract port number, access size, direction for I/O instruction exits
  • Fields: Port number, access size (1-4 bytes), IN/OUT direction, string/repeat flags

EPT Violation Information

  • Function: ept_violation_info()
  • Purpose: Extract guest physical address and access type for memory violations
  • Returns: NestedPageFaultInfo with fault address and access flags

Control Register Access

  • Function: cr_access_info()
  • Purpose: Decode control register access attempts (MOV, CLTS, LMSW)
  • Fields: CR number, access type, GPR involved, LMSW source data

EFER Management

  • Function: update_efer()
  • Purpose: Handle guest IA32_EFER updates, particularly long mode transitions
  • Actions: Set LONG_MODE_ACTIVE bit, update VM-entry controls

Sources: src/vmx/vmcs.rs(L696 - L774) 

Per-CPU VMX State

Relevant source files

This document covers the per-CPU VMX (Virtual Machine Extensions) state management system, which handles processor-specific initialization, enablement, and disablement of Intel's VMX virtualization technology. This system ensures that each CPU core in a multi-processor system can independently manage its VMX capabilities while maintaining proper hardware validation and state tracking.

For information about VMCS field management and virtual machine control structures, see VMCS Field Management. For details about VMX data structures like VmxRegion, see VMX Data Structures.

VmxPerCpuState Structure

The VmxPerCpuState<H: AxVCpuHal> struct serves as the central management entity for VMX state on individual processor cores. It maintains the essential components needed for VMX operation including hardware compatibility information and memory regions.

flowchart TD
subgraph subGraph2["Hardware Abstractions"]
    AxVCpuHal["AxVCpuHal traitHardware abstraction layer"]
    VmxRegionImpl["VmxRegion implementation4KB aligned memory"]
end
subgraph subGraph1["AxArchPerCpu Trait Implementation"]
    NewMethod["new(cpu_id: usize)Initialize per-CPU state"]
    IsEnabled["is_enabled()Check VMX status"]
    HwEnable["hardware_enable()Enable VMX on CPU"]
    HwDisable["hardware_disable()Disable VMX on CPU"]
end
subgraph subGraph0["VmxPerCpuState Structure"]
    VmxPerCpu["VmxPerCpuState<H>"]
    RevisionId["vmcs_revision_id: u32VMCS compatibility identifier"]
    VmxRegion["vmx_region: VmxRegion<H>Memory region for VMXON"]
end

VmxPerCpu --> HwDisable
VmxPerCpu --> HwEnable
VmxPerCpu --> IsEnabled
VmxPerCpu --> NewMethod
VmxPerCpu --> RevisionId
VmxPerCpu --> VmxRegion
VmxRegion --> VmxRegionImpl
VmxRegionImpl --> AxVCpuHal

Sources: src/vmx/percpu.rs(L17 - L29) 

The structure contains two primary fields:

FieldTypePurpose
vmcs_revision_idu32VMCS revision identifier ensuring software-hardware compatibility
vmx_regionVmxRegionMemory region used for VMXON instruction execution

Hardware Enable Process

The hardware_enable() method implements a comprehensive VMX initialization sequence that validates hardware support, configures control registers, and executes the VMXON instruction to enter VMX operation mode.

sequenceDiagram
    participant Caller as "Caller"
    participant VmxPerCpuState as "VmxPerCpuState"
    participant CPUHardware as "CPU Hardware"
    participant ModelSpecificRegisters as "Model Specific Registers"
    participant ControlRegisters as "Control Registers"

    Caller ->> VmxPerCpuState: hardware_enable()
    VmxPerCpuState ->> CPUHardware: has_hardware_support()
    alt No VMX support
        CPUHardware -->> VmxPerCpuState: false
        VmxPerCpuState -->> Caller: AxError::Unsupported
    end
    VmxPerCpuState ->> ControlRegisters: Check CR4.VMXE
    alt Already enabled
        ControlRegisters -->> VmxPerCpuState: VMX enabled
        VmxPerCpuState -->> Caller: AxError::ResourceBusy
    end
    VmxPerCpuState ->> VmxPerCpuState: XState::enable_xsave()
    VmxPerCpuState ->> ModelSpecificRegisters: Read FeatureControl MSR
    VmxPerCpuState ->> ModelSpecificRegisters: Enable VMXON if unlocked
    VmxPerCpuState ->> ModelSpecificRegisters: Validate CR0/CR4 against VMX_FIXED MSRs
    alt Invalid control registers
        ModelSpecificRegisters -->> VmxPerCpuState: Validation failed
        VmxPerCpuState -->> Caller: AxError::BadState
    end
    VmxPerCpuState ->> ModelSpecificRegisters: Read IA32_VMX_BASIC
    VmxPerCpuState ->> VmxPerCpuState: Validate VMX capabilities
    VmxPerCpuState ->> VmxPerCpuState: Create VmxRegion with revision_id
    VmxPerCpuState ->> ControlRegisters: Set CR4.VMXE
    VmxPerCpuState ->> CPUHardware: vmxon(vmx_region.phys_addr())
    CPUHardware -->> VmxPerCpuState: Success
    VmxPerCpuState -->> Caller: Ok(())

Sources: src/vmx/percpu.rs(L43 - L117) 

Hardware Validation Steps

The enable process performs several critical validation steps:

  1. VMX Support Detection: Verifies CPU supports VMX extensions src/vmx/percpu.rs(L44 - L46) 
  2. Feature Control MSR: Enables VMXON capability in IA32_FEATURE_CONTROL src/vmx/percpu.rs(L55 - L64) 
  3. Control Register Validation: Ensures CR0 and CR4 comply with VMX requirements src/vmx/percpu.rs(L67 - L81) 
  4. VMX Basic Capabilities: Validates memory type, addressing, and control features src/vmx/percpu.rs(L84 - L99) 

Control Register Validation

The implementation uses a macro-based approach to validate control registers against VMX fixed MSRs:

flowchart TD
subgraph subGraph1["Control Register Validation"]
    CR0["CR0 Register"]
    ValidationMacro["cr_is_valid! macro(!fixed0 | value != 0) && (fixed1 | !value != 0)"]
    CR4["CR4 Register"]
    subgraph subGraph0["VMX Fixed MSRs"]
        Fixed0["IA32_VMX_CR0_FIXED0Must be set bits"]
        Fixed1["IA32_VMX_CR0_FIXED1Must be clear bits"]
        Fixed0_CR4["IA32_VMX_CR4_FIXED0"]
        Fixed1_CR4["IA32_VMX_CR4_FIXED1"]
    end
end

CR0 --> ValidationMacro
CR4 --> ValidationMacro
Fixed0 --> ValidationMacro
Fixed0_CR4 --> ValidationMacro
Fixed1 --> ValidationMacro
Fixed1_CR4 --> ValidationMacro

Sources: src/vmx/percpu.rs(L67 - L81) 

Hardware Disable Process

The hardware_disable() method provides a safe teardown sequence for VMX operation, executing VMXOFF and restoring the processor to non-VMX state.

flowchart TD
Start["hardware_disable()"]
CheckEnabled["Check is_enabled()"]
Error["Return BadState error"]
VmxOff["Execute vmxoff instruction"]
ClearCR4["Clear CR4.VMXE bit"]
UninitRegion["vmx_region = VmxRegion::uninit()"]
Success["Return Ok(())"]
VmxOffError["Return BadState error"]

CheckEnabled --> Error
CheckEnabled --> VmxOff
ClearCR4 --> UninitRegion
Start --> CheckEnabled
UninitRegion --> Success
VmxOff --> ClearCR4
VmxOff --> VmxOffError

Sources: src/vmx/percpu.rs(L119 - L140) 

State Management and Lifecycle

The VmxPerCpuState follows a clear lifecycle pattern that ensures proper resource management and state transitions.

MethodPurposeKey Operations
new()Initialize uninitialized stateSetsvmcs_revision_idto 0, creates uninitializedVmxRegion
is_enabled()Check current VMX statusReads CR4.VMXE flag
hardware_enable()Enable VMX on current CPUHardware validation, VMXON execution, state setup
hardware_disable()Disable VMX on current CPUVMXOFF execution, CR4 cleanup, resource deallocation

Sources: src/vmx/percpu.rs(L31 - L140) 

Integration with Hardware Abstraction

The per-CPU VMX state integrates with the broader hardware abstraction system through the AxVCpuHal trait and AxArchPerCpu interface.

flowchart TD
subgraph subGraph1["Hardware Abstraction Integration"]
    AxArchPerCpu["AxArchPerCpu traitGeneric per-CPU interface"]
    VmxPerCpuImpl["VmxPerCpuState<H>VMX-specific implementation"]
    AxVCpuHal["AxVCpuHal traitHardware abstraction layer"]
    subgraph Dependencies["Dependencies"]
        VmxRegionDep["VmxRegion<H>Memory management"]
        FeatureControl["FeatureControl MSRHardware feature control"]
        VmxBasic["VmxBasic MSRVMX capabilities"]
        HasSupport["has_hardware_support()Hardware detection"]
    end
end

AxArchPerCpu --> VmxPerCpuImpl
VmxPerCpuImpl --> AxVCpuHal
VmxPerCpuImpl --> FeatureControl
VmxPerCpuImpl --> HasSupport
VmxPerCpuImpl --> VmxBasic
VmxPerCpuImpl --> VmxRegionDep
VmxRegionDep --> AxVCpuHal

Sources: src/vmx/percpu.rs(L1 - L11)  src/vmx/percpu.rs(L31 - L37) 

The system leverages several key abstractions:

  • AxArchPerCpu: Provides a generic interface for per-CPU architecture-specific state
  • AxVCpuHal: Hardware abstraction layer for memory management and physical addressing
  • VmxRegion: RAII-managed memory regions for VMX operations

This design enables the VMX per-CPU state to operate independently on each processor core while maintaining consistent interfaces for higher-level hypervisor components.

Memory Management

Relevant source files

Purpose and Scope

This document covers the memory management systems in the x86_vcpu hypervisor library, which handle both host physical memory allocation and guest memory virtualization. The memory management architecture provides two primary functions: RAII-based physical frame allocation for VMX data structures and Extended Page Table (EPT) support for guest memory virtualization.

For details on how memory management integrates with specific VMX structures, see VMX Data Structures. For information about the hardware abstraction layer that enables memory management, see Physical Frame Management.

Memory Management Architecture

The memory management system operates at two distinct levels: host physical memory management through the PhysFrame abstraction, and guest memory virtualization through EPT mechanisms.

Memory Management Overview

flowchart TD
subgraph subGraph3["External Systems"]
    GuestOS["Guest Operating System"]
    HostOS["Host Operating System"]
end
subgraph subGraph2["VMX Memory Consumers"]
    VmxRegion["VmxRegion"]
    IOBitmap["IOBitmap"]
    MsrBitmap["MsrBitmap"]
end
subgraph subGraph1["Guest Memory Virtualization"]
    GuestPageWalkInfo["GuestPageWalkInfo"]
    EPTPointer["EPT Pointer"]
end
subgraph subGraph0["Host Memory Management"]
    AxVCpuHal["AxVCpuHal Trait"]
    PhysFrame["PhysFrame<H>"]
end

AxVCpuHal --> PhysFrame
GuestOS --> GuestPageWalkInfo
GuestPageWalkInfo --> EPTPointer
HostOS --> AxVCpuHal
IOBitmap --> PhysFrame
MsrBitmap --> PhysFrame
VmxRegion --> PhysFrame

Sources: src/frame.rs(L1 - L63)  src/ept.rs(L1 - L28) 

Physical Memory Abstraction

The PhysFrame<H: AxVCpuHal> struct provides RAII-based management of 4KB physical memory pages. Each frame is automatically deallocated when dropped, preventing memory leaks in the hypervisor.

PhysFrame Lifecycle Management

flowchart TD
subgraph Deallocation["Deallocation"]
    drop["Drop::drop()"]
    dealloc_frame["H::dealloc_frame()"]
end
subgraph subGraph2["Access Methods"]
    start_paddr["start_paddr()"]
    as_mut_ptr["as_mut_ptr()"]
    fill["fill(byte)"]
end
subgraph subGraph1["PhysFrame State"]
    allocated["Allocated Framestart_paddr: Some(addr)"]
    uninitialized["Uninitialized Framestart_paddr: None"]
end
subgraph subGraph0["Allocation Methods"]
    alloc["PhysFrame::alloc()"]
    alloc_zero["PhysFrame::alloc_zero()"]
    uninit["PhysFrame::uninit()"]
end

alloc --> allocated
alloc_zero --> allocated
allocated --> as_mut_ptr
allocated --> drop
allocated --> fill
allocated --> start_paddr
drop --> dealloc_frame
uninit --> uninitialized

Sources: src/frame.rs(L18 - L62) 

Hardware Abstraction Interface

The AxVCpuHal trait defines the interface that must be implemented by the underlying system to support physical memory operations:

MethodPurposeReturn Type
alloc_frame()Allocate a 4KB physical frameOption
dealloc_frame(addr)Deallocate a physical frame()
phys_to_virt(addr)Convert physical to virtual addressVirtAddr

Sources: src/frame.rs(L6)  src/frame.rs(L20 - L21)  src/frame.rs(L47)  src/frame.rs(L58) 

Guest Memory Virtualization

Guest memory virtualization is handled through the Extended Page Tables (EPT) mechanism and guest page walk information tracking.

Guest Page Walk Information Structure

flowchart TD
subgraph subGraph1["Access Control Flags"]
    nxe["nxe: boolMSR_IA32_EFER_NXE_BIT"]
    width["width: u32Page table width"]
    subgraph subGraph3["Security Features"]
        is_smap_on["is_smap_on: boolSupervisor mode access prevention"]
        is_smep_on["is_smep_on: boolSupervisor mode execution protection"]
        pse["pse: boolCR4.PSE for 32bit paging"]
        wp["wp: boolCR0.WP"]
        is_user_mode["is_user_mode_access: bool"]
        is_write["is_write_access: bool"]
        is_inst_fetch["is_inst_fetch: bool"]
        top_entry["top_entry: usizeTop level paging structure"]
        level["level: usizePage table level"]
    end
end
subgraph subGraph0["GuestPageWalkInfo Fields"]
    subgraph subGraph2["Paging Control Flags"]
        is_smap_on["is_smap_on: boolSupervisor mode access prevention"]
        is_smep_on["is_smep_on: boolSupervisor mode execution protection"]
        pse["pse: boolCR4.PSE for 32bit paging"]
        wp["wp: boolCR0.WP"]
        nxe["nxe: boolMSR_IA32_EFER_NXE_BIT"]
        is_user_mode["is_user_mode_access: bool"]
        is_write["is_write_access: bool"]
        is_inst_fetch["is_inst_fetch: bool"]
        top_entry["top_entry: usizeTop level paging structure"]
        level["level: usizePage table level"]
        width["width: u32Page table width"]
    end
end

Sources: src/ept.rs(L3 - L27) 

Memory Management Integration

The memory management system integrates with VMX structures by providing the underlying physical memory backing for virtualization data structures.

VMX Structure Memory Dependencies

VMX StructureMemory RequirementPurpose
VmxRegion1xPhysFrameVMXON/VMCS memory region
IOBitmap2xPhysFrameI/O port interception bitmap (64KB)
MsrBitmap1xPhysFrameMSR access control bitmap (4KB)

The PAGE_SIZE_4K constant from the memory_addr crate defines the standard 4KB frame size used throughout the system src/frame.rs(L8) 

Memory Safety Guarantees

The PhysFrame implementation provides several safety guarantees:

  1. RAII Management: Automatic deallocation on drop prevents memory leaks src/frame.rs(L55 - L62) 
  2. Initialization Validation: start_paddr() panics on uninitialized frames src/frame.rs(L42 - L44) 
  3. Zero-fill Support: alloc_zero() provides zero-initialized frames src/frame.rs(L29 - L33) 
  4. Address Validation: Allocation asserts non-zero physical addresses src/frame.rs(L22) 

Sources: src/frame.rs(L10 - L16)  src/frame.rs(L18 - L53)  src/frame.rs(L55 - L62) 

Physical Frame Management

Relevant source files

Purpose and Scope

The Physical Frame Management system provides RAII-based allocation and management of 4KB-aligned physical memory pages for VMX operations. This system abstracts physical memory allocation through the AxVCpuHal trait and ensures automatic cleanup through Rust's ownership model. The PhysFrame<H> type serves as the fundamental building block for VMX data structures including VMCS regions, I/O bitmaps, and MSR bitmaps.

For information about how these physical frames are used in Extended Page Tables, see Extended Page Tables and Guest Memory. For details on the broader VMX data structures that consume these frames, see VMX Data Structures.

Core Architecture

PhysFrame RAII Wrapper

The PhysFrame<H: AxVCpuHal> struct provides a type-safe wrapper around 4KB physical memory pages with automatic resource management:

flowchart TD
subgraph subGraph2["Trait Implementations"]
    Drop["Drop::drop()"]
    Debug["Debug"]
end
subgraph subGraph1["Core Methods"]
    Alloc["alloc()"]
    AllocZero["alloc_zero()"]
    Uninit["unsafe uninit()"]
    StartPaddrMethod["start_paddr()"]
    AsMutPtr["as_mut_ptr()"]
    Fill["fill(byte: u8)"]
end
subgraph subGraph0["PhysFrame Structure"]
    PhysFrameStruct["PhysFrame<H>"]
    StartPaddr["start_paddr: Option<HostPhysAddr>"]
    Marker["_marker: PhantomData<H>"]
end

PhysFrameStruct --> Alloc
PhysFrameStruct --> AllocZero
PhysFrameStruct --> AsMutPtr
PhysFrameStruct --> Debug
PhysFrameStruct --> Drop
PhysFrameStruct --> Fill
PhysFrameStruct --> Marker
PhysFrameStruct --> StartPaddr
PhysFrameStruct --> StartPaddrMethod
PhysFrameStruct --> Uninit

Sources: src/frame.rs(L12 - L16)  src/frame.rs(L18 - L53) 

Hardware Abstraction Layer

The AxVCpuHal trait defines the interface between the frame allocator and the underlying platform:

flowchart TD
subgraph subGraph1["PhysFrame Operations"]
    PhysFrameAlloc["PhysFrame::alloc()"]
    PhysFrameDrop["PhysFrame::drop()"]
    PhysFrameAccess["PhysFrame::as_mut_ptr()"]
end
subgraph subGraph0["AxVCpuHal Trait Interface"]
    AxVCpuHal["AxVCpuHal"]
    AllocFrame["alloc_frame() -> Option<HostPhysAddr>"]
    DeallocFrame["dealloc_frame(paddr: HostPhysAddr)"]
    PhysToVirt["phys_to_virt(paddr: HostPhysAddr) -> VirtAddr"]
end

AxVCpuHal --> AllocFrame
AxVCpuHal --> DeallocFrame
AxVCpuHal --> PhysToVirt
PhysFrameAccess --> PhysToVirt
PhysFrameAlloc --> AllocFrame
PhysFrameDrop --> DeallocFrame

Sources: src/frame.rs(L6)  src/frame.rs(L20 - L21)  src/frame.rs(L47)  src/frame.rs(L58) 

Memory Allocation Lifecycle

Allocation Methods

The system provides three allocation strategies:

MethodPurposeZero-filledSafety
alloc()Standard allocationNoSafe
alloc_zero()Zero-initialized allocationYesSafe
uninit()Uninitialized frameN/AUnsafe

Sources: src/frame.rs(L19 - L27)  src/frame.rs(L29 - L33)  src/frame.rs(L35 - L40) 

Automatic Deallocation

The Drop implementation ensures memory is automatically returned to the system:

flowchart TD
subgraph subGraph1["Drop Implementation Details"]
    CheckPaddr["Check start_paddr.is_some()"]
    CallDealloc["H::dealloc_frame(start_paddr)"]
    LogDealloc["debug! log deallocation"]
end
subgraph subGraph0["RAII Lifecycle"]
    Creation["PhysFrame::alloc()"]
    Usage["Memory Usage Phase"]
    Destruction["PhysFrame goes out of scope"]
    DropCall["Drop::drop() called"]
    Deallocation["HAL::dealloc_frame()"]
end

CallDealloc --> LogDealloc
CheckPaddr --> CallDealloc
Creation --> Usage
Destruction --> DropCall
DropCall --> CheckPaddr
DropCall --> Deallocation
Usage --> Destruction

Sources: src/frame.rs(L55 - L62) 

Memory Access Patterns

Address Translation and Access

The frame system provides both physical and virtual access to allocated memory:

flowchart TD
subgraph subGraph2["HAL Translation"]
    PhysToVirt["phys_to_virt()"]
end
subgraph subGraph1["PhysFrame Access Methods"]
    StartPaddr["start_paddr()"]
    AsMutPtr["as_mut_ptr()"]
    Fill["fill(byte)"]
end
subgraph subGraph0["Address Types"]
    HostPhysAddr["HostPhysAddr"]
    VirtAddr["VirtAddr"]
    MutPtr["*mut u8"]
end

AsMutPtr --> PhysToVirt
Fill --> AsMutPtr
PhysToVirt --> VirtAddr
StartPaddr --> HostPhysAddr
VirtAddr --> MutPtr

Sources: src/frame.rs(L42 - L44)  src/frame.rs(L46 - L48)  src/frame.rs(L50 - L52) 

Memory Operations

The system supports direct memory manipulation through safe abstractions:

OperationMethodSafetyPurpose
Get physical addressstart_paddr()SafeVMX structure setup
Get virtual pointeras_mut_ptr()SafeDirect memory access
Fill with patternfill(byte)SafeZero-initialization

Integration with VMX Components

VMX Data Structure Usage

Physical frames serve as the foundation for critical VMX data structures:

flowchart TD
subgraph PhysFrame&lt;H&gt;["PhysFrame<H>"]
    CoreFrame["PhysFrame<H>4KB alignedRAII managed"]
end
subgraph subGraph1["Frame Requirements"]
    VmxReq["1x 4KB frameVMXON/VMCS region"]
    IOReq["2x 4KB framesPort 0-0xFFFF bitmap"]
    MsrReq["1x 4KB frameMSR access bitmap"]
    EPTReq["Multiple 4KB framesPage table hierarchy"]
end
subgraph subGraph0["PhysFrame Consumers"]
    VmxRegion["VmxRegion"]
    IOBitmap["IOBitmap"]
    MsrBitmap["MsrBitmap"]
    EPTStructures["EPT Page Tables"]
end

EPTReq --> CoreFrame
EPTStructures --> EPTReq
IOBitmap --> IOReq
IOReq --> CoreFrame
MsrBitmap --> MsrReq
MsrReq --> CoreFrame
VmxRegion --> VmxReq
VmxReq --> CoreFrame

Sources: src/frame.rs(L8)  (PAGE_SIZE_4K constant)

Error Handling

The allocation system uses AxResult<T> for error propagation:

flowchart TD
subgraph subGraph0["Error Flow"]
    AllocCall["PhysFrame::alloc()"]
    HALCall["H::alloc_frame()"]
    HALResult["Option<HostPhysAddr>"]
    ErrorCheck["Check for None"]
    ErrorResult["Err(NoMemory)"]
    SuccessResult["Ok(PhysFrame)"]
end

AllocCall --> HALCall
ErrorCheck --> ErrorResult
ErrorCheck --> SuccessResult
HALCall --> HALResult
HALResult --> ErrorCheck

Sources: src/frame.rs(L19 - L27)  src/frame.rs(L4)  (AxResult import)

Constants and Configuration

The system uses standardized page sizing:

ConstantValueSourcePurpose
PAGE_SIZEPAGE_SIZE_4Kmemory_addrcrateStandard frame size
Frame alignment4KBHardware requirementVMX compatibility

Sources: src/frame.rs(L8) 

Extended Page Tables and Guest Memory

Relevant source files

Purpose and Scope

This document covers the Extended Page Tables (EPT) implementation and guest memory virtualization mechanisms in the x86_vcpu hypervisor. EPT provides hardware-assisted second-level address translation, enabling efficient memory virtualization for guest virtual machines. The document focuses on the GuestPageWalkInfo structure and how guest memory access information is captured and processed.

For physical memory allocation and the underlying frame management system, see Physical Frame Management. For overall VMX virtual machine management that utilizes EPT, see Virtual CPU Management.

Extended Page Tables Overview

Extended Page Tables (EPT) is Intel's hardware-assisted memory virtualization technology that provides a second level of address translation. In a virtualized environment, memory accesses undergo a two-stage translation process:

  1. Guest Virtual → Guest Physical: Performed by the guest OS page tables
  2. Guest Physical → Host Physical: Performed by EPT page tables managed by the hypervisor

This two-level translation allows guests to manage their own virtual memory while the hypervisor maintains control over physical memory allocation.

Memory Translation Architecture

flowchart TD
subgraph subGraph2["Hardware Translation"]
    MMU["Memory Management Unit"]
    PageWalk["Hardware Page Walk"]
end
subgraph subGraph1["Hypervisor Memory Management"]
    EPTTables["EPT Page Tables"]
    HostPA["Host Physical Address"]
    PhysFrame["PhysFrame Management"]
end
subgraph subGraph0["Guest Virtual Machine"]
    GuestVA["Guest Virtual Address"]
    GuestPT["Guest Page Tables"]
    GuestPA["Guest Physical Address"]
end
GuestPageWalkInfo["GuestPageWalkInfoCapture access details"]

EPTTables --> HostPA
GuestPA --> EPTTables
GuestPA --> PageWalk
GuestPT --> GuestPA
GuestVA --> GuestPT
HostPA --> PhysFrame
MMU --> HostPA
PageWalk --> GuestPageWalkInfo
PageWalk --> MMU

Sources: src/ept.rs(L1 - L28) 

Guest Page Walk Information

The GuestPageWalkInfo structure captures detailed information about guest memory access attempts, particularly during EPT violations (nested page faults). This information is essential for the hypervisor to understand the guest's memory access patterns and handle them appropriately.

GuestPageWalkInfo Structure

flowchart TD
subgraph subGraph3["GuestPageWalkInfo Fields"]
    TopEntry["top_entry: usizeTop level paging structure entry"]
    Level["level: usizeGuest page table level"]
    Width["width: u32Guest page table width"]
    subgraph subGraph2["Security Features"]
        SMAP["is_smap_on: boolSupervisor Mode Access Prevention"]
        SMEP["is_smep_on: boolSupervisor Mode Execution Prevention"]
    end
    subgraph subGraph1["Control Register State"]
        PSE["pse: boolCR4.PSE for 32bit paging"]
        WP["wp: boolCR0.WP write protection"]
        NXE["nxe: boolMSR_IA32_EFER_NXE_BIT"]
    end
    subgraph subGraph0["Access Type Information"]
        UserMode["is_user_mode_access: boolUser vs kernel access"]
        WriteAccess["is_write_access: boolRead vs write operation"]
        InstFetch["is_inst_fetch: boolInstruction fetch access"]
    end
end
AccessInfo["Memory Access Context"]

InstFetch --> AccessInfo
Level --> AccessInfo
NXE --> AccessInfo
PSE --> AccessInfo
SMAP --> AccessInfo
SMEP --> AccessInfo
TopEntry --> AccessInfo
UserMode --> AccessInfo
WP --> AccessInfo
Width --> AccessInfo
WriteAccess --> AccessInfo

Sources: src/ept.rs(L3 - L27) 

Key Fields and Their Purpose

FieldPurposeUsage Context
top_entryPhysical address of the top-level page tableIdentifies the root of guest page table hierarchy
levelCurrent page table level being accessedDetermines page size and translation depth
widthPage table entry width (32/64-bit)Architecture-specific page table format
is_user_mode_accessUser vs supervisor mode accessPrivilege level validation
is_write_accessRead vs write operationAccess permission checking
is_inst_fetchInstruction fetch vs data accessExecution permission validation
psePage Size Extension enabledSupport for large pages (2MB/4MB)
wpWrite Protection enabledCR0.WP supervisor write protection
nxeNo-Execute bit enabledExecution prevention support
is_smap_onSMAP protection activePrevents supervisor access to user pages
is_smep_onSMEP protection activePrevents supervisor execution of user pages

Sources: src/ept.rs(L4 - L26) 

EPT Integration with VMX System

flowchart TD
subgraph subGraph2["Physical Memory"]
    PhysFrame["PhysFrame"]
    AxVCpuHal["AxVCpuHal Trait"]
    HostMemory["Host Physical Memory"]
end
subgraph subGraph1["Memory Management Layer"]
    EPTPointer["EPT Pointer(VMCS Field)"]
    EPTPageTables["EPT Page Tables"]
    GuestPageWalkInfo["GuestPageWalkInfo"]
end
subgraph subGraph0["VMX Virtual Machine"]
    VmxVcpu["VmxVcpu"]
    VMExit["VM Exit Handler"]
    EPTViolation["EPT Violation Handler"]
end
note1["EPT violations trigger VM exitswith GuestPageWalkInfo details"]

AxVCpuHal --> HostMemory
EPTPageTables --> PhysFrame
EPTPointer --> EPTPageTables
EPTViolation --> GuestPageWalkInfo
EPTViolation --> note1
GuestPageWalkInfo --> VMExit
PhysFrame --> AxVCpuHal
VMExit --> EPTViolation
VmxVcpu --> EPTPointer

Sources: src/ept.rs(L1 - L28) 

Memory Access Handling Flow

The GuestPageWalkInfo structure plays a crucial role in handling memory access violations and implementing memory virtualization policies:

EPT Violation Processing

  1. Guest Memory Access: Guest attempts to access memory through its virtual address space
  2. Hardware Translation: MMU performs guest virtual → guest physical translation
  3. EPT Lookup: Hardware attempts guest physical → host physical translation via EPT
  4. EPT Violation: If EPT entry is missing or access violates permissions, VM exit occurs
  5. Information Capture: Hardware populates GuestPageWalkInfo with access details
  6. Hypervisor Handling: VMX exit handler processes the violation using captured information

Security Feature Integration

The structure captures several x86 security features that affect memory access:

  • SMAP (Supervisor Mode Access Prevention): Prevents supervisor code from accessing user-mode pages
  • SMEP (Supervisor Mode Execution Prevention): Prevents supervisor code from executing user-mode pages
  • NXE (No-Execute): Enables execution prevention for marked pages
  • WP (Write Protection): Controls supervisor write access to read-only pages

These features are essential for maintaining security boundaries in virtualized environments.

Sources: src/ept.rs(L19 - L26) 

Current Implementation Status

The current EPT implementation in the codebase provides the foundational data structure for capturing guest page walk information. The GuestPageWalkInfo struct serves as the interface between the hardware EPT violation mechanism and the hypervisor's memory management logic.

The minimal implementation suggests that:

  • EPT page table management may be handled in other components
  • The focus is on capturing and processing EPT violation information
  • Integration with the broader VMX system occurs through other modules

For the complete memory virtualization picture, this EPT information works in conjunction with the physical frame management system documented in Physical Frame Management and the VMX virtual machine control structures covered in VMX Data Structures.

Sources: src/ept.rs(L1 - L28) 

Supporting Systems

Relevant source files

This document covers the foundational support systems that enable VMX functionality in the x86_vcpu hypervisor library. These systems provide low-level abstractions for register management, Model-Specific Register (MSR) access, VMX type definitions, and instruction wrappers that are consumed by the core VMX virtualization engine.

For information about the main VMX virtualization components, see VMX Virtualization Engine. For memory management abstractions, see Memory Management.

Architecture Overview

The supporting systems form the foundation layer beneath the VMX virtualization engine, providing essential abstractions for hardware interaction and type safety.

flowchart TD
subgraph subGraph6["Hardware Layer"]
    X86Hardware["x86_64 Hardware"]
    VmxInstructions["VMX Instructions"]
    Msrs["Model-Specific Registers"]
end
subgraph subGraph5["Supporting Systems Layer"]
    subgraph subGraph4["VMX Instructions"]
        InveptWrapper["invept wrapper"]
        VmxStatusHandling["VMX status handling"]
    end
    subgraph subGraph3["VMX Definitions"]
        VmxExitReason["VmxExitReason enum"]
        VmxInterruptionType["VmxInterruptionType enum"]
        VmxInstructionError["VmxInstructionError struct"]
    end
    subgraph subGraph2["MSR Access"]
        MsrEnum["Msr enum"]
        MsrReadWrite["MsrReadWrite trait"]
        RdmsrWrapper["rdmsr wrapper"]
        WrmsrWrapper["wrmsr wrapper"]
    end
    subgraph subGraph1["Register Management"]
        GeneralRegisters["GeneralRegisters"]
        SaveRegsMacro["save_regs_to_stack!"]
        RestoreRegsMacro["restore_regs_from_stack!"]
    end
end
subgraph subGraph0["VMX Virtualization Engine"]
    VmxVcpu["VmxVcpu"]
    VmxModule["vmx::mod"]
    VmcsManager["vmcs management"]
end

GeneralRegisters --> RestoreRegsMacro
GeneralRegisters --> SaveRegsMacro
InveptWrapper --> VmxInstructions
MsrEnum --> RdmsrWrapper
MsrEnum --> WrmsrWrapper
MsrReadWrite --> MsrEnum
RdmsrWrapper --> Msrs
RestoreRegsMacro --> X86Hardware
SaveRegsMacro --> X86Hardware
VmcsManager --> VmxInstructionError
VmxModule --> VmxExitReason
VmxVcpu --> GeneralRegisters
VmxVcpu --> MsrEnum
WrmsrWrapper --> Msrs

Sources: src/regs.rs(L1 - L197)  src/msr.rs(L1 - L74)  src/vmx/definitions.rs(L1 - L275) 

Register Management System

The register management system provides abstractions for handling x86-64 general-purpose registers and register state transitions during VM exits and entries.

GeneralRegisters Structure

The GeneralRegisters struct represents the complete set of x86-64 general-purpose registers in a VM context:

RegisterPurposeIndex
raxReturn values, accumulator0
rcxCounter, function arguments1
rdxI/O operations, function arguments2
rbxBase pointer, preserved across calls3
rbpFrame pointer5
rsiSource index for string operations6
rdiDestination index for string operations7
r8-r15Additional 64-bit registers8-15

The structure excludes rsp (index 4) as it requires special handling during context switches. The get_reg_of_index() and set_reg_of_index() methods provide indexed access to registers, enabling dynamic register manipulation based on instruction operands.

Register Save/Restore Macros

flowchart TD
subgraph subGraph2["Stack Layout"]
    StackTop["Stack Top"]
    RaxSlot["rax"]
    RcxSlot["rcx"]
    RdxSlot["rdx"]
    RbxSlot["rbx"]
    RspGap["8-byte gap (rsp)"]
    RbpSlot["rbp"]
    OtherRegs["rsi, rdi, r8-r15"]
    StackBottom["Stack Bottom"]
end
subgraph subGraph1["VM Entry Flow"]
    RestoreMacro["restore_regs_from_stack!"]
    VmEntry["VM Entry Event"]
    RegistersRestored["Registers Restored"]
end
subgraph subGraph0["VM Exit Flow"]
    VmExit["VM Exit Event"]
    SaveMacro["save_regs_to_stack!"]
    StackState["Register State on Stack"]
end

OtherRegs --> StackBottom
RaxSlot --> RcxSlot
RbpSlot --> OtherRegs
RbxSlot --> RspGap
RcxSlot --> RdxSlot
RdxSlot --> RbxSlot
RestoreMacro --> VmEntry
RspGap --> RbpSlot
SaveMacro --> StackState
StackState --> RestoreMacro
StackState --> StackTop
StackTop --> RaxSlot
VmEntry --> RegistersRestored
VmExit --> SaveMacro

The macros implement precise stack-based register preservation:

  • save_regs_to_stack! pushes registers in reverse order (r15 to rax)
  • An 8-byte gap maintains stack alignment where rsp would be stored
  • restore_regs_from_stack! pops registers in forward order (rax to r15)

Sources: src/regs.rs(L1 - L197) 

MSR Access System

The MSR access system provides type-safe abstractions for reading and writing Model-Specific Registers critical to VMX operation.

MSR Enumeration

The Msr enum defines VMX-related MSRs with their canonical addresses:

flowchart TD
subgraph subGraph3["MSR Access Methods"]
    ReadMethod["Msr::read()"]
    WriteMethod["Msr::write()"]
    RdmsrCall["x86::msr::rdmsr()"]
    WrmsrCall["x86::msr::wrmsr()"]
end
subgraph subGraph2["System MSRs"]
    FeatureControl["IA32_FEATURE_CONTROL (0x3a)"]
    Efer["IA32_EFER (0xc0000080)"]
    Pat["IA32_PAT (0x277)"]
    FsBase["IA32_FS_BASE (0xc0000100)"]
    GsBase["IA32_GS_BASE (0xc0000101)"]
end
subgraph subGraph0["VMX Control MSRs"]
    PinbasedMsr["IA32_VMX_PINBASED_CTLS (0x481)"]
    ProcbasedMsr["IA32_VMX_PROCBASED_CTLS (0x482)"]
    ExitMsr["IA32_VMX_EXIT_CTLS (0x483)"]
    EntryMsr["IA32_VMX_ENTRY_CTLS (0x484)"]
    subgraph subGraph1["VMX Capability MSRs"]
        MiscMsr["IA32_VMX_MISC (0x485)"]
        Cr0Fixed0["IA32_VMX_CR0_FIXED0 (0x486)"]
        Cr0Fixed1["IA32_VMX_CR0_FIXED1 (0x487)"]
        Cr4Fixed0["IA32_VMX_CR4_FIXED0 (0x488)"]
        Cr4Fixed1["IA32_VMX_CR4_FIXED1 (0x489)"]
        EptVpidCap["IA32_VMX_EPT_VPID_CAP (0x48c)"]
        BasicMsr["IA32_VMX_BASIC (0x480)"]
    end
end

BasicMsr --> ReadMethod
FeatureControl --> WriteMethod
ReadMethod --> RdmsrCall
WriteMethod --> WrmsrCall

MsrReadWrite Trait Pattern

The MsrReadWrite trait provides a type-safe pattern for MSR access:

trait MsrReadWrite {
    const MSR: Msr;
    fn read_raw() -> u64;
    unsafe fn write_raw(flags: u64);
}

This pattern enables implementing types to associate with specific MSRs while providing consistent read/write interfaces. The trait methods delegate to the underlying Msr::read() and Msr::write() implementations.

Sources: src/msr.rs(L1 - L74) 

VMX Type Definitions

The VMX definitions provide comprehensive type safety for VMX exit handling and interrupt processing.

Exit Reason Classification

flowchart TD
subgraph subGraph3["Control/Memory Exits (28-49)"]
    CrAccess["CR_ACCESS (28)"]
    DrAccess["DR_ACCESS (29)"]
    IoInstruction["IO_INSTRUCTION (30)"]
    MsrRead["MSR_READ (31)"]
    MsrWrite["MSR_WRITE (32)"]
    EptViolation["EPT_VIOLATION (48)"]
    EptMisconfig["EPT_MISCONFIG (49)"]
end
subgraph subGraph2["Instruction Exits (10-27)"]
    CpuidExit["CPUID (10)"]
    HltExit["HLT (12)"]
    VmcallExit["VMCALL (18)"]
    VmxInstructionExits["VMX Instructions (19-26)"]
end
subgraph subGraph1["Exception/Interrupt (0-8)"]
    ExceptionNmi["EXCEPTION_NMI (0)"]
    ExternalInt["EXTERNAL_INTERRUPT (1)"]
    TripleFault["TRIPLE_FAULT (2)"]
    InitSignal["INIT (3)"]
    SipiSignal["SIPI (4)"]
end
subgraph subGraph0["VmxExitReason Categories"]
    ExceptionCategory["Exception/Interrupt Exits"]
    InstructionCategory["Instruction-Based Exits"]
    ControlCategory["Control Register Exits"]
    SystemCategory["System Management Exits"]
end

ControlCategory --> CrAccess
ControlCategory --> EptViolation
ExceptionCategory --> ExceptionNmi
ExceptionCategory --> ExternalInt
InstructionCategory --> CpuidExit
InstructionCategory --> VmcallExit

Interruption Type System

The VmxInterruptionType enum classifies interrupt and exception events for VM-entry and VM-exit processing:

TypeValueDescriptionError Code
External0External hardware interruptNo
NMI2Non-maskable interruptNo
HardException3Hardware exception (#PF, #GP)Some vectors
SoftIntr4Software interrupt (INT n)No
PrivSoftException5Privileged software exception (INT1)No
SoftException6Software exception (INT3, INTO)No

The vector_has_error_code() method determines if specific exception vectors push error codes, while from_vector() automatically classifies interruption types based on vector numbers.

Error Handling

The VmxInstructionError struct provides human-readable descriptions for VMX instruction failures, mapping error codes to detailed explanations per the Intel SDM specifications.

Sources: src/vmx/definitions.rs(L1 - L275) 

Integration Patterns

The supporting systems integrate with the core VMX engine through several key patterns:

flowchart TD
subgraph subGraph3["Integration Flow"]
    VmxVcpu["VmxVcpu"]
    subgraph subGraph2["Exit Processing"]
        ExitReasonCheck["exit_reason matching"]
        InterruptClassify["interruption classification"]
        ErrorDiagnostics["instruction error handling"]
    end
    subgraph subGraph1["MSR Integration"]
        MsrOperations["MSR read/write operations"]
        VmcsFieldAccess["VMCS field access"]
        ControlRegSetup["Control register setup"]
    end
    subgraph subGraph0["Register Integration"]
        GuestRegs["guest_registers: GeneralRegisters"]
        RegAccess["get_reg_of_index()"]
        RegModify["set_reg_of_index()"]
    end
end

ExitReasonCheck --> ErrorDiagnostics
ExitReasonCheck --> InterruptClassify
GuestRegs --> RegAccess
GuestRegs --> RegModify
MsrOperations --> ControlRegSetup
MsrOperations --> VmcsFieldAccess
VmxVcpu --> ExitReasonCheck
VmxVcpu --> GuestRegs
VmxVcpu --> MsrOperations

The supporting systems provide the essential building blocks that enable the VMX virtualization engine to:

  • Maintain guest register state across VM transitions
  • Configure VMX controls through MSR capabilities
  • Process VM exits with type-safe reason classification
  • Handle errors with detailed diagnostic information

Sources: src/regs.rs(L1 - L197)  src/msr.rs(L1 - L74)  src/vmx/definitions.rs(L1 - L275) 

Register Management

Relevant source files

Purpose and Scope

The register management system provides abstractions for handling x86-64 general-purpose registers within the hypervisor context. This module defines the GeneralRegisters structure for storing and manipulating register state, along with assembly macros for efficient stack-based register preservation. These components are essential for managing guest CPU state during VM exits and entries in the VMX virtualization engine.

For information about model-specific register (MSR) access, see MSR Access. For VMX-specific register handling during VM exits, see Virtual CPU Management.

GeneralRegisters Structure

The GeneralRegisters struct provides a C-compatible representation of the x86-64 general-purpose register set. This structure serves as the primary container for guest register state during hypervisor operations.

flowchart TD
subgraph subGraph1["Key Features"]
    CRepr["#[repr(C)]C ABI Compatible"]
    Debug["#[derive(Debug)]Debug output"]
    Default["#[derive(Default)]Zero initialization"]
    Clone["#[derive(Clone)]Copy semantics"]
end
subgraph subGraph0["GeneralRegisters Structure"]
    RAX["rax: u64Return values"]
    RCX["rcx: u64Loop counter"]
    RDX["rdx: u64I/O operations"]
    RBX["rbx: u64Base pointer"]
    RSP["_unused_rsp: u64(Padding)"]
    RBP["rbp: u64Frame pointer"]
    RSI["rsi: u64Source index"]
    RDI["rdi: u64Dest index"]
    R8["r8: u64GP register"]
    R9["r9: u64GP register"]
    R10["r10: u64GP register"]
    R11["r11: u64GP register"]
    R12["r12: u64GP register"]
    R13["r13: u64GP register"]
    R14["r14: u64GP register"]
    R15["r15: u64GP register"]
end

CRepr --> RAX
Clone --> RAX
Debug --> RAX
Default --> RAX

Register Layout and Characteristics:

RegisterPurposeIndexNotes
raxReturn values, accumulator0Primary return register
rcxLoop counter, 4th argument1Often used in loops
rdxI/O operations, 3rd argument2Data register
rbxBase pointer, callee-saved3Preserved across calls
_unused_rspStack pointer (unused)4Excluded from direct access
rbpFrame pointer, callee-saved5Stack frame base
rsiSource index, 2nd argument6String operations source
rdiDestination index, 1st argument7String operations destination
r8-r15Extended registers8-15Additional 64-bit registers

Sources: src/regs.rs(L1 - L40) 

Index-Based Register Access

The GeneralRegisters struct provides index-based access methods that map numerical indices to specific registers, following the x86-64 register encoding scheme used in instruction operands.

flowchart TD
subgraph subGraph1["Access Methods"]
    GetReg["get_reg_of_index(index: u8) -> u64"]
    SetReg["set_reg_of_index(index: u8, value: u64)"]
end
subgraph subGraph0["Register Index Mapping"]
    I0["Index 0"]
    RAX["rax"]
    I1["Index 1"]
    RCX["rcx"]
    I2["Index 2"]
    RDX["rdx"]
    I3["Index 3"]
    RBX["rbx"]
    I4["Index 4"]
    SKIP["(unused - rsp)"]
    I5["Index 5"]
    RBP["rbp"]
    I6["Index 6"]
    RSI["rsi"]
    I7["Index 7"]
    RDI["rdi"]
    I8["Index 8"]
    R8["r8"]
    I9["Index 9"]
    R9["r9"]
    I10["Index 10"]
    R10["r10"]
    I11["Index 11"]
    R11["r11"]
    I12["Index 12"]
    R12["r12"]
    I13["Index 13"]
    R13["r13"]
    I14["Index 14"]
    R14["r14"]
    I15["Index 15"]
    R15["r15"]
end

GetReg --> SetReg
I0 --> GetReg
I0 --> RAX
I1 --> RCX
I10 --> R10
I11 --> R11
I12 --> R12
I13 --> R13
I14 --> R14
I15 --> R15
I2 --> RDX
I3 --> RBX
I4 --> SKIP
I5 --> RBP
I6 --> RSI
I7 --> RDI
I8 --> R8
I9 --> R9

Key Implementation Details:

  • Index Range: Valid indices are 0-15, excluding index 4 (RSP)
  • Panic Behavior: Both methods panic on invalid indices or attempts to access RSP
  • Performance: Direct match statements provide efficient O(1) access
  • Safety: Index validation prevents undefined behavior

Usage Pattern:

// Reading a register by index
let rax_value = registers.get_reg_of_index(0);  // Gets RAX

// Writing a register by index  
registers.set_reg_of_index(8, 0x12345678);     // Sets R8

Sources: src/regs.rs(L42 - L152) 

Stack-Based Register Preservation

The module provides assembly macros for efficient bulk register save and restore operations, essential for hypervisor context switching and interrupt handling.

sequenceDiagram
    participant HypervisorCode as "Hypervisor Code"
    participant StackMemory as "Stack Memory"
    participant CPURegisters as "CPU Registers"

    Note over HypervisorCode: save_regs_to_stack!()
    HypervisorCode ->> StackMemory: push r15
    HypervisorCode ->> StackMemory: push r14
    HypervisorCode ->> StackMemory: push r13
    HypervisorCode ->> StackMemory: push r12
    HypervisorCode ->> StackMemory: push r11
    HypervisorCode ->> StackMemory: push r10
    HypervisorCode ->> StackMemory: push r9
    HypervisorCode ->> StackMemory: push r8
    HypervisorCode ->> StackMemory: push rdi
    HypervisorCode ->> StackMemory: push rsi
    HypervisorCode ->> StackMemory: push rbp
    HypervisorCode ->> StackMemory: sub rsp, 8 (skip rsp)
    HypervisorCode ->> StackMemory: push rbx
    HypervisorCode ->> StackMemory: push rdx
    HypervisorCode ->> StackMemory: push rcx
    HypervisorCode ->> StackMemory: push rax
    Note over HypervisorCode: Critical section executes
    Note over HypervisorCode: restore_regs_from_stack!()
    StackMemory ->> CPURegisters: pop rax
    StackMemory ->> CPURegisters: pop rcx
    StackMemory ->> CPURegisters: pop rdx
    StackMemory ->> CPURegisters: pop rbx
    HypervisorCode ->> HypervisorCode: add rsp, 8 (skip rsp)
    StackMemory ->> CPURegisters: pop rbp
    StackMemory ->> CPURegisters: pop rsi
    StackMemory ->> CPURegisters: pop rdi
    StackMemory ->> CPURegisters: pop r8
    StackMemory ->> CPURegisters: pop r9
    StackMemory ->> CPURegisters: pop r10
    StackMemory ->> CPURegisters: pop r11
    StackMemory ->> CPURegisters: pop r12
    StackMemory ->> CPURegisters: pop r13
    StackMemory ->> CPURegisters: pop r14
    StackMemory ->> CPURegisters: pop r15

Macro Implementation:

MacroPurposeStack OperationsRSP Handling
save_regs_to_stack!Preserve all GP registers15 push operations + 1 subtractsub rsp, 8to skip RSP slot
restore_regs_from_stack!Restore all GP registers15 pop operations + 1 addadd rsp, 8to skip RSP slot

Key Features:

  • Order Preservation: Restore operations reverse the save order exactly
  • RSP Handling: Stack pointer excluded from save/restore, space reserved with arithmetic
  • Atomicity: Complete register set saved/restored as a unit
  • Performance: Inline assembly provides minimal overhead

Sources: src/regs.rs(L154 - L196) 

Integration with VMX System

The register management components integrate tightly with the VMX virtualization engine to support guest state management during VM transitions.

flowchart TD
subgraph subGraph2["VMX Components"]
    VmxVcpu["VmxVcpu"]
    VMCS["VMCS Fields"]
    ExitHandler["Exit Handlers"]
end
subgraph subGraph1["Register Operations"]
    SaveMacro["save_regs_to_stack!()"]
    RestoreMacro["restore_regs_from_stack!()"]
    GeneralRegs["GeneralRegisters"]
    IndexAccess["Index-based Access"]
end
subgraph subGraph0["VMX Context Usage"]
    VmExit["VM Exit Handler"]
    VmEntry["VM Entry Preparation"]
    GuestState["Guest Register State"]
    HostState["Host Register State"]
end

ExitHandler --> IndexAccess
GeneralRegs --> IndexAccess
GuestState --> GeneralRegs
RestoreMacro --> HostState
SaveMacro --> GuestState
VMCS --> GuestState
VmEntry --> RestoreMacro
VmExit --> SaveMacro
VmxVcpu --> VmEntry
VmxVcpu --> VmExit

Integration Points:

  • VM Exit Processing: Register state captured using stack macros during VM exits
  • Guest State Management: GeneralRegisters stores guest CPU state between VM operations
  • Instruction Emulation: Index-based access enables register modification for emulated instructions
  • Context Switching: Stack preservation ensures host state integrity during guest execution

Performance Considerations:

  • Zero-Copy: Direct register field access avoids unnecessary copying
  • Cache Efficiency: C-compatible layout optimizes memory access patterns
  • Assembly Integration: Macros provide efficient bulk operations for critical paths

Sources: src/regs.rs(L1 - L196) 

Model-Specific Register Access

Relevant source files

This document covers the Model-Specific Register (MSR) access abstraction layer in the x86_vcpu hypervisor library. MSRs are processor-specific configuration and status registers that control various aspects of CPU behavior, particularly important for VMX virtualization features.

The MSR access system provides a type-safe abstraction over x86 MSR operations, with a focus on VMX-related registers and other MSRs essential for hypervisor operation. For information about VMX-specific data structures and configuration, see VMX Data Structures. For details about VMCS field management, see VMCS Field Management.

MSR Access Architecture

The MSR access system is built around two main components: the Msr enum that defines specific MSR constants, and the MsrReadWrite trait that provides a higher-level abstraction for typed MSR access.

flowchart TD
subgraph subGraph3["VMX Usage"]
    VmxInit["VMX InitializationFeature Detection"]
    VmxConfig["VMX ConfigurationControl Settings"]
    GuestState["Guest StateBase Registers"]
end
subgraph subGraph2["Hardware Interface"]
    X86Crate["x86 craterdmsr() / wrmsr()"]
    Hardware["x86 MSR HardwarePhysical Registers"]
end
subgraph subGraph1["MSR Access Layer"]
    MsrEnum["Msr enumMSR Definitions"]
    MsrReadWriteTrait["MsrReadWrite traitType-safe Access"]
    subgraph subGraph0["Core Methods"]
        ReadMethod["read()Safe MSR Read"]
        WriteMethod["write()Unsafe MSR Write"]
        ReadRaw["read_raw()Trait Default"]
        WriteRaw["write_raw()Trait Default"]
    end
end

GuestState --> MsrEnum
MsrEnum --> ReadMethod
MsrEnum --> WriteMethod
MsrReadWriteTrait --> ReadRaw
MsrReadWriteTrait --> WriteRaw
ReadMethod --> X86Crate
ReadRaw --> ReadMethod
VmxConfig --> MsrEnum
VmxInit --> MsrEnum
WriteMethod --> X86Crate
WriteRaw --> WriteMethod
X86Crate --> Hardware

Sources: src/msr.rs(L1 - L74) 

MSR Definitions and Categories

The Msr enum defines specific MSR constants organized by functional categories. Each MSR is represented as a variant with its corresponding hardware register number.

VMX Control and Capability MSRs

The largest category of MSRs relates to VMX virtualization capabilities and control settings:

MSR NameValuePurpose
IA32_VMX_BASIC0x480Basic VMX capabilities
IA32_VMX_PINBASED_CTLS0x481Pin-based VM execution controls
IA32_VMX_PROCBASED_CTLS0x482Primary processor-based controls
IA32_VMX_EXIT_CTLS0x483VM exit controls
IA32_VMX_ENTRY_CTLS0x484VM entry controls
IA32_VMX_PROCBASED_CTLS20x48bSecondary processor-based controls
IA32_VMX_EPT_VPID_CAP0x48cEPT and VPID capabilities
flowchart TD
subgraph subGraph5["VMX MSR Categories"]
    subgraph subGraph2["True Controls"]
        TruePinBased["IA32_VMX_TRUE_PINBASED_CTLS0x48d"]
        TrueProcBased["IA32_VMX_TRUE_PROCBASED_CTLS0x48e"]
        TrueExit["IA32_VMX_TRUE_EXIT_CTLS0x48f"]
        TrueEntry["IA32_VMX_TRUE_ENTRY_CTLS0x490"]
    end
    subgraph subGraph1["Control Capabilities"]
        PinBased["IA32_VMX_PINBASED_CTLS0x481"]
        ProcBased["IA32_VMX_PROCBASED_CTLS0x482"]
        ProcBased2["IA32_VMX_PROCBASED_CTLS20x48b"]
        ExitCtls["IA32_VMX_EXIT_CTLS0x483"]
        EntryCtls["IA32_VMX_ENTRY_CTLS0x484"]
    end
    subgraph subGraph0["Basic VMX Info"]
        VmxBasic["IA32_VMX_BASIC0x480Core VMX Features"]
        VmxMisc["IA32_VMX_MISC0x485Miscellaneous Data"]
    end
    subgraph subGraph4["Memory Virtualization"]
        EptVpid["IA32_VMX_EPT_VPID_CAP0x48cEPT & VPID Features"]
        Cr0Fixed0["IA32_VMX_CR0_FIXED00x486"]
    end
    subgraph subGraph3["Fixed Settings"]
        EptVpid["IA32_VMX_EPT_VPID_CAP0x48cEPT & VPID Features"]
        Cr0Fixed0["IA32_VMX_CR0_FIXED00x486"]
        Cr0Fixed1["IA32_VMX_CR0_FIXED10x487"]
        Cr4Fixed0["IA32_VMX_CR4_FIXED00x488"]
        Cr4Fixed1["IA32_VMX_CR4_FIXED10x489"]
    end
end

EntryCtls --> TrueEntry
ExitCtls --> TrueExit
PinBased --> TruePinBased
ProcBased --> ProcBased2
ProcBased --> TrueProcBased
VmxBasic --> EntryCtls
VmxBasic --> ExitCtls
VmxBasic --> PinBased
VmxBasic --> ProcBased

Sources: src/msr.rs(L8 - L28) 

System Configuration MSRs

Additional MSRs handle general x86 system configuration:

  • Feature Control: IA32_FEATURE_CONTROL (0x3a) - Controls processor feature enablement
  • Memory Management: IA32_PAT (0x277) - Page Attribute Table configuration
  • Extended State: IA32_XSS (0xda0) - Extended State Save configuration

Long Mode and Segment MSRs

MSRs for 64-bit mode operation and segment base addresses:

  • Mode Control: IA32_EFER (0xc0000080) - Extended Feature Enable Register
  • System Call: IA32_STAR, IA32_LSTAR, IA32_CSTAR, IA32_FMASK - System call configuration
  • Segment Bases: IA32_FS_BASE, IA32_GS_BASE, IA32_KERNEL_GSBASE - Segment base addresses

Sources: src/msr.rs(L8 - L40) 

MSR Access Interface

The Msr enum provides direct read and write methods for MSR access:

Read Operations

The read() method provides safe MSR reading:

#![allow(unused)]
fn main() {
pub fn read(self) -> u64
}

This method is marked as #[inline(always)] for performance and uses the x86 crate's rdmsr function internally. Reading MSRs is generally safe as it does not modify processor state.

Write Operations

The write() method provides unsafe MSR writing:

#![allow(unused)]
fn main() {
pub unsafe fn write(self, value: u64)
}

Writing to MSRs is marked as unsafe because incorrect values can cause system instability, crashes, or security vulnerabilities. The caller must ensure that the write operation has no unsafe side effects.

Sources: src/msr.rs(L42 - L59) 

MsrReadWrite Trait

The MsrReadWrite trait provides a higher-level abstraction for types that represent specific MSR functionality:

flowchart TD
subgraph subGraph3["MsrReadWrite Trait Pattern"]
    TraitDef["MsrReadWrite traitGeneric MSR Access"]
    subgraph subGraph2["Implementing Types"]
        VmxStructs["VMX Control StructuresType-safe Access"]
        OtherTypes["Other MSR WrappersDomain-specific Logic"]
    end
    subgraph subGraph1["Default Implementations"]
        ReadRawMethod["read_raw() -> u64Self::MSR.read()"]
        WriteRawMethod["write_raw(flags: u64)Self::MSR.write(flags)"]
    end
    subgraph subGraph0["Required Definition"]
        MsrConst["const MSR: MsrAssociated MSR"]
    end
end

OtherTypes --> TraitDef
ReadRawMethod --> MsrConst
TraitDef --> MsrConst
TraitDef --> ReadRawMethod
TraitDef --> WriteRawMethod
VmxStructs --> TraitDef
WriteRawMethod --> MsrConst

The trait defines:

  • Associated MSR: const MSR: Msr - Links the implementing type to a specific MSR
  • Raw Read: read_raw() -> u64 - Default implementation calls Self::MSR.read()
  • Raw Write: unsafe write_raw(flags: u64) - Default implementation calls Self::MSR.write(flags)

This pattern allows creating type-safe wrappers around specific MSRs while maintaining the same underlying access interface.

Sources: src/msr.rs(L61 - L73) 

Integration with VMX System

The MSR access system integrates closely with the VMX virtualization engine:

flowchart TD
subgraph subGraph1["Guest Configuration"]
    GuestBase["Guest Segment BasesIA32_*S_BASE"]
    GuestEfer["Guest EFERIA32_EFER"]
    GuestPat["Guest PATIA32_PAT"]
end
subgraph subGraph0["VMX Initialization Flow"]
    FeatureCheck["Feature DetectionIA32_FEATURE_CONTROL"]
    BasicCaps["Capability ReadingIA32_VMX_BASIC"]
    ControlCaps["Control CapabilitiesIA32_VMX_*_CTLS"]
    FixedBits["Fixed Bit RequirementsIA32_VMX_CR*_FIXED*"]
end

BasicCaps --> ControlCaps
ControlCaps --> FixedBits
FeatureCheck --> BasicCaps
FixedBits --> GuestBase
FixedBits --> GuestEfer
FixedBits --> GuestPat

The MSR system enables:

  1. Hardware Capability Discovery - Reading VMX capability MSRs to determine supported features
  2. Control Register Validation - Using fixed bit MSRs to validate CR0/CR4 settings
  3. Guest State Management - Configuring guest segment bases and control registers
  4. Feature Control - Managing processor feature enablement through dedicated MSRs

Sources: src/msr.rs(L8 - L40) 

VMX Definitions and Types

Relevant source files

Purpose and Scope

This document covers the core VMX (Virtual Machine Extensions) definitions and enumerations that form the foundation of Intel VMX virtualization. These types define the various exit reasons, error conditions, and interrupt classifications used throughout the VMX virtualization engine.

For information about how these definitions are used in practice, see Virtual CPU Management and VMCS Field Management. For low-level VMX instruction wrappers that utilize these types, see VMX Instructions.

VM Instruction Error Types

The VmxInstructionError struct provides human-readable descriptions for VMX instruction failures. VMX operations can fail for various reasons, and this type maps the numeric error codes to descriptive strings.

Error Classification

VMX Instruction Error Handling

flowchart TD
subgraph subGraph1["Error Categories"]
    StateErrors["State Errors• VMLAUNCH with non-clear VMCS• VMRESUME with non-launched VMCS• VMXON in VMX root operation"]
    AddressErrors["Address Errors• VMCLEAR with invalid address• VMPTRLD with invalid address• VMCLEAR with VMXON pointer"]
    ConfigErrors["Configuration Errors• Invalid control fields• Invalid host-state fields• Read-only VMCS component write"]
    AccessErrors["Access Errors• Unsupported VMCS component• Invalid operand to INVEPT/INVVPID• VMREAD/VMWRITE errors"]
end
subgraph subGraph0["VMX Operation Flow"]
    VmxOp["VMX Instruction"]
    Success["Operation Success"]
    Error["VmxInstructionError"]
    ErrorCode["u32 Error Code"]
    ErrorStr["Human Readable String"]
end

Error --> AccessErrors
Error --> AddressErrors
Error --> ConfigErrors
Error --> ErrorCode
Error --> StateErrors
ErrorCode --> ErrorStr
VmxOp --> Error
VmxOp --> Success

The error mapping covers 28 distinct error conditions defined in the Intel SDM Volume 3C, Section 30.4. Key error categories include:

Error TypeExamplesImpact
State ViolationsVMLAUNCH with non-clear VMCS (4)VM entry failure
Address IssuesInvalid physical addresses (2, 18)Memory access violations
ConfigurationInvalid control fields (7, 8)VM setup failure
Access ControlRead-only component write (13)VMCS corruption prevention

Sources: src/vmx/definitions.rs(L3 - L60) 

VM Exit Reasons

The VmxExitReason enum defines all possible reasons for VM exits in Intel VMX. This comprehensive enumeration covers 70 distinct exit reasons ranging from basic exceptions to advanced virtualization features.

Exit Reason Categories

VM Exit Reason Classification

flowchart TD
subgraph subGraph1["Exit Handling Flow"]
    VmExit["VM Exit Occurs"]
    ExitReason["Read Exit Reason"]
    Handler["Route to Handler"]
    Builtin["Built-in Handler"]
    External["External Handler"]
end
subgraph subGraph0["VmxExitReason Categories"]
    Exceptions["Exception/Interrupt Exits• EXCEPTION_NMI (0)• EXTERNAL_INTERRUPT (1)• TRIPLE_FAULT (2)• INIT (3), SIPI (4)"]
    Instructions["Instruction-Specific Exits• CPUID (10)• HLT (12)• RDTSC (16)• VMCALL (18)• CR_ACCESS (28)• MSR_READ/WRITE (31/32)"]
    Virtualization["Virtualization Features• EPT_VIOLATION (48)• EPT_MISCONFIG (49)• APIC_ACCESS (44)• VIRTUALIZED_EOI (45)• PREEMPTION_TIMER (52)"]
    Advanced["Advanced Instructions• INVEPT (50)• INVVPID (53)• XSETBV (55)• XSAVES/XRSTORS (63/64)• ENCLS (60)"]
end

Advanced --> ExitReason
Exceptions --> ExitReason
ExitReason --> Handler
Handler --> Builtin
Handler --> External
Instructions --> ExitReason
Virtualization --> ExitReason
VmExit --> ExitReason

Common Exit Reasons

The most frequently handled exit reasons include:

  • CPUID (10): Guest queries processor capabilities
  • CR_ACCESS (28): Control register modifications
  • MSR_READ/WRITE (31/32): Model-specific register access
  • EPT_VIOLATION (48): Memory access violations in nested paging
  • IO_INSTRUCTION (30): I/O port access
  • HLT (12): CPU halt instruction

Sources: src/vmx/definitions.rs(L62 - L208) 

Interruption Types

The VmxInterruptionType enum classifies interrupt and exception types for both VM-Entry and VM-Exit interruption information fields as defined in Intel SDM Volume 3C, Sections 24.8.3 and 24.9.2.

Interruption Classification

VMX Interruption Type System

flowchart TD
subgraph subGraph2["Error Code Vectors"]
    ErrorVectors["Vectors with Error Codes• Double Fault (8)• Invalid TSS (10)• Segment Not Present (11)• Stack Fault (12)• General Protection (13)• Page Fault (14)• Alignment Check (17)"]
end
subgraph subGraph1["Vector Processing"]
    Vector["Interrupt Vector"]
    TypeDetermination["from_vector()"]
    ErrorCheck["vector_has_error_code()"]
    SoftCheck["is_soft()"]
end
subgraph subGraph0["VmxInterruptionType Variants"]
    External["External (0)Hardware interruptsVectors 32-255"]
    NMI["NMI (2)Non-maskable interruptVector 2"]
    HardException["HardException (3)CPU exceptionsVectors 0-21"]
    SoftIntr["SoftIntr (4)Software interruptINT n instruction"]
    PrivSoft["PrivSoftException (5)Privileged softwareINT1/Debug"]
    SoftException["SoftException (6)Software exceptionsINT3, INTO"]
    Other["Other (7)Special events"]
end

HardException --> ErrorVectors
PrivSoft --> SoftCheck
SoftException --> SoftCheck
SoftIntr --> SoftCheck
TypeDetermination --> External
TypeDetermination --> HardException
TypeDetermination --> NMI
TypeDetermination --> PrivSoft
TypeDetermination --> SoftException
TypeDetermination --> SoftIntr
Vector --> ErrorCheck
Vector --> TypeDetermination

Vector Determination Logic

The VmxInterruptionType provides several utility methods:

MethodPurposeReturn Type
from_vector(vector: u8)Determines interruption type from vector numberVmxInterruptionType
vector_has_error_code(vector: u8)Checks if vector pushes error codebool
is_soft()Identifies software-generated interruptsbool

Key classification rules:

  • Vectors 0-21: Hardware exceptions (except special cases)
  • Vector 1: Debug exception (privileged software)
  • Vector 2: NMI (non-maskable interrupt)
  • Vectors 3, 4: Software exceptions (INT3, INTO)
  • Vectors 32-255: External interrupts

Sources: src/vmx/definitions.rs(L210 - L275) 

Integration with VMX System

These definitions form the foundation for VMX operation throughout the hypervisor:

VMX Definitions Usage Flow

flowchart TD
subgraph subGraph2["Usage Scenarios"]
    VmExit["VM Exit Processing"]
    EventInject["Interrupt Injection"]
    ErrorHandle["Error Handling"]
    VmEntry["VM Entry Setup"]
end
subgraph subGraph1["Definition Types"]
    VmxExitReason["VmxExitReason"]
    VmxInterruptionType["VmxInterruptionType"]
    VmxInstructionError["VmxInstructionError"]
end
subgraph subGraph0["VMX Core Components"]
    VmxVcpu["VmxVcpu"]
    VmcsManager["VMCS Manager"]
    ExitHandler["Exit Handler"]
    EventInjection["Event Injection"]
end

ErrorHandle --> VmxInstructionError
EventInject --> VmxInterruptionType
EventInjection --> VmxInterruptionType
ExitHandler --> VmxExitReason
VmEntry --> VmxInterruptionType
VmExit --> VmxExitReason
VmcsManager --> VmxInstructionError
VmxVcpu --> ErrorHandle
VmxVcpu --> EventInject
VmxVcpu --> VmEntry
VmxVcpu --> VmExit

These enumerations are used extensively in:

  • Exit handling: Determining which handler to invoke based on VmxExitReason
  • Event injection: Setting up interruption information with VmxInterruptionType
  • Error reporting: Providing meaningful error messages via VmxInstructionError
  • VMCS configuration: Validating and setting control fields

Sources: src/vmx/definitions.rs(L1 - L275) 

VMX Instructions

Relevant source files

This document covers the low-level VMX instruction wrappers and hardware status handling mechanisms that provide primitive access to Intel VMX functionality. These instructions form the foundation for higher-level VMX operations throughout the hypervisor.

For broader VMX system functionality, see VMX Virtualization Engine. For VMX-specific data types and enumerations, see VMX Definitions and Types.

Purpose and Scope

The VMX instructions module provides direct wrappers around Intel VMX hardware instructions, focusing on proper status handling and type-safe interfaces. Currently, this module specifically implements Extended Page Table (EPT) invalidation through the INVEPT instruction and establishes patterns for VMX result handling that can be extended to other VMX instructions.

Sources: src/vmx/instructions.rs(L1 - L51) 

VMX Status Handling Mechanism

VMX instructions communicate their success or failure through CPU flags rather than traditional return values. The vmx_capture_status function implements the standard pattern for interpreting these flags according to Intel SDM conventions.

Status Interpretation Flow

flowchart TD
VMXInstr["VMX Instruction Execution"]
ReadFlags["vmx_capture_status()"]
CheckZF["Check Zero Flag (ZF)"]
CheckCF["Check Carry Flag (CF)"]
Success["Ok(())"]
VmFailValid["VmFail::VmFailValid"]
VmFailInvalid["VmFail::VmFailInvalid"]

CheckCF --> Success
CheckCF --> VmFailInvalid
CheckZF --> CheckCF
CheckZF --> VmFailValid
ReadFlags --> CheckZF
VMXInstr --> ReadFlags

The function uses #[inline(always)] to ensure no intervening code can clobber the RFLAGS register between instruction execution and status reading. This critical timing requirement prevents race conditions that could corrupt status information.

Flag StateResultMeaning
ZF = 1VmFail::VmFailValidInstruction failed with valid VMCS
CF = 1VmFail::VmFailInvalidInstruction failed with invalid VMCS
ZF = 0, CF = 0Ok(())Instruction succeeded

Sources: src/vmx/instructions.rs(L5 - L22) 

INVEPT Instruction Support

The INVEPT (Invalidate Extended Page Tables) instruction invalidates cached translations derived from EPT structures. This is essential for maintaining memory coherency when EPT mappings change.

INVEPT Types and Implementation


The invept function constructs an INVEPT descriptor containing the EPT pointer and executes the instruction through inline assembly. The descriptor format follows Intel SDM specifications with the EPTP in the first 64-bit word and a reserved zero in the second word.

INVEPT Operation Types

TypeValueScopeUse Case
SingleContext1Specific EPTP (bits 51:12)Invalidate translations for one guest
Global2All EPTPsSystem-wide invalidation

Sources: src/vmx/instructions.rs(L24 - L50) 

Integration with VMX System

The VMX instructions module serves as the hardware abstraction foundation for higher-level VMX operations throughout the hypervisor system.

VMX Instruction Integration Points

flowchart TD
subgraph subGraph2["External Dependencies"]
    X86Crate["x86 crateRFlags, VmFail types"]
    CoreAsm["core::arch::asm"]
end
subgraph subGraph1["VMX Instructions Layer"]
    InstructionsModule["instructions.rs"]
    InveptWrapper["invept()"]
    StatusHandler["vmx_capture_status()"]
end
subgraph subGraph0["High-Level VMX Components"]
    VmxVcpu["VmxVcpusrc/vmx/vcpu.rs"]
    EPTSystem["EPT Systemsrc/ept.rs"]
    VMCSManager["VMCS Managersrc/vmx/vmcs.rs"]
end

EPTSystem --> InveptWrapper
InveptWrapper --> CoreAsm
InveptWrapper --> StatusHandler
StatusHandler --> X86Crate
VMCSManager --> StatusHandler
VmxVcpu --> InveptWrapper

The instruction wrappers provide type-safe interfaces that prevent common errors in VMX programming:

  • Result Type Safety: All VMX instructions return Result<()> with standardized error handling
  • Descriptor Management: Complex instruction descriptors are constructed internally with correct formatting
  • Timing Guarantees: Critical timing requirements for status reading are enforced through compiler attributes

Sources: src/vmx/instructions.rs(L1 - L51) 

Extension Pattern for Additional VMX Instructions

The current implementation establishes patterns that can be extended for other VMX instructions:

  1. Status Handling: Use vmx_capture_status() after any VMX instruction
  2. Type Enums: Define instruction-specific enums with appropriate #[repr()] attributes
  3. Descriptor Construction: Build instruction descriptors as arrays with proper field ordering
  4. Inline Assembly: Use direct asm! macros with appropriate register constraints

This foundation enables safe, efficient access to the complete VMX instruction set while maintaining the hypervisor's type safety and performance requirements.

Sources: src/vmx/instructions.rs(L1 - L51) 

Development and Configuration

Relevant source files

This document covers the development environment setup, build configuration, and continuous integration pipeline for the x86_vcpu hypervisor library. It provides guidance on project dependencies, feature flags, build toolchain requirements, and the automated testing and documentation deployment processes.

For information about the VMX-specific code organization and module structure, see VMX Virtualization Engine. For details about the physical frame allocation interface that integrates with external systems, see Physical Frame Management.

Project Structure and Dependencies

The x86_vcpu library is configured as a Rust 2024 edition crate with carefully selected dependencies that support bare-metal x86_64 virtualization. The project uses a modular architecture with optional feature flags to support different virtualization technologies.

Dependency Architecture

flowchart TD
subgraph subGraph5["Interface System"]
    CrateInterface["crate_interface (0.1)"]
end
subgraph subGraph4["ArceOS Ecosystem"]
    AxErrno["axerrno (0.1.0)"]
    AxAddrspace["axaddrspace (git)"]
    AxVcpu["axvcpu (git)"]
end
subgraph subGraph3["Memory Management"]
    PageTableEntry["page_table_entry (0.5)"]
    MemoryAddr["memory_addr (0.3.1)"]
end
subgraph subGraph2["Utility Crates"]
    Log["log (0.4.19)"]
    CfgIf["cfg-if (1.0)"]
    Bitflags["bitflags (2.2)"]
    BitField["bit_field (0.10)"]
    NumericEnum["numeric-enum-macro (0.2)"]
end
subgraph subGraph1["Low-Level x86 Support"]
    X86["x86 (0.52)"]
    X86_64["x86_64 (0.15)"]
    RawCpuid["raw-cpuid (11.0)"]
end
subgraph subGraph0["x86_vcpu Core"]
    MainCrate["x86_vcpu(edition 2024)"]
end

MainCrate --> AxAddrspace
MainCrate --> AxErrno
MainCrate --> AxVcpu
MainCrate --> BitField
MainCrate --> Bitflags
MainCrate --> CfgIf
MainCrate --> CrateInterface
MainCrate --> Log
MainCrate --> MemoryAddr
MainCrate --> NumericEnum
MainCrate --> PageTableEntry
MainCrate --> RawCpuid
MainCrate --> X86
MainCrate --> X86_64

Sources: Cargo.toml(L6 - L22) 

Feature Configuration

The project supports conditional compilation through feature flags defined in the Cargo.toml configuration:

FeaturePurposeDefault
vmxEnables Intel VMX (Virtual Machine Extensions) support
amdEnables AMD SVM (Secure Virtual Machine) support

The default feature set includes only vmx, making Intel VMX the primary supported virtualization technology. The amd feature can be enabled for AMD SVM support in future implementations.

Sources: Cargo.toml(L24 - L27) 

Build System and Toolchain Requirements

The x86_vcpu library requires specific Rust toolchain configurations and targets to support bare-metal x86_64 development. The build process is designed for no-std environments with custom memory management.

Toolchain Configuration

The project uses Rust nightly toolchains with the following requirements:

  • Primary Toolchain: nightly-2024-12-25 (pinned for stability)
  • Development Toolchain: nightly (latest for development)
  • Target Architecture: x86_64-unknown-none (bare-metal)
  • Required Components: rust-src, clippy, rustfmt

Build Target Specifications

flowchart TD
subgraph subGraph3["Build Outputs"]
    LibraryBuild["x86_vcpu Library"]
    Documentation["API Documentation"]
end
subgraph subGraph2["Build Components"]
    RustSrc["rust-src(Core Library Source)"]
    Clippy["clippy(Linting)"]
    Rustfmt["rustfmt(Formatting)"]
end
subgraph subGraph1["Target Platform"]
    X86Target["x86_64-unknown-none(Bare Metal)"]
end
subgraph subGraph0["Rust Toolchain Matrix"]
    Nightly2024["nightly-2024-12-25(Stable Build)"]
    NightlyLatest["nightly(Development)"]
end

Clippy --> LibraryBuild
LibraryBuild --> Documentation
Nightly2024 --> X86Target
NightlyLatest --> X86Target
RustSrc --> LibraryBuild
Rustfmt --> LibraryBuild
X86Target --> Clippy
X86Target --> RustSrc
X86Target --> Rustfmt

Sources: .github/workflows/ci.yml(L11 - L19) 

Continuous Integration Pipeline

The CI/CD system uses GitHub Actions to ensure code quality, build verification, and automatic documentation deployment. The pipeline runs on multiple Rust toolchain versions to ensure compatibility.

CI Workflow Structure

flowchart TD
subgraph subGraph3["Documentation Job"]
    DocCheckout["actions/checkout@v4"]
    DocToolchain["dtolnay/rust-toolchainnightly-2024-12-25"]
    DocBuild["cargo doc --no-deps --all-features"]
    PagesDeploy["JamesIves/github-pages-deploy-action@v4"]
end
subgraph subGraph2["Build Pipeline Steps"]
    Checkout["actions/checkout@v4"]
    ToolchainSetup["dtolnay/rust-toolchain"]
    VersionCheck["rustc --version --verbose"]
    FormatCheck["cargo fmt --all -- --check"]
    ClippyLint["cargo clippy --target x86_64-unknown-none --all-features"]
    Build["cargo build --target x86_64-unknown-none --all-features"]
    UnitTest["cargo test --target x86_64-unknown-linux-gnu"]
end
subgraph subGraph1["CI Job Matrix"]
    Job1["ubuntu-latestnightly-2024-12-25x86_64-unknown-none"]
    Job2["ubuntu-latestnightlyx86_64-unknown-none"]
end
subgraph subGraph0["Trigger Events"]
    Push["git push"]
    PullRequest["pull_request"]
end

Build --> UnitTest
Checkout --> ToolchainSetup
ClippyLint --> Build
DocBuild --> PagesDeploy
DocCheckout --> DocToolchain
DocToolchain --> DocBuild
FormatCheck --> ClippyLint
Job1 --> Checkout
Job2 --> Checkout
PullRequest --> Job1
PullRequest --> Job2
Push --> DocCheckout
Push --> Job1
Push --> Job2
ToolchainSetup --> VersionCheck
VersionCheck --> FormatCheck

Sources: .github/workflows/ci.yml(L1 - L62) 

Quality Assurance Steps

The CI pipeline enforces several quality checks:

  1. Code Formatting: Uses cargo fmt with --check flag to ensure consistent code style
  2. Linting: Runs cargo clippy with --all-features and custom lint configuration
  3. Build Verification: Compiles the library for the target architecture
  4. Unit Testing: Executes tests when applicable target platforms are available

The pipeline includes error tolerance for the latest nightly toolchain to prevent CI failures from bleeding-edge compiler changes.

Sources: .github/workflows/ci.yml(L23 - L34) 

Documentation Deployment

The documentation system automatically builds and deploys API documentation to GitHub Pages:

  • Documentation Flags: -D rustdoc::broken_intra_doc_links -D missing-docs
  • Build Command: cargo doc --no-deps --all-features
  • Deployment: Automatic deployment to gh-pages branch on main branch updates
  • Index Generation: Creates redirect index.html pointing to the main crate documentation

Sources: .github/workflows/ci.yml(L36 - L61) 

Development Workflow

Local Development Setup

  1. Install Rust Toolchain:
rustup toolchain install nightly-2024-12-25
rustup component add rust-src clippy rustfmt --toolchain nightly-2024-12-25
rustup target add x86_64-unknown-none --toolchain nightly-2024-12-25
  1. Build Commands:
cargo +nightly-2024-12-25 build --target x86_64-unknown-none --all-features
cargo +nightly-2024-12-25 clippy --target x86_64-unknown-none --all-features
cargo +nightly-2024-12-25 fmt --all
  1. Documentation Generation:
cargo +nightly-2024-12-25 doc --no-deps --all-features --open

Ignored Files and Build Artifacts

The .gitignore configuration excludes build outputs and development artifacts:

  • Build Outputs: /target, *.asm, *.img, *.bin, *.elf
  • Log Files: actual.out, qemu.log
  • IDE Configuration: /.vscode
  • System Files: .DS_Store
  • Dependency Lock: Cargo.lock (ignored because x86_vcpu is a library)

Sources: .gitignore(L1 - L18) 

Project Configuration

Relevant source files

This document covers the project configuration, dependency management, and build requirements for the x86_vcpu hypervisor library. It explains the Cargo.toml configuration, feature flags for different virtualization technologies, and the crate interface requirements that users must implement.

For information about the build system and CI/CD pipeline, see Build System and CI.

Core Dependencies

The x86_vcpu crate is built upon a carefully selected set of dependencies that provide low-level x86 hardware access, memory management, and hypervisor framework integration.

Dependency Architecture

flowchart TD
subgraph subGraph5["ArceOS Hypervisor Framework"]
    axaddrspace["axaddrspace (git)"]
    axvcpu["axvcpu (git)"]
end
subgraph subGraph4["Interface System"]
    crate_interface["crate_interface = \0.1\"]
end
subgraph subGraph3["Memory Management"]
    page_table_entry["page_table_entry = \0.5\"]
    memory_addr["memory_addr = \0.3.1\"]
    axerrno["axerrno = \0.1.0\"]
end
subgraph subGraph2["Utility Crates"]
    log["log = \0.4.19\"]
    cfg_if["cfg-if = \1.0\"]
    bitflags["bitflags = \2.2\"]
    bit_field["bit_field = \0.10\"]
    numeric_enum["numeric-enum-macro = \0.2\"]
end
subgraph subGraph1["Low-Level x86 Support"]
    x86["x86 = \0.52\"]
    x86_64["x86_64 = \0.15\"]
    raw_cpuid["raw-cpuid = \11.0\"]
end
subgraph subGraph0["x86_vcpu Project"]
    Cargo["Cargo.toml"]
    LibMain["lib.rs"]
end

Cargo --> axaddrspace
Cargo --> axerrno
Cargo --> axvcpu
Cargo --> bit_field
Cargo --> bitflags
Cargo --> cfg_if
Cargo --> crate_interface
Cargo --> log
Cargo --> memory_addr
Cargo --> numeric_enum
Cargo --> page_table_entry
Cargo --> raw_cpuid
Cargo --> x86
Cargo --> x86_64
LibMain --> crate_interface
LibMain --> x86
LibMain --> x86_64

Sources: Cargo.toml(L6 - L22) 

Dependency Categories

CategoryCratesPurpose
x86 Hardwarex86,x86_64,raw-cpuidLow-level x86/x86_64 instruction access, CPUID feature detection
Bit Manipulationbitflags,bit_field,numeric-enum-macroHardware register field manipulation, flag definitions
Memory Managementpage_table_entry,memory_addr,axerrnoPage table abstractions, address types, error handling
Interface Systemcrate_interfaceTrait-based dependency injection for hardware abstraction
Hypervisor Frameworkaxaddrspace,axvcpuArceOS hypervisor ecosystem integration
Utilitieslog,cfg-ifLogging and conditional compilation

Sources: Cargo.toml(L6 - L22) 

Feature Flags

The crate uses feature flags to support different virtualization technologies while maintaining a single codebase.

Feature Configuration


Sources: Cargo.toml(L24 - L27) 

Available Features

FeatureDefaultDescription
vmxIntel VMX (Virtual Machine Extensions) support
amdAMD SVM (Secure Virtual Machine) support

The default feature set includes vmx, making Intel VMX the primary supported virtualization technology. Users can explicitly enable AMD support by specifying features = ["amd"] in their Cargo.toml or disable VMX with default-features = false.

Sources: Cargo.toml(L24 - L27) 

Crate Interface Requirements

The x86_vcpu crate requires users to implement the PhysFrameIf trait to provide hardware abstraction for physical memory management.

Interface Implementation Pattern

flowchart TD
subgraph subGraph3["Hardware Layer"]
    PhysicalMemory["Physical Memory"]
    VirtualMemory["Virtual Memory Mapping"]
end
subgraph subGraph2["Required Methods"]
    alloc_frame["alloc_frame() -> Option"]
    dealloc_frame["dealloc_frame(paddr: PhysAddr)"]
    phys_to_virt["phys_to_virt(paddr: PhysAddr) -> VirtAddr"]
end
subgraph subGraph1["x86_vcpu Crate"]
    PhysFrameIf["PhysFrameIf trait"]
    frame_rs["frame.rs"]
    PhysFrame["PhysFrame"]
end
subgraph subGraph0["User Application"]
    UserImpl["PhysFrameIfImpl struct"]
    ImplMacro["#[crate_interface::impl_interface]"]
end

ImplMacro --> PhysFrameIf
PhysFrameIf --> alloc_frame
PhysFrameIf --> dealloc_frame
PhysFrameIf --> frame_rs
PhysFrameIf --> phys_to_virt
UserImpl --> ImplMacro
alloc_frame --> PhysicalMemory
dealloc_frame --> PhysicalMemory
frame_rs --> PhysFrame
phys_to_virt --> VirtualMemory

Sources: README.md(L7 - L29) 

Implementation Requirements

Users must provide a concrete implementation of the PhysFrameIf trait using the crate_interface::impl_interface macro. The trait defines three essential methods:

  • alloc_frame(): Allocates a 4KB-aligned physical memory frame, returning Some(PhysAddr) on success or None if allocation fails
  • dealloc_frame(paddr: PhysAddr): Deallocates a previously allocated physical frame at the specified address
  • phys_to_virt(paddr: PhysAddr) -> VirtAddr: Converts a physical address to its corresponding virtual address in the current address space

Sources: README.md(L13 - L29) 

Example Implementation Structure

The README provides a template showing the required implementation pattern:

#![allow(unused)]
fn main() {
// From README.md:13-29
struct PhysFrameIfImpl;

#[crate_interface::impl_interface]
impl axvm::PhysFrameIf for PhysFrameIfImpl {
    fn alloc_frame() -> Option<PhysAddr> { /* implementation */ }
    fn dealloc_frame(paddr: PhysAddr) { /* implementation */ }
    fn phys_to_virt(paddr: PhysAddr) -> VirtAddr { /* implementation */ }
}
}

The implementation must handle physical memory allocation and mapping according to the host system's memory management requirements. A reference implementation can be found in the [ArceOS project](https://github.com/arceos-hypervisor/x86_vcpu/blob/2cc42349/ArceOS project)

Sources: README.md(L9) 

Project Metadata

The project uses Rust edition 2024 and follows semantic versioning with the current version 0.1.0, indicating it is in initial development phase.

Sources: Cargo.toml(L1 - L4) 

Build System and CI

Relevant source files

This document covers the continuous integration pipeline, build matrix configuration, code quality enforcement, and documentation deployment for the x86_vcpu hypervisor library. The build system is designed to ensure code quality across multiple Rust toolchain versions while providing automated documentation deployment.

For information about project dependencies and feature configuration, see Project Configuration.

CI Pipeline Overview

The x86_vcpu project uses GitHub Actions for continuous integration with a dual-job pipeline that handles both code validation and documentation deployment. The pipeline is triggered on push and pull_request events to ensure all changes are properly validated.

CI Workflow Structure

flowchart TD
subgraph subGraph3["Doc Job Steps"]
    DocCheckout["actions/checkout@v4"]
    DocRustSetup["dtolnay/rust-toolchain@nightly"]
    DocBuild["cargo doc --no-deps --all-features"]
    GHPagesDeploy["JamesIves/github-pages-deploy-action@v4"]
end
subgraph subGraph2["CI Job Steps"]
    Checkout["actions/checkout@v4"]
    RustSetup["dtolnay/rust-toolchain@nightly"]
    VersionCheck["rustc --version --verbose"]
    FormatCheck["cargo fmt --all -- --check"]
    ClippyCheck["cargo clippy --target x86_64-unknown-none"]
    Build["cargo build --target x86_64-unknown-none"]
    UnitTest["cargo test --target x86_64-unknown-linux-gnu"]
end
subgraph subGraph1["CI Pipeline Jobs"]
    CIJob["ci jobCode Quality & Build"]
    DocJob["doc jobDocumentation Generation"]
end
subgraph subGraph0["GitHub Events"]
    Push["push event"]
    PR["pull_request event"]
end

Build --> UnitTest
CIJob --> Checkout
Checkout --> RustSetup
ClippyCheck --> Build
DocBuild --> GHPagesDeploy
DocCheckout --> DocRustSetup
DocJob --> DocCheckout
DocRustSetup --> DocBuild
FormatCheck --> ClippyCheck
PR --> CIJob
PR --> DocJob
Push --> CIJob
Push --> DocJob
RustSetup --> VersionCheck
VersionCheck --> FormatCheck

Sources: .github/workflows/ci.yml(L1 - L62) 

Build Matrix Strategy

The CI pipeline employs a matrix strategy to test against multiple Rust toolchain versions, ensuring compatibility across both stable nightly builds and the latest nightly releases.

Toolchain Matrix Configuration

Matrix DimensionValues
rust-toolchainnightly-2024-12-25,nightly
targetsx86_64-unknown-none
Runnerubuntu-latest

The matrix configuration uses fail-fast: false to ensure all combinations are tested even if one fails. The nightly toolchain uses continue-on-error: true for all steps, allowing bleeding-edge compatibility testing without blocking releases.

Target Architecture Rationale

The primary build target x86_64-unknown-none is specifically chosen for bare-metal hypervisor deployment:

  • No standard library: Suitable for kernel-level virtualization code
  • x86_64 architecture: Aligns with Intel VMX requirements
  • Bare-metal execution: Matches hypervisor runtime environment

Sources: .github/workflows/ci.yml(L8 - L19) 

Code Quality Enforcement

The CI pipeline implements comprehensive code quality checks through automated tools and formatting validation.

Quality Check Pipeline

flowchart TD
subgraph subGraph1["Exception Handling"]
    ContinueOnError["continue-on-error: nightly toolchain"]
    ClippyExceptions["-A clippy::new_without_defaultAllowed lint suppressions"]
end
subgraph subGraph0["Code Quality Checks"]
    FormatCheck["cargo fmt --all -- --checkRust formatting validation"]
    ClippyLints["cargo clippy --target x86_64-unknown-noneLint analysis with exception rules"]
    BuildValidation["cargo build --target x86_64-unknown-noneCompilation verification"]
end

ClippyExceptions --> ClippyLints
ClippyLints --> BuildValidation
ContinueOnError --> BuildValidation
ContinueOnError --> ClippyLints
ContinueOnError --> FormatCheck
FormatCheck --> ClippyLints

Quality Check Configuration

The clippy configuration includes specific lint suppressions appropriate for hypervisor code:

  • clippy::new_without_default: Suppressed due to specialized constructor requirements in VMX structures
  • All features enabled: Ensures comprehensive linting across feature gates
  • Target-specific linting: Validates against bare-metal target constraints

Sources: .github/workflows/ci.yml(L22 - L30) 

Testing Strategy

The testing strategy accommodates the bare-metal nature of the hypervisor library while providing comprehensive validation where possible.

Test Execution Matrix

Test TypeTargetConditionPurpose
Unit Testsx86_64-unknown-linux-gnuLinux-compatible targets onlyValidate core logic
Build Testsx86_64-unknown-noneAll targetsEnsure bare-metal compilation
Integration TestsNot applicableN/AHardware-dependent functionality

The unit tests are conditionally executed only for Linux-compatible targets since the primary x86_64-unknown-none target cannot execute tests in the CI environment.

Sources: .github/workflows/ci.yml(L31 - L34) 

Documentation Generation and Deployment

The documentation system provides automated API documentation generation and GitHub Pages deployment with strict documentation quality enforcement.

Documentation Build Pipeline

flowchart TD
subgraph subGraph2["Deployment Strategy"]
    BranchCheck["github.ref == default-branch"]
    GHPagesDeployment["JamesIves/github-pages-deploy-action@v4"]
    SingleCommit["single-commit: true"]
end
subgraph subGraph1["Quality Enforcement"]
    BrokenLinks["-D rustdoc::broken_intra_doc_links"]
    MissingDocs["-D missing-docs"]
end
subgraph subGraph0["Documentation Generation"]
    DocBuild["cargo doc --no-deps --all-features"]
    IndexGeneration["printf redirect to generated docs"]
    QualityCheck["RUSTDOCFLAGS validation"]
end

BranchCheck --> GHPagesDeployment
BrokenLinks --> QualityCheck
DocBuild --> IndexGeneration
GHPagesDeployment --> SingleCommit
IndexGeneration --> BranchCheck
MissingDocs --> QualityCheck
QualityCheck --> DocBuild

Documentation Quality Standards

The documentation build enforces strict quality standards through RUSTDOCFLAGS:

  • Broken intra-doc links: Treated as build failures to ensure documentation integrity
  • Missing documentation: All public APIs must be documented
  • All features enabled: Comprehensive documentation across all feature gates

Deployment Configuration

Documentation deployment uses the following strategy:

  • Branch restriction: Only deploys from the default branch
  • Single commit mode: Maintains clean documentation history
  • Target folder: target/doc with generated index redirect

Sources: .github/workflows/ci.yml(L36 - L61) 

Build Artifact Management

The build system excludes specific artifacts and temporary files to maintain a clean repository while supporting the ArceOS hypervisor development workflow.

Excluded Artifacts

CategoryPatternsPurpose
Build Output/target,.asm,.img,.bin,.elfStandard Rust and ArceOS build artifacts
Runtime Filesactual.out,qemu.logHypervisor testing and emulation logs
Development Tools/.vscode,.DS_Store,rusty-tags.viEditor and system-specific files
Dependency LockCargo.lockLibrary projects exclude lock files

The exclusion of Cargo.lock follows Rust library best practices, allowing downstream consumers to resolve their own dependency versions while maintaining compatibility constraints defined in Cargo.toml.

Sources: .gitignore(L1 - L19) 

Overview

Relevant source files

Purpose and Scope

This document provides an overview of the arm_vgic crate, which implements a Virtual Generic Interrupt Controller (VGIC) for ARM-based virtualized systems within the ArceOS hypervisor ecosystem. The crate enables guest virtual machines to interact with a virtualized ARM Generic Interrupt Controller (GIC) interface, providing essential interrupt management capabilities for ARM virtualization scenarios.

This overview covers the crate's architecture, main components, and integration points with the broader ArceOS framework. For detailed implementation specifics of individual components, see Core Components. For dependency analysis and build configuration details, see Dependencies and Integration.

System Purpose and Role

The arm_vgic crate serves as a critical virtualization component that bridges guest operating systems and the physical ARM GIC hardware. It provides:

  • Virtual interrupt controller emulation for ARM guest VMs
  • Memory-mapped register interface compatible with ARM GIC specifications
  • Integration with ArceOS device framework through standardized interfaces
  • Thread-safe state management for multi-core virtualization scenarios

The crate operates as an intermediary layer that intercepts guest VM accesses to GIC registers and translates them into appropriate operations on the underlying physical hardware or virtualized state.

Sources: Cargo.toml(L1 - L18)  src/lib.rs(L1 - L10) 

ArceOS Ecosystem Integration

Hypervisor Stack Positioning

flowchart TD
subgraph Physical_Hardware["Physical Hardware"]
    ARM_CPU["ARM CPU Cores"]
    Physical_GIC["Physical GIC Hardware"]
    Memory_HW["Physical Memory"]
end
subgraph ArceOS_Hypervisor["ArceOS Hypervisor Layer"]
    subgraph Hardware_Abstraction["Hardware Abstraction"]
        arm_gicv2["arm_gicv2Physical GIC Driver"]
        memory_addr["memory_addrAddress Types"]
    end
    subgraph Framework_Services["Framework Services"]
        axdevice_base["axdevice_baseDevice Framework"]
        axaddrspace["axaddrspaceMemory Management"]
        axerrno["axerrnoError Handling"]
    end
    subgraph Device_Emulation["Device Emulation"]
        arm_vgic["arm_vgic crateVirtual GIC Controller"]
        OtherDevices["Other Virtual Devices"]
    end
end
subgraph Guest_VM["Guest Virtual Machine"]
    GuestOS["Guest Operating System"]
    GuestDrivers["Device Drivers"]
    GuestKernel["Kernel Interrupt Handlers"]
end

GuestDrivers --> arm_vgic
GuestKernel --> arm_vgic
arm_gicv2 --> Physical_GIC
arm_vgic --> arm_gicv2
arm_vgic --> axaddrspace
arm_vgic --> axdevice_base
axaddrspace --> Memory_HW

Sources: Cargo.toml(L7 - L17) 

Core Architecture Components

Main Code Entity Structure

flowchart TD
subgraph consts_module["consts.rs Module"]
    register_offsets["VGICD Register Offsets"]
    irq_limits["SGI/PPI/SPI ID Limits"]
    lr_config["List Register Configuration"]
end
subgraph devops_impl_module["devops_impl.rs Module"]
    BaseDeviceOps_impl["BaseDeviceOps implementation"]
    address_range["Address Range: 0x800_0000-0x800_FFFF"]
    dispatch_logic["Width-based Dispatch"]
end
subgraph vgicc_module["vgicc.rs Module"]
    Vgicc_struct["Vgicc struct"]
    link_registers["Link Register Arrays"]
    processor_state["Saved Processor State"]
end
subgraph vgic_module["vgic.rs Module"]
    Vgic_struct["Vgic struct"]
    VgicInner_struct["VgicInner struct"]
    handle_read_functions["handle_read8/16/32"]
    handle_write_functions["handle_write8/16/32"]
end
subgraph lib_rs["src/lib.rs - Public API"]
    pub_use_vgic["pub use vgic::Vgic"]
    mod_declarations["mod devops_implmod vgicmod constsmod vgicc"]
end

BaseDeviceOps_impl --> handle_read_functions
BaseDeviceOps_impl --> handle_write_functions
VgicInner_struct --> Vgicc_struct
Vgic_struct --> VgicInner_struct
Vgicc_struct --> link_registers
handle_read_functions --> register_offsets
handle_write_functions --> register_offsets
link_registers --> lr_config
pub_use_vgic --> Vgic_struct

Sources: src/lib.rs(L1 - L10) 

Key Dependencies and Integration Points

The crate integrates with several ArceOS framework components and external libraries:

DependencyPurposeIntegration Point
axdevice_baseDevice framework foundationBaseDeviceOpstrait implementation
axaddrspaceMemory management abstractionsAddress space operations
arm_gicv2Physical GIC hardware driverHardware interrupt management
memory_addrMemory address type definitionsAddress handling and validation
axerrnoStandardized error handlingError propagation across components
spinSynchronization primitivesThread-safe state protection
logLogging infrastructureDebug and operational logging

Component Communication Flow

flowchart TD
subgraph Hardware_Interface["Hardware Interface"]
    arm_gicv2_driver["arm_gicv2::GicInterface"]
    Physical_Operations["Physical GIC Operations"]
end
subgraph VGIC_Processing["arm_vgic Processing"]
    BaseDeviceOps_impl["BaseDeviceOps impl"]
    Vgic_handlers["Vgic read/write handlers"]
    VgicInner_state["VgicInner state"]
    Vgicc_interface["Vgicc CPU interface"]
end
subgraph VM_Access["VM Memory Access"]
    VM_Read["VM Read Operation"]
    VM_Write["VM Write Operation"]
end

BaseDeviceOps_impl --> Vgic_handlers
VM_Read --> BaseDeviceOps_impl
VM_Write --> BaseDeviceOps_impl
VgicInner_state --> Vgicc_interface
Vgic_handlers --> VgicInner_state
Vgic_handlers --> arm_gicv2_driver
arm_gicv2_driver --> Physical_Operations

Sources: Cargo.toml(L7 - L17)  src/lib.rs(L3 - L10) 

Functional Scope

The arm_vgic crate provides virtualization for ARM Generic Interrupt Controller functionality, specifically focusing on:

  • Distributor interface emulation - Handles guest accesses to GIC distributor registers
  • CPU interface abstraction - Manages per-CPU interrupt controller state
  • Interrupt routing and prioritization - Implements ARM GIC interrupt handling semantics
  • State persistence - Maintains interrupt controller state across VM operations
  • Hardware integration - Coordinates with physical GIC hardware through arm_gicv2

The crate operates within the memory address range 0x800_0000 to 0x800_FFFF, providing a standardized MMIO interface that guest VMs can interact with as if accessing a physical ARM GIC controller.

Sources: Cargo.toml(L1 - L18)  src/lib.rs(L1 - L10) 

System Architecture

Relevant source files

Purpose and Scope

This document provides a comprehensive overview of the arm_vgic crate's system architecture, detailing how it implements a Virtual Generic Interrupt Controller (VGIC) for ARM systems within the ArceOS hypervisor ecosystem. The architecture encompasses the virtualization of ARM GICv2 hardware, memory-mapped register emulation, and integration with the hypervisor's device framework.

For detailed information about individual components and their APIs, see Core Components. For dependency analysis and integration patterns, see Dependencies and Integration.

Virtualization Stack Positioning

The arm_vgic crate operates as a critical virtualization layer between guest operating systems and physical ARM GIC hardware. The architecture follows a clean separation of concerns across multiple abstraction levels.

Hypervisor Stack Architecture

flowchart TD
subgraph PhysicalHW["Physical Hardware"]
    ARMCores["ARM CPU Cores"]
    PhysicalGIC["Physical GIC Hardware"]
    MemorySystem["Memory Subsystem"]
end
subgraph ArceOSHypervisor["ArceOS Hypervisor Layer"]
    subgraph HardwareAbstraction["Hardware Abstraction"]
        ArmGicV2["arm_gicv2::GicInterface"]
        MemoryAddr["memory_addr"]
    end
    subgraph FrameworkLayer["Framework Layer"]
        BaseDeviceOps["BaseDeviceOps"]
        AxDeviceBase["axdevice_base"]
        AxAddrSpace["axaddrspace"]
    end
    subgraph DeviceEmulation["Device Emulation Layer"]
        VgicController["Vgic"]
        VgicInner["VgicInner"]
        VgiccState["Vgicc"]
    end
end
subgraph GuestVM["Guest Virtual Machine"]
    GuestOS["Guest OS Kernel"]
    GuestDrivers["Device Drivers"]
    GuestHandlers["Interrupt Handlers"]
end

ArmGicV2 --> PhysicalGIC
ArmGicV2 --> VgicController
BaseDeviceOps --> AxDeviceBase
GuestDrivers --> VgicController
GuestHandlers --> VgicController
MemoryAddr --> MemorySystem
PhysicalGIC --> ArmGicV2
VgicController --> AxAddrSpace
VgicController --> BaseDeviceOps
VgicController --> GuestHandlers
VgicController --> VgicInner
VgicInner --> VgiccState

Sources: src/vgic.rs(L32 - L34)  src/devops_impl.rs(L10)  src/vgicc.rs(L3) 

The Vgic struct serves as the primary interface between guest VMs and the physical interrupt controller, implementing memory-mapped register emulation at the standardized ARM GIC address space of 0x800_0000 to 0x800_FFFF.

Core Architecture Components

The system architecture is built around four primary components that work together to provide complete interrupt controller virtualization.

Component Relationship Architecture

flowchart TD
subgraph ExternalDeps["External Dependencies"]
    SpinMutex["spin::Mutex"]
    ArmGicV2Interface["arm_gicv2::GicInterface"]
    AxDeviceBase["axdevice_base"]
end
subgraph CoreImplementation["Core Implementation"]
    subgraph DevOpsModule["devops_impl.rs"]
        BaseDeviceOpsImpl["BaseDeviceOps impl"]
        EmuType["emu_type()"]
        AddressRange["address_range()"]
        HandleRead["handle_read()"]
        HandleWrite["handle_write()"]
    end
    subgraph ConstsModule["consts.rs"]
        SGI_ID_MAX["SGI_ID_MAX"]
        PPI_ID_MAX["PPI_ID_MAX"]
        SPI_ID_MAX["SPI_ID_MAX"]
        VGICD_CTLR["VGICD_CTLR"]
        VGICD_ISENABLER_SGI_PPI["VGICD_ISENABLER_SGI_PPI"]
        VGICD_ISENABLER_SPI["VGICD_ISENABLER_SPI"]
    end
    subgraph VgiccModule["vgicc.rs"]
        VgiccStruct["Vgicc"]
        PendingLR["pending_lr[SPI_ID_MAX]"]
        SavedLR["saved_lr[GICD_LR_NUM]"]
        SavedRegs["saved_elsr0, saved_apr, saved_hcr"]
    end
    subgraph VgicModule["vgic.rs"]
        VgicStruct["Vgic"]
        VgicInnerStruct["VgicInner"]
        HandleRead8["handle_read8"]
        HandleRead16["handle_read16"]
        HandleRead32["handle_read32"]
        HandleWrite8["handle_write8"]
        HandleWrite16["handle_write16"]
        HandleWrite32["handle_write32"]
    end
end
subgraph PublicAPI["Public API Layer"]
    LibRS["lib.rs"]
    VgicExport["pub use vgic::Vgic"]
end

BaseDeviceOpsImpl --> AxDeviceBase
BaseDeviceOpsImpl --> HandleRead
BaseDeviceOpsImpl --> HandleWrite
HandleRead --> HandleRead16
HandleRead --> HandleRead32
HandleRead --> HandleRead8
HandleRead8 --> VGICD_CTLR
HandleWrite --> HandleWrite16
HandleWrite --> HandleWrite32
HandleWrite --> HandleWrite8
HandleWrite8 --> ArmGicV2Interface
HandleWrite8 --> SGI_ID_MAX
HandleWrite8 --> SPI_ID_MAX
HandleWrite8 --> VGICD_CTLR
HandleWrite8 --> VGICD_ISENABLER_SGI_PPI
LibRS --> VgicExport
VgicExport --> VgicStruct
VgicInnerStruct --> VgiccStruct
VgicStruct --> SpinMutex
VgicStruct --> VgicInnerStruct
VgiccStruct --> PendingLR
VgiccStruct --> SavedLR
VgiccStruct --> SavedRegs

Sources: src/vgic.rs(L15 - L30)  src/vgicc.rs(L3 - L14)  src/consts.rs(L1 - L19)  src/devops_impl.rs(L10 - L99) 

Memory Layout and Address Handling

The VGIC implements a memory-mapped interface that emulates the standard ARM GICv2 distributor registers. The address decoding and dispatch mechanism ensures proper isolation and functionality.

Address Space and Register Layout

Address RangeRegister CategoryHandler Functions
0x800_0000 + 0x000Control Register (VGICD_CTLR)handle_write8/16/32
0x800_0000 + 0x100-0x104Interrupt Set Enable (VGICD_ISENABLER)handle_write8/16/32
0x800_0000 + 0x180-0x184Interrupt Clear Enable (VGICD_ICENABLER)handle_write8/16/32
0x800_0000 + 0x200Interrupt Set Pending (VGICD_ISPENDR)handle_write8/16/32

Sources: src/consts.rs(L7 - L18)  src/devops_impl.rs(L29 - L31)  src/devops_impl.rs(L47) 

The address range spans 64KB (0x10000 bytes) starting at 0x800_0000, with address masking applied using addr & 0xfff to ensure proper alignment within the 4KB register space.

Data Flow Architecture

The system processes guest memory accesses through a well-defined pipeline that maintains virtualization transparency while interfacing with physical hardware.

Memory Access Processing Pipeline

flowchart TD
subgraph PhysicalInterface["Physical Interface"]
    GicInterface["arm_gicv2::GicInterface"]
    SetEnable["set_enable(irq, bool)"]
    SetPriority["set_priority(irq, u8)"]
end
subgraph StateManagement["State Management"]
    UsedIRQ["used_irq[SPI_ID_MAX/32]"]
    PTOV["ptov[SPI_ID_MAX]"]
    VTOP["vtop[SPI_ID_MAX]"]
    GICDRegs["gicd_* register arrays"]
end
subgraph RegisterHandling["Register Handling"]
    CTLRHandler["VGICD_CTLR Handler"]
    ENABLERHandler["ISENABLER/ICENABLER"]
    PENDRHandler["ISPENDR/ICPENDR"]
    MutexLock["VgicInner Mutex Lock"]
end
subgraph AddressProcessing["Address Processing"]
    AddrDecode["Address DecodingGuestPhysAddr"]
    AddrMask["Address Maskingaddr & 0xfff"]
    WidthDispatch["Width Dispatch1/2/4 bytes"]
end
subgraph GuestAccess["Guest Memory Access"]
    VMRead["VM Read Operation"]
    VMWrite["VM Write Operation"]
end

AddrDecode --> AddrMask
AddrMask --> WidthDispatch
CTLRHandler --> GicInterface
CTLRHandler --> MutexLock
ENABLERHandler --> MutexLock
GicInterface --> SetEnable
GicInterface --> SetPriority
MutexLock --> GICDRegs
MutexLock --> PTOV
MutexLock --> UsedIRQ
MutexLock --> VTOP
VMRead --> AddrDecode
VMWrite --> AddrDecode
WidthDispatch --> CTLRHandler
WidthDispatch --> ENABLERHandler
WidthDispatch --> PENDRHandler

Sources: src/devops_impl.rs(L45 - L66)  src/devops_impl.rs(L77 - L98)  src/vgic.rs(L68 - L133)  src/vgic.rs(L15 - L30) 

The data flow ensures thread-safe access through mutex protection of the VgicInner state, while maintaining efficient dispatch based on access width and register offset. Physical GIC operations are performed only when necessary, such as during control register updates that enable or disable interrupt groups.

Thread Safety and Concurrency

The architecture employs a mutex-protected inner state design to ensure thread-safe operation across multiple CPU cores accessing the virtual interrupt controller simultaneously.

The VgicInner struct src/vgic.rs(L15 - L30)  contains all mutable state protected by a spin::Mutex src/vgic.rs(L33)  including interrupt tracking arrays, virtual-to-physical mapping tables, and register state. This design allows multiple guest CPUs to safely access the VGIC while maintaining consistency of the virtualized interrupt controller state.

Sources: src/vgic.rs(L32 - L53)  src/vgic.rs(L76)  src/devops_impl.rs(L1 - L8) 

Core Components

Relevant source files

This document provides an overview of the four primary components that make up the arm_vgic virtual interrupt controller system. These components work together to provide complete ARM GIC (Generic Interrupt Controller) virtualization within the ArceOS hypervisor ecosystem.

For detailed information about system architecture and positioning within the broader virtualization stack, see System Architecture. For dependency analysis and integration details, see Dependencies and Integration.

Component Architecture Overview

The arm_vgic crate is structured around four core components that provide different aspects of interrupt controller virtualization:

Component Architecture

flowchart TD
subgraph subGraph1["External Framework"]
    axdevice_base["axdevice_base::BaseDeviceOps"]
    arm_gicv2["arm_gicv2::GicInterface"]
end
subgraph subGraph0["Core Components"]
    Vgic["VgicMain Controller"]
    VgicInner["VgicInnerProtected State"]
    Vgicc["VgiccCPU Interface"]
    BaseDeviceOps["BaseDeviceOps ImplementationDevice Framework Integration"]
    Constants["Constants ModuleRegister Definitions"]
end

BaseDeviceOps --> Vgic
BaseDeviceOps --> axdevice_base
Constants --> Vgicc
Vgic --> Constants
Vgic --> VgicInner
Vgic --> arm_gicv2
VgicInner --> Vgicc

Sources: src/vgic.rs(L32 - L34)  src/vgicc.rs(L3 - L14)  src/devops_impl.rs(L10)  src/consts.rs(L1 - L19) 

Data Flow and Component Interaction

The components interact through a well-defined data flow that handles virtual machine memory accesses and translates them to physical interrupt controller operations:

Data Flow Between Components

flowchart TD
subgraph subGraph4["Hardware Interface"]
    GicInterface_set_enable["GicInterface::set_enable"]
    GicInterface_set_priority["GicInterface::set_priority"]
end
subgraph subGraph3["State Management"]
    VgicInner_mutex["VgicInner (Mutex)"]
    gicc_vec["Vec"]
    register_arrays["Register State Arrays"]
end
subgraph subGraph2["VGIC Processing"]
    handle_read8["Vgic::handle_read8"]
    handle_read16["Vgic::handle_read16"]
    handle_read32["Vgic::handle_read32"]
    handle_write8["Vgic::handle_write8"]
    handle_write16["Vgic::handle_write16"]
    handle_write32["Vgic::handle_write32"]
end
subgraph subGraph1["Device Framework Layer"]
    handle_read["BaseDeviceOps::handle_read"]
    handle_write["BaseDeviceOps::handle_write"]
    address_range["address_range()0x800_0000-0x800_FFFF"]
end
subgraph subGraph0["VM Access"]
    VM_Read["VM Read Access"]
    VM_Write["VM Write Access"]
end

VM_Read --> handle_read
VM_Write --> handle_write
VgicInner_mutex --> GicInterface_set_enable
VgicInner_mutex --> GicInterface_set_priority
VgicInner_mutex --> gicc_vec
VgicInner_mutex --> register_arrays
handle_read --> handle_read16
handle_read --> handle_read32
handle_read --> handle_read8
handle_write --> handle_write16
handle_write --> handle_write32
handle_write --> handle_write8
handle_write8 --> VgicInner_mutex

Sources: src/devops_impl.rs(L45 - L66)  src/devops_impl.rs(L77 - L98)  src/vgic.rs(L56 - L66)  src/vgic.rs(L68 - L106)  src/vgic.rs(L15 - L30) 

Component Responsibilities

Virtual GIC Controller (Vgic)

The Vgic struct serves as the main controller that orchestrates interrupt virtualization. It maintains thread-safe access to the internal state through a Mutex<VgicInner> and provides the primary interface for handling virtual machine memory accesses to interrupt controller registers.

Key responsibilities:

  • Memory-mapped register access handling via handle_read8/16/32 and handle_write8/16/32 methods
  • Thread-safe state management through VgicInner mutex protection
  • Physical interrupt controller coordination via arm_gicv2::GicInterface
  • Interrupt enable/disable control and priority management

For comprehensive details, see Virtual GIC Controller (Vgic).

Sources: src/vgic.rs(L32 - L34)  src/vgic.rs(L36 - L54) 

CPU Interface (Vgicc)

The Vgicc struct manages per-CPU interrupt controller state, including link registers for interrupt delivery and saved processor state for context switching operations.

Key responsibilities:

  • Per-CPU interrupt state management with unique id field
  • Link register arrays for interrupt queuing (pending_lr, saved_lr)
  • Processor state preservation (saved_elsr0, saved_apr, saved_hcr)
  • Local interrupt enable/priority configuration (isenabler, priorityr)

For detailed analysis, see CPU Interface (Vgicc).

Sources: src/vgicc.rs(L3 - L14) 

Device Operations Interface

The BaseDeviceOps trait implementation bridges the Vgic controller with the ArceOS device framework, providing standardized device emulation capabilities.

Key responsibilities:

  • Device type identification via emu_type() returning EmuDeviceTGicdV2
  • Address space definition through address_range() covering 0x800_0000 to 0x800_FFFF
  • Width-based memory access dispatch (8/16/32-bit operations)
  • Address masking and alignment handling

For implementation details, see Device Operations Interface.

Sources: src/devops_impl.rs(L10)  src/devops_impl.rs(L18 - L31) 

Constants and Register Layout

The constants module defines critical system parameters, interrupt ID limits, and register offset mappings that govern the virtual interrupt controller behavior.

Key definitions:

  • Interrupt ID boundaries: SGI_ID_MAX, PPI_ID_MAX, SPI_ID_MAX
  • Link register configuration: GICD_LR_NUM
  • VGICD register offsets: VGICD_CTLR, VGICD_ISENABLER_*, VGICD_ICENABLER_*
  • Control and configuration register addresses

For complete reference, see Constants and Register Layout.

Sources: src/consts.rs(L1 - L19) 

Integration Architecture

Component Integration with External Systems

flowchart TD
subgraph subGraph3["System Utilities"]
    spin_mutex["spin::Mutex"]
    log_macros["log::error!"]
    alloc_vec["alloc::Vec"]
end
subgraph subGraph2["Hardware Abstraction"]
    arm_gicv2_interface["arm_gicv2::GicInterface"]
    memory_addr_range["memory_addr::AddrRange"]
end
subgraph subGraph1["ArceOS Framework"]
    BaseDeviceOps_trait["BaseDeviceOps trait"]
    axdevice_base_crate["axdevice_base crate"]
    axaddrspace_addr["axaddrspace::GuestPhysAddr"]
    axerrno_result["axerrno::AxResult"]
end
subgraph subGraph0["arm_vgic Components"]
    Vgic_main["Vgic"]
    VgicInner_state["VgicInner"]
    Vgicc_cpu["Vgicc"]
    consts_defs["Constants"]
end

BaseDeviceOps_trait --> axaddrspace_addr
BaseDeviceOps_trait --> axdevice_base_crate
BaseDeviceOps_trait --> axerrno_result
BaseDeviceOps_trait --> memory_addr_range
VgicInner_state --> Vgicc_cpu
VgicInner_state --> alloc_vec
VgicInner_state --> spin_mutex
Vgic_main --> BaseDeviceOps_trait
Vgic_main --> VgicInner_state
Vgic_main --> arm_gicv2_interface
Vgic_main --> consts_defs
Vgic_main --> log_macros

Sources: src/vgic.rs(L1 - L11)  src/devops_impl.rs(L1 - L8)  src/vgicc.rs(L2) 

The four core components work together to provide a complete virtual interrupt controller implementation that integrates seamlessly with the ArceOS hypervisor framework while maintaining efficient virtualization of ARM GIC functionality.

Virtual GIC Controller (Vgic)

Relevant source files

This document covers the Vgic struct and its associated components, which form the core of the virtual interrupt controller implementation in the arm_vgic crate. The Vgic provides virtualized access to ARM Generic Interrupt Controller (GIC) functionality for guest virtual machines within the ArceOS hypervisor ecosystem.

For information about the CPU-specific interface components, see CPU Interface (Vgicc). For details about the device framework integration, see Device Operations Interface.

Core Structure and Components

The Virtual GIC Controller is implemented through two primary structures that work together to manage interrupt virtualization state and provide thread-safe access to interrupt controller resources.

Primary Data Structures

The Vgic implementation follows a layered approach with an outer wrapper providing thread-safe access to the internal state:

flowchart TD
subgraph subGraph4["Control State"]
    ctrlr["ctrlr: u32"]
    typer["typer: u32"]
    iidr["iidr: u32"]
end
subgraph subGraph3["GIC Registers"]
    gicd_igroupr["gicd_igroupr[SPI_ID_MAX/32]"]
    gicd_isenabler["gicd_isenabler[SPI_ID_MAX/32]"]
    gicd_ipriorityr["gicd_ipriorityr[SPI_ID_MAX]"]
    gicd_itargetsr["gicd_itargetsr[SPI_ID_MAX]"]
    gicd_icfgr["gicd_icfgr[SPI_ID_MAX/16]"]
end
subgraph subGraph2["State Arrays"]
    used_irq["used_irq[SPI_ID_MAX/32]"]
    ptov["ptov[SPI_ID_MAX]"]
    vtop["vtop[SPI_ID_MAX]"]
    gicc_vec["gicc: Vec"]
end
subgraph subGraph1["Protected State"]
    VgicInner["VgicInner"]
    Mutex["Mutex"]
end
subgraph subGraph0["Public Interface"]
    Vgic["Vgic"]
end

Mutex --> VgicInner
Vgic --> Mutex
VgicInner --> ctrlr
VgicInner --> gicc_vec
VgicInner --> gicd_icfgr
VgicInner --> gicd_igroupr
VgicInner --> gicd_ipriorityr
VgicInner --> gicd_isenabler
VgicInner --> gicd_itargetsr
VgicInner --> iidr
VgicInner --> ptov
VgicInner --> typer
VgicInner --> used_irq
VgicInner --> vtop

Sources: src/vgic.rs(L15 - L34) 

State Management Arrays

The VgicInner struct maintains several key data structures for interrupt management:

ArrayTypeSizePurpose
used_irq[u32; SPI_ID_MAX/32]16 wordsTracks which interrupt IDs are allocated
ptov[u32; SPI_ID_MAX]512 entriesPhysical-to-virtual interrupt ID mapping
vtop[u32; SPI_ID_MAX]512 entriesVirtual-to-physical interrupt ID mapping
gicd_igroupr[u32; SPI_ID_MAX/32]16 wordsInterrupt group register state
gicd_isenabler[u32; SPI_ID_MAX/32]16 wordsInterrupt enable register state
gicd_ipriorityr[u8; SPI_ID_MAX]512 bytesInterrupt priority values
gicd_itargetsr[u8; SPI_ID_MAX]512 bytesInterrupt target CPU mapping
gicd_icfgr[u32; SPI_ID_MAX/16]32 wordsInterrupt configuration register state

Sources: src/vgic.rs(L15 - L30)  src/consts.rs(L1 - L4) 

Memory Access Handling

The Vgic provides width-specific handlers for processing virtual machine memory accesses to the interrupt controller's memory-mapped registers. These handlers implement the virtualization layer between guest VM register accesses and the underlying interrupt controller state.

Handler Method Dispatch

flowchart TD
subgraph subGraph2["Register Processing"]
    addr_decode["Address Decoding"]
    register_logic["Register-Specific Logic"]
    state_update["VgicInner State Update"]
end
subgraph subGraph1["Handler Methods"]
    handle_read8["handle_read8()"]
    handle_read16["handle_read16()"]
    handle_read32["handle_read32()"]
    handle_write8["handle_write8()"]
    handle_write16["handle_write16()"]
    handle_write32["handle_write32()"]
end
subgraph subGraph0["VM Memory Access"]
    vm_read8["VM 8-bit Read"]
    vm_read16["VM 16-bit Read"]
    vm_read32["VM 32-bit Read"]
    vm_write8["VM 8-bit Write"]
    vm_write16["VM 16-bit Write"]
    vm_write32["VM 32-bit Write"]
end

addr_decode --> register_logic
handle_read16 --> addr_decode
handle_read32 --> addr_decode
handle_read8 --> addr_decode
handle_write16 --> addr_decode
handle_write32 --> addr_decode
handle_write8 --> addr_decode
register_logic --> state_update
vm_read16 --> handle_read16
vm_read32 --> handle_read32
vm_read8 --> handle_read8
vm_write16 --> handle_write16
vm_write32 --> handle_write32
vm_write8 --> handle_write8

Sources: src/vgic.rs(L56 - L133) 

Read Handler Implementation

The read handlers currently provide basic functionality with placeholder implementations:

  • handle_read8(): Returns 0 for all 8-bit register reads
  • handle_read16(): Returns 0 for all 16-bit register reads
  • handle_read32(): Returns 0 for all 32-bit register reads

These methods use the signature fn handle_readX(&self, addr: usize) -> AxResult<usize> and are marked as pub(crate) for internal crate access.

Sources: src/vgic.rs(L56 - L66) 

Register Handling and Interrupt Control

The write handlers implement the core interrupt virtualization logic by processing writes to specific GIC distributor registers and coordinating with the physical interrupt controller.

Control Register Handling

The VGICD_CTLR register controls the overall enable/disable state of the interrupt distributor:

flowchart TD
subgraph subGraph2["Physical GIC Operations"]
    scan_used["Scan used_irq array"]
    set_enable["GicInterface::set_enable()"]
    set_priority["GicInterface::set_priority()"]
end
subgraph subGraph1["Conditional Processing"]
    check_enable["ctrlr > 0?"]
    enable_path["Enable Path"]
    disable_path["Disable Path"]
end
subgraph subGraph0["Control Register Write Flow"]
    write_ctlr["Write to VGICD_CTLR"]
    extract_bits["Extract bits [1:0]"]
    lock_state["Lock VgicInner"]
    update_ctrlr["Update ctrlr field"]
end

check_enable --> disable_path
check_enable --> enable_path
disable_path --> scan_used
enable_path --> scan_used
enable_path --> set_priority
extract_bits --> lock_state
lock_state --> update_ctrlr
scan_used --> set_enable
update_ctrlr --> check_enable
write_ctlr --> extract_bits

Sources: src/vgic.rs(L70 - L95) 

Enable Register Handling

The interrupt enable registers (VGICD_ISENABLER_SGI_PPI and VGICD_ISENABLER_SPI) are handled through delegation to wider access methods:

  • 8-bit writes to enable registers delegate to handle_write32()
  • 16-bit writes to enable registers delegate to handle_write32()
  • 32-bit writes to enable registers are handled directly but currently contain placeholder logic

Sources: src/vgic.rs(L96 - L132) 

Physical Hardware Integration

The Vgic coordinates with the physical ARM GIC hardware through the arm_gicv2::GicInterface to ensure that virtual interrupt state changes are reflected in the underlying hardware configuration.

Hardware Interface Operations

OperationMethodPurpose
Enable/DisableGicInterface::set_enable(irq_id, enabled)Control interrupt enable state
Priority SettingGicInterface::set_priority(irq_id, priority)Set interrupt priority level

The integration logic scans the used_irq bitmap to identify allocated interrupts and applies configuration changes only to interrupts that are actively managed by the virtual controller.

Sources: src/vgic.rs(L5)  src/vgic.rs(L82 - L92) 

Initialization and Construction

The Vgic::new() constructor initializes all internal state arrays to zero values and creates an empty vector for CPU interface objects:

flowchart TD
subgraph subGraph1["Default Values"]
    zero_ctrlr["ctrlr = 0"]
    zero_arrays["All arrays = [0; SIZE]"]
    empty_gicc["gicc = Vec::new()"]
end
subgraph subGraph0["Constructor Process"]
    new_call["Vgic::new()"]
    create_mutex["Create Mutex"]
    init_arrays["Initialize State Arrays"]
    empty_vec["Empty gicc Vec"]
end

create_mutex --> init_arrays
empty_vec --> empty_gicc
init_arrays --> empty_vec
init_arrays --> zero_arrays
init_arrays --> zero_ctrlr
new_call --> create_mutex

The constructor ensures that the virtual interrupt controller starts in a clean, disabled state with no allocated interrupts or configured CPU interfaces.

Sources: src/vgic.rs(L37 - L54) 

CPU Interface (Vgicc)

Relevant source files

This document covers the Vgicc struct, which implements the per-CPU interface portion of the virtual Generic Interrupt Controller (GIC). The Vgicc manages CPU-specific interrupt state, list registers for interrupt virtualization, and processor state saving/restoration.

For information about the main VGIC controller that coordinates multiple CPU interfaces, see Virtual GIC Controller (Vgic). For system-wide constants and register layouts, see Constants and Register Layout.

Structure Overview

The Vgicc struct represents a single CPU's interface to the virtual interrupt controller. Each CPU core in the virtualized system has its own Vgicc instance that maintains independent interrupt state and configuration.

Vgicc Struct Layout

flowchart TD
subgraph subGraph3["Vgicc Struct"]
    ID["id: u32CPU Identifier"]
    subgraph subGraph1["Processor State Registers"]
        SAVED_ELSR["saved_elsr0: u32Empty List Status Register"]
        SAVED_APR["saved_apr: u32Active Priority Register"]
        SAVED_HCR["saved_hcr: u32Hypervisor Control Register"]
    end
    subgraph subGraph0["List Register Management"]
        PENDING_LR["pending_lr: [u32; 512]SPI Pending List Registers"]
        SAVED_LR["saved_lr: [u32; 4]Hardware List Register State"]
    end
    subgraph subGraph2["Interrupt Configuration"]
        ISENABLER["isenabler: u32SGI/PPI Enable Bits 0-31"]
        PRIORITYR["priorityr: [u8; 32]PPI Priority Configuration"]
    end
end

ID --> PENDING_LR
ISENABLER --> PRIORITYR
SAVED_ELSR --> SAVED_LR
SAVED_LR --> PENDING_LR

Sources: src/vgicc.rs(L3 - L14) 

Field Categories and Responsibilities

CategoryFieldsPurpose
CPU IdentityidUniquely identifies the CPU core this interface serves
List Register Statepending_lr,saved_lrManages virtual interrupt injection through hardware list registers
Processor Contextsaved_elsr0,saved_apr,saved_hcrPreserves CPU-specific GIC state during VM context switches
Interrupt Controlisenabler,priorityrConfigures interrupt enables and priorities for SGIs and PPIs

Sources: src/vgicc.rs(L4 - L13)  src/consts.rs(L1 - L4) 

List Register Management

The Vgicc manages two types of list register arrays that are central to ARM GIC virtualization:

Hardware List Registers vs Pending Arrays

flowchart TD
subgraph subGraph2["Interrupt Sources"]
    SPI["SPI Interrupts(32-543)"]
    PPI_SGI["PPI/SGI Interrupts(0-31)"]
end
subgraph subGraph1["Vgicc State"]
    SAVED_LR["saved_lr[4]Hardware LR Backup"]
    PENDING_LR["pending_lr[512]SPI Virtual Queue"]
    SAVED_ELSR["saved_elsr0Empty LR Status"]
end
subgraph subGraph0["Physical Hardware"]
    HW_LR["Physical List Registers(4 registers)"]
end

HW_LR --> SAVED_LR
PENDING_LR --> SAVED_LR
PPI_SGI --> HW_LR
SAVED_LR --> HW_LR
SAVED_LR --> SAVED_ELSR
SPI --> PENDING_LR

Sources: src/vgicc.rs(L5 - L6)  src/consts.rs(L3 - L4) 

The saved_lr array holds the contents of the 4 physical hardware list registers, while pending_lr provides a much larger virtual queue for SPI interrupts that cannot fit in the limited hardware registers.

Processor State Management

The Vgicc maintains three critical processor state registers that must be preserved across VM context switches:

State Register Functions

flowchart TD
subgraph subGraph2["Hardware Registers"]
    HW_HCR["Physical GICH_HCR"]
    HW_APR["Physical GICH_APR"]
    HW_ELSR["Physical GICH_ELSR"]
end
subgraph subGraph1["Vgicc State Preservation"]
    HCR["saved_hcrHypervisor Control• Virtual IRQ/FIQ enables• List register usage"]
    APR["saved_aprActive Priority• Currently active interrupt priority• Priority grouping config"]
    ELSR["saved_elsr0Empty List Status• Which LRs are available• Hardware resource tracking"]
end
subgraph subGraph0["VM Context Switch"]
    VM_EXIT["VM Exit"]
    VM_ENTRY["VM Entry"]
end

APR --> HW_APR
APR --> VM_ENTRY
ELSR --> HW_ELSR
ELSR --> VM_ENTRY
HCR --> HW_HCR
HCR --> VM_ENTRY
VM_EXIT --> APR
VM_EXIT --> ELSR
VM_EXIT --> HCR

Sources: src/vgicc.rs(L8 - L10) 

Interrupt Configuration State

The Vgicc maintains per-CPU interrupt configuration for Software Generated Interrupts (SGIs) and Private Peripheral Interrupts (PPIs):

SGI and PPI Management

flowchart TD
subgraph subGraph2["Per-bit Mapping"]
    BIT0["Bit 0: SGI 0"]
    BIT15["Bit 15: SGI 15"]
    BIT16["Bit 16: PPI 16"]
    BIT31["Bit 31: PPI 31"]
end
subgraph subGraph1["Vgicc Configuration"]
    ISENABLER["isenabler: u3232-bit enable maskOne bit per interrupt 0-31"]
    PRIORITYR["priorityr: [u8; 32]8-bit priority per PPIIndices 0-31 for PPI IDs 0-31"]
end
subgraph subGraph0["Interrupt ID Space"]
    SGI["SGI IDs 0-15Software Generated"]
    PPI["PPI IDs 16-31Private Peripheral"]
end

ISENABLER --> BIT0
ISENABLER --> BIT15
ISENABLER --> BIT16
ISENABLER --> BIT31
PPI --> ISENABLER
PPI --> PRIORITYR
SGI --> ISENABLER

Sources: src/vgicc.rs(L12 - L13)  src/consts.rs(L1 - L2) 

The isenabler field uses individual bits to track enable/disable state for each of the 32 SGI and PPI interrupts, while priorityr stores 8-bit priority values specifically for the 32 PPI interrupts.

Integration with VGIC System

The Vgicc operates as part of the larger VGIC virtualization system:

System Integration Flow

flowchart TD
subgraph subGraph2["Physical Hardware"]
    PHYSICAL_GIC["arm_gicv2Physical GIC Driver"]
    HW_CPU_IF["Hardware CPUInterface Registers"]
end
subgraph subGraph1["VGIC System"]
    VGIC_MAIN["Vgic Controller(Main coordinator)"]
    VGICC_INSTANCES["Multiple Vgicc(Per-CPU instances)"]
end
subgraph subGraph0["Guest VM"]
    GUEST["Guest OSInterrupt Operations"]
end

GUEST --> VGIC_MAIN
PHYSICAL_GIC --> HW_CPU_IF
VGICC_INSTANCES --> HW_CPU_IF
VGICC_INSTANCES --> PHYSICAL_GIC
VGIC_MAIN --> VGICC_INSTANCES

Sources: src/vgicc.rs(L1 - L14) 

Each Vgicc instance manages the virtualization state for one CPU core, coordinating with the main Vgic controller to provide a complete virtual interrupt controller implementation to guest operating systems.

Device Operations Interface

Relevant source files

Purpose and Scope

This document covers the Device Operations Interface implementation in the arm_vgic crate, which provides the integration layer between the Virtual Generic Interrupt Controller (VGIC) and the ArceOS device framework. The interface enables the hypervisor to expose the virtual GIC as a memory-mapped device to guest virtual machines.

For details about the core VGIC controller functionality, see Virtual GIC Controller (Vgic). For information about the CPU interface components, see CPU Interface (Vgicc).

BaseDeviceOps Trait Implementation

The VGIC integrates with the ArceOS device framework through the BaseDeviceOps trait implementation. This trait provides the standard interface for emulated devices within the hypervisor ecosystem.

Device Operations Structure

flowchart TD
subgraph subGraph3["VGIC Core Methods"]
    Read8["handle_read8()"]
    Read16["handle_read16()"]
    Read32["handle_read32()"]
    Write8["handle_write8()"]
    Write16["handle_write16()"]
    Write32["handle_write32()"]
end
subgraph subGraph2["VGIC Implementation"]
    VgicStruct["VgicMain Controller"]
    DevOpsImpl["impl BaseDeviceOps for Vgic"]
    subgraph subGraph1["Trait Methods"]
        EmuType["emu_type()→ EmuDeviceTGicdV2"]
        AddrRange["address_range()→ 0x800_0000-0x800_FFFF"]
        HandleRead["handle_read()→ Width Dispatch"]
        HandleWrite["handle_write()→ Width Dispatch"]
    end
end
subgraph subGraph0["ArceOS Device Framework"]
    BaseDeviceOps["BaseDeviceOpsTrait"]
    EmuDeviceType["EmuDeviceTypeEnum"]
    GuestPhysAddr["GuestPhysAddrAddress Type"]
end

BaseDeviceOps --> DevOpsImpl
DevOpsImpl --> AddrRange
DevOpsImpl --> EmuType
DevOpsImpl --> HandleRead
DevOpsImpl --> HandleWrite
HandleRead --> Read16
HandleRead --> Read32
HandleRead --> Read8
HandleWrite --> Write16
HandleWrite --> Write32
HandleWrite --> Write8
VgicStruct --> DevOpsImpl

Sources: src/devops_impl.rs(L1 - L99)  src/vgic.rs(L32 - L34) 

Device Type and Address Mapping

The VGIC device is identified as EmuDeviceTGicdV2, indicating it emulates a Generic Interrupt Controller Distributor version 2. The device occupies a 64KB memory region in the guest physical address space.

PropertyValueDescription
Device TypeEmuDeviceTGicdV2GICv2 Distributor Emulation
Base Address0x800_0000Guest physical start address
Size0x10000(64KB)Total address space
Address Mask0xfff4KB page alignment

Address Range Implementation

flowchart TD
subgraph subGraph2["Register Space"]
    RegisterOffset["Register OffsetWithin 4KB page"]
    VGICDRegs["VGICD RegistersControl, Enable, Priority, etc."]
end
subgraph subGraph1["Address Processing"]
    AddrRange["address_range()Returns: 0x800_0000-0x800_FFFF"]
    AddrMask["Address Maskingaddr & 0xfff"]
end
subgraph subGraph0["Guest VM Memory Access"]
    GuestAccess["Guest Memory Access0x800_0000 + offset"]
end

AddrMask --> RegisterOffset
AddrRange --> AddrMask
GuestAccess --> AddrRange
RegisterOffset --> VGICDRegs

Sources: src/devops_impl.rs(L29 - L31)  src/devops_impl.rs(L47)  src/devops_impl.rs(L79) 

Read Operation Handling

The handle_read method provides width-based dispatching for memory read operations. The implementation supports 8-bit, 16-bit, and 32-bit read operations.

Read Operation Flow

flowchart TD
subgraph subGraph2["VGIC Read Handlers"]
    HandleRead8["handle_read8(addr)Returns: AxResult"]
    HandleRead16["handle_read16(addr)Returns: AxResult"]
    HandleRead32["handle_read32(addr)Returns: AxResult"]
end
subgraph subGraph1["Width Dispatch"]
    WidthMatch["match width"]
    Width1["width == 18-bit read"]
    Width2["width == 216-bit read"]
    Width4["width == 432-bit read"]
    WidthOther["_ => Ok(0)Unsupported width"]
end
subgraph subGraph0["Read Entry Point"]
    HandleRead["handle_read()Parameters: addr, width"]
    AddrMask["addr = addr.as_usize() & 0xfff"]
end

AddrMask --> WidthMatch
HandleRead --> AddrMask
Width1 --> HandleRead8
Width2 --> HandleRead16
Width4 --> HandleRead32
WidthMatch --> Width1
WidthMatch --> Width2
WidthMatch --> Width4
WidthMatch --> WidthOther

Sources: src/devops_impl.rs(L45 - L66)  src/vgic.rs(L56 - L66) 

Write Operation Handling

The handle_write method manages memory write operations with similar width-based dispatching. Write operations modify the virtual GIC state and may trigger hardware GIC operations.

Write Operation Dispatch and Register Handling

flowchart TD
subgraph subGraph3["State Management"]
    MutexLock["vgic_inner.lock()"]
    CtrlrUpdate["vgic_inner.ctrlr = val & 0b11"]
    IRQLoop["Loop: SGI_ID_MAX..SPI_ID_MAX"]
    GicInterface["GicInterface::set_enable()GicInterface::set_priority()"]
end
subgraph subGraph2["Register Address Matching"]
    AddrMatch["match addr"]
    VGICD_CTLR["VGICD_CTLRControl Register"]
    VGICD_ISENABLER["VGICD_ISENABLER_*Enable Registers"]
    UnknownAddr["_ => error!()"]
end
subgraph subGraph1["Width Dispatch"]
    WriteMatch["match width"]
    WriteWidth1["width == 1handle_write8()"]
    WriteWidth2["width == 2handle_write16()"]
    WriteWidth4["width == 4handle_write32()"]
end
subgraph subGraph0["Write Entry Point"]
    HandleWrite["handle_write()Parameters: addr, width, val"]
    WriteMask["addr = addr.as_usize() & 0xfff"]
end

AddrMatch --> UnknownAddr
AddrMatch --> VGICD_CTLR
AddrMatch --> VGICD_ISENABLER
CtrlrUpdate --> IRQLoop
HandleWrite --> WriteMask
IRQLoop --> GicInterface
MutexLock --> CtrlrUpdate
VGICD_CTLR --> MutexLock
WriteMask --> WriteMatch
WriteMatch --> WriteWidth1
WriteMatch --> WriteWidth2
WriteMatch --> WriteWidth4
WriteWidth1 --> AddrMatch
WriteWidth2 --> AddrMatch
WriteWidth4 --> AddrMatch

Sources: src/devops_impl.rs(L77 - L98)  src/vgic.rs(L68 - L133) 

Integration with VGIC Core

The device operations interface serves as the bridge between guest VM memory accesses and the internal VGIC state management. Key integration points include:

Core Integration Components

ComponentPurposeImplementation
VgicInnerProtected state storageMutex-wrapped internal state
Register handlersAddress-specific logicMatch-based dispatch in write methods
Hardware interfacePhysical GIC operationsarm_gicv2::GicInterfacecalls
Error handlingOperation validationAxResultreturn types

State Synchronization

The interface ensures thread-safe access to the VGIC state through mutex protection. Write operations that modify interrupt enable states trigger corresponding operations on the physical GIC hardware.

flowchart TD
subgraph subGraph2["Hardware Interface"]
    GicSetEnable["GicInterface::set_enable()"]
    GicSetPriority["GicInterface::set_priority()"]
    PhysicalGIC["Physical GIC Hardware"]
end
subgraph subGraph1["VGIC Processing"]
    DevOpsWrite["handle_write()"]
    StateUpdate["VgicInner Updateused_irq[] modification"]
    MutexProtection["Mutex"]
end
subgraph subGraph0["Guest Operation"]
    GuestWrite["Guest WriteEnable Interrupt"]
end

DevOpsWrite --> MutexProtection
GicSetEnable --> PhysicalGIC
GicSetPriority --> PhysicalGIC
GuestWrite --> DevOpsWrite
MutexProtection --> StateUpdate
StateUpdate --> GicSetEnable
StateUpdate --> GicSetPriority

Sources: src/vgic.rs(L15 - L30)  src/vgic.rs(L76 - L93) 

Constants and Register Layout

Relevant source files

This page documents the system constants, register offsets, and memory layout configuration used by the Virtual Generic Interrupt Controller (VGIC) implementation. These constants define the interrupt ID ranges, register mappings, and hardware configuration limits that govern how the virtual interrupt controller emulates ARM GIC functionality for guest virtual machines.

For information about how these constants are used in the main VGIC implementation, see Virtual GIC Controller (Vgic). For details on the CPU interface configuration, see CPU Interface (Vgicc).

Interrupt ID Ranges and Classifications

The VGIC implementation supports three categories of interrupts as defined by the ARM GIC specification, each with specific ID ranges and usage patterns.

Interrupt Type Limits

Interrupt TypeConstantValueID RangeDescription
Software Generated Interrupts (SGI)SGI_ID_MAX160-15Inter-processor interrupts for VM communication
Private Peripheral Interrupts (PPI)PPI_ID_MAX3216-31Per-CPU private interrupts
Shared Peripheral Interrupts (SPI)SPI_ID_MAX51232-543Shared interrupts across multiple CPUs

List Register Configuration

The CPU interface supports a fixed number of list registers for interrupt virtualization:

ConfigurationConstantValuePurpose
List RegistersGICD_LR_NUM4Maximum number of pending interrupts per CPU

Interrupt ID Space Layout

flowchart TD
subgraph subGraph0["Interrupt ID Space (0-543)"]
    SGI["SGI0-15SGI_ID_MAX=16"]
    PPI["PPI16-31PPI_ID_MAX=32"]
    SPI["SPI32-543SPI_ID_MAX=512"]
end

PPI --> SPI
SGI --> PPI

Sources: src/consts.rs(L1 - L4) 

VGIC Distributor Register Layout

The Virtual GIC Distributor (VGICD) registers are mapped to specific offsets within the device memory space. These offsets correspond to standard ARM GIC register locations that guest VMs expect to access.

Control and Status Registers

RegisterConstantOffsetAccessPurpose
Control RegisterVGICD_CTLR0x0000RWGlobal interrupt controller enable/disable

Interrupt Enable Registers

The interrupt enable registers are split by interrupt type to optimize access patterns:

RegisterConstantOffsetAccessPurpose
Set Enable SGI/PPIVGICD_ISENABLER_SGI_PPI0x0100RWEnable SGI and PPI interrupts (ID 0-31)
Set Enable SPIVGICD_ISENABLER_SPI0x0104RWEnable SPI interrupts (ID 32+)
Clear Enable SGI/PPIVGICD_ICENABLER_SGI_PPI0x0180RWDisable SGI and PPI interrupts (ID 0-31)
Clear Enable SPIVGICD_ICENABLER_SPI0x0184RWDisable SPI interrupts (ID 32+)

Interrupt State Registers

RegisterConstantOffsetAccessPurpose
Set PendingVGICD_ISPENDR0x0200RWSet interrupt pending state
Clear PendingVGICD_ICPENDR0x5RWClear interrupt pending state
Set ActiveVGICD_ISACTIVER0x6RWSet interrupt active state
Clear ActiveVGICD_ICACTIVER0x7RWClear interrupt active state

Configuration and Control Registers

RegisterConstantOffsetAccessPurpose
ConfigurationVGICD_ICFGR0x18RWInterrupt trigger type configuration
Software Generated InterruptVGICD_SGIR0x1eWOGenerate software interrupts

VGIC Register Memory Map

flowchart TD
subgraph subGraph4["VGIC Distributor Register Space"]
    ISACTIVER["VGICD_ISACTIVERSet Active"]
    ICACTIVER["VGICD_ICACTIVERClear Active"]
    ICENABLER_SGIPPI["VGICD_ICENABLER_SGI_PPI0x0180Clear Enable SGI/PPI"]
    subgraph subGraph1["Enable Registers (0x0100-0x01FF)"]
        ICFGR["VGICD_ICFGRConfiguration"]
        SGIR["VGICD_SGIRSoftware Generated IRQ"]
        ISPENDR["VGICD_ISPENDR0x0200Set Pending"]
        ICPENDR["VGICD_ICPENDRClear Pending"]
        ISENABLER_SGIPPI["VGICD_ISENABLER_SGI_PPI0x0100Set Enable SGI/PPI"]
        ISENABLER_SPI["VGICD_ISENABLER_SPI0x0104Set Enable SPI"]
        ICENABLER_SPI["VGICD_ICENABLER_SPI0x0184Clear Enable SPI"]
        CTLR["VGICD_CTLR0x0000Control Register"]
        subgraph subGraph2["Pending/Active Registers (0x0200+)"]
            ISACTIVER["VGICD_ISACTIVERSet Active"]
            ICACTIVER["VGICD_ICACTIVERClear Active"]
            ICENABLER_SGIPPI["VGICD_ICENABLER_SGI_PPI0x0180Clear Enable SGI/PPI"]
            subgraph subGraph3["Configuration (Various Offsets)"]
                ICFGR["VGICD_ICFGRConfiguration"]
                SGIR["VGICD_SGIRSoftware Generated IRQ"]
                ISPENDR["VGICD_ISPENDR0x0200Set Pending"]
                ICPENDR["VGICD_ICPENDRClear Pending"]
                ISENABLER_SGIPPI["VGICD_ISENABLER_SGI_PPI0x0100Set Enable SGI/PPI"]
                ISENABLER_SPI["VGICD_ISENABLER_SPI0x0104Set Enable SPI"]
                CTLR["VGICD_CTLR0x0000Control Register"]
            end
            subgraph subGraph0["Control Block (0x0000-0x00FF)"]
                ICFGR["VGICD_ICFGRConfiguration"]
                ISPENDR["VGICD_ISPENDR0x0200Set Pending"]
                ISENABLER_SGIPPI["VGICD_ISENABLER_SGI_PPI0x0100Set Enable SGI/PPI"]
                CTLR["VGICD_CTLR0x0000Control Register"]
            end
        end
    end
end

Sources: src/consts.rs(L6 - L19) 

Register Access Patterns

The register layout follows ARM GIC specification patterns where related registers are grouped by function and interrupt type. The separation of SGI/PPI and SPI enable registers reflects the different handling requirements for private versus shared interrupts.

Enable Register Organization

Enable Register Bit Mapping

flowchart TD
subgraph subGraph1["VGICD_ISENABLER_SPI (0x0104)"]
    SPI_BITS["Bits 0-31SPI Enable(ID 32-63)"]
    SPI_NEXT["Additional SPIregisters forID 64+"]
    SGI_BITS["Bits 0-15SGI Enable(ID 0-15)"]
    PPI_BITS["Bits 16-31PPI Enable(ID 16-31)"]
end
subgraph subGraph0["VGICD_ISENABLER_SGI_PPI (0x0100)"]
    SPI_BITS["Bits 0-31SPI Enable(ID 32-63)"]
    SPI_NEXT["Additional SPIregisters forID 64+"]
    SGI_BITS["Bits 0-15SGI Enable(ID 0-15)"]
    PPI_BITS["Bits 16-31PPI Enable(ID 16-31)"]
end

SGI_BITS --> PPI_BITS
SPI_BITS --> SPI_NEXT

Register Offset Patterns

The register offsets follow standard ARM GIC conventions:

  • 0x0000-0x00FF: Control and identification registers
  • 0x0100-0x017F: Interrupt Set Enable registers
  • 0x0180-0x01FF: Interrupt Clear Enable registers
  • 0x0200-0x027F: Interrupt Set Pending registers
  • 0x0280-0x02FF: Interrupt Clear Pending registers

This layout ensures compatibility with guest operating systems expecting standard GIC register locations while allowing the hypervisor to intercept and virtualize all interrupt controller access.

Sources: src/consts.rs(L7 - L19) 

Dependencies and Integration

Relevant source files

This document analyzes the external dependencies of the arm_vgic crate and explains how it integrates with the broader ArceOS hypervisor ecosystem. It covers dependency categorization, version management, integration patterns, and build system configuration.

For detailed information about the internal system architecture, see System Architecture. For documentation of specific component implementations, see Core Components.

Dependency Overview

The arm_vgic crate integrates with the ArceOS hypervisor ecosystem through a carefully structured set of dependencies that provide device framework support, memory management, hardware abstraction, and system utilities.

Dependency Categories

The dependencies can be categorized into four main groups based on their role in the system:

CategoryDependenciesPurpose
ArceOS Frameworkaxdevice_base,axaddrspace,axerrnoCore hypervisor framework integration
Hardware Abstractionarm_gicv2,memory_addrPhysical hardware interface and memory primitives
System Utilitiesspin,logThread synchronization and logging
Development Tools(Cargo build system)Build configuration and dependency resolution

Dependency Resolution Architecture

flowchart TD
subgraph subGraph4["Transitive Dependencies"]
    TOCK["tock-registers"]
    CRATE_IF["crate_interface"]
    BITFLAGS["bitflags"]
    MEMORY_SET["memory_set"]
end
subgraph subGraph3["System Utilities"]
    SPIN["spin::Mutex"]
    LOG["log macros"]
end
subgraph subGraph2["Hardware Abstraction"]
    ARM_GIC["arm_gicv2"]
    MEM_ADDR["memory_addr"]
end
subgraph subGraph1["ArceOS Framework Dependencies"]
    AXDEVICE["axdevice_base"]
    AXADDR["axaddrspace"]
    AXERRNO["axerrno"]
end
subgraph subGraph0["arm_vgic Crate"]
    VGIC["Vgic"]
    DEVOPS["BaseDeviceOps"]
    VGICC["Vgicc"]
end

ARM_GIC --> CRATE_IF
ARM_GIC --> TOCK
AXADDR --> BITFLAGS
AXADDR --> MEMORY_SET
AXDEVICE --> AXADDR
DEVOPS --> AXDEVICE
VGIC --> ARM_GIC
VGIC --> AXADDR
VGIC --> AXERRNO
VGIC --> LOG
VGIC --> MEM_ADDR
VGIC --> SPIN

Sources: Cargo.toml(L7 - L17)  Cargo.lock(L24 - L34) 

ArceOS Framework Integration

Device Framework Integration

The arm_vgic crate integrates with the ArceOS device framework through the axdevice_base dependency, which provides the foundational device abstraction layer.

flowchart TD
subgraph subGraph1["axdevice_base Components"]
    BASE_DEV["BaseDevice"]
    ADDR_RANGE["AddressRange"]
    DEV_ERROR["DeviceError"]
end
subgraph subGraph0["Device Framework Flow"]
    VM["VM Memory Access"]
    TRAIT["BaseDeviceOps trait"]
    IMPL["DevOps Implementation"]
    VGIC_CORE["Vgic Core Logic"]
end
AXADDR_DEP["axaddrspace"]
AXERRNO_DEP["axerrno"]

BASE_DEV --> AXADDR_DEP
DEV_ERROR --> AXERRNO_DEP
IMPL --> ADDR_RANGE
IMPL --> BASE_DEV
IMPL --> DEV_ERROR
IMPL --> VGIC_CORE
TRAIT --> IMPL
VM --> TRAIT

Sources: Cargo.toml(L8)  Cargo.lock(L60 - L69) 

Memory Management Integration

The axaddrspace dependency provides memory management capabilities including address space abstraction and page table management.

Key Integration Points:

  • Address Translation: Converting guest virtual addresses to host physical addresses
  • Memory Protection: Enforcing access permissions for virtualized GIC registers
  • Address Range Validation: Ensuring memory accesses fall within valid GIC register ranges

Error Handling Integration

The axerrno dependency provides standardized error handling across the ArceOS ecosystem.

Error Integration Pattern:

  • Device operation errors propagate through axerrno::AxResult<T>
  • Error codes maintain consistency with the broader hypervisor framework
  • Logging integration provides structured error reporting

Sources: Cargo.toml(L9 - L11)  Cargo.lock(L43 - L57)  Cargo.lock(L72 - L78) 

Hardware Abstraction Layer

Physical GIC Driver Integration

The arm_gicv2 dependency provides the interface to physical ARM Generic Interrupt Controller hardware.

flowchart TD
subgraph subGraph2["Hardware Registers"]
    GICD["GICD_* Registers"]
    GICC["GICC_* Registers"]
    GICH["GICH_* Registers"]
end
subgraph subGraph1["arm_gicv2 Physical Driver"]
    GIC_INTERFACE["GicInterface"]
    GIC_DISTRIBUTOR["GicDistributor"]
    GIC_CPU["GicCpu"]
    PHYS_OPS["Physical Operations"]
end
subgraph subGraph0["VGIC Virtualization Layer"]
    VGIC_CTRL["Vgic Controller"]
    REG_HANDLERS["Register Handlers"]
    STATE_MGT["State Management"]
end

GIC_INTERFACE --> PHYS_OPS
PHYS_OPS --> GICC
PHYS_OPS --> GICD
PHYS_OPS --> GICH
REG_HANDLERS --> GIC_INTERFACE
STATE_MGT --> GIC_CPU
STATE_MGT --> GIC_DISTRIBUTOR

Integration Characteristics:

  • Version Pinning: Uses specific git revision 2289063 for stability
  • Hardware Abstraction: Provides register-level access through tock-registers
  • Interface Consistency: Maintains consistent API with virtual GIC operations

Sources: Cargo.toml(L12)  Cargo.lock(L15 - L21) 

Memory Address Primitives

The memory_addr dependency provides type-safe memory address handling.

Memory Address Types Used:

  • PhysAddr: Physical memory addresses for hardware register access
  • VirtAddr: Virtual addresses for guest memory mapping
  • HostVirtAddr: Host virtual addresses for hypervisor operations

Sources: Cargo.toml(L10)  Cargo.lock(L150 - L159) 

System Utilities and Threading

Synchronization Primitives

The spin dependency provides lock-free synchronization primitives essential for hypervisor operation.

flowchart TD
subgraph subGraph1["spin Crate Primitives"]
    SPIN_MUTEX["spin::Mutex"]
    LOCK_API["lock_api"]
    MUTEX_GUARD_TYPE["MutexGuard"]
end
subgraph subGraph0["VGIC Synchronization Model"]
    VGIC_STRUCT["Vgic"]
    MUTEX_GUARD["Mutex"]
    INNER_STATE["VgicInner"]
    CRITICAL_SECTION["Critical Sections"]
end

INNER_STATE --> CRITICAL_SECTION
MUTEX_GUARD --> INNER_STATE
MUTEX_GUARD --> SPIN_MUTEX
SPIN_MUTEX --> LOCK_API
SPIN_MUTEX --> MUTEX_GUARD_TYPE
VGIC_STRUCT --> MUTEX_GUARD

Synchronization Strategy:

  • Lock-Free Design: Uses spinlocks suitable for hypervisor context
  • Fine-Grained Locking: Protects internal VGIC state without blocking interrupt handling
  • Deadlock Prevention: Consistent lock ordering across operations

Sources: Cargo.toml(L17)  Cargo.lock(L271 - L277) 

Logging Infrastructure

The log dependency provides structured logging capabilities integrated with the ArceOS logging framework.

Logging Integration:

  • Debug Information: Register access patterns and state transitions
  • Error Reporting: Failed operations and validation errors
  • Performance Monitoring: Operation timing and frequency analysis

Sources: Cargo.toml(L13)  Cargo.lock(L144 - L147) 

Build System Integration

Version Management Strategy

The build system uses a mixed approach to dependency version management:

Dependency TypeVersion StrategyRationale
ArceOS FrameworkGit repositories with latestActive development coordination
Hardware DriversGit with pinned revisionStability and compatibility
System UtilitiesSemantic versioningMature, stable APIs

Dependency Resolution Chain

flowchart TD
subgraph subGraph1["Transitive Dependencies"]
    TRANS1["tock-registers"]
    TRANS2["crate_interface"]
    TRANS3["bitflags"]
    TRANS4["lock_api"]
    TRANS5["memory_set"]
    TRANS6["page_table_entry"]
end
subgraph subGraph0["Direct Dependencies"]
    ARM_VGIC["arm_vgic"]
    DEP1["axdevice_base"]
    DEP2["axaddrspace"]
    DEP3["arm_gicv2"]
    DEP4["memory_addr"]
    DEP5["axerrno"]
    DEP6["spin"]
    DEP7["log"]
end

ARM_VGIC --> DEP1
ARM_VGIC --> DEP2
ARM_VGIC --> DEP3
ARM_VGIC --> DEP4
ARM_VGIC --> DEP5
ARM_VGIC --> DEP6
ARM_VGIC --> DEP7
DEP2 --> TRANS3
DEP2 --> TRANS5
DEP2 --> TRANS6
DEP3 --> TRANS1
DEP3 --> TRANS2
DEP6 --> TRANS4

Build Configuration Characteristics:

  • Edition 2021: Uses latest Rust edition features
  • No Default Features: Minimal dependency surface
  • Git Source Priority: Framework dependencies use git sources for latest features

Sources: Cargo.toml(L1 - L18)  Cargo.lock(L1 - L330) 

Integration Patterns and Architectural Considerations

Layered Integration Model

The arm_vgic crate follows a layered integration pattern that maintains clear separation of concerns:

  1. Framework Layer: Integrates with ArceOS device and memory management
  2. Abstraction Layer: Uses hardware abstraction for physical GIC access
  3. Utility Layer: Leverages system utilities for synchronization and logging
  4. Application Layer: Exposes virtualization capabilities to hypervisor

Dependency Injection and Inversion of Control

The crate uses dependency inversion principles where high-level virtualization logic depends on abstractions rather than concrete implementations, enabling:

  • Testability: Mock implementations for unit testing
  • Portability: Different hardware backends through common interfaces
  • Maintainability: Clear separation between virtualization logic and platform specifics

Sources: Cargo.toml(L7 - L17)  Cargo.lock(L24 - L34) 

Dependency Analysis

Relevant source files

This document provides a comprehensive analysis of the external dependencies used by the arm_vgic crate and how they integrate to enable virtual interrupt controller functionality within the ArceOS hypervisor ecosystem. The analysis covers direct dependencies, their roles in the system architecture, version constraints, and integration patterns.

For information about the overall system architecture and component interactions, see System Architecture. For details about how dependencies are configured in the build system, see Build Configuration.

Direct Dependencies Overview

The arm_vgic crate relies on seven direct dependencies that provide foundational capabilities for device emulation, memory management, hardware abstraction, and system utilities. Each dependency serves a specific architectural role in implementing the virtual GIC functionality.

ArceOS Framework Dependencies

The core ArceOS framework dependencies provide the foundational abstractions for device emulation and memory management within the hypervisor environment.

DependencyVersion/SourcePrimary Role
axdevice_baseGit repositoryDevice framework integration and trait definitions
axaddrspaceGit repositoryVirtual memory management and address space operations
axerrno0.1.0Standardized error handling across ArceOS components

Device Framework Integration

The axdevice_base dependency provides the BaseDeviceOps trait that enables the VGIC to integrate with the ArceOS device management framework. This integration is implemented in devops_impl.rs(L1 - L87)  where the Vgic struct implements device operations for memory-mapped I/O handling.

Memory Management Abstraction

The axaddrspace dependency supplies memory management primitives used throughout the VGIC implementation for address translation, memory region management, and virtual memory operations. The integration occurs primarily in the device operations where guest physical addresses are translated and validated.

Sources: Cargo.toml(L7 - L11)  Cargo.lock(L43 - L78) 

Hardware Abstraction Dependencies

These dependencies provide low-level hardware abstractions and type definitions for ARM GIC hardware and memory address handling.

DependencyVersion/SourceHardware Component
arm_gicv2Git commit 2289063Physical ARM GICv2 hardware interface
memory_addr0.3Memory address types and operations

Physical GIC Interface

The arm_gicv2 dependency provides the hardware abstraction layer for interfacing with physical ARM Generic Interrupt Controller version 2 hardware. The VGIC uses this interface to forward virtual interrupt operations to the underlying physical hardware when necessary.

flowchart TD
subgraph subGraph1["Memory Abstraction"]
    MEMORY_ADDR["memory_addr types"]
    ADDR_OPS["Address Operations"]
end
subgraph subGraph0["Hardware Abstraction Layer"]
    VGIC["Vgic struct"]
    ARM_GIC["arm_gicv2::GicInterface"]
    PHYSICAL["Physical GIC Hardware"]
end

ARM_GIC --> PHYSICAL
MEMORY_ADDR --> ADDR_OPS
VGIC --> ARM_GIC
VGIC --> MEMORY_ADDR

Sources: Cargo.toml(L10 - L12)  Cargo.lock(L15 - L159) 

System Utility Dependencies

These dependencies provide essential system-level utilities for logging, synchronization, and concurrent access control.

DependencyVersionFunctionality
spin0.9Spinlock-based synchronization primitives
log0.4.21Structured logging and debug output

Synchronization Infrastructure

The spin crate provides the Mutex type used to protect the VgicInner state from concurrent access. This is critical for thread-safe operation of the virtual interrupt controller across multiple CPU cores.

Logging Framework

The log crate enables structured logging throughout the VGIC implementation for debugging, monitoring, and system analysis. Log statements are used extensively in interrupt handling paths for troubleshooting virtualization behavior.

Sources: Cargo.toml(L13 - L17)  Cargo.lock(L144 - L277) 

Dependency Relationship Analysis

The following diagram illustrates how dependencies interact and form the foundation for VGIC functionality:

flowchart TD
subgraph subGraph2["Transitive Dependencies"]
    TOCK["tock-registers"]
    BITFLAGS["bitflags"]
    LOCK_API["lock_api"]
    SERDE["serde"]
end
subgraph subGraph1["Direct Dependencies"]
    AXDEVICE["axdevice_base"]
    AXADDR["axaddrspace"]
    AXERRNO["axerrno"]
    ARM_GIC["arm_gicv2"]
    MEMORY_ADDR["memory_addr"]
    SPIN["spin"]
    LOG["log"]
end
subgraph subGraph0["arm_vgic Crate"]
    VGIC_IMPL["Vgic Implementation"]
    DEVOPS_IMPL["BaseDeviceOps Implementation"]
    VGIC_INNER["VgicInner Protected State"]
end

ARM_GIC --> TOCK
AXADDR --> BITFLAGS
AXDEVICE --> SERDE
DEVOPS_IMPL --> AXDEVICE
SPIN --> LOCK_API
VGIC_IMPL --> ARM_GIC
VGIC_IMPL --> AXADDR
VGIC_IMPL --> AXERRNO
VGIC_IMPL --> LOG
VGIC_IMPL --> MEMORY_ADDR
VGIC_INNER --> SPIN

Sources: Cargo.toml(L7 - L17)  Cargo.lock(L24 - L34) 

Version Constraints and Compatibility

The dependency version strategy reflects the development status and integration requirements within the ArceOS ecosystem.

Git-based Dependencies

Four dependencies are sourced directly from Git repositories, indicating active development and tight integration with the ArceOS ecosystem:

RepositoryCommit/BranchIntegration Level
axdevice_crates.gitLatestCore device framework
axaddrspace.gitLatestMemory management
arm_gicv2.gitCommit 2289063Hardware abstraction

The use of specific commit hashes for arm_gicv2 ensures reproducible builds while allowing controlled updates to the hardware abstraction layer.

Crates.io Dependencies

Standard dependencies from crates.io follow semantic versioning with conservative version constraints:

flowchart TD
subgraph subGraph1["Compatibility Strategy"]
    CONSERVATIVE["Conservative Updates"]
    STABILITY["API Stability"]
    ECOSYSTEM["Ecosystem Integration"]
end
subgraph subGraph0["Version Constraints"]
    MEMORY["memory_addr = 0.3"]
    AXERRNO["axerrno = 0.1.0"]
    LOG["log = 0.4.21"]
    SPIN["spin = 0.9"]
end

AXERRNO --> STABILITY
LOG --> ECOSYSTEM
MEMORY --> CONSERVATIVE
SPIN --> STABILITY

Sources: Cargo.toml(L10 - L17) 

Integration Patterns

The dependencies are integrated into the VGIC implementation through several distinct patterns that reflect their architectural roles.

Device Framework Integration Pattern

The axdevice_base integration follows the trait-based device abstraction pattern:

flowchart TD
subgraph subGraph0["Device Framework Pattern"]
    TRAIT["BaseDeviceOps trait"]
    IMPL["Vgic BaseDeviceOps impl"]
    METHODS["read/write methods"]
    DISPATCH["Width-based dispatch"]
end

IMPL --> METHODS
METHODS --> DISPATCH
TRAIT --> IMPL

This pattern enables the VGIC to participate in the broader ArceOS device management ecosystem while maintaining type safety and performance.

Hardware Abstraction Pattern

The arm_gicv2 dependency is used through a delegation pattern where virtual operations are selectively forwarded to physical hardware:

flowchart TD
subgraph subGraph0["Hardware Delegation Pattern"]
    VIRTUAL_OP["Virtual GIC Operation"]
    VALIDATION["Operation Validation"]
    DECISION["Forward Decision"]
    PHYSICAL_OP["Physical GIC Operation"]
    STATE_UPDATE["Virtual State Update"]
end

DECISION --> PHYSICAL_OP
DECISION --> STATE_UPDATE
VALIDATION --> DECISION
VIRTUAL_OP --> VALIDATION

Synchronization Pattern

The spin dependency provides thread-safe access to shared VGIC state through a mutex-protected inner structure:

flowchart TD
subgraph subGraph0["Synchronization Pattern"]
    EXTERNAL_ACCESS["External Access Request"]
    MUTEX_ACQUIRE["spin::Mutex::lock()"]
    PROTECTED_ACCESS["VgicInner Access"]
    MUTEX_RELEASE["Automatic Drop"]
end

EXTERNAL_ACCESS --> MUTEX_ACQUIRE
MUTEX_ACQUIRE --> PROTECTED_ACCESS
PROTECTED_ACCESS --> MUTEX_RELEASE

Sources: devops_impl.rs(L1 - L87)  vgic.rs(L1 - L200) 

Transitive Dependency Analysis

The transitive dependencies reveal the underlying infrastructure that supports the direct dependencies:

Transitive DependencyUsed ByPurpose
tock-registersarm_gicv2Register-level hardware access
bitflagsaxaddrspaceBit field manipulation
lock_apispinLock trait abstractions
serdeaxdevice_baseSerialization support
memory_addr(0.2.1)axaddrspaceAddress type compatibility

The presence of two versions of memory_addr (0.2.1 and 0.3.0) indicates a transitional period in the ecosystem where different components are migrating to newer versions at different rates.

Sources: Cargo.lock(L150 - L168) 

Dependency Security and Maintenance

All dependencies follow established Rust ecosystem patterns for security and maintenance:

  • Crates.io dependencies benefit from the Rust Security Advisory database monitoring
  • Git dependencies require manual monitoring but provide direct control over updates
  • Version pinning for arm_gicv2 ensures consistent hardware behavior across builds
  • Semantic versioning for utility crates allows safe automatic updates within major version boundaries

The dependency structure supports both stability (through version constraints) and flexibility (through Git-sourced framework components) required for hypervisor development.

Sources: Cargo.toml(L1 - L18)  Cargo.lock(L1 - L330) 

Build Configuration

Relevant source files

This document covers the build system configuration for the arm_vgic crate, including package metadata, dependency management, and development workflow. It explains how the crate is compiled and integrated within the ArceOS hypervisor ecosystem.

For information about runtime dependencies and system integration, see Dependency Analysis. For architectural details about the components themselves, see Core Components.

Package Configuration

The arm_vgic crate is configured as a standard Rust library package using Cargo. The package metadata defines the basic build parameters and compilation target.

ConfigurationValuePurpose
Package Namearm_vgicCrate identifier for dependency resolution
Rust Edition2021Language feature set and compatibility
Package TypeLibraryCompiled as a library crate for integration

Build Targets and Artifacts

flowchart TD
subgraph subGraph3["Integration Points"]
    HYPERVISOR["ArceOS Hypervisor"]
    DEVICE_FRAMEWORK["axdevice_base"]
end
subgraph subGraph2["Build Artifacts"]
    RLIB["libarm_vgic.rlib"]
    METADATA["Crate Metadata"]
    TARGET_DIR["target/ directory"]
end
subgraph subGraph1["Cargo Build Process"]
    CARGO_BUILD["cargo build"]
    RUSTC["rustc compiler"]
    DEP_RESOLUTION["Dependency Resolution"]
end
subgraph subGraph0["Source Files"]
    SRC_LIB["src/lib.rs"]
    SRC_VGIC["src/vgic.rs"]
    SRC_VGICC["src/vgicc.rs"]
    SRC_CONSTS["src/consts.rs"]
    SRC_DEVOPS["src/devops_impl.rs"]
end

CARGO_BUILD --> DEP_RESOLUTION
CARGO_BUILD --> RUSTC
DEP_RESOLUTION --> RUSTC
DEVICE_FRAMEWORK --> DEP_RESOLUTION
METADATA --> TARGET_DIR
RLIB --> HYPERVISOR
RLIB --> TARGET_DIR
RUSTC --> METADATA
RUSTC --> RLIB
SRC_CONSTS --> CARGO_BUILD
SRC_DEVOPS --> CARGO_BUILD
SRC_LIB --> CARGO_BUILD
SRC_VGIC --> CARGO_BUILD
SRC_VGICC --> CARGO_BUILD

Sources: Cargo.toml(L1 - L18) 

Dependency Configuration

The crate's dependencies are configured to integrate with the ArceOS ecosystem and provide ARM virtualization capabilities. Dependencies are sourced from both Git repositories and crates.io.

Git Repository Dependencies

DependencySource RepositoryPurpose
axdevice_basegithub.com/arceos-hypervisor/axdevice_crates.gitDevice framework integration
axaddrspacegithub.com/arceos-hypervisor/axaddrspace.gitMemory management interface
arm_gicv2github.com/luodeb/arm_gicv2.git (rev: 2289063)Physical GIC hardware driver

Crates.io Dependencies

DependencyVersionPurpose
memory_addr0.3Memory address abstractions
axerrno0.1.0Error handling types
log0.4.21Logging infrastructure
spin0.9Synchronization primitives
flowchart TD
subgraph subGraph4["arm_vgic Build Dependencies"]
    VGIC["arm_vgic crate"]
    subgraph subGraph3["Source Locations"]
        GIT_REPOS["Git Repositories"]
        CRATES_IO["crates.io"]
    end
    subgraph subGraph2["System Utilities"]
        LOG["log"]
        SPIN["spin"]
    end
    subgraph subGraph1["Hardware Abstraction"]
        ARM_GIC["arm_gicv2"]
        MEMORY_ADDR["memory_addr"]
    end
    subgraph subGraph0["ArceOS Framework"]
        AXDEVICE["axdevice_base"]
        AXADDR["axaddrspace"]
        AXERRNO["axerrno"]
    end
end

ARM_GIC --> GIT_REPOS
AXADDR --> GIT_REPOS
AXDEVICE --> GIT_REPOS
AXERRNO --> CRATES_IO
LOG --> CRATES_IO
MEMORY_ADDR --> CRATES_IO
SPIN --> CRATES_IO
VGIC --> ARM_GIC
VGIC --> AXADDR
VGIC --> AXDEVICE
VGIC --> AXERRNO
VGIC --> LOG
VGIC --> MEMORY_ADDR
VGIC --> SPIN

Sources: Cargo.toml(L7 - L17) 

Development Dependencies and Optional Features

The configuration includes commented-out dependencies that indicate potential future integrations or alternative development paths.

Commented Dependencies

// vcpu_if = { path = "../vcpu_if" }
// crate_interface = "0.1.3"

These suggest:

  • Local development with a vcpu_if crate for CPU virtualization interfaces
  • Potential use of crate_interface for dynamic plugin systems

The current build configuration prioritizes direct integration with ArceOS framework components rather than abstract interface layers.

Sources: Cargo.toml(L14 - L15) 

Build Workflow

The standard development workflow follows Cargo conventions with specific considerations for the hypervisor development environment.

Development Commands

flowchart TD
subgraph subGraph3["Development Workflow"]
    DEV_START["Development Start"]
    subgraph Artifacts["Artifacts"]
        DEBUG_BUILD["Debug Build"]
        RELEASE_BUILD["Release Build"]
        DOCUMENTATION["Generated Docs"]
    end
    subgraph subGraph1["Integration Steps"]
        CLONE_DEPS["Clone Git Dependencies"]
        RESOLVE_VERSIONS["Resolve Version Constraints"]
        COMPILE_DEPS["Compile Dependencies"]
        LINK_ARTIFACTS["Link Final Artifacts"]
    end
    subgraph subGraph0["Build Commands"]
        CARGO_CHECK["cargo check"]
        CARGO_BUILD["cargo build"]
        CARGO_TEST["cargo test"]
        CARGO_DOC["cargo doc"]
    end
end

CARGO_BUILD --> CARGO_TEST
CARGO_BUILD --> CLONE_DEPS
CARGO_CHECK --> CARGO_BUILD
CARGO_DOC --> DOCUMENTATION
CARGO_TEST --> CARGO_DOC
CLONE_DEPS --> RESOLVE_VERSIONS
COMPILE_DEPS --> LINK_ARTIFACTS
DEV_START --> CARGO_CHECK
LINK_ARTIFACTS --> DEBUG_BUILD
LINK_ARTIFACTS --> RELEASE_BUILD
RESOLVE_VERSIONS --> COMPILE_DEPS

Build Environment Requirements

RequirementPurpose
Rust toolchainCompilation and dependency management
Git accessFetching Git-based dependencies
Network connectivityDownloading crates.io dependencies
ARM target supportCross-compilation for ARM platforms

Sources: Cargo.toml(L1 - L18)  .gitignore(L1) 

Git Configuration

The project includes basic Git configuration to exclude build artifacts from version control.

Ignored Paths

  • /target - Cargo build output directory containing compiled artifacts

This ensures that only source code and configuration files are tracked, while build outputs are regenerated locally.

Sources: .gitignore(L1) 

Overview

Relevant source files

Purpose and Scope

The x86_vlapic crate provides a software implementation of a virtual Local Advanced Programmable Interrupt Controller (LAPIC) for x86 hypervisor systems. This crate enables hypervisors to emulate LAPIC functionality for guest virtual machines without requiring direct hardware access to the physical LAPIC.

The crate implements both legacy xAPIC (MMIO-based) and modern x2APIC (MSR-based) access methods, allowing guest VMs to interact with a fully functional virtual interrupt controller. For detailed information about the architectural design and component interactions, see Core Architecture. For comprehensive register documentation and functionality, see Register System.

Virtual LAPIC Fundamentals

A Local APIC is a critical component in x86 multiprocessor systems that handles interrupt delivery, inter-processor communication, and timer functionality for each CPU core. In virtualized environments, guest operating systems expect to interact with LAPIC hardware, but direct hardware access would compromise isolation between virtual machines.

The x86_vlapic crate solves this by providing a software-based LAPIC implementation that:

  • Virtualizes all standard LAPIC registers and functionality
  • Supports both xAPIC MMIO accesses (address range 0xFEE00000-0xFEE00FFF) and x2APIC MSR accesses (MSR range 0x800-0x8FF)
  • Manages interrupt state through virtualized register arrays (ISR, TMR, IRR)
  • Implements Local Vector Table (LVT) registers for various interrupt sources
  • Provides timer functionality with support for one-shot, periodic, and TSC-deadline modes

High-Level System Architecture

flowchart TD
subgraph subGraph4["ArceOS Infrastructure"]
    AddrSpace["axaddrspaceMemory Management"]
    DeviceBase["axdevice_baseDevice Framework"]
    ErrNo["axerrnoError Handling"]
end
subgraph subGraph3["Physical Memory"]
    PhysPage["4KB PhysFrameAllocated via axaddrspace"]
end
subgraph subGraph2["Virtual Register Layer"]
    VirtRegs["VirtualApicRegs<H: AxMmHal>4KB Memory Page Management"]
    RegStructure["LocalAPICRegstock-registers Structure"]
end
subgraph subGraph1["EmulatedLocalApic Device"]
    EmuDev["EmulatedLocalApic<H: AxMmHal>BaseDeviceOps Implementation"]
    AddrXlate["Address Translation Layerxapic_mmio_access_reg_offset()x2apic_msr_access_reg()"]
end
subgraph subGraph0["Guest VM Access Layer"]
    GuestMMIO["Guest xAPIC MMIO Access0xFEE00000-0xFEE00FFF"]
    GuestMSR["Guest x2APIC MSR Access0x800-0x8FF"]
end

AddrXlate --> VirtRegs
EmuDev --> AddrXlate
EmuDev --> DeviceBase
EmuDev --> ErrNo
GuestMMIO --> EmuDev
GuestMSR --> EmuDev
RegStructure --> PhysPage
VirtRegs --> AddrSpace
VirtRegs --> RegStructure

Sources: src/lib.rs(L32 - L44)  Cargo.toml(L14 - L16) 

Dual Access Method Support

The virtual LAPIC supports both traditional and modern CPU access methods through a unified interface:

xAPIC Mode (MMIO-Based)

  • Address Range: 0xFEE00000 to 0xFEE00FFF (4KB)
  • Access Method: Memory-mapped I/O through guest physical addresses
  • Implementation: BaseDeviceOps<AddrRange<GuestPhysAddr>> trait
  • Translation Function: xapic_mmio_access_reg_offset()

x2APIC Mode (MSR-Based)

  • Address Range: MSR 0x800 to 0x8FF
  • Access Method: Model-Specific Register instructions (RDMSR/WRMSR)
  • Implementation: BaseDeviceOps<SysRegAddrRange> trait
  • Translation Function: x2apic_msr_access_reg()
flowchart TD
subgraph subGraph2["Unified Register Interface"]
    RegOffset["ApicRegOffset enumCommon register addressing"]
    VirtRegsRead["VirtualApicRegs.handle_read()"]
    VirtRegsWrite["VirtualApicRegs.handle_write()"]
end
subgraph subGraph1["Address Translation"]
    XAPICXlate["xapic_mmio_access_reg_offsetGuestPhysAddr → ApicRegOffset"]
    X2APICXlate["x2apic_msr_access_regSysRegAddr → ApicRegOffset"]
end
subgraph subGraph0["Access Method Implementations"]
    XAPICImpl["BaseDeviceOps<AddrRange<GuestPhysAddr>>handle_read/handle_write"]
    X2APICImpl["BaseDeviceOps<SysRegAddrRange>handle_read/handle_write"]
end

RegOffset --> VirtRegsRead
RegOffset --> VirtRegsWrite
X2APICImpl --> X2APICXlate
X2APICXlate --> RegOffset
XAPICImpl --> XAPICXlate
XAPICXlate --> RegOffset

Sources: src/lib.rs(L67 - L112)  src/lib.rs(L114 - L159)  src/lib.rs(L23 - L24) 

Core Components

EmulatedLocalApic Device

The EmulatedLocalApic<H: AxMmHal> struct serves as the primary device interface, implementing the ArceOS device framework through BaseDeviceOps. It coordinates between guest access requests and the underlying virtual register management system.

VirtualApicRegs Management

The VirtualApicRegs<H: AxMmHal> component manages a 4KB memory page that contains all virtualized LAPIC registers. This page is structured according to the standard LAPIC register layout and uses the tock-registers crate for type-safe register access.

Static APIC Access Page

A statically allocated 4KB page (VIRTUAL_APIC_ACCESS_PAGE) provides the foundation for hardware virtualization features. The address of this page can be configured in hardware virtualization controls to enable optimized guest access handling.

Register System

The crate implements a comprehensive register system including:

  • Control Registers: ID, Version, Task Priority Register (TPR), Spurious Interrupt Vector Register (SVR)
  • Interrupt State Arrays: In-Service Register (ISR), Trigger Mode Register (TMR), Interrupt Request Register (IRR)
  • Local Vector Table: Timer, Thermal, Performance Counter, LINT0/1, Error, CMCI registers
  • Timer Subsystem: Initial Count, Current Count, Divide Configuration registers

Sources: src/lib.rs(L27 - L30)  src/lib.rs(L33 - L43)  src/lib.rs(L47 - L64)  Cargo.toml(L7 - L16) 

Dependencies and Integration

The crate integrates with the ArceOS hypervisor ecosystem through several key dependencies:

ComponentPurposeUsage
axaddrspaceMemory management and address space operationsPhysical frame allocation, address translation
axdevice_baseDevice emulation frameworkBaseDeviceOpstrait implementation, interrupt injection
axerrnoError handlingStandardized error types (AxResult)
tock-registersType-safe register accessRegister field definitions and access patterns
memory_addrAddress abstractionsGuestPhysAddr,HostPhysAddr,HostVirtAddrtypes

The crate is designed to work within hypervisor systems that provide hardware-assisted virtualization features, supporting both Intel VT-x and AMD-V virtualization extensions through the abstracted AxMmHal trait.

Sources: Cargo.toml(L7 - L16)  src/lib.rs(L16 - L21) 

Core Architecture

Relevant source files

This document explains the fundamental architectural design of the x86_vlapic virtual Local APIC implementation, covering the main components and their interactions. The architecture provides virtualization of x86 Local APIC functionality for hypervisor environments, supporting both legacy xAPIC (MMIO-based) and modern x2APIC (MSR-based) access patterns.

For detailed information about individual register implementations, see Register System. For specific LVT register functionality, see Local Vector Table (LVT).

Main Architectural Components

The x86_vlapic crate implements a three-layer architecture that provides complete virtualization of Local APIC functionality:

flowchart TD
subgraph subGraph3["Physical Memory"]
    PHYS_PAGE["PhysFrame<H>4KB Allocated Page"]
end
subgraph subGraph2["Register Virtualization Layer"]
    VAR["VirtualApicRegs<H>4KB Page Management"]
    LAPIC_REGS["LocalAPICRegsMemory-Mapped Structure"]
    LVT_CACHE["LocalVectorTableCached LVT State"]
    SVR_CACHE["SpuriousInterruptVectorRegisterLocalCached SVR State"]
end
subgraph subGraph1["Device Interface Layer"]
    ELA["EmulatedLocalApic<H>BaseDeviceOps Implementation"]
    XAPIC_TRANS["xapic_mmio_access_reg_offset()GuestPhysAddr → ApicRegOffset"]
    X2APIC_TRANS["x2apic_msr_access_reg()SysRegAddr → ApicRegOffset"]
end
subgraph subGraph0["Guest VM Access Layer"]
    GUEST_MMIO["Guest MMIO Access0xFEE0_0000-0xFEE0_0FFF"]
    GUEST_MSR["Guest MSR Access0x800-0x8FF"]
end

ELA --> X2APIC_TRANS
ELA --> XAPIC_TRANS
GUEST_MMIO --> ELA
GUEST_MSR --> ELA
LAPIC_REGS --> PHYS_PAGE
VAR --> LAPIC_REGS
VAR --> LVT_CACHE
VAR --> SVR_CACHE
X2APIC_TRANS --> VAR
XAPIC_TRANS --> VAR

Sources: src/lib.rs(L33 - L44)  src/vlapic.rs(L15 - L28)  src/consts.rs(L200 - L202)  src/consts.rs(L213 - L215) 

The EmulatedLocalApic<H> struct serves as the primary device interface, implementing BaseDeviceOps for both AddrRange<GuestPhysAddr> (xAPIC) and SysRegAddrRange (x2APIC) access patterns. It contains a single VirtualApicRegs<H> instance that manages the actual register virtualization.

Address Translation Architecture

The system implements two distinct address translation paths to convert guest accesses into internal register offsets:

flowchart TD
subgraph subGraph2["Common Register Space"]
    REG_ENUM["ApicRegOffset enumID, Version, TPR, etc."]
end
subgraph subGraph1["x2APIC Translation Path"]
    MSR_ADDR["SysRegAddr0x800 + offset"]
    MSR_SUB["addr - 0x800"]
    X2APIC_OFFSET["ApicRegOffset"]
end
subgraph subGraph0["xAPIC Translation Path"]
    MMIO_ADDR["GuestPhysAddr0xFEE0_0000 + offset"]
    MMIO_MASK["addr & 0xFFF"]
    MMIO_SHIFT["result >> 4"]
    MMIO_OFFSET["ApicRegOffset"]
end

MMIO_ADDR --> MMIO_MASK
MMIO_MASK --> MMIO_SHIFT
MMIO_OFFSET --> REG_ENUM
MMIO_SHIFT --> MMIO_OFFSET
MSR_ADDR --> MSR_SUB
MSR_SUB --> X2APIC_OFFSET
X2APIC_OFFSET --> REG_ENUM

Sources: src/consts.rs(L200 - L202)  src/consts.rs(L213 - L215)  src/consts.rs(L117 - L148) 

The xapic_mmio_access_reg_offset() function extracts the register offset from xAPIC MMIO addresses by masking the lower 12 bits and shifting right by 4, effectively converting byte offsets to 16-byte aligned register indices. The x2apic_msr_access_reg() function performs simple arithmetic subtraction from the MSR base address. Both paths converge on the same ApicRegOffset enum, enabling unified register handling.

Virtual Register Memory Management

The VirtualApicRegs<H> struct implements a sophisticated memory management system that provides both hardware-compatible register layout and software-accessible caching:

flowchart TD
subgraph subGraph2["Access Patterns"]
    DIRECT_ACCESS["Direct Hardware Accessvia virtual_lapic pointer"]
    CACHED_ACCESS["Cached Accessvia _last structures"]
end
subgraph subGraph1["4KB Physical Page"]
    LAPIC_LAYOUT["LocalAPICRegs Memory LayoutHardware-compatible structure"]
    REG_ARRAYS["Register ArraysISR[8], TMR[8], IRR[8]"]
    LVT_REGS["LVT RegistersTimer, Thermal, PMC, etc."]
    CTRL_REGS["Control RegistersID, Version, TPR, SVR, etc."]
end
subgraph subGraph0["VirtualApicRegs Structure"]
    VLAPIC_PTR["virtual_lapic: NonNull<LocalAPICRegs>Direct memory access"]
    APIC_PAGE["apic_page: PhysFrame<H>4KB page ownership"]
    SVR_LAST["svr_last: SpuriousInterruptVectorRegisterLocalChange detection cache"]
    LVT_LAST["lvt_last: LocalVectorTableCoherent snapshot cache"]
end

APIC_PAGE --> LAPIC_LAYOUT
LAPIC_LAYOUT --> CTRL_REGS
LAPIC_LAYOUT --> LVT_REGS
LAPIC_LAYOUT --> REG_ARRAYS
LVT_LAST --> CACHED_ACCESS
SVR_LAST --> CACHED_ACCESS
VLAPIC_PTR --> DIRECT_ACCESS
VLAPIC_PTR --> LAPIC_LAYOUT

Sources: src/vlapic.rs(L15 - L28)  src/vlapic.rs(L32 - L40)  src/vlapic.rs(L55 - L59) 

The system allocates a zero-initialized 4KB page using PhysFrame::alloc_zero() and maps the LocalAPICRegs structure directly onto this memory. The NonNull<LocalAPICRegs> pointer provides direct access to hardware-compatible register layout, while cached copies in svr_last and lvt_last enable change detection and maintain coherent snapshots of critical registers.

Device Interface Implementation

The EmulatedLocalApic<H> implements dual BaseDeviceOps traits to handle both xAPIC and x2APIC access patterns:


Sources: src/lib.rs(L67 - L112)  src/lib.rs(L114 - L159)  src/vlapic.rs(L62 - L176) 

Both access patterns converge on the same VirtualApicRegs::handle_read() and VirtualApicRegs::handle_write() methods, ensuring consistent behavior regardless of the guest's chosen access mode. The EmulatedLocalApic provides address range information through address_range() methods and implements device type identification via emu_type() returning EmuDeviceTInterruptController.

Static Memory Layout

The architecture includes a static 4KB page for APIC access virtualization:

ComponentPurposeMemory Management
VIRTUAL_APIC_ACCESS_PAGEStatic 4KB page for VMX APIC-access pageStatically allocated, aligned to 4KB
VirtualApicRegs.apic_pageDynamic 4KB page for register storageDynamically allocated viaPhysFrame::alloc_zero()
virtual_lapicpointerDirect access to register structurePoints to dynamically allocated page

Sources: src/lib.rs(L27 - L30)  src/lib.rs(L52 - L56)  src/vlapic.rs(L32 - L40) 

The static VIRTUAL_APIC_ACCESS_PAGE supports VMX virtualization features, while the dynamic page in VirtualApicRegs provides the actual register storage and implements proper memory lifecycle management through the Drop trait.

EmulatedLocalApic Device Interface

Relevant source files

Purpose and Scope

This document covers the EmulatedLocalApic struct, which serves as the primary device interface for virtual Local APIC emulation in the x86_vlapic crate. The EmulatedLocalApic implements the BaseDeviceOps trait twice to provide unified handling for both xAPIC MMIO accesses and x2APIC MSR accesses, while delegating actual register operations to the underlying virtual register management system.

For details on the virtual register management system that EmulatedLocalApic delegates to, see Virtual Register Management. For information about address translation mechanisms, see Register Address Translation.

Core EmulatedLocalApic Structure

The EmulatedLocalApic struct is a thin wrapper around the virtual register management system that provides the device interface layer required by the ArceOS hypervisor framework.

flowchart TD
subgraph subGraph3["Address Translation"]
    XAPIC_XLATE["xapic_mmio_access_reg_offset()"]
    X2APIC_XLATE["x2apic_msr_access_reg()"]
end
subgraph subGraph2["Device Framework Integration"]
    BDO_MMIO["BaseDeviceOps<AddrRange<GuestPhysAddr>>"]
    BDO_MSR["BaseDeviceOps<SysRegAddrRange>"]
end
subgraph subGraph0["EmulatedLocalApic Device Interface"]
    ELA["EmulatedLocalApic<H: AxMmHal>"]
    VAR["vlapic_regs: VirtualApicRegs<H>"]
end
subgraph subGraph1["Memory Management"]
    AAP["VIRTUAL_APIC_ACCESS_PAGE: APICAccessPage"]
    STATIC["Static 4KB aligned page"]
end

AAP --> STATIC
BDO_MMIO --> XAPIC_XLATE
BDO_MSR --> X2APIC_XLATE
ELA --> BDO_MMIO
ELA --> BDO_MSR
ELA --> VAR

EmulatedLocalApic Structure Components

ComponentTypePurpose
vlapic_regsVirtualApicRegsManages actual APIC register virtualization and storage

The struct uses a generic type parameter H: AxMmHal to integrate with the ArceOS memory management abstraction layer.

Sources: src/lib.rs(L33 - L35)  src/lib.rs(L39 - L43) 

Device Interface Implementation

The EmulatedLocalApic implements BaseDeviceOps twice to handle the two distinct access methods for Local APIC registers: xAPIC memory-mapped I/O and x2APIC model-specific registers.

Dual Interface Architecture

flowchart TD
subgraph subGraph4["Register Operations"]
    VLAPIC_REGS["VirtualApicRegs<H>"]
    READ_OP["handle_read()"]
    WRITE_OP["handle_write()"]
end
subgraph subGraph3["Address Translation Layer"]
    XAPIC_FUNC["xapic_mmio_access_reg_offset()"]
    X2APIC_FUNC["x2apic_msr_access_reg()"]
end
subgraph subGraph2["EmulatedLocalApic Interface Layer"]
    ELA["EmulatedLocalApic<H>"]
    subgraph subGraph1["BaseDeviceOps Implementations"]
        MMIO_IMPL["BaseDeviceOps<AddrRange<GuestPhysAddr>>"]
        MSR_IMPL["BaseDeviceOps<SysRegAddrRange>"]
    end
end
subgraph subGraph0["Guest Access Methods"]
    GUEST_MMIO["Guest MMIO Access0xFEE00000-0xFEE00FFF"]
    GUEST_MSR["Guest MSR Access0x800-0x8FF"]
end

ELA --> MMIO_IMPL
ELA --> MSR_IMPL
GUEST_MMIO --> MMIO_IMPL
GUEST_MSR --> MSR_IMPL
MMIO_IMPL --> XAPIC_FUNC
MSR_IMPL --> X2APIC_FUNC
READ_OP --> VLAPIC_REGS
WRITE_OP --> VLAPIC_REGS
X2APIC_FUNC --> READ_OP
X2APIC_FUNC --> WRITE_OP
XAPIC_FUNC --> READ_OP
XAPIC_FUNC --> WRITE_OP

Sources: src/lib.rs(L67 - L112)  src/lib.rs(L114 - L159) 

xAPIC MMIO Interface

The xAPIC interface handles memory-mapped I/O accesses in the traditional Local APIC address range.

Interface Configuration

PropertyValueDescription
Address Range0xFEE00000 - 0xFEE00FFF4KB MMIO region for xAPIC registers
Device TypeEmuDeviceTInterruptControllerIdentifies as interrupt controller device
Access Translationxapic_mmio_access_reg_offset()ConvertsGuestPhysAddrtoApicRegOffset

Access Flow

  1. Guest performs MMIO read/write to xAPIC address range
  2. handle_read() or handle_write() called with GuestPhysAddr
  3. Address translated via xapic_mmio_access_reg_offset()
  4. Operation delegated to VirtualApicRegs::handle_read()/handle_write()

Sources: src/lib.rs(L67 - L112)  src/lib.rs(L90 - L91)  src/lib.rs(L105 - L106) 

x2APIC MSR Interface

The x2APIC interface handles model-specific register accesses for the extended Local APIC mode.

Interface Configuration

PropertyValueDescription
Address Range0x800 - 0x8FFMSR range for x2APIC registers
Device TypeEmuDeviceTInterruptControllerIdentifies as interrupt controller device
Access Translationx2apic_msr_access_reg()ConvertsSysRegAddrtoApicRegOffset

Access Flow

  1. Guest performs MSR read/write to x2APIC address range
  2. handle_read() or handle_write() called with SysRegAddr
  3. Address translated via x2apic_msr_access_reg()
  4. Operation delegated to VirtualApicRegs::handle_read()/handle_write()

Sources: src/lib.rs(L114 - L159)  src/lib.rs(L137 - L138)  src/lib.rs(L152 - L153) 

Memory Management

The EmulatedLocalApic manages two distinct 4KB memory pages for virtual Local APIC functionality, each serving different purposes in the virtualization architecture.

APIC Access Page

flowchart TD
subgraph subGraph2["Access Methods"]
    ACCESSOR["virtual_apic_access_addr()"]
    HAL_CONVERT["H::virt_to_phys()"]
end
subgraph subGraph1["Hypervisor Integration"]
    VMCS["VM-execution control'virtualize APIC accesses'"]
    VM_EXIT["VM exits or processor virtualization"]
end
subgraph subGraph0["APIC Access Page Management"]
    STATIC_PAGE["VIRTUAL_APIC_ACCESS_PAGEAPICAccessPage"]
    ALIGNED_ARRAY["[u8; PAGE_SIZE_4K]4KB aligned static array"]
    VIRT_ADDR["HostVirtAddr"]
    PHYS_ADDR["HostPhysAddr"]
end

ACCESSOR --> HAL_CONVERT
ALIGNED_ARRAY --> VIRT_ADDR
HAL_CONVERT --> PHYS_ADDR
PHYS_ADDR --> VMCS
STATIC_PAGE --> ALIGNED_ARRAY
VIRT_ADDR --> PHYS_ADDR
VMCS --> VM_EXIT

The APIC access page is a static 4KB page used by hardware virtualization features to control guest access to the APIC MMIO region.

APIC Access Page Characteristics

PropertyValuePurpose
TypeAPICAccessPage4KB aligned wrapper around byte array
Alignment4096 bytesRequired by hardware virtualization
InitializationZero-filledSafe default state
ScopeStatic globalShared across all EmulatedLocalApic instances

Sources: src/lib.rs(L27 - L30)  src/lib.rs(L47 - L56) 

Virtual APIC Page

The virtual APIC page contains the actual register storage and is managed by the VirtualApicRegs component.

flowchart TD
subgraph subGraph0["Virtual APIC Page Access"]
    REG_STORAGE["VirtualApicRegs register storage"]
    subgraph subGraph1["Register Layout"]
        PHYS_PAGE["4KB Physical Page"]
        LAPIC_REGS["LocalAPICRegs structure"]
        REG_FIELDS["Individual APIC registers"]
        ELA_METHOD["virtual_apic_page_addr()"]
        DELEGATE["self.vlapic_regs.virtual_apic_page_addr()"]
    end
end

DELEGATE --> REG_STORAGE
ELA_METHOD --> DELEGATE
LAPIC_REGS --> REG_FIELDS
PHYS_PAGE --> LAPIC_REGS

The virtual APIC page provides the actual register storage that guests read from and write to during APIC operations.

Sources: src/lib.rs(L58 - L64) 

Address Translation and Delegation

The EmulatedLocalApic acts as an address translation layer, converting guest addresses to register offsets before delegating operations to the virtual register system.

Translation Architecture

sequenceDiagram
    participant GuestVM as "Guest VM"
    participant EmulatedLocalApic as "EmulatedLocalApic"
    participant AddressTranslator as "Address Translator"
    participant VirtualApicRegs as "VirtualApicRegs"

    Note over GuestVM,VirtualApicRegs: xAPIC MMIO Access Example
    GuestVM ->> EmulatedLocalApic: "handle_read(GuestPhysAddr, width, context)"
    EmulatedLocalApic ->> AddressTranslator: "xapic_mmio_access_reg_offset(addr)"
    AddressTranslator -->> EmulatedLocalApic: "ApicRegOffset"
    EmulatedLocalApic ->> VirtualApicRegs: "handle_read(reg_off, width, context)"
    VirtualApicRegs -->> EmulatedLocalApic: "AxResult<usize>"
    EmulatedLocalApic -->> GuestVM: "Register value"
    Note over GuestVM,VirtualApicRegs: x2APIC MSR Access Example
    GuestVM ->> EmulatedLocalApic: "handle_write(SysRegAddr, width, val, context)"
    EmulatedLocalApic ->> AddressTranslator: "x2apic_msr_access_reg(addr)"
    AddressTranslator -->> EmulatedLocalApic: "ApicRegOffset"
    EmulatedLocalApic ->> VirtualApicRegs: "handle_write(reg_off, width, context)"
    VirtualApicRegs -->> EmulatedLocalApic: "AxResult"
    EmulatedLocalApic -->> GuestVM: "Write complete"

Translation Functions

FunctionInput TypeOutput TypePurpose
xapic_mmio_access_reg_offset()GuestPhysAddrApicRegOffsetConvert xAPIC MMIO address to register offset
x2apic_msr_access_reg()SysRegAddrApicRegOffsetConvert x2APIC MSR address to register offset

Both translation functions convert different address formats to the unified ApicRegOffset enum used by the virtual register system.

Sources: src/lib.rs(L23 - L24)  src/lib.rs(L90 - L91)  src/lib.rs(L105 - L106)  src/lib.rs(L137 - L138)  src/lib.rs(L152 - L153) 

Virtual Register Management

Relevant source files

The Virtual Register Management system implements the core virtualization layer for APIC registers through the VirtualApicRegs struct. This system allocates and manages a 4KB memory page that contains a complete set of virtualized Local APIC registers, providing the foundation for both xAPIC MMIO and x2APIC MSR access patterns. For information about the device interface that uses this register management, see EmulatedLocalApic Device Interface. For details about address translation that routes to these registers, see Register Address Translation.

Memory Management Architecture

The VirtualApicRegs<H: AxMmHal> struct serves as the primary memory manager for virtualized APIC registers. It allocates a single 4KB physical memory page and maps the LocalAPICRegs structure directly onto this page, providing a complete virtualized APIC register set.

flowchart TD
subgraph subGraph2["Register Layout in Memory"]
    ID["ID (0x20)"]
    VER["VERSION (0x30)"]
    TPR["TPR (0x80)"]
    SVR["SVR (0xF0)"]
    ISR["ISR Arrays (0x100-0x170)"]
    TMR["TMR Arrays (0x180-0x1F0)"]
    IRR["IRR Arrays (0x200-0x270)"]
    LVT["LVT Registers (0x2F0-0x370)"]
    TIMER["Timer Registers (0x380-0x3E0)"]
end
subgraph subGraph1["Physical Memory"]
    PM["4KB PhysFrame"]
    LAR["LocalAPICRegs Structure"]
end
subgraph subGraph0["VirtualApicRegs Structure"]
    VR["VirtualApicRegs<H>"]
    VLP["virtual_lapic: NonNull<LocalAPICRegs>"]
    AP["apic_page: PhysFrame<H>"]
    SL["svr_last: SpuriousInterruptVectorRegisterLocal"]
    LL["lvt_last: LocalVectorTable"]
end

AP --> PM
LAR --> ID
LAR --> IRR
LAR --> ISR
LAR --> LVT
LAR --> SVR
LAR --> TIMER
LAR --> TMR
LAR --> TPR
LAR --> VER
PM --> LAR
VLP --> LAR
VR --> AP
VR --> LL
VR --> SL
VR --> VLP

The allocation process uses PhysFrame::alloc_zero() to obtain a zeroed 4KB page, ensuring clean initial register state. The NonNull<LocalAPICRegs> pointer provides direct access to the register structure while maintaining memory safety.

Sources: src/vlapic.rs(L15 - L40) 

Register Structure and Layout

The LocalAPICRegs structure defines the exact memory layout of the 4KB virtual APIC page, mapping all register offsets according to the Intel APIC specification. This structure uses the tock_registers framework to provide type-safe register access with proper read/write permissions.

flowchart TD
subgraph subGraph4["LocalAPICRegs Memory Map (4KB Page)"]
    subgraph subGraph2["Local Vector Table"]
        ICR_TIMER["ICR_TIMER: ReadWrite<u32> (0x380)"]
        CCR_TIMER["CCR_TIMER: ReadOnly<u32> (0x390)"]
        DCR_TIMER["DCR_TIMER: ReadWrite<u32> (0x3E0)"]
        LVT_CMCI["LVT_CMCI: LvtCmciRegisterMmio (0x2F0)"]
        LVT_TIMER["LVT_TIMER: LvtTimerRegisterMmio (0x320)"]
        LVT_THERMAL["LVT_THERMAL: LvtThermalMonitorRegisterMmio (0x330)"]
        LVT_PMI["LVT_PMI: LvtPerformanceCounterRegisterMmio (0x340)"]
        LVT_LINT0["LVT_LINT0: LvtLint0RegisterMmio (0x350)"]
        LVT_LINT1["LVT_LINT1: LvtLint1RegisterMmio (0x360)"]
        LVT_ERROR["LVT_ERROR: LvtErrorRegisterMmio (0x370)"]
        ISR_REG["ISR: [ReadOnly<u128>; 8] (0x100-0x170)"]
        TMR_REG["TMR: [ReadOnly<u128>; 8] (0x180-0x1F0)"]
        IRR_REG["IRR: [ReadOnly<u128>; 8] (0x200-0x270)"]
        ID_REG["ID: ReadWrite<u32> (0x20)"]
        VERSION_REG["VERSION: ReadOnly<u32> (0x30)"]
        TPR_REG["TPR: ReadWrite<u32> (0x80)"]
        SVR_REG["SVR: SpuriousInterruptVectorRegisterMmio (0xF0)"]
    end
    subgraph subGraph0["Control Registers"]
        ICR_TIMER["ICR_TIMER: ReadWrite<u32> (0x380)"]
        CCR_TIMER["CCR_TIMER: ReadOnly<u32> (0x390)"]
        DCR_TIMER["DCR_TIMER: ReadWrite<u32> (0x3E0)"]
        LVT_CMCI["LVT_CMCI: LvtCmciRegisterMmio (0x2F0)"]
        LVT_TIMER["LVT_TIMER: LvtTimerRegisterMmio (0x320)"]
        LVT_THERMAL["LVT_THERMAL: LvtThermalMonitorRegisterMmio (0x330)"]
        LVT_PMI["LVT_PMI: LvtPerformanceCounterRegisterMmio (0x340)"]
        ISR_REG["ISR: [ReadOnly<u128>; 8] (0x100-0x170)"]
        TMR_REG["TMR: [ReadOnly<u128>; 8] (0x180-0x1F0)"]
        IRR_REG["IRR: [ReadOnly<u128>; 8] (0x200-0x270)"]
        ID_REG["ID: ReadWrite<u32> (0x20)"]
        VERSION_REG["VERSION: ReadOnly<u32> (0x30)"]
        TPR_REG["TPR: ReadWrite<u32> (0x80)"]
        SVR_REG["SVR: SpuriousInterruptVectorRegisterMmio (0xF0)"]
    end
end

The register arrays (ISR, TMR, IRR) each consist of 8 x 128-bit fields that collectively represent 256-bit interrupt state vectors, with each bit corresponding to a specific interrupt vector number.

Sources: src/regs/mod.rs(L13 - L104) 

Register Access Patterns

The VirtualApicRegs implements register access through handle_read() and handle_write() methods that translate ApicRegOffset enum values to direct memory operations on the LocalAPICRegs structure. This provides a unified interface for both xAPIC MMIO and x2APIC MSR access patterns.

sequenceDiagram
    participant AccessClient as "Access Client"
    participant VirtualApicRegs as "VirtualApicRegs"
    participant LocalAPICRegs as "LocalAPICRegs"
    participant LocalCopies as "Local Copies"

    Note over AccessClient,LocalCopies: Register Read Operation
    AccessClient ->> VirtualApicRegs: "handle_read(offset, width, context)"
    VirtualApicRegs ->> VirtualApicRegs: "match offset"
    alt Standard Register
        VirtualApicRegs ->> LocalAPICRegs: "self.regs().REGISTER.get()"
        LocalAPICRegs -->> VirtualApicRegs: "register_value"
    else LVT Register
        VirtualApicRegs ->> LocalCopies: "self.lvt_last.register.get()"
        LocalCopies -->> VirtualApicRegs: "cached_value"
    else Array Register (ISR/TMR/IRR)
        VirtualApicRegs ->> LocalAPICRegs: "self.regs().ARRAY[index].get()"
        LocalAPICRegs -->> VirtualApicRegs: "array_element_value"
    end
    VirtualApicRegs -->> AccessClient: "Ok(value)"
    Note over AccessClient,LocalCopies: Register Write Operation
    AccessClient ->> VirtualApicRegs: "handle_write(offset, width, context)"
    VirtualApicRegs ->> VirtualApicRegs: "match offset and validate"
    VirtualApicRegs -->> AccessClient: "Ok(())"

The read operations access different register types through specific patterns:

  • Standard registers: Direct access via self.regs().REGISTER.get()
  • LVT registers: Cached access via self.lvt_last.register.get()
  • Array registers: Indexed access via self.regs().ARRAY[index].get()

Sources: src/vlapic.rs(L62 - L186) 

Caching Strategy

The VirtualApicRegs maintains local copies of certain registers to support change detection and coherent snapshots. This caching strategy ensures consistent behavior during register modifications and enables tracking of register state changes.

Cached Register TypeField NamePurpose
Spurious Interrupt Vectorsvr_last: SpuriousInterruptVectorRegisterLocalChange detection for SVR modifications
Local Vector Tablelvt_last: LocalVectorTableCoherent snapshot of all LVT registers
flowchart TD
subgraph subGraph3["VirtualApicRegs Caching Architecture"]
    subgraph subGraph2["Access Patterns"]
        READ_SVR["SVR Read Operations"]
        READ_LVT["LVT Read Operations"]
    end
    subgraph subGraph1["Local Copies"]
        CACHE_SVR["svr_last: SpuriousInterruptVectorRegisterLocal"]
        CACHE_LVT["lvt_last: LocalVectorTable"]
    end
    subgraph subGraph0["Hardware Registers"]
        HW_SVR["SVR in LocalAPICRegs"]
        HW_LVT["LVT Registers in LocalAPICRegs"]
    end
end

HW_LVT --> CACHE_LVT
HW_SVR --> CACHE_SVR
READ_LVT --> CACHE_LVT
READ_SVR --> HW_SVR

The cached LVT registers are accessed during read operations instead of the hardware registers, providing stable values during register modifications. The SVR cache enables detection of configuration changes that affect APIC behavior.

Sources: src/vlapic.rs(L21 - L26)  src/vlapic.rs(L37 - L38)  src/vlapic.rs(L131 - L157) 

Memory Management Lifecycle

The VirtualApicRegs implements proper resource management through RAII patterns, automatically deallocating the 4KB page when the instance is dropped.

flowchart TD
subgraph subGraph0["Lifecycle Management"]
    CREATE["VirtualApicRegs::new()"]
    ALLOC["PhysFrame::alloc_zero()"]
    INIT["Initialize cached registers"]
    USE["Runtime register access"]
    DROP["Drop trait implementation"]
    DEALLOC["H::dealloc_frame()"]
end

ALLOC --> INIT
CREATE --> ALLOC
DROP --> DEALLOC
INIT --> USE
USE --> DROP

The initialization process sets default values for cached registers using RESET_SPURIOUS_INTERRUPT_VECTOR for the SVR and LocalVectorTable::default() for the LVT state, ensuring proper initial configuration.

Sources: src/vlapic.rs(L30 - L40)  src/vlapic.rs(L55 - L59) 

Register Address Translation

Relevant source files

This document describes the address translation mechanism that converts guest memory accesses into internal register identifiers within the x86_vlapic crate. The translation layer serves as a bridge between two different APIC access methods (xAPIC MMIO and x2APIC MSR) and the unified internal register representation using the ApicRegOffset enum.

For information about the overall virtual register management system, see Virtual Register Management. For details about specific register layouts and functionality, see Register System.

Translation Architecture Overview

The address translation system provides a unified interface for accessing APIC registers regardless of whether the guest uses xAPIC MMIO-based access or x2APIC MSR-based access. Both access methods are translated into the same internal ApicRegOffset enum representation.

flowchart TD
subgraph subGraph4["Register Access Handlers"]
    VLAPIC_REGS["VirtualApicRegshandle_read/write methods"]
end
subgraph subGraph3["Unified Register Space"]
    APIC_REG_OFFSET["ApicRegOffset enumsrc/consts.rs:61-114"]
    subgraph subGraph2["Register Categories"]
        CONTROL_REGS["Control RegistersID, Version, TPR, etc."]
        ARRAY_REGS["Array RegistersISR, TMR, IRR"]
        LVT_REGS["LVT RegistersTimer, Thermal, PMC, etc."]
        TIMER_REGS["Timer RegistersInitCount, CurCount, DivConf"]
    end
end
subgraph subGraph1["Translation Functions"]
    XAPIC_FUNC["xapic_mmio_access_reg_offset()src/consts.rs:200-202"]
    X2APIC_FUNC["x2apic_msr_access_reg()src/consts.rs:213-215"]
end
subgraph subGraph0["Guest Access Methods"]
    GUEST_MMIO["Guest xAPIC MMIO AccessGuestPhysAddr0xFEE0_0000-0xFEE0_0FFF"]
    GUEST_MSR["Guest x2APIC MSR AccessSysRegAddr0x800-0x8FF"]
end

APIC_REG_OFFSET --> ARRAY_REGS
APIC_REG_OFFSET --> CONTROL_REGS
APIC_REG_OFFSET --> LVT_REGS
APIC_REG_OFFSET --> TIMER_REGS
ARRAY_REGS --> VLAPIC_REGS
CONTROL_REGS --> VLAPIC_REGS
GUEST_MMIO --> XAPIC_FUNC
GUEST_MSR --> X2APIC_FUNC
LVT_REGS --> VLAPIC_REGS
TIMER_REGS --> VLAPIC_REGS
X2APIC_FUNC --> APIC_REG_OFFSET
XAPIC_FUNC --> APIC_REG_OFFSET

Sources: src/consts.rs(L200 - L202)  src/consts.rs(L213 - L215)  src/consts.rs(L61 - L114)  src/lib.rs(L90)  src/lib.rs(L105)  src/lib.rs(L137)  src/lib.rs(L152) 

xAPIC MMIO Address Translation

The xAPIC mode uses Memory-Mapped I/O (MMIO) for register access. Guest physical addresses in the range 0xFEE0_0000 to 0xFEE0_0FFF are translated to register offsets using the xapic_mmio_access_reg_offset() function.

Translation Process

flowchart TD
subgraph Constants["Constants"]
    APIC_BASE["DEFAULT_APIC_BASE0xFEE0_0000"]
    APIC_SIZE["APIC_MMIO_SIZE0x1000 (4KB)"]
end
START["Guest Physical AddressGuestPhysAddr"]
MASK["Apply MMIO Size Maskaddr & (APIC_MMIO_SIZE - 1)Extract lower 12 bits"]
SHIFT["Right Shift by 4result >> 4Convert to 16-byte aligned offset"]
CONVERT["ApicRegOffset::from()Map offset to enum variant"]
END["ApicRegOffset enum"]

CONVERT --> END
MASK --> SHIFT
SHIFT --> CONVERT
START --> MASK

The translation algorithm:

  1. Mask Address: addr.as_usize() & (APIC_MMIO_SIZE - 1) extracts the offset within the 4KB APIC region
  2. Normalize to 16-byte boundaries: result >> 4 converts byte offset to register offset (APIC registers are 16-byte aligned)
  3. Convert to enum: ApicRegOffset::from(offset) maps the numeric offset to the appropriate enum variant

Address Mapping Examples

Guest Physical AddressMasked OffsetRegister OffsetApicRegOffset Enum
0xFEE0_00200x0200x2ApicRegOffset::ID
0xFEE0_00300x0300x3ApicRegOffset::Version
0xFEE0_01000x1000x10ApicRegOffset::ISR(ISRIndex0)
0xFEE0_03200x3200x32ApicRegOffset::LvtTimer

Sources: src/consts.rs(L192 - L203)  src/consts.rs(L197 - L198)  src/lib.rs(L90)  src/lib.rs(L105) 

x2APIC MSR Address Translation

The x2APIC mode uses Model-Specific Registers (MSRs) for register access. MSR addresses in the range 0x800 to 0x8FF are translated using the x2apic_msr_access_reg() function.

Translation Process

flowchart TD
subgraph Constants["Constants"]
    MSR_BASE["X2APIC_MSE_REG_BASE0x800"]
    MSR_SIZE["X2APIC_MSE_REG_SIZE0x100 (256 registers)"]
end
START["System Register AddressSysRegAddr"]
SUBTRACT["Subtract Base Addressaddr.addr() - X2APIC_MSE_REG_BASEConvert to relative offset"]
CONVERT["ApicRegOffset::from()Map offset to enum variant"]
END["ApicRegOffset enum"]

CONVERT --> END
START --> SUBTRACT
SUBTRACT --> CONVERT

The translation is simpler than xAPIC because x2APIC MSRs directly correspond to register offsets:

  1. Calculate relative offset: addr.addr() - X2APIC_MSE_REG_BASE gives the register offset
  2. Convert to enum: ApicRegOffset::from(offset) maps the offset to the enum variant

MSR Address Mapping Examples

MSR AddressRelative OffsetApicRegOffset Enum
0x8020x2ApicRegOffset::ID
0x8030x3ApicRegOffset::Version
0x8100x10ApicRegOffset::ISR(ISRIndex0)
0x8320x32ApicRegOffset::LvtTimer

Sources: src/consts.rs(L205 - L216)  src/consts.rs(L210 - L211)  src/lib.rs(L137)  src/lib.rs(L152) 

ApicRegOffset Enum Structure

The ApicRegOffset enum serves as the unified internal representation for all APIC registers. It categorizes registers into distinct types and handles special cases like register arrays.

flowchart TD
subgraph subGraph5["Index Enums"]
    ISR_INDEX["ISRIndexISRIndex0-ISRIndex7"]
    TMR_INDEX["TMRIndexTMRIndex0-TMRIndex7"]
    IRR_INDEX["IRRIndexIRRIndex0-IRRIndex7"]
end
subgraph subGraph4["ApicRegOffset Enum Variants"]
    LVT_LINT0["LvtLint0 (0x35)"]
    SIVR_REG["SIVR (0xF)"]
    subgraph subGraph1["Interrupt State Arrays"]
        ISR_ARRAY["ISR(ISRIndex)0x10-0x178 registers"]
        TMR_ARRAY["TMR(TMRIndex)0x18-0x1F8 registers"]
        IRR_ARRAY["IRR(IRRIndex)0x20-0x278 registers"]
    end
    subgraph subGraph3["Timer Control"]
        TIMER_INIT["TimerInitCount (0x38)"]
        TIMER_CUR["TimerCurCount (0x39)"]
        TIMER_DIV["TimerDivConf (0x3E)"]
        LVT_TIMER["LvtTimer (0x32)"]
        LVT_THERMAL["LvtThermal (0x33)"]
        LVT_PMC["LvtPmc (0x34)"]
        ID_REG["ID (0x2)"]
        VER_REG["Version (0x3)"]
        TPR_REG["TPR (0x8)"]
    end
    subgraph subGraph2["Local Vector Table"]
        LVT_LINT1["LvtLint1 (0x36)"]
        LVT_ERR["LvtErr (0x37)"]
        LVT_CMCI["LvtCMCI (0x2F)"]
        subgraph subGraph0["Basic Control Registers"]
            TIMER_INIT["TimerInitCount (0x38)"]
            TIMER_CUR["TimerCurCount (0x39)"]
            TIMER_DIV["TimerDivConf (0x3E)"]
            LVT_TIMER["LvtTimer (0x32)"]
            LVT_THERMAL["LvtThermal (0x33)"]
            LVT_PMC["LvtPmc (0x34)"]
            LVT_LINT0["LvtLint0 (0x35)"]
            ID_REG["ID (0x2)"]
            VER_REG["Version (0x3)"]
            TPR_REG["TPR (0x8)"]
            SIVR_REG["SIVR (0xF)"]
        end
    end
end

IRR_ARRAY --> IRR_INDEX
ISR_ARRAY --> ISR_INDEX
TMR_ARRAY --> TMR_INDEX

Register Array Handling

The enum includes special handling for register arrays (ISR, TMR, IRR) that span multiple consecutive offsets. Each array uses a dedicated index enum generated by the define_index_enum! macro:

  • ISR (In-Service Register): 8 registers at offsets 0x10-0x17
  • TMR (Trigger Mode Register): 8 registers at offsets 0x18-0x1F
  • IRR (Interrupt Request Register): 8 registers at offsets 0x20-0x27

The index calculation uses: IndexType::from(offset - base_offset) where base_offset is the starting offset for each array.

Sources: src/consts.rs(L61 - L114)  src/consts.rs(L129 - L131)  src/consts.rs(L3 - L54)  src/consts.rs(L56 - L58) 

Translation Implementation Details

The core translation logic is implemented in the ApicRegOffset::from() method, which uses pattern matching to map numeric offsets to enum variants.

Pattern Matching Strategy

flowchart TD
START["Numeric Offset (usize)"]
CAST["Cast to u32"]
MATCH["Pattern Match"]
SINGLE["Single Register0x2, 0x3, 0x8, etc."]
RANGE["Range Patterns0x10..=0x170x18..=0x1F0x20..=0x27"]
INVALID["Invalid Offsetpanic!"]
SINGLE_VARIANT["Direct Enum VariantApicRegOffset::ID"]
ARRAY_VARIANT["Array Enum VariantApicRegOffset::ISR(index)"]

CAST --> MATCH
MATCH --> INVALID
MATCH --> RANGE
MATCH --> SINGLE
RANGE --> ARRAY_VARIANT
SINGLE --> SINGLE_VARIANT
START --> CAST

The implementation handles three categories of offsets:

  1. Direct mappings: Single offsets like 0x2 => ApicRegOffset::ID
  2. Range mappings: Consecutive ranges like 0x10..=0x17 => ApicRegOffset::ISR(index)
  3. Invalid offsets: Any unmapped offset triggers a panic

Index Calculation for Arrays

For register arrays, the index is calculated by subtracting the base offset:

// Example for ISR array (offset 0x10-0x17)
0x10..=0x17 => ApicRegOffset::ISR(ISRIndex::from(value - 0x10))

This ensures that offset 0x10 maps to ISRIndex0, offset 0x11 maps to ISRIndex1, and so on.

Sources: src/consts.rs(L116 - L148)  src/consts.rs(L129 - L131)  src/consts.rs(L19 - L31) 

Register System

Relevant source files

This document provides comprehensive documentation of the virtual Local APIC register system implemented in the x86_vlapic crate. It covers all APIC registers, their memory layouts, address translation mechanisms, and access patterns for both xAPIC and x2APIC operating modes. For details about specific Local Vector Table register implementations, see Local Vector Table (LVT). For information about the higher-level device interface and virtualization architecture, see Core Architecture.

Register Organization Overview

The virtual LAPIC register system virtualizes the complete set of x86 Local APIC registers within a 4KB memory page. The system supports dual access modes: legacy xAPIC MMIO access and modern x2APIC MSR access, both mapping to the same underlying register state.

Register Address Translation

The system translates guest access addresses to internal register offsets using the ApicRegOffset enum, which serves as the canonical register identifier throughout the codebase.

flowchart TD
subgraph subGraph3["Physical Memory Layout"]
    LocalAPICRegs["LocalAPICRegs struct4KB tock-registers layout"]
end
subgraph subGraph2["Canonical Register Space"]
    ApicRegOffsetEnum["ApicRegOffset enumUnified register identifier"]
end
subgraph subGraph1["Address Translation Layer"]
    XAPICTranslate["xapic_mmio_access_reg_offset()GuestPhysAddr → ApicRegOffset"]
    X2APICTranslate["x2apic_msr_access_reg()SysRegAddr → ApicRegOffset"]
end
subgraph subGraph0["Guest Access Methods"]
    GuestMMIO["Guest xAPIC MMIO0xFEE00000-0xFEE00FFF"]
    GuestMSR["Guest x2APIC MSR0x800-0x8FF"]
end

ApicRegOffsetEnum --> LocalAPICRegs
GuestMMIO --> XAPICTranslate
GuestMSR --> X2APICTranslate
X2APICTranslate --> ApicRegOffsetEnum
XAPICTranslate --> ApicRegOffsetEnum

Sources: src/consts.rs(L200 - L202)  src/consts.rs(L213 - L215)  src/consts.rs(L61 - L114) 

Register Categories

The APIC register space is organized into several functional categories, each serving distinct purposes in interrupt handling and APIC management.

flowchart TD
subgraph subGraph5["APIC Register Categories"]
    subgraph subGraph2["Local Vector Table"]
        ICR_LOW["ICR_LO (0x300)Command Register Low"]
        ICR_HIGH["ICR_HI (0x310)Command Register High"]
        TIMER_INIT["ICR_TIMER (0x380)Initial Count"]
        TIMER_CUR["CCR_TIMER (0x390)Current Count"]
        TIMER_DIV["DCR_TIMER (0x3E0)Divide Configuration"]
        LVT_TIMER["LVT_TIMER (0x320)Timer Interrupt"]
        LVT_THERMAL["LVT_THERMAL (0x330)Thermal Monitor"]
        LVT_PMC["LVT_PMC (0x340)Performance Counter"]
        LVT_LINT1["LVT_LINT1 (0x360)External Pin 1"]
        LVT_ERROR["LVT_ERROR (0x370)Error Interrupt"]
        LVT_CMCI["LVT_CMCI (0x2F0)Corrected Machine Check"]
        ISR["ISR Array (0x100-0x170)In-Service Registers8 x 128-bit"]
        TMR["TMR Array (0x180-0x1F0)Trigger Mode Registers8 x 128-bit"]
        IRR["IRR Array (0x200-0x270)Interrupt Request Registers8 x 128-bit"]
        ID["ID (0x20)Local APIC Identifier"]
        VERSION["VERSION (0x30)APIC Version Info"]
        TPR["TPR (0x80)Task Priority"]
        subgraph subGraph0["Control Registers"]
            LVT_LINT0["LVT_LINT0 (0x350)External Pin 0"]
            SVR["SVR (0xF0)Spurious Interrupt Vector"]
            subgraph subGraph4["Interrupt Command"]
                ICR_LOW["ICR_LO (0x300)Command Register Low"]
                ICR_HIGH["ICR_HI (0x310)Command Register High"]
                TIMER_INIT["ICR_TIMER (0x380)Initial Count"]
                TIMER_CUR["CCR_TIMER (0x390)Current Count"]
                LVT_TIMER["LVT_TIMER (0x320)Timer Interrupt"]
                LVT_THERMAL["LVT_THERMAL (0x330)Thermal Monitor"]
                ISR["ISR Array (0x100-0x170)In-Service Registers8 x 128-bit"]
                TMR["TMR Array (0x180-0x1F0)Trigger Mode Registers8 x 128-bit"]
                ID["ID (0x20)Local APIC Identifier"]
                VERSION["VERSION (0x30)APIC Version Info"]
            end
            subgraph subGraph3["Timer Subsystem"]
                ICR_LOW["ICR_LO (0x300)Command Register Low"]
                ICR_HIGH["ICR_HI (0x310)Command Register High"]
                TIMER_INIT["ICR_TIMER (0x380)Initial Count"]
                TIMER_CUR["CCR_TIMER (0x390)Current Count"]
                TIMER_DIV["DCR_TIMER (0x3E0)Divide Configuration"]
                LVT_TIMER["LVT_TIMER (0x320)Timer Interrupt"]
                LVT_THERMAL["LVT_THERMAL (0x330)Thermal Monitor"]
                LVT_PMC["LVT_PMC (0x340)Performance Counter"]
                ISR["ISR Array (0x100-0x170)In-Service Registers8 x 128-bit"]
                TMR["TMR Array (0x180-0x1F0)Trigger Mode Registers8 x 128-bit"]
                IRR["IRR Array (0x200-0x270)Interrupt Request Registers8 x 128-bit"]
                ID["ID (0x20)Local APIC Identifier"]
                VERSION["VERSION (0x30)APIC Version Info"]
                TPR["TPR (0x80)Task Priority"]
            end
        end
    end
    subgraph subGraph1["Interrupt State Arrays"]
        LVT_LINT0["LVT_LINT0 (0x350)External Pin 0"]
        SVR["SVR (0xF0)Spurious Interrupt Vector"]
        subgraph subGraph4["Interrupt Command"]
            ICR_LOW["ICR_LO (0x300)Command Register Low"]
            ICR_HIGH["ICR_HI (0x310)Command Register High"]
            TIMER_INIT["ICR_TIMER (0x380)Initial Count"]
            TIMER_CUR["CCR_TIMER (0x390)Current Count"]
            LVT_TIMER["LVT_TIMER (0x320)Timer Interrupt"]
            LVT_THERMAL["LVT_THERMAL (0x330)Thermal Monitor"]
            ISR["ISR Array (0x100-0x170)In-Service Registers8 x 128-bit"]
            TMR["TMR Array (0x180-0x1F0)Trigger Mode Registers8 x 128-bit"]
            ID["ID (0x20)Local APIC Identifier"]
            VERSION["VERSION (0x30)APIC Version Info"]
        end
        subgraph subGraph3["Timer Subsystem"]
            ICR_LOW["ICR_LO (0x300)Command Register Low"]
            ICR_HIGH["ICR_HI (0x310)Command Register High"]
            TIMER_INIT["ICR_TIMER (0x380)Initial Count"]
            TIMER_CUR["CCR_TIMER (0x390)Current Count"]
            TIMER_DIV["DCR_TIMER (0x3E0)Divide Configuration"]
            LVT_TIMER["LVT_TIMER (0x320)Timer Interrupt"]
            LVT_THERMAL["LVT_THERMAL (0x330)Thermal Monitor"]
            LVT_PMC["LVT_PMC (0x340)Performance Counter"]
            ISR["ISR Array (0x100-0x170)In-Service Registers8 x 128-bit"]
            TMR["TMR Array (0x180-0x1F0)Trigger Mode Registers8 x 128-bit"]
            IRR["IRR Array (0x200-0x270)Interrupt Request Registers8 x 128-bit"]
            ID["ID (0x20)Local APIC Identifier"]
            VERSION["VERSION (0x30)APIC Version Info"]
            TPR["TPR (0x80)Task Priority"]
        end
    end
end

Sources: src/consts.rs(L61 - L114)  src/regs/mod.rs(L15 - L104) 

Address Translation Implementation

The ApicRegOffset enum provides a unified addressing scheme that abstracts the differences between xAPIC and x2APIC access patterns.

ApicRegOffset Enum Structure

Register CategoryOffset RangeApicRegOffset Variants
Control Registers0x2-0xFID,Version,TPR,APR,PPR,EOI,RRR,LDR,DFR,SIVR
Interrupt Arrays0x10-0x27ISR(ISRIndex),TMR(TMRIndex),IRR(IRRIndex)
Error Status0x28ESR
LVT Registers0x2F, 0x32-0x37LvtCMCI,LvtTimer,LvtThermal,LvtPmc,LvtLint0,LvtLint1,LvtErr
Interrupt Command0x30-0x31ICRLow,ICRHi
Timer Registers0x38-0x39, 0x3ETimerInitCount,TimerCurCount,TimerDivConf

Sources: src/consts.rs(L61 - L114)  src/consts.rs(L116 - L148) 

Index Enums for Register Arrays

The system uses generated index enums for the three 256-bit interrupt state arrays, each comprising 8 128-bit registers.

flowchart TD
subgraph subGraph2["Usage in ApicRegOffset"]
    ISRVariant["ApicRegOffset::ISR(ISRIndex)"]
    TMRVariant["ApicRegOffset::TMR(TMRIndex)"]
    IRRVariant["ApicRegOffset::IRR(IRRIndex)"]
end
subgraph subGraph1["Array Index Generation"]
    MacroGenerate["define_index_enum! macroGenerates 0-7 indices"]
    subgraph subGraph0["Generated Enums"]
        ISRIndex["ISRIndexISRIndex0-ISRIndex7"]
        TMRIndex["TMRIndexTMRIndex0-TMRIndex7"]
        IRRIndex["IRRIndexIRRIndex0-IRRIndex7"]
    end
end

IRRIndex --> IRRVariant
ISRIndex --> ISRVariant
MacroGenerate --> IRRIndex
MacroGenerate --> ISRIndex
MacroGenerate --> TMRIndex
TMRIndex --> TMRVariant

Sources: src/consts.rs(L3 - L54)  src/consts.rs(L56 - L58) 

Memory Layout Implementation

The LocalAPICRegs struct defines the physical memory layout using the tock-registers crate, providing type-safe register access with appropriate read/write permissions.

Register Access Patterns

Access TypeUsageExamples
ReadWriteConfigurable control registersID,TPR,LDR,DFR,ESR
ReadOnlyStatus and version registersVERSION,APR,PPR,RRD,CCR_TIMER
WriteOnlyCommand registersEOI,SELF_IPI
ReadOnlyInterrupt state arraysISR,TMR,IRRarrays
LVT Register TypesSpecialized LVT registersLvtTimerRegisterMmio,LvtThermalMonitorRegisterMmio, etc.

Sources: src/regs/mod.rs(L15 - L104) 

Reset Values and Constants

The system defines standardized reset values for APIC registers according to the x86 specification:

ConstantValuePurpose
RESET_LVT_REG0x0001_0000Default LVT register state with interrupt masked
RESET_SPURIOUS_INTERRUPT_VECTOR0x0000_00FFDefault spurious interrupt vector configuration

Sources: src/consts.rs(L183 - L190) 

Access Mode Implementation

The register system supports both xAPIC and x2APIC access modes through dedicated translation functions.

xAPIC MMIO Access

flowchart TD
subgraph subGraph0["xAPIC MMIO Translation"]
    MMIOAddr["Guest Physical Address0xFEE00000 + offset"]
    MMIOTranslate["xapic_mmio_access_reg_offset()(addr & 0xFFF) >> 4"]
    MMIOConstants["DEFAULT_APIC_BASE: 0xFEE00000APIC_MMIO_SIZE: 0x1000"]
    MMIOResult["ApicRegOffset enum value"]
end

MMIOAddr --> MMIOTranslate
MMIOConstants --> MMIOTranslate
MMIOTranslate --> MMIOResult

Sources: src/consts.rs(L192 - L202) 

x2APIC MSR Access

flowchart TD
subgraph subGraph0["x2APIC MSR Translation"]
    MSRAddr["System Register Address0x800-0x8FF range"]
    MSRTranslate["x2apic_msr_access_reg()addr - 0x800"]
    MSRConstants["X2APIC_MSE_REG_BASE: 0x800X2APIC_MSE_REG_SIZE: 0x100"]
    MSRResult["ApicRegOffset enum value"]
end

MSRAddr --> MSRTranslate
MSRConstants --> MSRTranslate
MSRTranslate --> MSRResult

Sources: src/consts.rs(L205 - L216) 

Register Structure Organization

The complete register system provides a comprehensive virtualization of Local APIC functionality, supporting interrupt handling, inter-processor communication, and timer operations through a unified, type-safe interface that abstracts the complexities of dual-mode APIC access.

Sources: src/consts.rs(L1 - L217)  src/regs/mod.rs(L1 - L105) 

Register Constants and Offsets

Relevant source files

This document covers the register constant definitions and address offset mappings used by the virtual Local APIC implementation. It documents the ApicRegOffset enum that standardizes access to all APIC registers, the index enums for register arrays, and the address translation functions that convert between xAPIC MMIO addresses and x2APIC MSR addresses to unified register offsets.

For information about the actual register layouts and bit field definitions, see the Local Vector Table documentation (3.2) and Control and Status Registers (3.3).

ApicRegOffset Enum

The ApicRegOffset enum serves as the central abstraction for all APIC register types, providing a unified interface for both xAPIC and x2APIC access modes. Each variant corresponds to a specific APIC register defined in the Intel x86 architecture specification.

flowchart TD
subgraph subGraph5["ApicRegOffset Register Categories"]
    subgraph LVT_Registers["Local Vector Table"]
        TimerInitCount["TimerInitCount (0x38)"]
        TimerCurCount["TimerCurCount (0x39)"]
        TimerDivConf["TimerDivConf (0x3E)"]
        LvtTimer["LvtTimer (0x32)"]
        LvtThermal["LvtThermal (0x33)"]
        LvtPmc["LvtPmc (0x34)"]
        LvtLint0["LvtLint0 (0x35)"]
        LvtLint1["LvtLint1 (0x36)"]
        LvtErr["LvtErr (0x37)"]
        LvtCMCI["LvtCMCI (0x2F)"]
        ISR["ISR[0-7] (0x10-0x17)"]
        TMR["TMR[0-7] (0x18-0x1F)"]
        IRR["IRR[0-7] (0x20-0x27)"]
        EOI["EOI (0xB)"]
        SIVR["SIVR (0xF)"]
        ESR["ESR (0x28)"]
        ICRLow["ICRLow (0x30)"]
        ICRHi["ICRHi (0x31)"]
        ID["ID (0x2)"]
        Version["Version (0x3)"]
        TPR["TPR (0x8)"]
        APR["APR (0x9)"]
        PPR["PPR (0xA)"]
        subgraph Interrupt_Management["Interrupt Management"]
            subgraph Control_Registers["Control Registers"]
                TimerInitCount["TimerInitCount (0x38)"]
                TimerCurCount["TimerCurCount (0x39)"]
                TimerDivConf["TimerDivConf (0x3E)"]
                LvtTimer["LvtTimer (0x32)"]
                LvtThermal["LvtThermal (0x33)"]
                LvtPmc["LvtPmc (0x34)"]
                LvtLint0["LvtLint0 (0x35)"]
                ISR["ISR[0-7] (0x10-0x17)"]
                TMR["TMR[0-7] (0x18-0x1F)"]
                IRR["IRR[0-7] (0x20-0x27)"]
                EOI["EOI (0xB)"]
                SIVR["SIVR (0xF)"]
                ESR["ESR (0x28)"]
                ICRLow["ICRLow (0x30)"]
                ICRHi["ICRHi (0x31)"]
                ID["ID (0x2)"]
                Version["Version (0x3)"]
                TPR["TPR (0x8)"]
                APR["APR (0x9)"]
                PPR["PPR (0xA)"]
                subgraph Timer_Subsystem["Timer Subsystem"]
                    subgraph Register_Arrays["Register Arrays"]
                        TimerInitCount["TimerInitCount (0x38)"]
                        TimerCurCount["TimerCurCount (0x39)"]
                        TimerDivConf["TimerDivConf (0x3E)"]
                        LvtTimer["LvtTimer (0x32)"]
                        LvtThermal["LvtThermal (0x33)"]
                        LvtPmc["LvtPmc (0x34)"]
                        LvtLint0["LvtLint0 (0x35)"]
                        ISR["ISR[0-7] (0x10-0x17)"]
                        TMR["TMR[0-7] (0x18-0x1F)"]
                        IRR["IRR[0-7] (0x20-0x27)"]
                        EOI["EOI (0xB)"]
                        SIVR["SIVR (0xF)"]
                        ESR["ESR (0x28)"]
                        ICRLow["ICRLow (0x30)"]
                        ID["ID (0x2)"]
                        Version["Version (0x3)"]
                        TPR["TPR (0x8)"]
                        APR["APR (0x9)"]
                    end
                end
            end
        end
    end
end

The enum implements a from method that performs offset-to-register mapping based on the numeric offset values defined in the Intel specification. Register arrays like ISR, TMR, and IRR use index-based variants to represent their 8-register sequences.

Sources: src/consts.rs(L61 - L114)  src/consts.rs(L116 - L148) 

Register Array Index Enums

Three specialized index enums handle the APIC's 256-bit register arrays, each consisting of 8 consecutive 32-bit registers:

Array TypeIndex EnumRegister RangePurpose
In-Service RegisterISRIndex0x10-0x17Tracks interrupts currently being serviced
Trigger Mode RegisterTMRIndex0x18-0x1FStores trigger mode for each interrupt vector
Interrupt Request RegisterIRRIndex0x20-0x27Queues pending interrupt requests
flowchart TD
subgraph Concrete_Types["Concrete Index Types"]
    ISRIndex["ISRIndex (ISR0-ISR7)"]
    TMRIndex["TMRIndex (TMR0-TMR7)"]
    IRRIndex["IRRIndex (IRR0-IRR7)"]
end
subgraph Index_Enum_Pattern["Index Enum Pattern"]
    IndexEnum["Index0 through Index7"]
    subgraph Generated_Methods["Generated Methods"]
        from_method["from(usize) -> Self"]
        as_usize_method["as_usize() -> usize"]
        display_method["Display trait"]
    end
end

IndexEnum --> as_usize_method
IndexEnum --> display_method
IndexEnum --> from_method

The define_index_enum! macro generates identical implementations for all three index types, providing type-safe indexing into the 8-register arrays while maintaining zero runtime cost through const evaluation.

Sources: src/consts.rs(L3 - L54)  src/consts.rs(L56 - L58) 

Address Translation Functions

The virtual APIC supports both xAPIC (MMIO-based) and x2APIC (MSR-based) access modes through dedicated address translation functions that convert guest addresses to ApicRegOffset values.

flowchart TD
subgraph Unified_Output["Unified Register Access"]
    ApicRegOffset_Output["ApicRegOffset enumNormalized register identifier"]
end
subgraph Translation_Functions["Translation Functions"]
    xapic_mmio_access_reg_offset["xapic_mmio_access_reg_offset()(addr & 0xFFF) >> 4"]
    x2apic_msr_access_reg["x2apic_msr_access_reg()addr - 0x800"]
end
subgraph Guest_Access_Methods["Guest Access Methods"]
    xAPIC_MMIO["xAPIC MMIO AccessGuestPhysAddr0xFEE0_0000-0xFEE0_0FFF"]
    x2APIC_MSR["x2APIC MSR AccessSysRegAddr0x800-0x8FF"]
end

x2APIC_MSR --> x2apic_msr_access_reg
x2apic_msr_access_reg --> ApicRegOffset_Output
xAPIC_MMIO --> xapic_mmio_access_reg_offset
xapic_mmio_access_reg_offset --> ApicRegOffset_Output

xAPIC MMIO Translation

The xapic_mmio_access_reg_offset function extracts the 12-bit offset from the guest physical address and shifts right by 4 bits to obtain the register index. This follows the xAPIC specification where registers are aligned on 16-byte boundaries within the 4KB APIC page.

Address RangeCalculationRegister Type
0xFEE0_0020(0x20 & 0xFFF) >> 4 = 0x2ID Register
0xFEE0_0100(0x100 & 0xFFF) >> 4 = 0x10ISR[0]
0xFEE0_0320(0x320 & 0xFFF) >> 4 = 0x32LVT Timer

x2APIC MSR Translation

The x2apic_msr_access_reg function performs simple offset subtraction from the MSR base address 0x800. x2APIC mode provides a flat MSR address space for APIC registers.

MSR AddressCalculationRegister Type
0x8020x802 - 0x800 = 0x2ID Register
0x8100x810 - 0x800 = 0x10ISR[0]
0x8320x832 - 0x800 = 0x32LVT Timer

Sources: src/consts.rs(L192 - L203)  src/consts.rs(L205 - L216) 

Reset Values and Default Constants

The system defines standard reset values for APIC registers according to Intel specifications:

flowchart TD
subgraph Address_Constants["Address Space Constants"]
    X2APIC_MSE_REG_SIZE["X2APIC_MSE_REG_SIZE0x100256 MSR range"]
    subgraph Reset_Constants["Reset Value Constants"]
        DEFAULT_APIC_BASE["DEFAULT_APIC_BASE0xFEE0_0000xAPIC MMIO base"]
        APIC_MMIO_SIZE["APIC_MMIO_SIZE0x10004KB page size"]
        X2APIC_MSE_REG_BASE["X2APIC_MSE_REG_BASE0x800x2APIC MSR base"]
        RESET_LVT_REG["RESET_LVT_REG0x0001_0000LVT registers after reset"]
        RESET_SPURIOUS_INTERRUPT_VECTOR["RESET_SPURIOUS_INTERRUPT_VECTOR0x0000_00FFSIVR after reset"]
    end
end
ConstantValuePurposeSpecification Reference
RESET_LVT_REG0x0001_0000LVT register reset stateIntel Manual 11.5.1
RESET_SPURIOUS_INTERRUPT_VECTOR0x0000_00FFSIVR reset stateIntel Manual 11.9
DEFAULT_APIC_BASE0xFEE0_0000Standard xAPIC MMIO baseIntel Manual 11.4.1
APIC_MMIO_SIZE0x1000xAPIC page sizeIntel Manual 11.4.1

The reset values ensure that LVT registers start in a masked state (bit 16 set) and the spurious interrupt vector defaults to vector 0xFF, providing safe initialization conditions for the virtual APIC.

Sources: src/consts.rs(L183 - L191)  src/consts.rs(L197 - L198)  src/consts.rs(L210 - L211) 

Local Vector Table (LVT)

Relevant source files

Purpose and Scope

This document covers the Local Vector Table (LVT) subsystem within the x86_vlapic crate, which manages interrupt vector configuration for various local interrupt sources in the virtual Local APIC implementation. The LVT provides a standardized interface for configuring how different types of interrupts (timer, thermal, performance monitoring, external pins, errors) are delivered to the processor.

For detailed information about individual LVT register implementations, see Timer LVT Register, External Interrupt Pin Registers, System Monitoring LVT Registers, and Error Handling LVT Registers. For broader APIC register system context, see Register System.

LVT Architecture Overview

The Local Vector Table consists of seven specialized registers that control different interrupt sources within the Local APIC. Each register follows a common structural pattern while providing source-specific functionality.

LVT Register Layout

flowchart TD
subgraph subGraph4["LocalVectorTable Struct"]
    LVT["LocalVectorTable"]
    subgraph subGraph3["Error Handling"]
        ERROR["lvt_errLvtErrorRegisterLocal0x370"]
    end
    subgraph subGraph2["External Pin Sources"]
        LINT0["lvt_lint0LvtLint0RegisterLocalFEE0_0350H"]
        LINT1["lvt_lint1LvtLint1RegisterLocalFEE0_0360H"]
    end
    subgraph subGraph1["Timer Source"]
        TIMER["lvt_timerLvtTimerRegisterLocalFEE0_0320H"]
    end
    subgraph subGraph0["System Monitoring Sources"]
        CMCI["lvt_cmciLvtCmciRegisterLocalFEE0_02F0H"]
        THERMAL["lvt_thermalLvtThermalMonitorRegisterLocalFEE0_0330H"]
        PERFCNT["lvt_perf_countLvtPerformanceCounterRegisterLocalFEE0_0340H"]
    end
end

LVT --> CMCI
LVT --> ERROR
LVT --> LINT0
LVT --> LINT1
LVT --> PERFCNT
LVT --> THERMAL
LVT --> TIMER

Sources: src/lvt.rs(L9 - L24) 

Register Address Mapping

The LVT registers are mapped to specific offsets within the APIC register space:

RegisterTypeAddress OffsetPurpose
lvt_cmciLvtCmciRegisterLocalFEE0_02F0HCorrected Machine Check Interrupts
lvt_timerLvtTimerRegisterLocalFEE0_0320HLocal APIC Timer
lvt_thermalLvtThermalMonitorRegisterLocalFEE0_0330HThermal Monitoring
lvt_perf_countLvtPerformanceCounterRegisterLocalFEE0_0340HPerformance Counter Overflow
lvt_lint0LvtLint0RegisterLocalFEE0_0350HExternal Interrupt Pin 0
lvt_lint1LvtLint1RegisterLocalFEE0_0360HExternal Interrupt Pin 1
lvt_errLvtErrorRegisterLocal0x370APIC Error Conditions

Sources: src/lvt.rs(L10 - L23) 

Common LVT Structure and Functionality

All LVT registers share a common architectural pattern using the *RegisterLocal type structure, which provides local cached copies of register values. This design enables efficient access while maintaining consistency with the underlying APIC register model.

Initialization Pattern

flowchart TD
DEFAULT["Default::default()"]
RESET_VALUE["RESET_LVT_REG"]
CMCI_INIT["LvtCmciRegisterLocal::new()"]
TIMER_INIT["LvtTimerRegisterLocal::new()"]
THERMAL_INIT["LvtThermalMonitorRegisterLocal::new()"]
PERF_INIT["LvtPerformanceCounterRegisterLocal::new()"]
LINT0_INIT["LvtLint0RegisterLocal::new()"]
LINT1_INIT["LvtLint1RegisterLocal::new()"]
ERROR_INIT["LvtErrorRegisterLocal::new()"]

DEFAULT --> RESET_VALUE
RESET_VALUE --> CMCI_INIT
RESET_VALUE --> ERROR_INIT
RESET_VALUE --> LINT0_INIT
RESET_VALUE --> LINT1_INIT
RESET_VALUE --> PERF_INIT
RESET_VALUE --> THERMAL_INIT
RESET_VALUE --> TIMER_INIT

Sources: src/lvt.rs(L26 - L38) 

All LVT registers are initialized with the same reset value RESET_LVT_REG, providing a consistent starting state across all interrupt sources. This ensures predictable behavior during system initialization and reset conditions.

Register Organization

The LVT implementation follows a modular architecture where each register type is implemented in its own module, promoting code organization and maintainability.

Module Structure

flowchart TD
subgraph src/regs/lvt/["src/regs/lvt/"]
    MOD["mod.rsPublic Interface"]
    subgraph subGraph0["Individual Register Modules"]
        CMCI_MOD["cmci.rsLvtCmciRegisterLocal"]
        ERROR_MOD["error.rsLvtErrorRegisterLocal"]
        LINT0_MOD["lint0.rsLvtLint0RegisterLocal"]
        LINT1_MOD["lint1.rsLvtLint1RegisterLocal"]
        PERFMON_MOD["perfmon.rsLvtPerformanceCounterRegisterLocal"]
        THERMAL_MOD["thermal.rsLvtThermalMonitorRegisterLocal"]
        TIMER_MOD["timer.rsLvtTimerRegisterLocal"]
    end
end

MOD --> CMCI_MOD
MOD --> ERROR_MOD
MOD --> LINT0_MOD
MOD --> LINT1_MOD
MOD --> PERFMON_MOD
MOD --> THERMAL_MOD
MOD --> TIMER_MOD

Sources: src/regs/lvt/mod.rs(L1 - L16) 

Each module implements a specific LVT register type with its unique functionality while maintaining the common interface pattern. The mod.rs file serves as the public interface, re-exporting all register types for use by the LocalVectorTable struct.

Integration with Virtual APIC System

The LocalVectorTable is integrated into the broader virtual APIC register system through the VirtualApicRegs structure, where it manages all interrupt vector table functionality as a cohesive unit. This design separates interrupt configuration concerns from other APIC functionality while maintaining the standard APIC register interface.

Sources: src/lvt.rs(L1 - L39)  src/regs/lvt/mod.rs(L1 - L16) 

Timer LVT Register

Relevant source files

This document covers the Timer LVT (Local Vector Table) Register implementation in the x86_vlapic crate. The Timer LVT register controls the configuration and behavior of timer-based interrupts in the virtual Local APIC, including timer modes, interrupt masking, and delivery status monitoring.

For information about other LVT registers, see External Interrupt Pin Registers, System Monitoring LVT Registers, and Error Handling LVT Registers. For the broader LVT system architecture, see Local Vector Table (LVT).

Register Overview

The Timer LVT register is located at offset 0x320 in the APIC register space and provides control over timer interrupt generation and delivery. It supports three distinct timer modes and includes standard LVT fields for interrupt configuration.

Timer LVT Register Bit Layout

flowchart TD
subgraph subGraph0["Timer LVT Register (32-bit) - Offset 0x320"]
    Reserved2["Reserved2[31:19]13 bits"]
    TimerMode["TimerMode[18:17]2 bits"]
    Mask["Mask[16]1 bit"]
    Reserved1["Reserved1[15:13]3 bits"]
    DeliveryStatus["DeliveryStatus[12]1 bit (RO)"]
    Reserved0["Reserved0[11:8]4 bits"]
    Vector["Vector[7:0]8 bits"]
end

Sources: src/regs/lvt/timer.rs(L5 - L49) 

Register Fields

Timer Mode Field

The TimerMode field at bits [18:17] controls the operational mode of the timer:

ModeValueDescription
OneShot0b00One-shot mode using a count-down value
Periodic0b01Periodic mode reloading a count-down value
TSCDeadline0b10TSC-Deadline mode using absolute target value in IA32_TSC_DEADLINE MSR
Reserved0b11Reserved value (not used)

Sources: src/regs/lvt/timer.rs(L10 - L20) 

Mask Field

The Mask bit at position [16] controls interrupt reception:

  • NotMasked (0): Enables reception of the interrupt
  • Masked (1): Inhibits reception of the interrupt

The mask flag is automatically set to 1 on reset and can only be cleared by software. When the local APIC handles a timer interrupt, it may automatically set this mask flag depending on the timer configuration.

Sources: src/regs/lvt/timer.rs(L21 - L32) 

Delivery Status Field

The DeliveryStatus bit at position [12] is read-only and indicates interrupt delivery status:

  • Idle (0): No current activity for this interrupt source, or the previous interrupt was delivered and accepted
  • SendPending (1): An interrupt has been delivered to the processor core but not yet accepted

Sources: src/regs/lvt/timer.rs(L34 - L44) 

Vector Field

The Vector field at bits [7:0] specifies the interrupt vector number that will be used when the timer interrupt is delivered to the processor.

Sources: src/regs/lvt/timer.rs(L46 - L47) 

Timer Mode Details

Timer Mode Operation Flow

stateDiagram-v2
[*] --> Disabled : "Reset/Mask=1"
Disabled --> OneShot : "Configure OneShot modeSet initial count"
Disabled --> Periodic : "Configure Periodic modeSet initial count"
Disabled --> TSCDeadline : "Configure TSC-DeadlineSet deadline MSR"
OneShot --> CountingDown : "Timer starts"
CountingDown --> InterruptGenerated : "Count reaches 0"
InterruptGenerated --> Disabled : "One-shot complete"
Periodic --> PeriodicCounting : "Timer starts"
PeriodicCounting --> PeriodicInterrupt : "Count reaches 0"
PeriodicInterrupt --> PeriodicCounting : "Reload initial count"
PeriodicCounting --> Disabled : "Mask=1 or mode change"
TSCDeadline --> TSCMonitoring : "Monitor TSC value"
TSCMonitoring --> TSCInterrupt : "TSC >= deadline"
TSCInterrupt --> Disabled : "Deadline reached"
TSCMonitoring --> Disabled : "Deadline cleared or mode change"
Disabled --> [*] : "System reset"

Sources: src/regs/lvt/timer.rs(L11 - L19) 

Access Types

The Timer LVT register supports two access patterns for different use cases:

MMIO Access Type

flowchart TD
subgraph subGraph0["Direct MMIO Access Pattern"]
    Guest["Guest VM Timer Configuration"]
    MMIO["LvtTimerRegisterMmioReadWrite"]
    Hardware["Physical APIC Registerat 0xFEE00320"]
end

Guest --> MMIO
MMIO --> Hardware

The LvtTimerRegisterMmio type provides direct memory-mapped I/O access to the timer LVT register for real-time configuration changes.

Sources: src/regs/lvt/timer.rs(L52) 

Local Copy Access Type

flowchart TD
subgraph subGraph0["Local Copy Access Pattern"]
    VirtualAPIC["Virtual APIC Implementation"]
    Local["LvtTimerRegisterLocalLocalRegisterCopy"]
    Memory["Local Memory CopyCached Register State"]
end

Local --> Memory
VirtualAPIC --> Local

The LvtTimerRegisterLocal type maintains a local cached copy of the register contents in memory, avoiding volatile MMIO reads for each access while providing the same interface.

Sources: src/regs/lvt/timer.rs(L54 - L59) 

Integration with Virtual APIC System

Timer LVT in LVT System Context

flowchart TD
subgraph subGraph1["Timer Subsystem Integration"]
    TimerInit["TIMER_INIT_COUNTOffset 0x380Initial count value"]
    TimerCurrent["TIMER_CURRENT_COUNTOffset 0x390Current count value"]
    TimerDivide["TIMER_DIVIDE_CONFIGOffset 0x3E0Divide configuration"]
end
subgraph subGraph0["LVT Register System"]
    LVTTimer["LVT_TIMEROffset 0x320Timer interrupts"]
    LVTThermal["LVT_THERMALOffset 0x330"]
    LVTPMC["LVT_PMCOffset 0x340"]
    LVTLINT0["LVT_LINT0Offset 0x350"]
    LVTLINT1["LVT_LINT1Offset 0x360"]
    LVTError["LVT_ERROROffset 0x370"]
    LVTCMCI["LVT_CMCIOffset 0x2F0"]
end

LVTTimer --> TimerCurrent
LVTTimer --> TimerDivide
LVTTimer --> TimerInit

The Timer LVT register works in conjunction with the timer count and divide configuration registers to provide complete timer interrupt functionality within the virtual APIC system.

Sources: src/regs/lvt/timer.rs(L1 - L60) 

External Interrupt Pin Registers

Relevant source files

This document covers the LINT0 and LINT1 Local Vector Table (LVT) registers, which handle external interrupt pins in the virtual LAPIC implementation. These registers configure how the APIC responds to interrupts signaled through external hardware interrupt pins.

For information about other LVT registers like timer and thermal monitoring, see Timer LVT Register and System Monitoring LVT Registers.

Overview

The LINT0 and LINT1 registers are part of the Local Vector Table that configure external interrupt pin behavior. LINT0 is typically used for external interrupt controllers (like the legacy 8259A PIC), while LINT1 is commonly configured for NMI delivery from external sources.

Both registers share identical field layouts but serve different interrupt pins and have some operational constraints:

  • LINT0 supports all delivery modes and trigger configurations
  • LINT1 should always use edge-sensitive triggering and does not support level-sensitive interrupts
flowchart TD
subgraph subGraph3["Processor Core"]
    INT_HANDLER["Interrupt Handler"]
    SMI_HANDLER["SMI Handler"]
    NMI_HANDLER["NMI Handler"]
end
subgraph subGraph2["Virtual LAPIC LVT System"]
    LINT0_REG["LvtLint0RegisterMmio/LocalOFFSET 0x350FEE0_0350H"]
    LINT1_REG["LvtLint1RegisterLocal/MmioOFFSET 0x360FEE0_0360H"]
    subgraph subGraph1["Register Fields"]
        MASK["Mask Bit"]
        DELMODE["DeliveryModeFixed/SMI/NMI/INIT/ExtINT"]
        TRIGMODE["TriggerModeEdge/Level"]
        POLARITY["InterruptInputPinPolarityActiveHigh/ActiveLow"]
        VECTOR["Vector8-bit interrupt number"]
    end
end
subgraph subGraph0["External Hardware"]
    EXT_CTRL["External Interrupt Controller (8259A)"]
    NMI_SRC["NMI Source Hardware"]
    OTHER_EXT["Other External Devices"]
end

DELMODE --> INT_HANDLER
DELMODE --> NMI_HANDLER
DELMODE --> SMI_HANDLER
EXT_CTRL --> LINT0_REG
LINT0_REG --> DELMODE
LINT0_REG --> MASK
LINT1_REG --> DELMODE
LINT1_REG --> MASK
NMI_SRC --> LINT1_REG
OTHER_EXT --> LINT0_REG

Sources: src/regs/lvt/lint0.rs(L88 - L90)  src/regs/lvt/lint1.rs(L88 - L90) 

Register Structure and Implementation

Both LINT0 and LINT1 registers implement identical 32-bit layouts using the tock_registers framework. The implementation provides both MMIO and local cached register types.

flowchart TD
subgraph subGraph2["Access Patterns"]
    DIRECT_MMIO["Direct MMIO AccessVolatile reads/writes"]
    CACHED_LOCAL["Cached Local CopyMemory-resident copy"]
end
subgraph subGraph1["Bitfield Definition"]
    LVT_LINT0_BITFIELD["LVT_LINT0 register_bitfields!"]
    LVT_LINT1_BITFIELD["LVT_LINT1 register_bitfields!"]
end
subgraph subGraph0["Register Types"]
    MMIO_TYPE["LvtLint0RegisterMmioReadWrite"]
    LOCAL_TYPE["LvtLint0RegisterLocalLocalRegisterCopy"]
end

LOCAL_TYPE --> CACHED_LOCAL
LVT_LINT0_BITFIELD --> LOCAL_TYPE
LVT_LINT0_BITFIELD --> MMIO_TYPE
LVT_LINT1_BITFIELD --> LOCAL_TYPE
LVT_LINT1_BITFIELD --> MMIO_TYPE
MMIO_TYPE --> DIRECT_MMIO

Sources: src/regs/lvt/lint0.rs(L1 - L4)  src/regs/lvt/lint0.rs(L90 - L97)  src/regs/lvt/lint1.rs(L90 - L97) 

Register Field Layout

BitsFieldDescriptionValues
31-17Reserved1Reserved bitsMust be 0
16MaskEnable/disable interrupt0=NotMasked, 1=Masked
15TriggerModeEdge or level sensitive0=EdgeSensitive, 1=LevelSensitive
14RemoteIRRRemote IRR flag (RO)Set when interrupt accepted
13InterruptInputPinPolarityPin polarity0=ActiveHigh, 1=ActiveLow
12DeliveryStatusDelivery status (RO)0=Idle, 1=SendPending
11Reserved0ReservedMust be 0
10-8DeliveryModeInterrupt delivery typeSee delivery modes table
7-0VectorInterrupt vector number0-255

Sources: src/regs/lvt/lint0.rs(L7 - L86)  src/regs/lvt/lint1.rs(L7 - L86) 

Delivery Modes

The DeliveryMode field determines how the interrupt is delivered to the processor core. Different modes serve different purposes in the interrupt handling system.

ModeValueDescriptionVector Usage
Fixed000bStandard interrupt deliveryUses Vector field
SMI010bSystem Management InterruptVector should be 00H
NMI100bNon-Maskable InterruptVector ignored
INIT101bProcessor initializationVector should be 00H
ExtINT111bExternal interrupt controllerVector from external controller

Delivery Mode Constraints

  • Fixed Mode: Most common mode, uses the vector field to specify interrupt number
  • SMI Mode: Triggers System Management Mode entry, vector field ignored
  • NMI Mode: Generates non-maskable interrupt, bypasses interrupt masking
  • INIT Mode: Causes processor reset/initialization, not supported for thermal/PMC/CMCI registers
  • ExtINT Mode: Compatibility with 8259A-style external controllers, only one system-wide ExtINT source allowed

Sources: src/regs/lvt/lint0.rs(L56 - L82)  src/regs/lvt/lint1.rs(L56 - L82) 

Trigger Mode and Polarity Configuration

The trigger mode and polarity settings determine how the APIC interprets signals on the LINT pins.

Trigger Mode Behavior

  • Edge Sensitive (0): Interrupt triggered on signal transition
  • Level Sensitive (1): Interrupt triggered while signal is active

Important constraints:

  • LINT1 should always use edge-sensitive triggering
  • Level-sensitive interrupts are not supported for LINT1
  • Fixed delivery mode can use either trigger mode
  • NMI, SMI, and INIT modes always use edge-sensitive triggering
  • ExtINT mode always uses level-sensitive triggering

Polarity Configuration

The InterruptInputPinPolarity field configures the active state of the interrupt pin:

  • ActiveHigh (0): Interrupt active when pin is high
  • ActiveLow (1): Interrupt active when pin is low

Sources: src/regs/lvt/lint0.rs(L17 - L30)  src/regs/lvt/lint0.rs(L36 - L43)  src/regs/lvt/lint1.rs(L17 - L30)  src/regs/lvt/lint1.rs(L36 - L43) 

Status and Control Fields

Delivery Status (Read-Only)

The DeliveryStatus field indicates the current state of interrupt delivery:

  • Idle (0): No pending activity or previous interrupt accepted
  • SendPending (1): Interrupt delivered to core but not yet accepted

Remote IRR Flag (Read-Only)

For fixed mode, level-triggered interrupts, the RemoteIRR flag tracks interrupt servicing:

  • Set when the local APIC accepts the interrupt
  • Reset when an EOI command is received
  • Undefined for edge-triggered interrupts and other delivery modes

Mask Bit

The Mask field controls interrupt enabling:

  • NotMasked (0): Interrupt enabled
  • Masked (1): Interrupt disabled

Sources: src/regs/lvt/lint0.rs(L10 - L16)  src/regs/lvt/lint0.rs(L31 - L35)  src/regs/lvt/lint0.rs(L44 - L54) 

System Monitoring LVT Registers

Relevant source files

This document covers the system monitoring Local Vector Table (LVT) registers in the x86_vlapic implementation. These registers handle interrupts generated by thermal monitoring hardware and performance counter overflow events. System monitoring LVT registers are distinct from timer-based interrupts (see Timer LVT Register), external pin interrupts (see External Interrupt Pin Registers), and error condition interrupts (see Error Handling LVT Registers).

Overview

The x86_vlapic crate implements two system monitoring LVT registers that provide interrupt delivery for hardware monitoring subsystems:

  • Thermal Monitor Register: Handles thermal sensor threshold interrupts
  • Performance Counter Register: Handles performance monitoring unit (PMU) overflow and Intel Processor Trace (PT) interrupts

Both registers are implementation-specific extensions to the APIC architecture and share a common bit field structure for interrupt configuration.

System Monitoring LVT Architecture

System Monitoring LVT Register Structure

flowchart TD
subgraph INTERRUPT_SOURCES["Hardware Interrupt Sources"]
    MASK["Mask (16)"]
    THERMAL_SENSOR["Thermal Sensor Threshold Events"]
    PMU_OVERFLOW["Performance Counter Overflow"]
    INTEL_PT["Intel PT ToPA PMI"]
end
subgraph PERFMON["Performance Counter Register (0x340)"]
    PERFMON_BITFIELDS["LVT_PERFORMANCE_COUNTER Bitfields"]
    PERFMON_MMIO["LvtPerformanceCounterRegisterMmio"]
    PERFMON_LOCAL["LvtPerformanceCounterRegisterLocal"]
end
subgraph THERMAL["Thermal Monitor Register (0x330)"]
    VECTOR["Vector (0-7)"]
    DELIVERY_MODE["DeliveryMode (8-10)"]
    DELIVERY_STATUS["DeliveryStatus (12)"]
    THERMAL_BITFIELDS["LVT_THERMAL_MONITOR Bitfields"]
    THERMAL_MMIO["LvtThermalMonitorRegisterMmio"]
    THERMAL_LOCAL["LvtThermalMonitorRegisterLocal"]
end
subgraph COMMON["Common Bit Structure"]
    VECTOR["Vector (0-7)"]
    DELIVERY_MODE["DeliveryMode (8-10)"]
    DELIVERY_STATUS["DeliveryStatus (12)"]
    MASK["Mask (16)"]
    THERMAL_MMIO["LvtThermalMonitorRegisterMmio"]
    THERMAL_LOCAL["LvtThermalMonitorRegisterLocal"]
    THERMAL_SENSOR["Thermal Sensor Threshold Events"]
end

PERFMON_LOCAL --> PERFMON_BITFIELDS
PERFMON_MMIO --> PERFMON_BITFIELDS
THERMAL_LOCAL --> THERMAL_BITFIELDS
THERMAL_MMIO --> THERMAL_BITFIELDS

Sources: src/regs/lvt/thermal.rs(L1 - L74)  src/regs/lvt/perfmon.rs(L1 - L75) 

Thermal Monitor Register

The LVT Thermal Monitor Register at offset 0x330 delivers interrupts when thermal monitoring hardware detects temperature threshold violations. This register is implementation-specific and always located at base address FEE0 0330H when implemented.

Register Definition

The thermal monitor register is defined using the LVT_THERMAL_MONITOR bitfield structure:

FieldBitsDescription
Vector0-7Interrupt vector number
DeliveryMode8-10Interrupt delivery type
DeliveryStatus12Read-only delivery status
Mask16Interrupt enable/disable

Thermal Interrupt Conditions

The thermal monitor generates interrupts when:

  • Processor temperature exceeds configured thresholds
  • Thermal control circuit (TCC) activation occurs
  • Temperature monitoring hardware detects critical conditions

Register Types

The crate provides two access patterns for the thermal monitor register:

  • LvtThermalMonitorRegisterMmio: Direct MMIO access using ReadWrite<u32, LVT_THERMAL_MONITOR::Register>
  • LvtThermalMonitorRegisterLocal: Cached local copy using LocalRegisterCopy<u32, LVT_THERMAL_MONITOR::Register>

Sources: src/regs/lvt/thermal.rs(L5 - L66)  src/regs/lvt/thermal.rs(L68 - L73) 

Performance Counter Register

The LVT Performance Counter Register at offset 0x340 handles interrupts from performance monitoring units and Intel Processor Trace events. This register is implementation-specific and not guaranteed to be at the documented base address.

Register Definition

The performance counter register uses the LVT_PERFORMANCE_COUNTER bitfield structure with identical bit layout to the thermal monitor register:

FieldBitsDescription
Vector0-7Interrupt vector number
DeliveryMode8-10Interrupt delivery type
DeliveryStatus12Read-only delivery status
Mask16Interrupt enable/disable

Performance Monitoring Interrupt Sources

The performance counter register delivers interrupts for:

  1. Performance Counter Overflow: When hardware performance counters exceed their configured limits
  2. Intel PT ToPA PMI: Performance Monitoring Interrupt (PMI) signals from Intel Processor Trace Table of Physical Addresses (ToPA) mechanism

Register Types

Similar to the thermal register, two access patterns are provided:

  • LvtPerformanceCounterRegisterMmio: Direct MMIO access using ReadWrite<u32, LVT_PERFORMANCE_COUNTER::Register>
  • LvtPerformanceCounterRegisterLocal: Cached local copy using LocalRegisterCopy<u32, LVT_PERFORMANCE_COUNTER::Register>

Sources: src/regs/lvt/perfmon.rs(L5 - L66)  src/regs/lvt/perfmon.rs(L68 - L74) 

Common Bit Field Structure

Both system monitoring registers share identical bit field definitions, providing consistent configuration interfaces across different monitoring subsystems.

System Monitoring Register Bit Fields

flowchart TD
subgraph REG32["32-bit Register Layout"]
    FIXED["Fixed = 000"]
    IDLE["Idle = 0"]
    NOT_MASKED["NotMasked = 0"]
    RESERVED2["Reserved2(17-31)"]
    MASK_BIT["Mask(16)"]
    RESERVED1["Reserved1(13-15)"]
    DELIVERY_STATUS_BIT["DeliveryStatus(12)"]
    RESERVED0["Reserved0(11)"]
    DELIVERY_MODE_BITS["DeliveryMode(8-10)"]
    VECTOR_BITS["Vector(0-7)"]
end
subgraph MODE_VALUES["Delivery Mode Values"]
    FIXED["Fixed = 000"]
    SMI["SMI = 010"]
    NMI["NMI = 100"]
    RESERVED_MODE["Reserved = 110"]
    IDLE["Idle = 0"]
    SEND_PENDING["SendPending = 1"]
    NOT_MASKED["NotMasked = 0"]
    MASKED["Masked = 1"]
    RESERVED2["Reserved2(17-31)"]
    subgraph MASK_VALUES["Mask Field Values"]
        subgraph STATUS_VALUES["Delivery Status Values"]
            FIXED["Fixed = 000"]
            SMI["SMI = 010"]
            IDLE["Idle = 0"]
            SEND_PENDING["SendPending = 1"]
            NOT_MASKED["NotMasked = 0"]
            MASKED["Masked = 1"]
            RESERVED2["Reserved2(17-31)"]
        end
    end
end

Sources: src/regs/lvt/thermal.rs(L10 - L16)  src/regs/lvt/perfmon.rs(L10 - L16)  src/regs/lvt/thermal.rs(L30 - L56)  src/regs/lvt/perfmon.rs(L30 - L56) 

Delivery Mode Restrictions

System monitoring LVT registers have specific delivery mode restrictions compared to other LVT registers:

ModeBinarySupport Status
Fixed000✅ Supported
SMI010✅ Supported
NMI100✅ Supported
INIT101❌ Not supported
Reserved110❌ Not supported
ExtINT111❌ Not supported

The INIT and ExtINT delivery modes are explicitly not supported for thermal monitor and performance counter registers.

Sources: src/regs/lvt/thermal.rs(L44 - L45)  src/regs/lvt/perfmon.rs(L44 - L45)  src/regs/lvt/thermal.rs(L54 - L55)  src/regs/lvt/perfmon.rs(L54 - L55) 

Register Access Patterns

System monitoring LVT registers support both direct MMIO access and cached local copy patterns using the tock-registers framework.

LVT System Monitoring Register Access Architecture

sequenceDiagram
    participant GuestOS as "Guest OS"
    participant EmulatedLocalApic as "EmulatedLocalApic"
    participant VirtualApicRegs as "VirtualApicRegs"
    participant MMIORegister as "MMIO Register"
    participant LocalCopy as "Local Copy"

    Note over GuestOS,LocalCopy: Thermal Monitor Access (0x330)
    GuestOS ->> EmulatedLocalApic: "MMIO Read/Write FEE0_0330H"
    EmulatedLocalApic ->> VirtualApicRegs: "handle_read/write(ApicRegOffset::LvtThermal)"
    alt Direct MMIO Access
        VirtualApicRegs ->> MMIORegister: "LvtThermalMonitorRegisterMmio::read/write"
        MMIORegister -->> VirtualApicRegs: "LVT_THERMAL_MONITOR register value"
    else Cached Local Access
        VirtualApicRegs ->> LocalCopy: "LvtThermalMonitorRegisterLocal::read/write"
        LocalCopy -->> VirtualApicRegs: "Cached LVT_THERMAL_MONITOR value"
    end
    Note over GuestOS,LocalCopy: Performance Counter Access (0x340)
    GuestOS ->> EmulatedLocalApic: "MMIO Read/Write FEE0_0340H"
    EmulatedLocalApic ->> VirtualApicRegs: "handle_read/write(ApicRegOffset::LvtPerformanceCounter)"
    alt Direct MMIO Access
        VirtualApicRegs ->> MMIORegister: "LvtPerformanceCounterRegisterMmio::read/write"
        MMIORegister -->> VirtualApicRegs: "LVT_PERFORMANCE_COUNTER register value"
    else Cached Local Access
        VirtualApicRegs ->> LocalCopy: "LvtPerformanceCounterRegisterLocal::read/write"
        LocalCopy -->> VirtualApicRegs: "Cached LVT_PERFORMANCE_COUNTER value"
    end

Sources: src/regs/lvt/thermal.rs(L66 - L73)  src/regs/lvt/perfmon.rs(L66 - L74) 

Error Handling LVT Registers

Relevant source files

This document covers the two Local Vector Table (LVT) registers responsible for error handling in the virtual LAPIC implementation: the LVT Error Register and the LVT CMCI (Corrected Machine Check Interrupt) Register. These registers configure interrupt delivery for APIC internal errors and corrected machine check errors respectively.

For information about other LVT registers such as timer and external interrupt pins, see Timer LVT Register and External Interrupt Pin Registers. For system monitoring LVT registers, see System Monitoring LVT Registers.

Error Handling LVT Architecture

The error handling LVT registers are part of the broader LVT subsystem and provide interrupt configuration for different types of error conditions detected by the APIC.

Error Handling LVT Position in Register Space

flowchart TD
subgraph subGraph2["Virtual Register Space (4KB Page)"]
    subgraph subGraph1["Error_Sources[Error Detection Sources]"]
        APIC_INTERNAL["APIC InternalError Conditions"]
        MCE_CORRECTED["Machine CheckCorrected Errors"]
    end
    subgraph subGraph0["LVT_Registers[Local Vector Table Registers]"]
        LVT_ERROR["LVT_ERROR0x370APIC Internal Errors"]
        LVT_CMCI["LVT_CMCI0x2F0Corrected Machine Check"]
        LVT_TIMER["LVT_TIMER0x320Timer Interrupts"]
        LVT_THERMAL["LVT_THERMAL0x330Thermal Monitor"]
        LVT_PMC["LVT_PMC0x340Performance Counter"]
        LVT_LINT0["LVT_LINT00x350External Pin 0"]
        LVT_LINT1["LVT_LINT10x360External Pin 1"]
    end
end

APIC_INTERNAL --> LVT_ERROR
MCE_CORRECTED --> LVT_CMCI

Sources: src/regs/lvt/error.rs(L35 - L36)  src/regs/lvt/cmci.rs(L62 - L65) 

Code Entity Mapping

flowchart TD
subgraph subGraph1["Register_Access_Patterns[Register Access Patterns]"]
    DIRECT_MMIO["Direct MMIO AccessVolatile Hardware Read/Write"]
    CACHED_LOCAL["Cached Local CopyNon-volatile Memory Access"]
end
subgraph subGraph0["Rust_Types[Rust Type Definitions]"]
    LVT_ERROR_BITFIELD["LVT_ERRORregister_bitfields!"]
    LVT_CMCI_BITFIELD["LVT_CMCIregister_bitfields!"]
    ERROR_MMIO["LvtErrorRegisterMmioReadWrite"]
    ERROR_LOCAL["LvtErrorRegisterLocalLocalRegisterCopy"]
    CMCI_MMIO["LvtCmciRegisterMmioReadWrite"]
    CMCI_LOCAL["LvtCmciRegisterLocalLocalRegisterCopy"]
end

CMCI_LOCAL --> CACHED_LOCAL
CMCI_MMIO --> DIRECT_MMIO
ERROR_LOCAL --> CACHED_LOCAL
ERROR_MMIO --> DIRECT_MMIO
LVT_CMCI_BITFIELD --> CMCI_LOCAL
LVT_CMCI_BITFIELD --> CMCI_MMIO
LVT_ERROR_BITFIELD --> ERROR_LOCAL
LVT_ERROR_BITFIELD --> ERROR_MMIO

Sources: src/regs/lvt/error.rs(L37 - L44)  src/regs/lvt/cmci.rs(L66 - L73) 

LVT Error Register

The LVT Error Register handles interrupts generated when the APIC detects internal error conditions. It is located at offset 0x370 in the APIC register space.

Register Structure

BitsFieldDescription
31-17Reserved2Reserved bits
16MaskInterrupt mask (0=NotMasked, 1=Masked)
15-13Reserved1Reserved bits
12DeliveryStatusRead-only delivery status (0=Idle, 1=SendPending)
11-8Reserved0Reserved bits
7-0VectorInterrupt vector number

The LVT Error Register has a simplified structure compared to other LVT registers, lacking delivery mode configuration since error interrupts are always delivered using Fixed mode.

Key Characteristics

  • Fixed Delivery Mode Only: Unlike the CMCI register, the Error register implicitly uses Fixed delivery mode
  • Simple Configuration: Only requires vector number and mask bit configuration
  • Internal Error Detection: Triggers on APIC hardware-detected error conditions
  • Read-Only Status: The DeliveryStatus field provides read-only interrupt delivery state

Sources: src/regs/lvt/error.rs(L5 - L33)  src/regs/lvt/error.rs(L35 - L36) 

LVT CMCI Register

The LVT CMCI (Corrected Machine Check Interrupt) Register handles interrupts for corrected machine check errors when error counts reach threshold values. It is located at offset 0x2F0 in the APIC register space.

Register Structure

BitsFieldDescription
31-17Reserved2Reserved bits
16MaskInterrupt mask (0=NotMasked, 1=Masked)
15-13Reserved1Reserved bits
12DeliveryStatusRead-only delivery status (0=Idle, 1=SendPending)
11Reserved0Reserved bit
10-8DeliveryModeInterrupt delivery mode
7-0VectorInterrupt vector number

Delivery Modes

The CMCI register supports multiple delivery modes for flexible error handling:

ModeBinaryDescriptionSupported
Fixed000Delivers interrupt specified in vector field
SMI010System Management Interrupt
NMI100Non-Maskable Interrupt
INIT101Initialization request
Reserved110Not supported
ExtINT111External interrupt controller

Note: INIT and ExtINT delivery modes are explicitly not supported for the CMCI register.

Sources: src/regs/lvt/cmci.rs(L5 - L60)  src/regs/lvt/cmci.rs(L44 - L45)  src/regs/lvt/cmci.rs(L54 - L55) 

Register Field Comparison

flowchart TD
subgraph subGraph2["CMCI_Specific[LVT CMCI Specific]"]
    CMCI_RESERVED["Reserved0 (Bit 11)1 Reserved Bit"]
    CMCI_DELIVERY["DeliveryMode (Bits 10-8)Fixed/SMI/NMI Support"]
end
subgraph subGraph1["Error_Specific[LVT Error Specific]"]
    ERROR_RESERVED["Reserved0 (Bits 11-8)4 Reserved Bits"]
    ERROR_IMPLICIT["Implicit Fixed ModeNo DeliveryMode Field"]
end
subgraph subGraph0["Common_Fields[Common LVT Fields]"]
    MASK_FIELD["Mask (Bit 16)0=NotMasked, 1=Masked"]
    DELIVERY_STATUS["DeliveryStatus (Bit 12)0=Idle, 1=SendPending"]
    VECTOR_FIELD["Vector (Bits 7-0)Interrupt Vector Number"]
end

DELIVERY_STATUS --> CMCI_RESERVED
DELIVERY_STATUS --> ERROR_RESERVED
MASK_FIELD --> CMCI_RESERVED
MASK_FIELD --> ERROR_RESERVED
VECTOR_FIELD --> CMCI_DELIVERY
VECTOR_FIELD --> ERROR_IMPLICIT

Sources: src/regs/lvt/error.rs(L10 - L31)  src/regs/lvt/cmci.rs(L10 - L58) 

Error Handling Workflow

The error handling LVT registers integrate into the broader APIC interrupt delivery system to handle different categories of errors:

sequenceDiagram
    participant APICHardware as "APIC Hardware"
    participant LVT_ERRORRegister as "LVT_ERROR Register"
    participant LVT_CMCIRegister as "LVT_CMCI Register"
    participant ProcessorCore as "Processor Core"

    Note over APICHardware,ProcessorCore: Internal Error Detection
    APICHardware ->> LVT_ERRORRegister: Internal error detected
    LVT_ERRORRegister ->> LVT_ERRORRegister: Check Mask bit
    alt Error not masked
        LVT_ERRORRegister ->> ProcessorCore: Generate Fixed mode interrupt
        LVT_ERRORRegister ->> LVT_ERRORRegister: Set DeliveryStatus=SendPending
        ProcessorCore ->> LVT_ERRORRegister: Accept interrupt
        LVT_ERRORRegister ->> LVT_ERRORRegister: Set DeliveryStatus=Idle
    else Error masked
        LVT_ERRORRegister ->> LVT_ERRORRegister: Suppress interrupt
    end
    Note over APICHardware,ProcessorCore: Corrected Machine Check
    APICHardware ->> LVT_CMCIRegister: Corrected error threshold reached
    LVT_CMCIRegister ->> LVT_CMCIRegister: Check Mask bit and DeliveryMode
    alt CMCI not masked
    alt DeliveryMode=Fixed
        LVT_CMCIRegister ->> ProcessorCore: Generate vector interrupt
    else DeliveryMode=SMI
        LVT_CMCIRegister ->> ProcessorCore: Generate SMI
    else DeliveryMode=NMI
        LVT_CMCIRegister ->> ProcessorCore: Generate NMI
    end
    LVT_CMCIRegister ->> LVT_CMCIRegister: Set DeliveryStatus=SendPending
    ProcessorCore ->> LVT_CMCIRegister: Accept interrupt
    LVT_CMCIRegister ->> LVT_CMCIRegister: Set DeliveryStatus=Idle
    else CMCI masked
        LVT_CMCIRegister ->> LVT_CMCIRegister: Suppress interrupt
    end

Sources: src/regs/lvt/error.rs(L18 - L27)  src/regs/lvt/cmci.rs(L18 - L27)  src/regs/lvt/cmci.rs(L32 - L56) 

Register Type Definitions

Both registers provide dual access patterns through the tock-registers framework:

  • MMIO Types: LvtErrorRegisterMmio and LvtCmciRegisterMmio for direct hardware access
  • Local Copy Types: LvtErrorRegisterLocal and LvtCmciRegisterLocal for cached register copies

The local copy types enable efficient register manipulation without volatile MMIO operations for each access, which is particularly useful in virtualization scenarios where register state is maintained in memory.

Sources: src/regs/lvt/error.rs(L37 - L44)  src/regs/lvt/cmci.rs(L66 - L73) 

Control and Status Registers

Relevant source files

This document covers the non-LVT (Local Vector Table) control and status registers in the virtual Local APIC implementation. These registers handle APIC identification, interrupt state management, inter-processor communication, and various control functions. For information about Local Vector Table registers, see LVT System.

The control and status registers are defined as fields in the LocalAPICRegs struct and accessed through the VirtualApicRegs virtualization layer. These registers implement the core APIC functionality beyond interrupt vector configuration.

Register Categories Overview

The control and status registers can be organized into several functional categories based on their roles in the APIC system:

Register Categories and Memory Layout


Sources: src/regs/mod.rs(L13 - L104) 

Spurious Interrupt Vector Register

The SVR register is the most complex control register, implemented with detailed bitfield definitions. It controls APIC software enable/disable, spurious vector configuration, and various interrupt handling behaviors.

SVR Register Bitfield Structure

flowchart TD
subgraph subGraph0["SPURIOUS_INTERRUPT_VECTOR Bitfields"]
    RESERVED2["Reserved1Bits 13-3119 bits"]
    EOI_SUPPRESS["EOIBroadcastSuppressionBit 121 bit0: Disabled, 1: Enabled"]
    RESERVED1["Reserved0Bits 10-112 bits"]
    FOCUS_CHECK["FocusProcessorCheckingBit 91 bit0: Enabled, 1: Disabled"]
    APIC_ENABLE["APICSoftwareEnableDisableBit 81 bit0: Disabled, 1: Enabled"]
    SPURIOUS_VEC["SPURIOUS_VECTORBits 0-78 bitsVector number"]
end

APIC_ENABLE --> SPURIOUS_VEC
EOI_SUPPRESS --> RESERVED1
FOCUS_CHECK --> APIC_ENABLE
RESERVED1 --> FOCUS_CHECK
RESERVED2 --> EOI_SUPPRESS

The register provides two type aliases for different access patterns:

  • SpuriousInterruptVectorRegisterMmio: Direct MMIO access via ReadWrite<u32, SPURIOUS_INTERRUPT_VECTOR::Register>
  • SpuriousInterruptVectorRegisterLocal: Cached local copy via LocalRegisterCopy<u32, SPURIOUS_INTERRUPT_VECTOR::Register>

Sources: src/regs/svr.rs(L1 - L71)  src/regs/mod.rs(L44 - L45) 

Control and Identification Registers

These registers provide basic APIC identification and priority control functionality:

RegisterOffsetTypePurpose
ID0x20ReadWriteLocal APIC ID for processor identification
VERSION0x30ReadOnlyAPIC version information and capabilities
TPR0x80ReadWriteTask Priority Register - current task priority level
APR0x90ReadOnlyArbitration Priority Register - for bus arbitration
PPR0xA0ReadOnlyProcessor Priority Register - combined priority level

Priority Register Relationships


Sources: src/regs/mod.rs(L17 - L30) 

Interrupt State Registers

The interrupt state is maintained in three 256-bit register arrays, each consisting of 8 × 128-bit entries covering the full 256 interrupt vector space:

Register ArrayOffset RangeTypePurpose
ISR0x100-0x170[ReadOnly; 8]In-Service Register - currently serviced interrupts
TMR0x180-0x1F0[ReadOnly; 8]Trigger Mode Register - level vs edge triggered
IRR0x200-0x270[ReadOnly; 8]Interrupt Request Register - pending interrupts

Interrupt State Array Layout


The IRR uses a specific bit mapping where bit x is located at bit position (x & 1FH) at offset (200H | ((x & E0H) >> 1)), and only the low 4 bytes of each 16-byte field are used by the processor.

Sources: src/regs/mod.rs(L47 - L60) 

Communication Registers

These registers handle inter-processor communication and interrupt acknowledgment:

RegisterOffsetTypePurpose
EOI0xB0WriteOnlyEnd of Interrupt - signal interrupt completion
RRD0xC0ReadOnlyRemote Read Register - remote APIC access
LDR0xD0ReadWriteLogical Destination Register - logical addressing
DFR0xE0ReadWriteDestination Format Register - addressing mode
ICR_LO0x300ReadWriteInterrupt Command Register Low - IPI control
ICR_HI0x310ReadWriteInterrupt Command Register High - destination
SELF_IPI0x3F0WriteOnlySelf IPI Register - self-directed interrupts

Inter-Processor Communication Flow

sequenceDiagram
    participant ProcessorA as "Processor A"
    participant ICR_LO0x300 as "ICR_LO (0x300)"
    participant ICR_HI0x310 as "ICR_HI (0x310)"
    participant ProcessorB as "Processor B"
    participant EOI0xB0 as "EOI (0xB0)"
    participant SELF_IPI as SELF_IPI

    Note over ProcessorA,ProcessorB: Inter-Processor Interrupt Sequence
    ProcessorA ->> ICR_HI0x310: Write destination processor ID
    ProcessorA ->> ICR_LO0x300: Write vector + delivery mode + trigger
    ICR_LO0x300 ->> ProcessorB: Send IPI
    ProcessorB ->> ProcessorB: Handle interrupt
    ProcessorB ->> EOI0xB0: Write to signal completion
    Note over ProcessorA,EOI0xB0: Self-IPI Alternative
    ProcessorA ->> SELF_IPI: Write vector for self-interrupt

Sources: src/regs/mod.rs(L32 - L42)  src/regs/mod.rs(L67 - L70)  src/regs/mod.rs(L99 - L100) 

Timer Control Registers

The timer subsystem uses dedicated control registers separate from the LVT Timer register:

RegisterOffsetTypePurpose
ICR_TIMER0x380ReadWriteInitial Count Register - timer start value
CCR_TIMER0x390ReadOnlyCurrent Count Register - current timer value
DCR_TIMER0x3E0ReadWriteDivide Configuration Register - timer frequency

Timer Control Register Interaction


Sources: src/regs/mod.rs(L90 - L97) 

Error Status Register

The Error Status Register tracks various APIC error conditions:

RegisterOffsetTypePurpose
ESR0x280ReadWriteError Status Register - APIC error flags

This register works in conjunction with the LVT Error register to handle and report APIC internal errors.

Sources: src/regs/mod.rs(L61 - L62) 

Register Access Patterns

All control and status registers are accessed through the same virtualization infrastructure as LVT registers, supporting both xAPIC MMIO and x2APIC MSR access modes:

Register Access Flow


Sources: src/regs/mod.rs(L13 - L104) 

Development and Project Management

Relevant source files

This page documents the development workflow, continuous integration pipeline, build configuration, and testing procedures for the x86_vlapic crate. It covers the automated processes that ensure code quality, build consistency, and documentation deployment.

For information about the core system architecture and components, see Core Architecture. For details about the register system implementation, see Register System.

CI/CD Pipeline Overview

The x86_vlapic project uses GitHub Actions for continuous integration and deployment. The pipeline is designed to ensure code quality, build consistency across targets, and automated documentation deployment.

Pipeline Architecture

flowchart TD
subgraph subGraph3["Documentation Pipeline"]
    DOC_BUILD["cargo doc --no-deps --all-features"]
    DOC_INDEX["Generate index.html redirect"]
    DEPLOY["Deploy to gh-pages branch"]
end
subgraph subGraph2["Code Quality Gates"]
    FORMAT["cargo fmt --all --check"]
    CLIPPY["cargo clippy --all-features"]
    BUILD["cargo build --all-features"]
    TEST["cargo test (conditional)"]
end
subgraph subGraph1["CI Job Matrix"]
    MATRIX["Matrix Strategy"]
    RUST_NIGHTLY["rust-toolchain: nightly"]
    TARGET_X86["targets: x86_64-unknown-none"]
end
subgraph subGraph0["Trigger Events"]
    PUSH["Push to Repository"]
    PR["Pull Request"]
end

BUILD --> TEST
CLIPPY --> BUILD
DOC_BUILD --> DOC_INDEX
DOC_INDEX --> DEPLOY
FORMAT --> CLIPPY
MATRIX --> RUST_NIGHTLY
MATRIX --> TARGET_X86
PR --> MATRIX
PUSH --> MATRIX
RUST_NIGHTLY --> DOC_BUILD
RUST_NIGHTLY --> FORMAT

Sources: .github/workflows/ci.yml(L1 - L56) 

Toolchain Configuration

The project requires specific Rust toolchain configuration to support the bare-metal x86 target:

ComponentConfigurationPurpose
ToolchainnightlyRequired for no_std features and advanced compiler options
Targetx86_64-unknown-noneBare-metal x86_64 target for hypervisor environments
Componentsrust-srcSource code for core library compilation
ComponentsclippyLinting and code analysis
ComponentsrustfmtCode formatting enforcement

Sources: .github/workflows/ci.yml(L11 - L19) 

Build Process and Quality Checks

Code Quality Workflow

sequenceDiagram
    participant Developer as "Developer"
    participant Repository as "Repository"
    participant CIPipeline as "CI Pipeline"
    participant GitHubPages as "GitHub Pages"

    Developer ->> Repository: "git push / create PR"
    Repository ->> CIPipeline: "Trigger workflow"
    Note over CIPipeline: Setup Phase
    CIPipeline ->> CIPipeline: "Setup Rust nightly toolchain"
    CIPipeline ->> CIPipeline: "Install components: rust-src, clippy, rustfmt"
    CIPipeline ->> CIPipeline: "Add target: x86_64-unknown-none"
    Note over CIPipeline: Quality Gates
    CIPipeline ->> CIPipeline: "cargo fmt --all --check"
    Note over GitHubPages,CIPipeline: Fails if code not formatted
    CIPipeline ->> CIPipeline: "cargo clippy --target x86_64-unknown-none --all-features"
    Note over GitHubPages,CIPipeline: Allows clippy::new_without_default
    CIPipeline ->> CIPipeline: "cargo build --target x86_64-unknown-none --all-features"
    Note over GitHubPages,CIPipeline: Must build successfully
    alt Target is x86_64-unknown-linux-gnu
        CIPipeline ->> CIPipeline: "cargo test --target x86_64-unknown-linux-gnu"
        Note over GitHubPages,CIPipeline: Unit tests with --nocapture
    end
    Note over CIPipeline: Documentation Pipeline (main branch only)
    CIPipeline ->> CIPipeline: "cargo doc --no-deps --all-features"
    CIPipeline ->> CIPipeline: "Generate index.html redirect"
    CIPipeline ->> GitHubPages: "Deploy to gh-pages branch"

Sources: .github/workflows/ci.yml(L20 - L31)  .github/workflows/ci.yml(L44 - L55) 

Linting Configuration

The project uses specific Clippy configuration to balance code quality with practical considerations:

cargo clippy --target x86_64-unknown-none --all-features -- -A clippy::new_without_default

The -A clippy::new_without_default flag allows new() methods without requiring Default implementation, which is common in device driver patterns.

Sources: .github/workflows/ci.yml(L25) 

Documentation Management

Documentation Build Process

The documentation pipeline includes special configuration for comprehensive API documentation:

ConfigurationValuePurpose
RUSTDOCFLAGS-D rustdoc::broken_intra_doc_linksFail on broken internal links
RUSTDOCFLAGS-D missing-docsRequire documentation for all public items
Build flags--no-deps --all-featuresGenerate docs only for this crate with all features

Sources: .github/workflows/ci.yml(L40)  .github/workflows/ci.yml(L47) 

Documentation Deployment Strategy

flowchart TD
subgraph subGraph2["Deployment Conditions"]
    MAIN_ONLY["Deploy only from main branch"]
    CONTINUE_ERROR["Continue on error for non-main"]
end
subgraph subGraph1["Documentation Actions"]
    BUILD["cargo doc build"]
    REDIRECT["Generate index.html redirect"]
    DEPLOY["Single-commit deployment"]
end
subgraph subGraph0["Branch Strategy"]
    MAIN["main branch"]
    GHPAGES["gh-pages branch"]
    PR["Pull Request"]
end

BUILD --> REDIRECT
DEPLOY --> GHPAGES
MAIN --> BUILD
MAIN_ONLY --> DEPLOY
PR --> BUILD
PR --> CONTINUE_ERROR
REDIRECT --> MAIN_ONLY

Sources: .github/workflows/ci.yml(L45)  .github/workflows/ci.yml(L50 - L55) 

Testing Framework

Test Execution Strategy

The testing approach is target-specific due to the bare-metal nature of the codebase:

flowchart TD
subgraph subGraph2["Test Limitations"]
    NO_STD["no_std environment"]
    BARE_METAL["Bare-metal target constraints"]
    HOST_ONLY["Host-only test execution"]
end
subgraph subGraph1["Test Execution"]
    UNIT_TESTS["cargo test --target x86_64-unknown-linux-gnu"]
    NOCAPTURE["--nocapture flag"]
    CONDITIONAL["Conditional execution"]
end
subgraph subGraph0["Test Targets"]
    X86_NONE["x86_64-unknown-none(Build Only)"]
    X86_LINUX["x86_64-unknown-linux-gnu(Build + Test)"]
end

BARE_METAL --> HOST_ONLY
HOST_ONLY --> CONDITIONAL
UNIT_TESTS --> CONDITIONAL
UNIT_TESTS --> NOCAPTURE
X86_LINUX --> UNIT_TESTS
X86_NONE --> BARE_METAL

Sources: .github/workflows/ci.yml(L28 - L30) 

Development Workflow

StepCommandPurpose
1. Formatcargo fmt --allEnsure consistent code formatting
2. Lintcargo clippy --target x86_64-unknown-none --all-featuresCheck for common issues
3. Buildcargo build --target x86_64-unknown-none --all-featuresVerify compilation
4. Testcargo test(on host)Run unit tests if available
5. Documentcargo doc --all-featuresGenerate and verify documentation

Repository Management

The project follows standard Git practices with specific CI integration:

flowchart TD
subgraph subGraph2["Quality Gates"]
    FORMAT_CHECK["Format Check"]
    CLIPPY_CHECK["Clippy Check"]
    BUILD_CHECK["Build Check"]
    MERGE["Merge to main"]
end
subgraph subGraph1["Remote Integration"]
    PUSH["git push"]
    PR["Create PR"]
    CI_CHECK["CI Pipeline"]
end
subgraph subGraph0["Local Development"]
    EDIT["Edit Code"]
    FORMAT["cargo fmt"]
    TEST["cargo test"]
end

BUILD_CHECK --> MERGE
CI_CHECK --> FORMAT_CHECK
CLIPPY_CHECK --> BUILD_CHECK
EDIT --> FORMAT
FORMAT --> TEST
FORMAT_CHECK --> CLIPPY_CHECK
PR --> CI_CHECK
PUSH --> CI_CHECK
TEST --> PUSH

Sources: .github/workflows/ci.yml(L3)  .gitignore(L1 - L5) 

Build Artifacts and Exclusions

The .gitignore configuration excludes standard Rust build artifacts and development tools:

  • /target - Cargo build output directory
  • /.vscode - Visual Studio Code workspace files
  • .DS_Store - macOS system files
  • Cargo.lock - Lock file (typically excluded for libraries)

Sources: .gitignore(L1 - L5) 

This configuration ensures that only source code and essential project files are tracked in version control, while build artifacts and editor-specific files are excluded.

Overview

Relevant source files

Purpose and Scope

The arm_gicv2 crate provides a hardware abstraction layer for the ARM Generic Interrupt Controller version 2 (GICv2), enabling safe and efficient interrupt management in embedded systems, bare-metal applications, and hypervisors. This library offers a #![no_std] compatible interface for configuring, routing, and handling interrupts across ARM-based multi-core systems.

This overview covers the crate's fundamental architecture and interrupt classification system. For detailed information about the core interrupt handling components, see Core Architecture. For comprehensive interrupt type management, see Interrupt Types and Management. For register-level implementation details, see Register Interface.

Sources: src/lib.rs(L1 - L117)  Cargo.toml(L1 - L19)  README.md(L1 - L10) 

Core Components Architecture

The crate exposes two primary hardware abstraction components that work together to manage the complete interrupt processing pipeline:

flowchart TD
subgraph subGraph2["Interrupt Classification"]
    SGI_RANGE["SGI_RANGE (0..16)"]
    PPI_RANGE["PPI_RANGE (16..32)"]
    SPI_RANGE["SPI_RANGE (32..1020)"]
    TRANSLATE["translate_irq()"]
end
subgraph subGraph1["arm_gicv2 Abstractions"]
    GIC_DIST["GicDistributor"]
    GIC_CPU["GicCpuInterface"]
    LIB_RS["lib.rs exports"]
end
subgraph subGraph0["Hardware Layer"]
    HW_DIST["GIC Distributor Hardware"]
    HW_CPU["CPU Interface Hardware"]
    ARM_CORES["ARM CPU Cores"]
end

GIC_DIST --> GIC_CPU
HW_CPU --> ARM_CORES
HW_CPU --> GIC_CPU
HW_DIST --> GIC_DIST
LIB_RS --> GIC_CPU
LIB_RS --> GIC_DIST
PPI_RANGE --> TRANSLATE
SGI_RANGE --> TRANSLATE
SPI_RANGE --> TRANSLATE
TRANSLATE --> GIC_CPU
TRANSLATE --> GIC_DIST

The GicDistributor manages system-wide interrupt configuration, priority handling, and routing decisions, while the GicCpuInterface handles per-CPU interrupt acknowledgment and completion signaling. These components are exported from the main library interface defined in src/lib.rs(L12) 

Sources: src/lib.rs(L10 - L12) 

Interrupt Classification and Translation System

The crate implements a comprehensive interrupt classification system that categorizes all GIC interrupts into three distinct types, each with specific characteristics and use cases:

flowchart TD
subgraph subGraph2["Translation Function"]
    TRANSLATE_IRQ["translate_irq(id, int_type)-> Option"]
end
subgraph subGraph1["Interrupt ID Ranges"]
    SGI_IDS["SGI_RANGE: 0..16Software Generated"]
    PPI_IDS["PPI_RANGE: 16..32Private Peripheral"]
    SPI_IDS["SPI_RANGE: 32..1020Shared Peripheral"]
end
subgraph subGraph0["InterruptType Enum"]
    SGI["InterruptType::SGI"]
    PPI["InterruptType::PPI"]
    SPI["InterruptType::SPI"]
end
GIC_INTID["GIC INTID"]

PPI --> PPI_IDS
PPI_IDS --> TRANSLATE_IRQ
SGI --> SGI_IDS
SGI_IDS --> TRANSLATE_IRQ
SPI --> SPI_IDS
SPI_IDS --> TRANSLATE_IRQ
TRANSLATE_IRQ --> GIC_INTID

The translate_irq function provides safe conversion from logical interrupt IDs to GIC interrupt identifiers, ensuring type safety and preventing invalid interrupt configurations. This function implements bounds checking for each interrupt type and returns None for invalid combinations.

Interrupt TypeID RangeConstantPurpose
SGI0-15SGI_RANGEInter-processor communication
PPI16-31PPI_RANGESingle CPU peripheral interrupts
SPI32-1019SPI_RANGEMulti-CPU peripheral interrupts

Sources: src/lib.rs(L14 - L29)  src/lib.rs(L74 - L89)  src/lib.rs(L91 - L116) 

System Integration Context

The crate integrates into the broader embedded systems ecosystem through several key characteristics:

flowchart TD
subgraph subGraph2["Target Applications"]
    EMBEDDED["Embedded Systems"]
    BARE_METAL["Bare Metal Applications"]
    HYPERVISORS["Hypervisors/VMMs"]
    ARCEOS["ArceOS Operating System"]
end
subgraph subGraph1["arm_gicv2 Crate"]
    NO_STD["#![no_std] compatibility"]
    EL2_FEATURE["el2 feature flagHypervisor support"]
    CORE_TYPES["Core interrupt typesTriggerMode, InterruptType"]
end
subgraph Dependencies["Dependencies"]
    TOCK_REGS["tock-registers 0.8Hardware register abstractions"]
end

CORE_TYPES --> NO_STD
EL2_FEATURE --> NO_STD
NO_STD --> ARCEOS
NO_STD --> BARE_METAL
NO_STD --> EMBEDDED
NO_STD --> HYPERVISORS
TOCK_REGS --> NO_STD

The crate supports both basic interrupt management and advanced hypervisor features through the optional el2 feature flag. The TriggerMode enum supports both edge-triggered and level-sensitive interrupt configurations, providing flexibility for different hardware integration scenarios.

Key system constants include:

  • GIC_MAX_IRQ: Maximum of 1024 supported interrupts
  • GIC_CONFIG_BITS: 2 bits per interrupt for trigger mode configuration
  • GICC_CTLR_EN_BIT and GICD_CTLR_EN_BIT: Control register enable bits

Sources: src/lib.rs(L1 - L3)  src/lib.rs(L31 - L72)  Cargo.toml(L14 - L18) 

Core Architecture

Relevant source files

Purpose and Scope

This document covers the fundamental architectural design of the arm_gicv2 crate, focusing on the two primary hardware abstraction components and their interaction patterns. The architecture centers around providing safe, type-safe access to ARM GICv2 hardware through two main abstractions: system-wide interrupt distribution and per-CPU interrupt handling.

For detailed information about specific interrupt types and classification, see Interrupt Types and Management. For register-level implementation details, see Register Interface.

Architectural Overview

The arm_gicv2 crate implements a two-tier architecture that mirrors the ARM GICv2 hardware design. The core architecture consists of two primary components that work in coordination to manage interrupt processing across the system.

Component Architecture Diagram

flowchart TD
subgraph subGraph3["Hardware Layer"]
    HW_DIST["GIC Distributor Hardware"]
    HW_CPU["CPU Interface Hardware"]
end
subgraph subGraph2["Interrupt Classification"]
    SGI_RANGE["SGI_RANGE (0..16)Software Generated"]
    PPI_RANGE["PPI_RANGE (16..32)Private Peripheral"]
    SPI_RANGE["SPI_RANGE (32..1020)Shared Peripheral"]
    translate_irq["translate_irq()Type conversion"]
end
subgraph subGraph1["Register Abstractions"]
    GicDistributorRegs["GicDistributorRegs(memory-mapped registers)"]
    GicCpuInterfaceRegs["GicCpuInterfaceRegs(CPU interface registers)"]
end
subgraph subGraph0["Hardware Abstraction Layer"]
    GicDistributor["GicDistributor(system-wide control)"]
    GicCpuInterface["GicCpuInterface(per-CPU handling)"]
end

GicCpuInterface --> GicCpuInterfaceRegs
GicCpuInterfaceRegs --> HW_CPU
GicDistributor --> GicCpuInterface
GicDistributor --> GicDistributorRegs
GicDistributorRegs --> HW_DIST
PPI_RANGE --> translate_irq
SGI_RANGE --> translate_irq
SPI_RANGE --> translate_irq
translate_irq --> GicCpuInterface
translate_irq --> GicDistributor

Sources: src/gic_v2.rs(L92 - L131)  src/lib.rs(L12 - L29)  src/lib.rs(L91 - L116) 

Core Components

GicDistributor Component

The GicDistributor struct serves as the system-wide interrupt controller, responsible for global interrupt management and routing decisions. It encapsulates a pointer to memory-mapped distributor registers and maintains the maximum interrupt count.

Key Responsibilities:

  • Global interrupt enable/disable control via set_enable()
  • Priority management through set_priority() and get_priority()
  • CPU targeting via set_target_cpu() and get_target_cpu()
  • Interrupt configuration using configure_interrupt()
  • Software-generated interrupt (SGI) distribution via send_sgi() family of methods
  • System initialization through init()

GicCpuInterface Component

The GicCpuInterface struct handles per-CPU interrupt processing, managing the interface between the distributor and individual processor cores. Each CPU in the system has its own CPU interface instance.

Key Responsibilities:

  • Interrupt acknowledgment via iar() (Interrupt Acknowledge Register)
  • End-of-interrupt signaling through eoi() (End of Interrupt Register)
  • Interrupt deactivation using dir() (Deactivate Interrupt Register, EL2 feature)
  • Priority masking and control register management
  • Complete interrupt handling workflow via handle_irq()

Sources: src/gic_v2.rs(L92 - L136)  src/gic_v2.rs(L376 - L479) 

Component Interaction Flow

The two core components work together to process interrupts from generation to completion. The following diagram illustrates the interaction patterns and method calls involved in typical interrupt processing scenarios.

Interrupt Processing Coordination

sequenceDiagram
    participant HardwareSource as "Hardware Source"
    participant GicDistributor as "GicDistributor"
    participant GicCpuInterface as "GicCpuInterface"
    participant InterruptHandler as "Interrupt Handler"

    Note over HardwareSource,InterruptHandler: "Interrupt Configuration Phase"
    GicDistributor ->> GicDistributor: "init()"
    GicCpuInterface ->> GicCpuInterface: "init()"
    GicDistributor ->> GicDistributor: "set_enable(vector, true)"
    GicDistributor ->> GicDistributor: "set_priority(vector, priority)"
    GicDistributor ->> GicDistributor: "set_target_cpu(vector, cpu_mask)"
    GicDistributor ->> GicDistributor: "configure_interrupt(vector, TriggerMode)"
    Note over HardwareSource,InterruptHandler: "Interrupt Processing Phase"
    HardwareSource ->> GicDistributor: "Interrupt Signal"
    GicDistributor ->> GicCpuInterface: "Route to Target CPU"
    GicCpuInterface ->> GicCpuInterface: "iar() -> interrupt_id"
    GicCpuInterface ->> InterruptHandler: "Call handler(interrupt_id)"
    InterruptHandler ->> GicCpuInterface: "Processing complete"
    GicCpuInterface ->> GicCpuInterface: "eoi(iar_value)"
    Note over HardwareSource,InterruptHandler: "EL2 Feature: Separated Priority Drop"
    alt "EL2 mode
    alt enabled"
        GicCpuInterface ->> GicCpuInterface: "dir(iar_value)"
    end
    end
    Note over HardwareSource,InterruptHandler: "Software Generated Interrupt"
    GicDistributor ->> GicDistributor: "send_sgi(dest_cpu, sgi_num)"
    GicDistributor ->> GicCpuInterface: "SGI delivery"
    GicCpuInterface ->> GicCpuInterface: "handle_irq(handler)"

Sources: src/gic_v2.rs(L201 - L223)  src/gic_v2.rs(L443 - L459)  src/gic_v2.rs(L342 - L373)  src/gic_v2.rs(L461 - L478) 

Register Architecture Integration

The core components interact with hardware through structured register abstractions. Each component maintains a base pointer to its respective register structure, providing type-safe access to memory-mapped hardware registers.

Register Structure Mapping

flowchart TD
subgraph subGraph3["GicCpuInterfaceRegs Fields"]
    CPU_CTLR["CTLR(Control)"]
    IAR["IAR(Acknowledge)"]
    EOIR["EOIR(End of Interrupt)"]
    DIR["DIR(Deactivate)"]
    PMR["PMR(Priority Mask)"]
end
subgraph subGraph2["GicCpuInterface Methods"]
    handle_irq["handle_irq()"]
    iar["iar()"]
    eoi["eoi()"]
    dir["dir()"]
    cpu_init["init()"]
end
subgraph subGraph1["GicDistributorRegs Fields"]
    CTLR["CTLR(Control)"]
    ISENABLER["ISENABLER(Set-Enable)"]
    ICENABLER["ICENABLER(Clear-Enable)"]
    SGIR["SGIR(SGI Register)"]
    IPRIORITYR["IPRIORITYR(Priority)"]
    ITARGETSR["ITARGETSR(Target)"]
    ICFGR["ICFGR(Configuration)"]
end
subgraph subGraph0["GicDistributor Methods"]
    init["init()"]
    set_enable["set_enable()"]
    send_sgi["send_sgi()"]
    set_priority["set_priority()"]
    configure_interrupt["configure_interrupt()"]
end

configure_interrupt --> ICFGR
cpu_init --> CPU_CTLR
cpu_init --> PMR
dir --> DIR
eoi --> EOIR
handle_irq --> DIR
handle_irq --> EOIR
handle_irq --> IAR
iar --> IAR
init --> CTLR
send_sgi --> SGIR
set_enable --> ICENABLER
set_enable --> ISENABLER
set_priority --> IPRIORITYR

Sources: src/gic_v2.rs(L20 - L62)  src/gic_v2.rs(L64 - L90)  src/gic_v2.rs(L147 - L149)  src/gic_v2.rs(L384 - L386) 

System State Management

The architecture maintains both global system state through the distributor and per-CPU state through individual CPU interfaces. The distributor manages the max_irqs field to track the hardware-supported interrupt count, while CPU interfaces remain stateless, relying on hardware register state.

Initialization and Configuration

ComponentInitialization MethodKey Configuration
GicDistributorinit()Disable all interrupts, set SPI targets to CPU 0, configure edge-triggered mode, enable GICD
GicCpuInterfaceinit()Set priority mask to maximum, enable CPU interface, configure EL2 mode if feature enabled

The architecture ensures thread safety through Send and Sync implementations for both core components, allowing safe concurrent access across multiple threads or CPU cores.

Sources: src/gic_v2.rs(L132 - L136)  src/gic_v2.rs(L342 - L373)  src/gic_v2.rs(L461 - L478) 

GIC Distributor Component

Relevant source files

The GIC Distributor Component provides system-wide interrupt management and routing functionality for ARM GICv2 systems. This component handles global interrupt configuration, priority management, CPU targeting, and distribution of interrupts to CPU interfaces. It serves as the central coordination point for all interrupt activity in the system.

For information about per-CPU interrupt handling and acknowledgment, see CPU Interface Component. For details on the complete interrupt flow, see Interrupt Processing Pipeline.

Overview and Architecture

The GicDistributor struct represents the hardware GIC Distributor block and provides a safe Rust abstraction over the memory-mapped distributor registers. The distributor performs interrupt prioritization and distribution to CPU interface blocks connected to processors in the system.

flowchart TD
subgraph subGraph2["Core Methods"]
    INIT["init()"]
    CONFIG_INT["configure_interrupt()"]
    SET_ENABLE["set_enable()"]
    SET_PRIORITY["set_priority()"]
    SET_TARGET["set_target_cpu()"]
    SEND_SGI["send_sgi()"]
end
subgraph subGraph1["Hardware Registers"]
    CTLR["CTLR - Control"]
    TYPER["TYPER - Type Info"]
    ISENABLER["ISENABLER - Enable Set"]
    ICENABLER["ICENABLER - Enable Clear"]
    IPRIORITYR["IPRIORITYR - Priority"]
    ITARGETSR["ITARGETSR - Target CPU"]
    ICFGR["ICFGR - Configuration"]
    SGIR["SGIR - Software Generated"]
end
subgraph subGraph0["GicDistributor Structure"]
    DIST_STRUCT["GicDistributor"]
    BASE_PTR["base: NonNull<GicDistributorRegs>"]
    MAX_IRQS["max_irqs: usize"]
end

BASE_PTR --> CTLR
BASE_PTR --> ICENABLER
BASE_PTR --> ICFGR
BASE_PTR --> IPRIORITYR
BASE_PTR --> ISENABLER
BASE_PTR --> ITARGETSR
BASE_PTR --> SGIR
BASE_PTR --> TYPER
CONFIG_INT --> ICFGR
DIST_STRUCT --> BASE_PTR
DIST_STRUCT --> MAX_IRQS
INIT --> CTLR
SEND_SGI --> SGIR
SET_ENABLE --> ICENABLER
SET_ENABLE --> ISENABLER
SET_PRIORITY --> IPRIORITYR
SET_TARGET --> ITARGETSR

Sources: src/gic_v2.rs(L110 - L113)  src/gic_v2.rs(L20 - L62)  src/gic_v2.rs(L138 - L374) 

Register Structure and Hardware Interface

The distributor manages a comprehensive set of memory-mapped registers defined in GicDistributorRegs. Each register serves specific interrupt management functions:

Register GroupPurposeKey Registers
ControlBasic distributor controlCTLR,TYPER,IIDR
Enable/DisableInterrupt enable managementISENABLER,ICENABLER
PendingInterrupt pending stateISPENDR,ICPENDR
ActiveInterrupt active stateISACTIVER,ICACTIVER
PriorityInterrupt priority levelsIPRIORITYR
TargetingCPU routing configurationITARGETSR
ConfigurationTrigger mode settingsICFGR
Software InterruptsSGI generationSGIR,CPENDSGIR,SPENDSGIR
flowchart TD
subgraph subGraph1["Register Categories"]
    CTRL_REGS["Control RegistersCTLR, TYPER, IIDR"]
    ENABLE_REGS["Enable RegistersISENABLER, ICENABLER"]
    STATE_REGS["State RegistersISPENDR, ICPENDRISACTIVER, ICACTIVER"]
    CONFIG_REGS["Config RegistersIPRIORITYR, ITARGETSRICFGR"]
    SGI_REGS["SGI RegistersSGIR, CPENDSGIRSPENDSGIR"]
end
subgraph subGraph0["Register Access Pattern"]
    METHOD_CALL["Method Call"]
    REGS_FUNC["regs()"]
    BASE_PTR["base.as_ref()"]
    HW_REGS["Hardware Registers"]
end

BASE_PTR --> HW_REGS
HW_REGS --> CONFIG_REGS
HW_REGS --> CTRL_REGS
HW_REGS --> ENABLE_REGS
HW_REGS --> SGI_REGS
HW_REGS --> STATE_REGS
METHOD_CALL --> REGS_FUNC
REGS_FUNC --> BASE_PTR

Sources: src/gic_v2.rs(L20 - L62)  src/gic_v2.rs(L147 - L149) 

Core Functionality and Methods

Initialization and Configuration

The init() method performs complete distributor initialization, setting up default configurations for all supported interrupts:

flowchart TD
START["init()"]
GET_MAX["max_irqs = self.max_irqs()"]
DISABLE_ALL["Disable all interruptsICENABLER, ICPENDR"]
CHECK_SMP["cpu_num() > 1"]
SET_TARGETS["Set SPI targets to CPU 0ITARGETSR"]
CONFIG_EDGE["Configure SPIs as edge-triggeredICFGR"]
ENABLE_DIST["Enable distributorCTLR |= GICD_CTLR_EN_BIT"]
END["Initialization Complete"]

CHECK_SMP --> CONFIG_EDGE
CHECK_SMP --> SET_TARGETS
CONFIG_EDGE --> ENABLE_DIST
DISABLE_ALL --> CHECK_SMP
ENABLE_DIST --> END
GET_MAX --> DISABLE_ALL
SET_TARGETS --> CONFIG_EDGE
START --> GET_MAX

Sources: src/gic_v2.rs(L348 - L373) 

Interrupt Management Operations

The distributor provides comprehensive interrupt control through several key method categories:

Method CategoryMethodsRegister Access
Enable Controlset_enable(),get_enable()ISENABLER,ICENABLER
Priority Managementset_priority(),get_priority()IPRIORITYR
CPU Targetingset_target_cpu(),get_target_cpu()ITARGETSR
State Managementset_pend(),set_active(),get_state()ISPENDR,ICPENDR,ISACTIVER,ICACTIVER
Configurationconfigure_interrupt(),set_icfgr()ICFGR

Software Generated Interrupts (SGI)

The distributor provides three SGI targeting modes through dedicated methods:

flowchart TD
subgraph subGraph2["Targeting Modes"]
    TO_CPU["ForwardToCPUTargetList"]
    ALL_EXCEPT["ForwardToAllExceptRequester"]
    TO_SELF["ForwardToRequester"]
end
subgraph subGraph1["SGIR Register Fields"]
    TARGET_FILTER["TargetListFilter"]
    CPU_TARGET["CPUTargetList"]
    SGI_INTID["SGIINTID"]
end
subgraph subGraph0["SGI Methods"]
    SEND_SGI["send_sgi(dest_cpu_id, sgi_num)"]
    SEND_ALL_EXCEPT["send_sgi_all_except_self(sgi_num)"]
    SEND_SELF["send_sgi_to_self(sgi_num)"]
end

SEND_ALL_EXCEPT --> SGI_INTID
SEND_ALL_EXCEPT --> TARGET_FILTER
SEND_SELF --> SGI_INTID
SEND_SELF --> TARGET_FILTER
SEND_SGI --> CPU_TARGET
SEND_SGI --> SGI_INTID
SEND_SGI --> TARGET_FILTER
TARGET_FILTER --> ALL_EXCEPT
TARGET_FILTER --> TO_CPU
TARGET_FILTER --> TO_SELF

Sources: src/gic_v2.rs(L202 - L223)  src/regs.rs

System Information and Capabilities

The distributor provides system capability discovery through hardware register queries:

  • cpu_num(): Returns the number of implemented CPU interfaces from TYPER register bits [7:5]
  • max_irqs(): Calculates maximum supported interrupts from TYPER register bits [4:0]
  • get_typer(): Returns raw TYPER register value for detailed configuration analysis
  • get_iidr(): Returns implementation identification from IIDR register

Sources: src/gic_v2.rs(L152 - L159)  src/gic_v2.rs(L322 - L330) 

Thread Safety and Ownership

The GicDistributor implements Send and Sync traits through unsafe implementations, enabling shared access across CPU cores in multi-processor systems. The struct uses NonNull<GicDistributorRegs> for safe hardware register access while maintaining zero-cost abstractions.

Sources: src/gic_v2.rs(L132 - L133)  src/gic_v2.rs(L110 - L113) 

Integration with System Architecture

The distributor works in coordination with the CPU Interface Component to provide complete interrupt management. It handles global interrupt configuration and routing, while CPU interfaces manage per-processor interrupt delivery and acknowledgment. For detailed information about the interaction between these components, see Interrupt Processing Pipeline and CPU Interface Component.

CPU Interface Component

Relevant source files

This document covers the GicCpuInterface component, which provides per-CPU interrupt handling functionality in the ARM GICv2 implementation. The CPU Interface handles interrupt acknowledgment, priority masking, and end-of-interrupt processing for individual processor cores. For system-wide interrupt distribution and configuration, see GIC Distributor Component. For the complete interrupt processing flow between components, see Interrupt Processing Pipeline.

Architecture Overview

The GicCpuInterface struct provides a Rust abstraction over the GIC CPU Interface hardware registers, enabling safe per-CPU interrupt management. Each CPU core in the system has its own CPU Interface instance that communicates with the shared GIC Distributor.

flowchart TD
subgraph subGraph2["Register Definition"]
    GICC_REGS["GicCpuInterfaceRegs struct"]
    TOCK_REGS["tock-registers abstractions"]
end
subgraph subGraph1["Software Abstraction"]
    GICC_STRUCT["GicCpuInterface struct"]
    NEW_METHOD["new() constructor"]
    IAR_METHOD["iar() method"]
    EOI_METHOD["eoi() method"]
    DIR_METHOD["dir() method"]
    HANDLE_IRQ["handle_irq() method"]
    INIT_METHOD["init() method"]
end
subgraph subGraph0["CPU Interface Hardware"]
    GICC_BASE["GIC CPU Interface Base Address"]
    CTLR_REG["GICC_CTLR: Control Register"]
    IAR_REG["GICC_IAR: Interrupt Acknowledge"]
    EOIR_REG["GICC_EOIR: End of Interrupt"]
    PMR_REG["GICC_PMR: Priority Mask"]
    BPR_REG["GICC_BPR: Binary Point"]
    DIR_REG["GICC_DIR: Deactivate (EL2)"]
end

DIR_METHOD --> DIR_REG
EOI_METHOD --> EOIR_REG
GICC_BASE --> BPR_REG
GICC_BASE --> CTLR_REG
GICC_BASE --> DIR_REG
GICC_BASE --> EOIR_REG
GICC_BASE --> IAR_REG
GICC_BASE --> PMR_REG
GICC_REGS --> GICC_STRUCT
GICC_STRUCT --> DIR_METHOD
GICC_STRUCT --> EOI_METHOD
GICC_STRUCT --> HANDLE_IRQ
GICC_STRUCT --> IAR_METHOD
GICC_STRUCT --> INIT_METHOD
GICC_STRUCT --> NEW_METHOD
IAR_METHOD --> IAR_REG
TOCK_REGS --> GICC_REGS

Sources: src/gic_v2.rs(L128 - L130)  src/gic_v2.rs(L64 - L90)  src/gic_v2.rs(L376 - L479) 

Register Interface Structure

The CPU Interface exposes its functionality through memory-mapped registers defined in the GicCpuInterfaceRegs struct. Each register serves a specific purpose in the interrupt handling workflow.

RegisterOffsetTypePurpose
CTLR0x0000ReadWriteControl register for enabling interface and configuring behavior
PMR0x0004ReadWritePriority mask register for filtering interrupts by priority
BPR0x0008ReadWriteBinary point register for priority grouping
IAR0x000cReadOnlyInterrupt acknowledge register returning pending interrupt ID
EOIR0x0010WriteOnlyEnd of interrupt register for completing interrupt processing
RPR0x0014ReadOnlyRunning priority register showing current interrupt priority
HPPIR0x0018ReadOnlyHighest priority pending interrupt register
IIDR0x00fcReadOnlyCPU interface identification register
DIR0x1000WriteOnlyDeactivate interrupt register (EL2 feature)

Sources: src/gic_v2.rs(L64 - L90) 

Core Interrupt Processing Methods

The GicCpuInterface provides several key methods for interrupt processing, each corresponding to specific hardware register operations.

flowchart TD
START["Interrupt Arrives at CPU"]
IAR_READ["iar() method reads GICC_IAR"]
CHECK_SPURIOUS["Is interrupt ID < 1020?"]
SPURIOUS["Spurious interrupt (1023)No action taken"]
EXTRACT_ID["Extract interrupt vectorvector = iar & 0x3ff"]
CALL_HANDLER["Call user-provided handler(vector)"]
EOI_WRITE["eoi(iar) writes GICC_EOIR"]
EL2_CHECK["EL2 feature enabled?"]
DIR_CHECK["EOIMODENS bit set?"]
DIR_WRITE["dir(iar) writes GICC_DIR"]
COMPLETE["Interrupt processing complete"]

CALL_HANDLER --> EOI_WRITE
CHECK_SPURIOUS --> EXTRACT_ID
CHECK_SPURIOUS --> SPURIOUS
DIR_CHECK --> COMPLETE
DIR_CHECK --> DIR_WRITE
DIR_WRITE --> COMPLETE
EL2_CHECK --> COMPLETE
EL2_CHECK --> DIR_CHECK
EOI_WRITE --> EL2_CHECK
EXTRACT_ID --> CALL_HANDLER
IAR_READ --> CHECK_SPURIOUS
SPURIOUS --> COMPLETE
START --> IAR_READ

Sources: src/gic_v2.rs(L443 - L459)  src/gic_v2.rs(L394 - L396)  src/gic_v2.rs(L406 - L408)  src/gic_v2.rs(L416 - L418) 

Interrupt Acknowledge Process

The iar() method reads the GICC_IAR register to obtain the interrupt ID of the highest priority pending interrupt. The method returns the full IAR value, which includes both the interrupt ID and the CPU ID for SGIs.

// Example usage pattern
let iar_value = cpu_interface.iar();
let interrupt_id = iar_value & 0x3ff;  // Extract interrupt ID from bits [9:0]

A return value of 1023 indicates either a spurious interrupt or that no interrupts are pending.

Sources: src/gic_v2.rs(L394 - L396) 

End of Interrupt Processing

The eoi() method writes to the GICC_EOIR register to signal completion of interrupt processing. The value written must be the exact IAR value returned from the acknowledge operation.

In hypervisor configurations (EL2 feature), the EOIR operation only performs priority drop, and a separate dir() call is required to fully deactivate the interrupt when GICC_CTLR_EOIMODENS_BIT is set.

Sources: src/gic_v2.rs(L406 - L408)  src/gic_v2.rs(L416 - L418)  src/gic_v2.rs(L452 - L455) 

High-Level Interrupt Handling

The handle_irq() method provides a convenient wrapper that encapsulates the complete interrupt processing sequence. It takes a closure that handles the actual interrupt processing logic.

flowchart TD
IAR_READ["Read IAR register"]
VECTOR_EXTRACT["Extract vector ID"]
SPURIOUS_CHECK["Vector < 1020?"]
HANDLER_CALL["Call handler(vector)"]
EOI_CALL["Call eoi(iar)"]
EL2_DIR["Conditional dir(iar)if EL2 enabled"]
SPURIOUS_END["Handle spurious"]
COMPLETE["Complete"]

EL2_DIR --> COMPLETE
EOI_CALL --> EL2_DIR
HANDLER_CALL --> EOI_CALL
IAR_READ --> VECTOR_EXTRACT
SPURIOUS_CHECK --> HANDLER_CALL
SPURIOUS_CHECK --> SPURIOUS_END
SPURIOUS_END --> COMPLETE
VECTOR_EXTRACT --> SPURIOUS_CHECK

Sources: src/gic_v2.rs(L443 - L459) 

Initialization and Configuration

The CPU Interface requires proper initialization through the init() method, which configures the interface for operation.

Standard Initialization (Non-EL2)

For standard operation, initialization sets:

  • GICC_CTLR register with GICC_CTLR_EN_BIT to enable the interface
  • GICC_PMR register to maximum value (0xFFFFFFFF) to unmask all priority levels

EL2 Hypervisor Initialization

When the el2 feature is enabled, initialization additionally sets:

  • GICC_CTLR_EOIMODENS_BIT in the control register to separate priority drop from interrupt deactivation

This separation allows hypervisors to maintain better control over interrupt lifecycle and support virtual interrupt injection.

ConfigurationGICC_CTLR ValuePMR ValuePurpose
StandardGICC_CTLR_EN_BIT0xFFFFFFFFBasic interrupt enabling
EL2 HypervisorGICC_CTLR_EN_BIT | GICC_CTLR_EOIMODENS_BIT0xFFFFFFFFHypervisor with split EOI/deactivation

Sources: src/gic_v2.rs(L466 - L478) 

Control Register Management

The CPU Interface provides direct access to the GICC_CTLR register through getter and setter methods, allowing fine-grained control over interface behavior.

Control Register Fields

The control register manages several aspects of CPU interface operation:

  • Interrupt group enabling
  • Signal bypass configuration
  • Binary point register selection
  • Priority drop and deactivation separation (EL2)
// Example control register manipulation
let current_ctlr = cpu_interface.get_ctlr();
let modified_ctlr = current_ctlr | CUSTOM_CONTROL_BITS;
cpu_interface.set_ctlr(modified_ctlr);

Sources: src/gic_v2.rs(L424 - L433) 

Thread Safety and Memory Management

The GicCpuInterface struct implements both Send and Sync traits, making it safe to share across threads. This is critical for multi-core systems where each CPU core needs access to its own CPU Interface instance.

The struct uses NonNull<GicCpuInterfaceRegs> for memory safety while maintaining the ability to perform direct hardware register access through memory-mapped I/O.

Sources: src/gic_v2.rs(L135 - L136)  src/gic_v2.rs(L128 - L130) 

Interrupt Processing Pipeline

Relevant source files

This document describes the complete interrupt processing pipeline in the ARM GICv2 implementation, from interrupt generation through final completion. It covers the coordination between the GicDistributor and GicCpuInterface components and the flow of control through the hardware abstraction layer.

For details about the individual components, see GIC Distributor Component and CPU Interface Component. For information about interrupt classification, see Interrupt Classification System.

Pipeline Overview

The interrupt processing pipeline consists of five main stages that coordinate between hardware interrupt sources, the GIC Distributor, CPU interfaces, and software interrupt handlers.

flowchart TD
HW_SOURCE["Hardware Peripheral"]
DIST_RECEIVE["GicDistributor receives interrupt"]
SW_SGI["Software SGI"]
DIST_CHECK["Distributor Processing:• Check enable/disable• Evaluate priority• Select target CPU• Check trigger mode"]
CPU_FORWARD["Forward to GicCpuInterface"]
CPU_SIGNAL["CPU Interface signals processor"]
PROC_ACK["Processor acknowledges via IAR read"]
HANDLER_EXEC["Interrupt handler execution"]
PROC_EOI["Processor signals completion via EOIR"]
OPTIONAL_DIR["Optional: Deactivation via DIR(EL2 mode only)"]
COMPLETE["Interrupt processing complete"]

CPU_FORWARD --> CPU_SIGNAL
CPU_SIGNAL --> PROC_ACK
DIST_CHECK --> CPU_FORWARD
DIST_RECEIVE --> DIST_CHECK
HANDLER_EXEC --> PROC_EOI
HW_SOURCE --> DIST_RECEIVE
OPTIONAL_DIR --> COMPLETE
PROC_ACK --> HANDLER_EXEC
PROC_EOI --> OPTIONAL_DIR
SW_SGI --> DIST_RECEIVE

Sources: src/gic_v2.rs(L1 - L480)  src/lib.rs(L1 - L117) 

Interrupt Generation Stage

Interrupts enter the pipeline through three different mechanisms based on their type classification.

flowchart TD
subgraph SPI_GEN["SPI Generation (32-1019)"]
    UART_INT["UART interrupts"]
    GPIO_INT["GPIO interrupts"]
    OTHER_SPI["Other shared peripherals"]
end
subgraph PPI_GEN["PPI Generation (16-31)"]
    TIMER_INT["Timer interrupts"]
    PMU_INT["PMU interrupts"]
    OTHER_PPI["Other private peripherals"]
end
subgraph SGI_GEN["SGI Generation (0-15)"]
    SEND_SGI["send_sgi()"]
    SEND_ALL["send_sgi_all_except_self()"]
    SEND_SELF["send_sgi_to_self()"]
end
GICD_SGIR["GICD_SGIR register write"]
HW_SIGNAL["Hardware interrupt signal"]
DIST_INPUT["GicDistributor input"]

GICD_SGIR --> DIST_INPUT
GPIO_INT --> HW_SIGNAL
HW_SIGNAL --> DIST_INPUT
OTHER_PPI --> HW_SIGNAL
OTHER_SPI --> HW_SIGNAL
PMU_INT --> HW_SIGNAL
SEND_ALL --> GICD_SGIR
SEND_SELF --> GICD_SGIR
SEND_SGI --> GICD_SGIR
TIMER_INT --> HW_SIGNAL
UART_INT --> HW_SIGNAL

Sources: src/gic_v2.rs(L201 - L223)  src/lib.rs(L14 - L29) 

Distributor Processing Stage

The GicDistributor performs comprehensive interrupt management including configuration validation, priority evaluation, and CPU targeting.

FunctionRegister AccessPurpose
set_enable()/get_enable()ISENABLER/ICENABLEREnable/disable interrupt delivery
set_priority()/get_priority()IPRIORITYRConfigure interrupt priority levels
set_target_cpu()/get_target_cpu()ITARGETSRSelect target processor for SPI interrupts
configure_interrupt()ICFGRSet edge/level trigger mode
set_state()/get_state()ISPENDR/ICPENDR/ISACTIVER/ICACTIVERManage pending and active states
flowchart TD
INT_INPUT["Interrupt arrives at Distributor"]
TYPE_CHECK["Check interrupt type"]
SGI_PROC["SGI Processing:• Check SPENDSGIR/CPENDSGIR• Inter-processor targeting"]
PPI_PROC["PPI Processing:• CPU-private handling• No target selection needed"]
SPI_PROC["SPI Processing:• Check ITARGETSR for target CPU• Multi-processor capable"]
ENABLE_CHECK["Check ISENABLER register"]
DROP["Drop interrupt"]
PRIORITY_CHECK["Check IPRIORITYR priority"]
TRIGGER_CHECK["Check ICFGR trigger mode"]
FORWARD["Forward to target GicCpuInterface"]

ENABLE_CHECK --> DROP
ENABLE_CHECK --> PRIORITY_CHECK
INT_INPUT --> TYPE_CHECK
PPI_PROC --> ENABLE_CHECK
PRIORITY_CHECK --> TRIGGER_CHECK
SGI_PROC --> ENABLE_CHECK
SPI_PROC --> ENABLE_CHECK
TRIGGER_CHECK --> FORWARD
TYPE_CHECK --> PPI_PROC
TYPE_CHECK --> SGI_PROC
TYPE_CHECK --> SPI_PROC

Sources: src/gic_v2.rs(L180 - L199)  src/gic_v2.rs(L225 - L261)  src/gic_v2.rs(L263 - L283) 

CPU Interface Processing Stage

The GicCpuInterface manages per-CPU interrupt delivery, acknowledgment, and completion signaling.

flowchart TD
CPU_RECEIVE["GicCpuInterface receives interrupt"]
PRIORITY_MASK["Check PMR priority mask"]
QUEUE["Queue interrupt"]
SIGNAL_CPU["Signal connected processor"]
PROC_READ_IAR["Processor reads GICC_IAR"]
IAR_VALUE["IAR returns interrupt ID"]
HANDLER_CALL["Call interrupt handler"]
IGNORE["Ignore spurious interrupt"]
HANDLER_COMPLETE["Handler completes"]
EOIR_WRITE["Write GICC_EOIR"]
EL2_CHECK["Check EL2 mode"]
COMPLETE_NORMAL["Interrupt complete"]
DIR_WRITE["Write GICC_DIR to deactivate"]
COMPLETE_EL2["Interrupt complete (EL2)"]

CPU_RECEIVE --> PRIORITY_MASK
DIR_WRITE --> COMPLETE_EL2
EL2_CHECK --> COMPLETE_NORMAL
EL2_CHECK --> DIR_WRITE
EOIR_WRITE --> EL2_CHECK
HANDLER_CALL --> HANDLER_COMPLETE
HANDLER_COMPLETE --> EOIR_WRITE
IAR_VALUE --> HANDLER_CALL
IAR_VALUE --> IGNORE
PRIORITY_MASK --> QUEUE
PRIORITY_MASK --> SIGNAL_CPU
PROC_READ_IAR --> IAR_VALUE
SIGNAL_CPU --> PROC_READ_IAR

Sources: src/gic_v2.rs(L394 - L396)  src/gic_v2.rs(L406 - L408)  src/gic_v2.rs(L416 - L418)  src/gic_v2.rs(L443 - L459) 

Register-Level Pipeline Flow

This diagram shows the specific register interactions during interrupt processing, mapping high-level operations to actual hardware register access.

sequenceDiagram
    participant HardwareSoftware as "Hardware/Software"
    participant GICDistributorRegisters as "GIC Distributor Registers"
    participant CPUInterfaceRegisters as "CPU Interface Registers"
    participant ARMProcessor as "ARM Processor"

    Note over HardwareSoftware,ARMProcessor: Interrupt Generation
    HardwareSoftware ->> GICDistributorRegisters: Interrupt signal / GICD_SGIR write
    Note over GICDistributorRegisters: Distributor Processing
    GICDistributorRegisters ->> GICDistributorRegisters: Check ISENABLER[n]
    GICDistributorRegisters ->> GICDistributorRegisters: Check IPRIORITYR[n]
    GICDistributorRegisters ->> GICDistributorRegisters: Check ITARGETSR[n] (SPI only)
    GICDistributorRegisters ->> GICDistributorRegisters: Check ICFGR[n] trigger mode
    Note over GICDistributorRegisters,CPUInterfaceRegisters: Forward to CPU Interface
    GICDistributorRegisters ->> CPUInterfaceRegisters: Forward interrupt to target CPU
    Note over CPUInterfaceRegisters: CPU Interface Processing
    CPUInterfaceRegisters ->> CPUInterfaceRegisters: Check PMR priority mask
    CPUInterfaceRegisters ->> ARMProcessor: Signal interrupt to processor
    Note over ARMProcessor: Processor Handling
    ARMProcessor ->> CPUInterfaceRegisters: Read GICC_IAR
    CPUInterfaceRegisters ->> ARMProcessor: Return interrupt ID
    ARMProcessor ->> ARMProcessor: Execute interrupt handler
    ARMProcessor ->> CPUInterfaceRegisters: Write GICC_EOIR
    alt EL2 Mode Enabled
        ARMProcessor ->> CPUInterfaceRegisters: Write GICC_DIR
    end
    Note over HardwareSoftware,ARMProcessor: Interrupt Complete

Sources: src/gic_v2.rs(L20 - L90)  src/gic_v2.rs(L443 - L459) 

Pipeline Integration Points

The pipeline integrates with external components through well-defined interfaces that abstract the hardware complexity.

flowchart TD
subgraph RUNTIME["Runtime Processing"]
    HANDLE_IRQ_FUNC["GicCpuInterface::handle_irq()"]
    TRANSLATE_FUNC["translate_irq()"]
end
subgraph INIT["Initialization Phase"]
    DIST_INIT["GicDistributor::init()"]
    CPU_INIT["GicCpuInterface::init()"]
end
subgraph CONFIG["Configuration Interface"]
    SET_ENABLE["set_enable()"]
    SET_PRIORITY["set_priority()"]
    SET_TARGET["set_target_cpu()"]
    CONFIG_INT["configure_interrupt()"]
end
IAR_READ["GICC_IAR read"]
HANDLER_EXEC["User handler execution"]
EOIR_WRITE["GICC_EOIR write"]
DIR_WRITE_OPT["Optional GICC_DIR write"]

EOIR_WRITE --> DIR_WRITE_OPT
HANDLER_EXEC --> EOIR_WRITE
HANDLE_IRQ_FUNC --> IAR_READ
IAR_READ --> HANDLER_EXEC
TRANSLATE_FUNC --> HANDLE_IRQ_FUNC

Sources: src/gic_v2.rs(L342 - L373)  src/gic_v2.rs(L461 - L478)  src/gic_v2.rs(L443 - L459)  src/lib.rs(L91 - L116) 

Error Handling and Edge Cases

The pipeline includes robust handling for edge cases and error conditions.

ConditionDetection MethodResponse
Spurious InterruptIAR & 0x3ff >= 1020No handler called, no EOIR write
Disabled InterruptISENABLER[n] == 0Interrupt dropped at distributor
Invalid IRQ Rangevector >= max_irqsEarly return from configuration functions
Priority MaskingPriority < PMRInterrupt queued at CPU interface

The handle_irq() function provides a complete wrapper that handles these edge cases automatically:

// Simplified logic from handle_irq() method
let iar = self.iar();
let vector = iar & 0x3ff;
if vector < 1020 {
    handler(vector);
    self.eoi(iar);
    #[cfg(feature = "el2")]
    if self.regs().CTLR.get() & GICC_CTLR_EOIMODENS_BIT != 0 {
        self.dir(iar);
    }
} else {
    // spurious interrupt - no action taken
}

Sources: src/gic_v2.rs(L443 - L459)  src/gic_v2.rs(L180 - L192)  src/gic_v2.rs(L162 - L178) 

Interrupt Types and Management

Relevant source files

This document covers the interrupt classification system and management operations provided by the arm_gicv2 crate. It details the three interrupt types supported by ARM GICv2 (SGI, PPI, SPI), their characteristics, and how they are managed through the distributor and CPU interface components.

For information about the core GIC architecture and components, see Core Architecture. For detailed register-level operations, see Register Interface.

Interrupt Classification System

The ARM GICv2 specification defines three distinct interrupt types, each serving different purposes and having specific characteristics. The crate implements this classification through constants, enums, and a translation function.

Interrupt Type Definitions

flowchart TD
subgraph subGraph2["Range Constants"]
    SGI_RANGE_CONST["SGI_RANGE: 0..16"]
    PPI_RANGE_CONST["PPI_RANGE: 16..32"]
    SPI_RANGE_CONST["SPI_RANGE: 32..1020"]
end
subgraph subGraph1["InterruptType Enum"]
    SGI_TYPE["InterruptType::SGI"]
    PPI_TYPE["InterruptType::PPI"]
    SPI_TYPE["InterruptType::SPI"]
end
subgraph subGraph0["Interrupt ID Space (0-1019)"]
    SGI_SPACE["SGI Range0-15Software Generated"]
    PPI_SPACE["PPI Range16-31Private Peripheral"]
    SPI_SPACE["SPI Range32-1019Shared Peripheral"]
end

PPI_RANGE_CONST --> PPI_SPACE
PPI_TYPE --> PPI_SPACE
SGI_RANGE_CONST --> SGI_SPACE
SGI_TYPE --> SGI_SPACE
SPI_RANGE_CONST --> SPI_SPACE
SPI_TYPE --> SPI_SPACE

Sources: src/lib.rs(L14 - L29)  src/lib.rs(L74 - L89) 

Interrupt TypeRangePurposeCPU Scope
SGI (Software Generated)0-15Inter-processor communicationAll CPUs
PPI (Private Peripheral)16-31CPU-specific peripheralsSingle CPU
SPI (Shared Peripheral)32-1019System-wide peripheralsMultiple CPUs

Interrupt Translation System

The translate_irq function provides a safe way to convert logical interrupt IDs within each type to absolute GIC interrupt IDs.

flowchart TD
INPUT["Logical ID + InterruptType"]
TRANSLATE_IRQ["translate_irq()"]
SGI_CHECK["id < 16?"]
PPI_CHECK["id < 16?"]
SPI_CHECK["id < 988?"]
SGI_RESULT["Some(id)"]
SGI_NONE["None"]
PPI_RESULT["Some(id + 16)"]
PPI_NONE["None"]
SPI_RESULT["Some(id + 32)"]
SPI_NONE["None"]
GIC_INTID["GIC INTID"]

INPUT --> TRANSLATE_IRQ
PPI_CHECK --> PPI_NONE
PPI_CHECK --> PPI_RESULT
PPI_RESULT --> GIC_INTID
SGI_CHECK --> SGI_NONE
SGI_CHECK --> SGI_RESULT
SGI_RESULT --> GIC_INTID
SPI_CHECK --> SPI_NONE
SPI_CHECK --> SPI_RESULT
SPI_RESULT --> GIC_INTID
TRANSLATE_IRQ --> PPI_CHECK
TRANSLATE_IRQ --> SGI_CHECK
TRANSLATE_IRQ --> SPI_CHECK

Sources: src/lib.rs(L91 - L116) 

Interrupt Management Operations

The GIC provides different management capabilities depending on the interrupt type. These operations are primarily handled through the GicDistributor component.

Core Management Functions

flowchart TD
subgraph subGraph1["Interrupt Type Applicability"]
    SGI_OPS["SGI Operations• send_sgi()• send_sgi_all_except_self()• send_sgi_to_self()• set_pend() (special)"]
    PPI_OPS["PPI Operations• set_enable()• set_priority()• set_state()"]
    SPI_OPS["SPI Operations• configure_interrupt()• set_enable()• set_priority()• set_target_cpu()• set_state()"]
end
subgraph subGraph0["GicDistributor Management Methods"]
    CONFIGURE["configure_interrupt()"]
    ENABLE["set_enable() / get_enable()"]
    PRIORITY["set_priority() / get_priority()"]
    TARGET["set_target_cpu() / get_target_cpu()"]
    STATE["set_state() / get_state()"]
    PEND["set_pend()"]
end

CONFIGURE --> SPI_OPS
ENABLE --> PPI_OPS
ENABLE --> SPI_OPS
PEND --> SGI_OPS
PRIORITY --> PPI_OPS
PRIORITY --> SPI_OPS
STATE --> PPI_OPS
STATE --> SGI_OPS
STATE --> SPI_OPS
TARGET --> SPI_OPS

Sources: src/gic_v2.rs(L161 - L283)  src/gic_v2.rs(L201 - L223) 

Trigger Mode Configuration

Only SPI interrupts support configurable trigger modes. The system restricts trigger mode configuration to the SPI range.

flowchart TD
CONFIG_REQ["configure_interrupt(vector, trigger_mode)"]
RANGE_CHECK["vector >= 32?"]
EARLY_RETURN["return (invalid)"]
MAX_CHECK["vector < max_irqs?"]
CALCULATE["Calculate ICFGR register"]
REG_ACCESS["Access ICFGR[reg_idx]"]
MODE_CHECK["TriggerMode?"]
SET_EDGE["Set bit (Edge)"]
CLEAR_BIT["Clear bit (Level)"]
WRITE_REG["Write ICFGR register"]

CALCULATE --> REG_ACCESS
CLEAR_BIT --> WRITE_REG
CONFIG_REQ --> RANGE_CHECK
MAX_CHECK --> CALCULATE
MAX_CHECK --> EARLY_RETURN
MODE_CHECK --> CLEAR_BIT
MODE_CHECK --> SET_EDGE
RANGE_CHECK --> EARLY_RETURN
RANGE_CHECK --> MAX_CHECK
REG_ACCESS --> MODE_CHECK
SET_EDGE --> WRITE_REG

Sources: src/gic_v2.rs(L161 - L178) 

Software Generated Interrupt Management

SGIs have special handling due to their role in inter-processor communication. They use dedicated register interfaces and targeting mechanisms.

SGI Generation Methods

MethodTargetUse Case
send_sgi(dest_cpu_id, sgi_num)Specific CPUDirect communication
send_sgi_all_except_self(sgi_num)All other CPUsBroadcast operations
send_sgi_to_self(sgi_num)Current CPUSelf-signaling
flowchart TD
subgraph subGraph1["GICD_SGIR Register Fields"]
    TARGET_FILTER["TargetListFilter"]
    CPU_LIST["CPUTargetList"]
    SGI_ID["SGIINTID"]
end
subgraph subGraph0["SGI Targeting Modes"]
    SPECIFIC["ForwardToCPUTargetList+ CPUTargetList"]
    BROADCAST["ForwardToAllExceptRequester"]
    SELF["ForwardToRequester"]
end
GICD_SGIR_REG["GICD_SGIR Register"]

BROADCAST --> TARGET_FILTER
CPU_LIST --> GICD_SGIR_REG
SELF --> TARGET_FILTER
SGI_ID --> GICD_SGIR_REG
SPECIFIC --> CPU_LIST
SPECIFIC --> TARGET_FILTER
TARGET_FILTER --> GICD_SGIR_REG

Sources: src/gic_v2.rs(L201 - L223)  src/regs/gicd_sgir.rs

SGI Pending State Management

SGIs require special handling for pending state management due to their per-CPU nature:

flowchart TD
SET_PEND["set_pend(int_id, is_pend, current_cpu_id)"]
SGI_CHECK["int_id in SGI_RANGE?"]
SGI_CALC["Calculate register offset"]
NORMAL_PEND["Use ISPENDR/ICPENDR"]
PEND_CHECK["is_pend?"]
SET_SPENDSGIR["Set SPENDSGIR bit"]
CLEAR_CPENDSGIR["Clear CPENDSGIR bits"]
CPU_SPECIFIC["Target specific CPU"]
ALL_CPUS["Clear for all CPUs"]

CLEAR_CPENDSGIR --> ALL_CPUS
PEND_CHECK --> CLEAR_CPENDSGIR
PEND_CHECK --> SET_SPENDSGIR
SET_PEND --> SGI_CHECK
SET_SPENDSGIR --> CPU_SPECIFIC
SGI_CALC --> PEND_CHECK
SGI_CHECK --> NORMAL_PEND
SGI_CHECK --> SGI_CALC

Sources: src/gic_v2.rs(L264 - L283) 

Integration with GIC Components

Interrupt management operations interface with both the distributor and CPU interface components, with different responsibilities for each type.

Distributor vs CPU Interface Responsibilities

flowchart TD
subgraph subGraph2["Interrupt Flow"]
    PERIPHERAL["Peripheral Device"]
    HANDLER["Interrupt Handler"]
end
subgraph subGraph1["GicCpuInterface Responsibilities"]
    CPU_ACK["Acknowledgment• iar() register read• Interrupt priority"]
    CPU_EOI["Completion• eoi() priority drop• dir() deactivation"]
    CPU_MASK["Priority Masking• PMR register• Preemption control"]
end
subgraph subGraph0["GicDistributor Responsibilities"]
    DIST_CONFIG["Configuration• Trigger modes• Priority levels• CPU targeting"]
    DIST_ENABLE["Enable/Disable• Per-interrupt control• Group assignment"]
    DIST_SGI["SGI Generation• Inter-processor signals• Targeting modes"]
    DIST_STATE["State Management• Pending state• Active state"]
end

CPU_ACK --> HANDLER
DIST_CONFIG --> CPU_ACK
HANDLER --> CPU_EOI
PERIPHERAL --> DIST_CONFIG

Sources: src/gic_v2.rs(L92 - L131)  src/gic_v2.rs(L376 - L479) 

The interrupt management system provides a complete abstraction over ARM GICv2 hardware while maintaining type safety and proper encapsulation of interrupt-type-specific behaviors.

Interrupt Classification System

Relevant source files

Purpose and Scope

This document covers the interrupt classification system implemented in the ARM GICv2 crate, which categorizes interrupts into three distinct types based on their ID ranges and intended usage patterns. The classification system provides the foundation for interrupt routing, priority management, and inter-processor communication within the GIC architecture.

For detailed information about how these classified interrupts are processed through the system, see Interrupt Processing Pipeline. For specific implementation details of Software Generated Interrupts, see Software Generated Interrupts.

Interrupt Type Classification

The ARM GICv2 specification defines three fundamental interrupt types, each with specific characteristics and use cases. The crate implements this classification through predefined ranges and an enumeration system.

Interrupt Type Definitions

flowchart TD
subgraph subGraph2["Range Constants"]
    SGI_RANGE_CONST["SGI_RANGE: 0..16"]
    PPI_RANGE_CONST["PPI_RANGE: 16..32"]
    SPI_RANGE_CONST["SPI_RANGE: 32..1020"]
end
subgraph subGraph1["InterruptType Enum"]
    SGI_ENUM["InterruptType::SGI"]
    PPI_ENUM["InterruptType::PPI"]
    SPI_ENUM["InterruptType::SPI"]
end
subgraph subGraph0["Interrupt ID Space (0-1019)"]
    SGI_IDS["SGI Range: 0-15Software Generated"]
    PPI_IDS["PPI Range: 16-31Private Peripheral"]
    SPI_IDS["SPI Range: 32-1019Shared Peripheral"]
    RESERVED["Reserved: 1020-1023"]
end

PPI_ENUM --> PPI_RANGE_CONST
PPI_IDS --> PPI_ENUM
SGI_ENUM --> SGI_RANGE_CONST
SGI_IDS --> SGI_ENUM
SPI_ENUM --> SPI_RANGE_CONST
SPI_IDS --> SPI_ENUM

Sources: src/lib.rs(L14 - L29)  src/lib.rs(L74 - L89) 

Interrupt TypeID RangeCountPrimary Use Case
SGI (Software Generated)0-1516Inter-processor communication
PPI (Private Peripheral)16-3116CPU-specific peripheral interrupts
SPI (Shared Peripheral)32-1019988System-wide peripheral interrupts

Software Generated Interrupts (SGI)

SGIs occupy interrupt IDs 0-15 and are generated through software writes to the GICD_SGIR register. These interrupts enable communication between processors in multi-core systems.

Key Characteristics:

  • Always generated by software, never by hardware peripherals
  • Can target specific CPUs or groups of CPUs
  • Used for synchronization and coordination between cores
  • Highest priority in the interrupt ID space

Private Peripheral Interrupts (PPI)

PPIs occupy interrupt IDs 16-31 and are specific to individual processors. Each CPU core has its own private set of these interrupt sources.

Key Characteristics:

  • Processor-specific interrupt sources
  • Cannot be routed to other CPUs
  • Typically used for CPU timers, PMU events, and other core-local peripherals
  • Each CPU core sees the same PPI ID but from different physical sources

Shared Peripheral Interrupts (SPI)

SPIs occupy interrupt IDs 32-1019 and represent the largest category of interrupts. These can be routed to any available CPU in the system.

Key Characteristics:

  • System-wide interrupt sources
  • Configurable CPU targeting and routing
  • Generated by external peripherals and devices
  • Support for load balancing and affinity settings

Interrupt Translation Mechanism

The crate provides a translation function that converts relative interrupt IDs within each type to absolute GIC interrupt IDs.

flowchart TD
subgraph Output["Output"]
    ABS_ID["Absolute GIC ID(0-1019)"]
    NONE_VAL["None (invalid input)"]
end
subgraph subGraph1["translate_irq Function"]
    VALIDATE["Validate ID againsttype-specific limits"]
    CALCULATE["Calculate absoluteGIC interrupt ID"]
    RETURN["Return Option"]
end
subgraph subGraph0["Input Parameters"]
    REL_ID["Relative ID(within type)"]
    INT_TYPE["InterruptType(SGI/PPI/SPI)"]
end

CALCULATE --> RETURN
INT_TYPE --> VALIDATE
REL_ID --> VALIDATE
RETURN --> ABS_ID
RETURN --> NONE_VAL
VALIDATE --> CALCULATE

Sources: src/lib.rs(L91 - L116) 

The translate_irq function performs the following transformations:

Input TypeCalculationExample
InterruptType::SGIid(direct mapping)translate_irq(5, SGI)→Some(5)
InterruptType::PPIid + PPI_RANGE.starttranslate_irq(3, PPI)→Some(19)
InterruptType::SPIid + SPI_RANGE.starttranslate_irq(10, SPI)→Some(42)

Range Validation Logic

The translation function includes bounds checking to ensure valid interrupt IDs:

  • SGI validation: id < SGI_RANGE.end (must be < 16)
  • PPI validation: id < PPI_RANGE.end - PPI_RANGE.start (must be < 16)
  • SPI validation: id < SPI_RANGE.end - SPI_RANGE.start (must be < 988)

Invalid inputs return None, providing safe interrupt ID translation.

System Integration

The interrupt classification system integrates with the broader GIC architecture through well-defined constants and type-safe interfaces.

flowchart TD
subgraph subGraph2["Usage Examples"]
    ENABLE_INT["Enable interrupt by type"]
    SET_PRIORITY["Set interrupt priority"]
    ROUTE_CPU["Route SPI to CPU"]
    SEND_SGI["Generate SGI"]
end
subgraph subGraph1["Core GIC Components"]
    DISTRIBUTOR["GicDistributor"]
    CPU_INTERFACE["GicCpuInterface"]
end
subgraph subGraph0["Classification Constants"]
    SGI_RANGE["SGI_RANGE: Range"]
    PPI_RANGE["PPI_RANGE: Range"]
    SPI_RANGE["SPI_RANGE: Range"]
    GIC_MAX_IRQ["GIC_MAX_IRQ: usize"]
end

CPU_INTERFACE --> ENABLE_INT
DISTRIBUTOR --> ENABLE_INT
DISTRIBUTOR --> ROUTE_CPU
DISTRIBUTOR --> SEND_SGI
DISTRIBUTOR --> SET_PRIORITY
GIC_MAX_IRQ --> DISTRIBUTOR
PPI_RANGE --> DISTRIBUTOR
SGI_RANGE --> DISTRIBUTOR
SPI_RANGE --> DISTRIBUTOR

Sources: src/lib.rs(L14 - L32)  src/lib.rs(L12) 

Configuration Constants

The system defines several key constants that govern interrupt handling:

  • GIC_MAX_IRQ: Maximum interrupt count (1024)
  • GIC_CONFIG_BITS: Bits per interrupt configuration (2)
  • Range constants: Define valid ID spaces for each interrupt type

These constants ensure consistent behavior across the GIC implementation and provide compile-time guarantees about interrupt ID validity.

Sources: src/lib.rs(L31 - L35) 

Software Generated Interrupts

Relevant source files

This document covers Software Generated Interrupts (SGIs) in the ARM GICv2 implementation, including their generation methods, register interface, and targeting mechanisms. SGIs are a critical component for inter-processor communication in multi-core systems.

For information about the overall interrupt classification system, see Interrupt Classification System. For details about the underlying register implementations, see GICD_SGIR Register Details.

SGI Overview and Characteristics

Software Generated Interrupts occupy interrupt IDs 0-15 and serve as the primary mechanism for inter-processor communication in ARM GICv2 systems. Unlike peripheral interrupts, SGIs are generated entirely through software writes to the GICD_SGIR register.

CharacteristicValue
Interrupt ID Range0-15
Total Count16 SGIs
Primary Use CaseInter-processor communication
Generation MethodSoftware write to GICD_SGIR
TargetingFlexible CPU targeting modes

SGIs are defined by the SGI_RANGE constant and classified through the InterruptType::SGI enum variant. The translate_irq function handles SGI ID validation, ensuring only IDs 0-15 are accepted for SGI operations.

SGI Generation Flow

flowchart TD
A["Software Request"]
B["GicDistributor::send_sgi*"]
C["GICD_SGIR Register Write"]
D["TargetListFilter Mode"]
E["ForwardToCPUTargetList"]
F["ForwardToAllExceptRequester"]
G["ForwardToRequester"]
H["CPUTargetList Field"]
I["All CPUs Except Current"]
J["Current CPU Only"]
K["Target CPU Interfaces"]
L["SGI Delivery"]

A --> B
B --> C
C --> D
D --> E
D --> F
D --> G
E --> H
F --> I
G --> J
H --> K
I --> K
J --> K
K --> L

Sources: src/lib.rs(L14 - L18)  src/lib.rs(L92 - L116)  src/gic_v2.rs(L202 - L223) 

SGI Generation Methods

The GicDistributor struct provides three primary methods for generating SGIs, each corresponding to different targeting strategies. These methods abstract the complexity of GICD_SGIR register programming.

send_sgi Method

The send_sgi method targets a specific CPU interface using the ForwardToCPUTargetList mode:

// Method signature from GicDistributor implementation
pub fn send_sgi(&mut self, dest_cpu_id: usize, sgi_num: usize)

This method constructs a GICD_SGIR register write with:

  • TargetListFilter = ForwardToCPUTargetList (0b00)
  • CPUTargetList = destination CPU ID bitmask
  • SGIINTID = SGI number (0-15)

send_sgi_all_except_self Method

The send_sgi_all_except_self method broadcasts to all CPU interfaces except the requesting processor:

// Method signature from GicDistributor implementation  
pub fn send_sgi_all_except_self(&mut self, sgi_num: usize)

This method uses TargetListFilter = ForwardToAllExceptRequester (0b01), eliminating the need to specify individual CPU targets.

send_sgi_to_self Method

The send_sgi_to_self method targets only the requesting CPU interface:

// Method signature from GicDistributor implementation
pub fn send_sgi_to_self(&mut self, sgi_num: usize)

This method uses TargetListFilter = ForwardToRequester (0b10) for local SGI generation.

SGI Method Mapping

flowchart TD
subgraph subGraph2["Hardware Behavior"]
    G["Target specific CPU interface"]
    H["Broadcast to all except sender"]
    I["Self-targeting only"]
end
subgraph subGraph1["GICD_SGIR TargetListFilter"]
    D["ForwardToCPUTargetList (0b00)"]
    E["ForwardToAllExceptRequester (0b01)"]
    F["ForwardToRequester (0b10)"]
end
subgraph subGraph0["GicDistributor Methods"]
    A["send_sgi(dest_cpu_id, sgi_num)"]
    B["send_sgi_all_except_self(sgi_num)"]
    C["send_sgi_to_self(sgi_num)"]
end

A --> D
B --> E
C --> F
D --> G
E --> H
F --> I

Sources: src/gic_v2.rs(L202 - L208)  src/gic_v2.rs(L211 - L216)  src/gic_v2.rs(L219 - L223) 

GICD_SGIR Register Interface

The GICD_SGIR register controls SGI generation through a structured bit field layout. The register is implemented as a write-only interface using the tock-registers abstraction.

Register Bit Fields

BitsFieldPurpose
[31:26]ReservedMust be zero
[25:24]TargetListFilterDetermines targeting mode
[23:16]CPUTargetListCPU interface bitmask (when TargetListFilter = 0b00)
[15]NSATTSecurity attribute (Security Extensions only)
[14:4]ReservedShould be zero
[3:0]SGIINTIDSGI interrupt ID (0-15)

The TargetListFilter field provides four targeting modes:

ValueModeDescription
0b00ForwardToCPUTargetListUse CPUTargetList bitmask
0b01ForwardToAllExceptRequesterBroadcast except sender
0b10ForwardToRequesterSelf-targeting only
0b11ReservedInvalid mode

GICD_SGIR Register Layout

flowchart TD
subgraph subGraph0["GICD_SGIR Register (32 bits)"]
    A["[31:26]Reserved"]
    B["[25:24]TargetListFilter"]
    C["[23:16]CPUTargetList"]
    D["[15]NSATT"]
    E["[14:4]Reserved"]
    F["[3:0]SGIINTID"]
end
G["Targeting Mode0b00: CPUTargetList0b01: All except self0b10: Self only"]
H["8-bit CPU maskEach bit = CPU interface"]
I["SGI ID (0-15)4-bit value"]

B --> G
C --> H
F --> I

Sources: src/regs/gicd_sgir.rs(L21 - L58)  src/gic_v2.rs(L18) 

SGI State Management

SGIs require special handling in the GIC distributor's state management functions due to their per-CPU nature. The set_pend method implements SGI-specific logic using dedicated pending registers.

SGI Pending State Registers

SGIs use separate pending state registers compared to other interrupt types:

  • SPENDSGIR: Set SGI pending registers (4 registers, 32 bits each)
  • CPENDSGIR: Clear SGI pending registers (4 registers, 32 bits each)

Each register covers 4 SGIs with 8 bits per SGI (one bit per CPU interface).

SGI Pending State Implementation

The set_pend method handles SGIs using a different code path:

// SGI range check and register access pattern
if SGI_RANGE.contains(&int_id) {
    let reg_idx = int_id / 4;        // SGI register index (0-3)
    let offset = (int_id % 4) * 8;   // Bit offset within register
    // Set/clear pending state per CPU
}

SGI Pending State Flow

flowchart TD
A["set_pend(int_id, is_pend, current_cpu_id)"]
B["SGI_RANGE.contains(int_id)?"]
C["SGI Pending Path"]
D["Standard Pending Path"]
E["reg_idx = int_id / 4"]
F["offset = (int_id % 4) * 8"]
G["is_pend?"]
H["SPENDSGIR[reg_idx]Set bit at offset + current_cpu_id"]
I["CPENDSGIR[reg_idx]Clear 8 bits at offset"]
J["ISPENDR/ICPENDR registers"]

A --> B
B --> C
B --> D
C --> E
C --> F
D --> J
E --> G
F --> G
G --> H
G --> I

Sources: src/gic_v2.rs(L264 - L283)  src/gic_v2.rs(L55 - L58)  src/lib.rs(L14 - L18) 

SGI Security and Access Control

When the GIC implements Security Extensions, the NSATT bit in GICD_SGIR controls SGI security attributes. This field determines whether SGIs are forwarded based on their Group 0 or Group 1 configuration.

Security Behavior

NSATT ValueSGI Forwarding Condition
0Forward only if SGI configured as Group 0
1Forward only if SGI configured as Group 1

Non-secure writes to GICD_SGIR can only generate Group 1 SGIs, regardless of the NSATT field value. This provides hardware-enforced security isolation between secure and non-secure SGI generation.

Sources: src/regs/gicd_sgir.rs(L42 - L49) 

Register Interface

Relevant source files

This document describes how the arm_gicv2 crate abstracts ARM GICv2 hardware registers and provides safe, type-checked access to GIC functionality. The register interface serves as the foundation layer that bridges Rust code with memory-mapped hardware registers.

For information about the higher-level GIC components that use these registers, see Core Architecture. For specific register details and bit field definitions, see Register Module Organization and GICD_SGIR Register Details.

Register Abstraction Architecture

The crate implements a three-layer abstraction for hardware register access:

flowchart TD
subgraph subGraph3["Hardware Layer"]
    MMIO["Memory-Mapped I/O"]
    GICD_HW["GIC Distributor Hardware"]
    GICC_HW["CPU Interface Hardware"]
end
subgraph subGraph2["Hardware Abstraction Layer"]
    TOCK_REGS["tock-registers crate"]
    BITFIELDS["register_bitfields! macro"]
    REGISTER_TYPES["ReadOnly, WriteOnly, ReadWrite"]
end
subgraph subGraph1["Register Structure Layer"]
    DIST_REGS["GicDistributorRegs struct"]
    CPU_REGS["GicCpuInterfaceRegs struct"]
    REG_FIELDS["Individual Register FieldsCTLR, SGIR, IAR, EOIR, etc."]
end
subgraph subGraph0["Application Layer"]
    APP["High-Level API Methods"]
    DIST_METHODS["GicDistributor methodssend_sgi(), set_enable(), etc."]
    CPU_METHODS["GicCpuInterface methodsiar(), eoi(), handle_irq(), etc."]
end

APP --> CPU_METHODS
APP --> DIST_METHODS
BITFIELDS --> MMIO
CPU_METHODS --> CPU_REGS
CPU_REGS --> REG_FIELDS
DIST_METHODS --> DIST_REGS
DIST_REGS --> REG_FIELDS
MMIO --> GICC_HW
MMIO --> GICD_HW
REGISTER_TYPES --> MMIO
REG_FIELDS --> BITFIELDS
REG_FIELDS --> REGISTER_TYPES
REG_FIELDS --> TOCK_REGS
TOCK_REGS --> MMIO

Sources: src/gic_v2.rs(L1 - L480)  src/regs/gicd_sgir.rs(L1 - L62) 

Memory-Mapped Register Structures

The crate defines two primary register structures that map directly to hardware memory layouts:

GicDistributorRegs Layout

flowchart TD
subgraph subGraph0["GicDistributorRegs Memory Layout"]
    CTLR["0x0000: CTLRDistributor Control RegisterReadWrite<u32>"]
    TYPER["0x0004: TYPERInterrupt Controller TypeReadOnly<u32>"]
    IIDR["0x0008: IIDRImplementer IdentificationReadOnly<u32>"]
    IGROUPR["0x0080: IGROUPR[0x20]Interrupt Group RegistersReadWrite<u32>"]
    ISENABLER["0x0100: ISENABLER[0x20]Interrupt Set-EnableReadWrite<u32>"]
    ICENABLER["0x0180: ICENABLER[0x20]Interrupt Clear-EnableReadWrite<u32>"]
    IPRIORITYR["0x0400: IPRIORITYR[0x100]Interrupt PriorityReadWrite<u32>"]
    ITARGETSR["0x0800: ITARGETSR[0x100]Interrupt Processor TargetsReadWrite<u32>"]
    ICFGR["0x0c00: ICFGR[0x40]Interrupt ConfigurationReadWrite<u32>"]
    SGIR["0x0f00: SGIRSoftware Generated InterruptGicdSgirReg (WriteOnly)"]
    CPENDSGIR["0x0f10: CPENDSGIR[0x4]SGI Clear-PendingReadWrite<u32>"]
    SPENDSGIR["0x0f20: SPENDSGIR[0x4]SGI Set-PendingReadWrite<u32>"]
end

CPENDSGIR --> SPENDSGIR
CTLR --> TYPER
ICENABLER --> IPRIORITYR
ICFGR --> SGIR
IGROUPR --> ISENABLER
IIDR --> IGROUPR
IPRIORITYR --> ITARGETSR
ISENABLER --> ICENABLER
ITARGETSR --> ICFGR
SGIR --> CPENDSGIR
TYPER --> IIDR

GicCpuInterfaceRegs Layout

flowchart TD
subgraph subGraph0["GicCpuInterfaceRegs Memory Layout"]
    CPU_CTLR["0x0000: CTLRCPU Interface ControlReadWrite<u32>"]
    PMR["0x0004: PMRInterrupt Priority MaskReadWrite<u32>"]
    BPR["0x0008: BPRBinary Point RegisterReadWrite<u32>"]
    IAR["0x000c: IARInterrupt AcknowledgeReadOnly<u32>"]
    EOIR["0x0010: EOIREnd of InterruptWriteOnly<u32>"]
    RPR["0x0014: RPRRunning PriorityReadOnly<u32>"]
    HPPIR["0x0018: HPPIRHighest Priority PendingReadOnly<u32>"]
    CPU_IIDR["0x00fc: IIDRCPU Interface IdentificationReadOnly<u32>"]
    DIR["0x1000: DIRDeactivate InterruptWriteOnly<u32>"]
end

BPR --> IAR
CPU_CTLR --> PMR
CPU_IIDR --> DIR
EOIR --> RPR
HPPIR --> CPU_IIDR
IAR --> EOIR
PMR --> BPR
RPR --> HPPIR

Sources: src/gic_v2.rs(L20 - L90) 

Register Access Patterns

The register interface implements type-safe access patterns through the tock-registers crate:

Register TypeAccess PatternExample Usage
ReadOnly.get()self.regs().IAR.get()
WriteOnly.set(value)self.regs().EOIR.set(iar)
ReadWrite.get(),.set(value)self.regs().CTLR.set(val)
GicdSgirReg.write(fields)self.regs().SGIR.write(fields)

Register Structure Instantiation

flowchart TD
BASE_PTR["Raw Base Pointer*mut u8"]
NONNULL["NonNull<GicDistributorRegs>NonNull<GicCpuInterfaceRegs>"]
STRUCT_FIELD["base field inGicDistributor/GicCpuInterface"]
REGS_METHOD["regs() methodunsafe { self.base.as_ref() }"]
REGISTER_ACCESS["Direct register accessself.regs().CTLR.get()"]

BASE_PTR --> NONNULL
NONNULL --> STRUCT_FIELD
REGS_METHOD --> REGISTER_ACCESS
STRUCT_FIELD --> REGS_METHOD

Sources: src/gic_v2.rs(L140 - L149)  src/gic_v2.rs(L378 - L386) 

Type-Safe Bit Field Access

The GICD_SGIR register demonstrates advanced bit field abstraction:

flowchart TD
subgraph subGraph1["Enum Values for TargetListFilter"]
    TLF_00["ForwardToCPUTargetList = 0b00"]
    TLF_01["ForwardToAllExceptRequester = 0b01"]
    TLF_10["ForwardToRequester = 0b10"]
    TLF_11["Reserved = 0b11"]
end
subgraph subGraph0["GICD_SGIR Register Bit Fields"]
    BITS_31_26["Bits [31:26]Reserved316 bits"]
    BITS_25_24["Bits [25:24]TargetListFilter2 bits"]
    BITS_23_16["Bits [23:16]CPUTargetList8 bits"]
    BITS_15["Bit [15]NSATT1 bit"]
    BITS_14_4["Bits [14:4]Reserved14_411 bits"]
    BITS_3_0["Bits [3:0]SGIINTID4 bits"]
end

Usage Example

The register is accessed through structured field composition:

self.regs().SGIR.write(
    GICD_SGIR::TargetListFilter::ForwardToCPUTargetList
        + GICD_SGIR::CPUTargetList.val(dest_cpu_id as _)
        + GICD_SGIR::SGIINTID.val(sgi_num as _)
);

Sources: src/regs/gicd_sgir.rs(L21 - L58)  src/gic_v2.rs(L203 - L208) 

Register Module Organization

The register definitions are organized in the regs/ module:

ModulePurposeKey Components
regs/mod.rsModule exportsRe-exportsgicd_sgiritems
regs/gicd_sgir.rsSGIR registerGICD_SGIRbitfields,GicdSgirRegtype

The module structure allows for extensible register definitions while maintaining clean separation between different register types.

Sources: src/regs/mod.rs(L1 - L4)  src/regs/gicd_sgir.rs(L1 - L62) 

Register Module Organization

Relevant source files

Purpose and Scope

This document covers the structural organization of the register abstraction layer within the arm_gicv2 crate. The register module provides hardware register definitions and abstractions that enable safe, type-safe access to ARM GICv2 hardware registers. This page focuses on how register definitions are organized, modularized, and exposed to the rest of the codebase.

For detailed information about specific register implementations, see GICD_SGIR Register Details. For broader context on how these registers integrate with the main GIC components, see Core Architecture.

Module Structure Overview

The register module follows a hierarchical organization pattern where individual register definitions are contained in dedicated submodules and selectively re-exported through the main module interface.

Register Module Hierarchy

flowchart TD
A["src/regs/mod.rs"]
B["gicd_sgir submodule"]
C["Public re-exports"]
D["gicd_sgir.rs file"]
E["GICD_SGIR register definition"]
F["pub use gicd_sgir::*"]
G["GicDistributor"]
H["GicCpuInterface"]
I["External consumers"]

A --> B
A --> C
A --> I
B --> D
C --> F
D --> E
G --> A
H --> A

Sources: src/regs/mod.rs(L1 - L4) 

The current module organization demonstrates a focused approach where each hardware register receives its own dedicated submodule. The mod.rs file serves as the central coordination point that aggregates and exposes register definitions.

Module Declaration and Re-export Pattern

The register module employs a straightforward declaration and re-export strategy:

Module ComponentPurposeImplementation
Submodule declarationDeclares register-specific modulesmod gicd_sgir;
Public re-exportExposes register APIs to consumerspub use gicd_sgir::*;

Sources: src/regs/mod.rs(L1 - L3) 

This pattern provides several architectural benefits:

  • Encapsulation: Each register definition is isolated in its own module
  • Selective exposure: Only intended APIs are re-exported through the main module
  • Maintainability: New registers can be added by following the same pattern
  • Namespace management: Wildcard re-exports flatten the API surface for consumers

Register Organization Patterns

Submodule Structure

flowchart TD
A["mod.rs"]
B["gicd_sgir"]
C["Register struct"]
D["Bit field definitions"]
E["Helper methods"]
F["Future register modules"]
G["Additional register types"]

A --> B
B --> C
B --> D
B --> E
F --> A
F --> G

Sources: src/regs/mod.rs(L1) 

The modular approach allows for systematic expansion of register support. Each submodule can contain:

  • Register structure definitions using tock-registers abstractions
  • Bit field layouts and access patterns
  • Register-specific helper functions and constants
  • Documentation specific to that hardware register

API Surface Management

The re-export strategy in the module creates a unified namespace for register access:

flowchart TD
A["src/regs/mod.rs"]
B["pub use gicd_sgir::*"]
C["GICD_SGIR register types"]
D["Related constants"]
E["Helper functions"]
F["GicDistributor"]
G["External crate users"]

A --> B
B --> C
B --> D
B --> E
F --> C
F --> D
F --> E
G --> A

Sources: src/regs/mod.rs(L3) 

This design enables consumers to import register definitions through a single, well-defined module path while maintaining internal organization through submodules.

Integration with Hardware Abstraction

Register Module Role in GIC Architecture

flowchart TD
A["Hardware Layer"]
B["Register Abstractions"]
C["src/regs/mod.rs"]
D["gicd_sgir module"]
E["GicDistributor"]
F["GicCpuInterface"]
G["Memory-mapped hardware"]
H["Type-safe register access"]

A --> B
B --> C
C --> D
E --> C
F --> C
G --> A
H --> E
H --> F

Sources: src/regs/mod.rs(L1 - L4) 

The register module serves as the foundational layer that translates raw hardware register layouts into type-safe Rust abstractions. This enables the higher-level GicDistributor and GicCpuInterface components to interact with hardware through well-defined, compile-time-verified interfaces.

Extensibility and Future Growth

The current organization pattern supports systematic expansion for additional GIC registers:

Register CategoryCurrent SupportExtension Pattern
Software Generated Interruptsgicd_sgirmoduleAdditional SGI-related registers
Distributor ControlNot yet implementedgicd_ctlr,gicd_typermodules
CPU InterfaceNot yet implementedgicc_*register modules
Priority ManagementNot yet implementedPriority and masking register modules

Sources: src/regs/mod.rs(L1 - L4) 

Each new register would follow the established pattern of creating a dedicated submodule and adding appropriate re-exports to maintain the unified API surface while preserving internal modularity.

GICD_SGIR Register Details

Relevant source files

This document provides comprehensive technical documentation for the GICD_SGIR (Software Generated Interrupt Register) implementation in the arm_gicv2 crate. The register controls the generation and routing of Software Generated Interrupts (SGIs) within the ARM GICv2 interrupt controller system.

For broader context on SGI interrupt types and their role in the interrupt system, see Software Generated Interrupts. For general register interface organization, see Register Module Organization.

Register Overview and Purpose

The GICD_SGIR register serves as the primary control interface for generating Software Generated Interrupts in the GIC Distributor. SGIs enable inter-processor communication in multi-core ARM systems by allowing one CPU to trigger interrupts on other CPUs or itself.

GICD_SGIR in GIC Architecture

GICD_SGIR Location in Interrupt Processing

flowchart TD
CPU0["CPU 0"]
DISTRIBUTOR["GicDistributor"]
CPU1["CPU 1"]
CPU2["CPU 2"]
CPU3["CPU 3"]
GICD_SGIR["GICD_SGIR RegisterWrite-Only0x00000F00 offset"]
SGI_LOGIC["SGI Generation Logic"]
TARGET_CPU0["Target CPU 0GicCpuInterface"]
TARGET_CPU1["Target CPU 1GicCpuInterface"]
TARGET_CPU2["Target CPU 2GicCpuInterface"]
TARGET_CPU3["Target CPU 3GicCpuInterface"]
INT0["Interrupt ID 0-15"]
INT1["Interrupt ID 0-15"]
INT2["Interrupt ID 0-15"]
INT3["Interrupt ID 0-15"]

CPU0 --> DISTRIBUTOR
CPU1 --> DISTRIBUTOR
CPU2 --> DISTRIBUTOR
CPU3 --> DISTRIBUTOR
DISTRIBUTOR --> GICD_SGIR
GICD_SGIR --> SGI_LOGIC
SGI_LOGIC --> TARGET_CPU0
SGI_LOGIC --> TARGET_CPU1
SGI_LOGIC --> TARGET_CPU2
SGI_LOGIC --> TARGET_CPU3
TARGET_CPU0 --> INT0
TARGET_CPU1 --> INT1
TARGET_CPU2 --> INT2
TARGET_CPU3 --> INT3

Sources: src/regs/gicd_sgir.rs(L1 - L62) 

Register Bit Field Layout

GICD_SGIR Bit Field Structure

flowchart TD
subgraph subGraph0["32-bit GICD_SGIR Register Layout"]
    BITS31_26["Bits 31:26ReservedOFFSET(26) NUMBITS(6)"]
    BITS25_24["Bits 25:24TargetListFilterOFFSET(24) NUMBITS(2)"]
    BITS23_16["Bits 23:16CPUTargetListOFFSET(16) NUMBITS(8)"]
    BIT15["Bit 15NSATTOFFSET(15) NUMBITS(1)"]
    BITS14_4["Bits 14:4Reserved SBZOFFSET(4) NUMBITS(11)"]
    BITS3_0["Bits 3:0SGIINTIDOFFSET(0) NUMBITS(4)"]
end

BIT15 --> BITS14_4

Sources: src/regs/gicd_sgir.rs(L21 - L58) 

Register Field Definitions

Core Field Components

FieldBitsTypePurpose
TargetListFilter25:24EnumControls SGI routing behavior
CPUTargetList23:16BitmapSpecifies target CPU interfaces
NSATT15BooleanSecurity group selection
SGIINTID3:0IntegerSGI interrupt ID (0-15)

Sources: src/regs/gicd_sgir.rs(L25 - L56) 

TargetListFilter Modes

The TargetListFilter field defines four distinct targeting modes implemented as register bitfield values:

flowchart TD
WRITE["Write to GICD_SGIR"]
FILTER_CHECK["Check TargetListFilter bits 25:24"]
MODE00["0b00: ForwardToCPUTargetListUse CPUTargetList bitmap"]
MODE01["0b01: ForwardToAllExceptRequesterAll CPUs except requesting CPU"]
MODE10["0b10: ForwardToRequesterOnly requesting CPU"]
MODE11["0b11: ReservedUndefined behavior"]
TARGET_BITMAP["Check CPUTargetList[7:0]Each bit = target CPU"]
ALL_EXCEPT["Generate SGI for allactive CPUs except requester"]
SELF_ONLY["Generate SGI only forrequesting CPU"]
SGI_GEN["Generate SGI(s)with SGIINTID value"]

ALL_EXCEPT --> SGI_GEN
FILTER_CHECK --> MODE00
FILTER_CHECK --> MODE01
FILTER_CHECK --> MODE10
FILTER_CHECK --> MODE11
MODE00 --> TARGET_BITMAP
MODE01 --> ALL_EXCEPT
MODE10 --> SELF_ONLY
SELF_ONLY --> SGI_GEN
TARGET_BITMAP --> SGI_GEN
WRITE --> FILTER_CHECK

Sources: src/regs/gicd_sgir.rs(L31 - L35) 

Register Implementation Details

Rust Type Definition

The register is implemented as a write-only register using the tock-registers framework:

pub type GicdSgirReg = WriteOnly<u32, GICD_SGIR::Register>;

Register Bitfield Structure

The implementation uses the register_bitfields! macro to define field layouts and valid values:

ConstantValueDescription
ForwardToCPUTargetList0b00Use explicit CPU target list
ForwardToAllExceptRequester0b01Broadcast except requester
ForwardToRequester0b10Self-targeting only
Reserved0b11Invalid/reserved value

Sources: src/regs/gicd_sgir.rs(L18 - L62) 

CPU Targeting Mechanisms

CPUTargetList Bitmap Interpretation

When TargetListFilter is set to ForwardToCPUTargetList (0b00), the CPUTargetList field operates as an 8-bit bitmap:

flowchart TD
subgraph subGraph0["CPUTargetList Bitmap (bits 23:16)"]
    BIT23["Bit 23CPU 7"]
    BIT22["Bit 22CPU 6"]
    BIT21["Bit 21CPU 5"]
    BIT20["Bit 20CPU 4"]
    BIT19["Bit 19CPU 3"]
    BIT18["Bit 18CPU 2"]
    BIT17["Bit 17CPU 1"]
    BIT16["Bit 16CPU 0"]
end
CPU7["GicCpuInterface 7"]
CPU6["GicCpuInterface 6"]
CPU5["GicCpuInterface 5"]
CPU4["GicCpuInterface 4"]
CPU3["GicCpuInterface 3"]
CPU2["GicCpuInterface 2"]
CPU1["GicCpuInterface 1"]
CPU0["GicCpuInterface 0"]

BIT16 --> CPU0
BIT17 --> CPU1
BIT18 --> CPU2
BIT19 --> CPU3
BIT20 --> CPU4
BIT21 --> CPU5
BIT22 --> CPU6
BIT23 --> CPU7

Sources: src/regs/gicd_sgir.rs(L37 - L41) 

Security Extensions Support

NSATT Field Behavior

The NSATT (Non-Secure Attribute) field controls security group targeting when Security Extensions are implemented:

NSATT ValueGroupAccess Behavior
0Group 0Forward SGI only if configured as Group 0
1Group 1Forward SGI only if configured as Group 1

Security Access Constraints

  • Secure Access: Can write any value to NSATT field
  • Non-Secure Access: Limited to Group 1 SGIs regardless of NSATT value written

Sources: src/regs/gicd_sgir.rs(L42 - L49) 

SGI Interrupt ID Specification

The SGIINTID field specifies which of the 16 available Software Generated Interrupts (0-15) to trigger. This field directly maps to interrupt IDs in the SGI range:

flowchart TD
SGIINTID_FIELD["SGIINTID bits 3:0"]
SGI_RANGE["SGI Interrupt IDs"]
ID0["ID 0: 0b0000"]
ID1["ID 1: 0b0001"]
ID2["ID 2: 0b0010"]
ID3["ID 3: 0b0011"]
DOTS["..."]
ID15["ID 15: 0b1111"]
CPU_INT0["CPU InterfaceSGI 0 pending"]
CPU_INT3["CPU InterfaceSGI 3 pending"]
CPU_INT15["CPU InterfaceSGI 15 pending"]

ID0 --> CPU_INT0
ID15 --> CPU_INT15
ID3 --> CPU_INT3
SGIINTID_FIELD --> SGI_RANGE
SGI_RANGE --> DOTS
SGI_RANGE --> ID0
SGI_RANGE --> ID1
SGI_RANGE --> ID15
SGI_RANGE --> ID2
SGI_RANGE --> ID3

Sources: src/regs/gicd_sgir.rs(L52 - L56) 

Implementation Considerations

Write-Only Register Characteristics

The GICD_SGIR register is implemented as WriteOnly<u32, GICD_SGIR::Register>, meaning:

  • No read operations are supported
  • Each write triggers immediate SGI processing
  • Register state is not persistent
  • Effects are immediate upon write completion

Reserved Field Handling

The implementation includes two reserved field ranges that must be handled appropriately:

  • Bits 31:26: Reserved, implementation behavior undefined
  • Bits 14:4: Reserved, Should Be Zero (SBZ)

Sources: src/regs/gicd_sgir.rs(L24 - L61) 

Development and Integration

Relevant source files

This document provides a comprehensive guide for developers on integrating and working with the arm_gicv2 crate. It covers the development workflow, dependency management, build configuration, and quality assurance processes that support this hardware abstraction layer.

For specific technical details about crate dependencies and feature configuration, see Crate Configuration and Dependencies. For detailed information about the build system and CI/CD pipeline, see Build System and Development Workflow.

Integration Overview

The arm_gicv2 crate is designed as a foundational component for ARM-based systems requiring interrupt controller management. The crate follows embedded Rust best practices with no_std compatibility and minimal dependencies.

Development Ecosystem Architecture

flowchart TD
subgraph subGraph3["Integration Targets"]
    ARCEOS["ArceOS Operating System"]
    EMBEDDED["Embedded Systems"]
    HYPERVISORS["Hypervisors/VMMs"]
    BARE_METAL["Bare Metal Applications"]
end
subgraph subGraph2["Target Platforms"]
    AARCH64_BARE["aarch64-unknown-none-softfloat"]
    X86_TEST["x86_64-unknown-linux-gnu (testing)"]
end
subgraph subGraph1["Core Dependencies"]
    TOCK_REGS["tock-registers 0.8"]
    RUST_NIGHTLY["Rust Nightly Toolchain"]
    NO_STD["no_std Environment"]
end
subgraph subGraph0["Development Infrastructure"]
    GITHUB["GitHub Repository"]
    CI_PIPELINE["CI Workflow (.github/workflows/ci.yml)"]
    DOCS_DEPLOY["GitHub Pages Documentation"]
    CARGO_TOML["Cargo.toml Configuration"]
end

AARCH64_BARE --> ARCEOS
AARCH64_BARE --> BARE_METAL
AARCH64_BARE --> EMBEDDED
AARCH64_BARE --> HYPERVISORS
CARGO_TOML --> CI_PIPELINE
CARGO_TOML --> TOCK_REGS
CI_PIPELINE --> AARCH64_BARE
CI_PIPELINE --> DOCS_DEPLOY
CI_PIPELINE --> X86_TEST
RUST_NIGHTLY --> NO_STD
TOCK_REGS --> RUST_NIGHTLY

Sources: Cargo.toml(L1 - L19)  .github/workflows/ci.yml(L1 - L56) 

Package Configuration and Metadata

The crate is configured for broad compatibility across the embedded systems ecosystem. Key configuration elements include:

ConfigurationValuePurpose
Edition2021Modern Rust language features
LicenseTriple-licensed (GPL-3.0-or-later, Apache-2.0, MulanPSL-2.0)Maximum compatibility
Categoriesembedded,no-std,hardware-support,osClear ecosystem positioning
Keywordsarceos,arm,aarch64,gic,interrupt-controllerDiscoverability

Sources: Cargo.toml(L4 - L12) 

Development Workflow Integration

Dependency Management Strategy

The crate maintains a minimal dependency footprint with a single external dependency:

flowchart TD
ARM_GICV2["arm_gicv2 crate"]
TOCK_REGS["tock-registers 0.8"]
REG_ABSTRACTION["Hardware Register Abstractions"]
SAFE_MMIO["Safe Memory-Mapped I/O"]
EL2_FEATURE["el2 feature flag"]
HYPERVISOR_SUPPORT["Hypervisor Extensions"]

ARM_GICV2 --> EL2_FEATURE
ARM_GICV2 --> TOCK_REGS
EL2_FEATURE --> HYPERVISOR_SUPPORT
REG_ABSTRACTION --> SAFE_MMIO
TOCK_REGS --> REG_ABSTRACTION

The tock-registers dependency provides type-safe hardware register access patterns essential for reliable interrupt controller management. The optional el2 feature enables hypervisor-specific functionality without imposing overhead on standard use cases.

Sources: Cargo.toml(L14 - L18) 

Build Target Configuration

The development process supports multiple build targets with different purposes:

flowchart TD
subgraph subGraph2["Build Process"]
    CARGO_BUILD["cargo build --target TARGET --all-features"]
    CARGO_TEST["cargo test --target x86_64-unknown-linux-gnu"]
    CARGO_CLIPPY["cargo clippy --target TARGET --all-features"]
end
subgraph subGraph1["Development Target"]
    X86_64["x86_64-unknown-linux-gnu"]
    UNIT_TESTING["Unit testing execution"]
end
subgraph subGraph0["Primary Target"]
    AARCH64["aarch64-unknown-none-softfloat"]
    BARE_METAL_USE["Bare metal ARM64 systems"]
end

AARCH64 --> BARE_METAL_USE
AARCH64 --> CARGO_BUILD
AARCH64 --> CARGO_CLIPPY

Sources: .github/workflows/ci.yml(L12 - L30) 

Quality Assurance Process

Continuous Integration Pipeline

The CI system ensures code quality through multiple validation stages:

StageToolCommandPurpose
Format Checkrustfmtcargo fmt --all -- --checkCode style consistency
Lintingclippycargo clippy --target TARGET --all-featuresCode quality analysis
Build Verificationcargocargo build --target TARGET --all-featuresCompilation validation
Unit Testingcargocargo test --target x86_64-unknown-linux-gnuFunctional verification

Sources: .github/workflows/ci.yml(L22 - L30) 

Documentation Generation and Deployment

flowchart TD
subgraph subGraph1["Quality Gates"]
    BROKEN_LINKS["rustdoc::broken_intra_doc_links"]
    MISSING_DOCS["missing-docs warnings"]
end
subgraph subGraph0["Documentation Workflow"]
    DOC_BUILD["cargo doc --no-deps --all-features"]
    DOC_FLAGS["RUSTDOCFLAGS validation"]
    INDEX_GEN["index.html generation"]
    GITHUB_PAGES["GitHub Pages deployment"]
end

DOC_BUILD --> DOC_FLAGS
DOC_BUILD --> INDEX_GEN
DOC_FLAGS --> BROKEN_LINKS
DOC_FLAGS --> MISSING_DOCS
INDEX_GEN --> GITHUB_PAGES

The documentation pipeline enforces strict quality standards, treating broken links and missing documentation as errors to ensure comprehensive API coverage.

Sources: .github/workflows/ci.yml(L32 - L55) 

Platform Compatibility Matrix

The crate supports multiple deployment scenarios with varying feature requirements:

Use CaseTarget PlatformFeatures RequiredIntegration Pattern
Embedded Systemsaarch64-unknown-none-softfloatNoneDirect hardware access
Hypervisorsaarch64-unknown-none-softfloatel2Extended privilege operations
Operating Systemsaarch64-unknown-none-softfloatOptionalel2Kernel-level integration
Development/Testingx86_64-unknown-linux-gnuAllCross-compilation testing

This matrix guides developers in selecting appropriate configuration options for their specific integration requirements.

Sources: Cargo.toml(L17 - L18)  .github/workflows/ci.yml(L12 - L29) 

Crate Configuration and Dependencies

Relevant source files

This document covers the configuration, dependencies, and build settings of the arm_gicv2 crate. It explains how the crate's dependencies enable hardware register abstractions, the purpose of feature flags, and the package metadata that defines its target use cases.

For information about the development workflow and build targets, see Build System and Development Workflow.

Dependencies Overview

The arm_gicv2 crate has a minimal dependency footprint designed for embedded and systems programming environments. The primary dependency provides safe hardware register abstractions while maintaining no_std compatibility.

Dependency Configuration

flowchart TD
subgraph register_usage["Register Implementation Usage"]
    register_trait["register::Register trait"]
    readwrite_trait["register::ReadWrite trait"]
    field_trait["register::Field trait"]
    localregister["register::LocalRegisterCopy"]
end
subgraph external_deps["External Dependencies"]
    tock_registers["tock-registers 0.8"]
end
subgraph arm_gicv2_crate["arm_gicv2 Crate"]
    lib["lib.rs"]
    gic_v2["gic_v2.rs"]
    regs_mod["regs/ module"]
end

field_trait --> regs_mod
lib --> gic_v2
lib --> regs_mod
localregister --> gic_v2
readwrite_trait --> gic_v2
register_trait --> gic_v2
tock_registers --> field_trait
tock_registers --> localregister
tock_registers --> readwrite_trait
tock_registers --> register_trait

Dependencies and their roles in hardware abstraction

Sources: Cargo.toml(L14 - L15) 

The tock-registers dependency provides the foundation for type-safe hardware register access throughout the codebase. This dependency enables the creation of register field definitions and memory-mapped I/O operations without runtime overhead.

DependencyVersionPurposeUsage Location
tock-registers0.8Hardware register abstractions and type-safe MMIOgic_v2.rs,regs/module

Feature Configuration

The crate defines feature flags to enable optional functionality based on the target execution environment and privilege level requirements.

Feature Flag Architecture

flowchart TD
subgraph target_environments["Target Environments"]
    bare_metal["Bare Metal (EL1)"]
    hypervisor["Hypervisor (EL2)"]
    vmm["Virtual Machine Monitor"]
end
subgraph conditional_code["Conditional Code Compilation"]
    hypervisor_support["Hypervisor Support"]
    el2_registers["EL2-specific Registers"]
    dir_register["DIR Register Access"]
end
subgraph feature_flags["Feature Flags"]
    el2_feature["el2 = []"]
end

bare_metal --> el2_feature
el2_feature --> dir_register
el2_feature --> el2_registers
el2_feature --> hypervisor_support
hypervisor_support --> hypervisor
hypervisor_support --> vmm

Feature flag configuration and target environments

Sources: Cargo.toml(L17 - L18) 

EL2 Feature Details

The el2 feature flag is an empty feature that serves as a compilation flag to enable hypervisor-specific functionality:

  • Purpose: Enable Exception Level 2 (hypervisor) specific register access
  • Implementation: Conditional compilation using #[cfg(feature = "el2")]
  • Target Use Cases: Hypervisors, Virtual Machine Monitors, Type-1 virtualization

Package Metadata and Configuration

The crate's package configuration defines its identity, licensing, and target ecosystem integration.

Package Identity Configuration

flowchart TD
subgraph licensing["Licensing Options"]
    apache2["Apache-2.0"]
    subgraph ecosystem_integration["Ecosystem Integration"]
        homepage["homepage = arceos-org/arceos"]
        repository["repository = arceos-org/arm_gicv2"]
        documentation["documentation = docs.rs/arm_gicv2"]
        gpl3["GPL-3.0-or-later"]
        mulan["MulanPSL-2.0"]
    end
end
subgraph categorization["Crate Categorization"]
    keywords["keywords = [arceos, arm, aarch64, gic, interrupt-controller]"]
    categories["categories = [embedded, no-std, hardware-support, os]"]
end
subgraph package_metadata["Package Metadata"]
    name["name = arm_gicv2"]
    version["version = 0.1.0"]
    edition["edition = 2021"]
    authors["authors = Yuekai Jia"]
end

Package configuration and ecosystem integration

Sources: Cargo.toml(L1 - L12) 

Metadata Configuration Table

ConfigurationValuePurpose
Namearm_gicv2Crate identifier for Cargo registry
Version0.1.0Semantic version following initial release
Edition2021Rust language edition for modern features
DescriptionARM GICv2 register definitions and operationsConcise functionality summary
LicenseTriple-licensed (GPL-3.0/Apache-2.0/MulanPSL-2.0)Flexible licensing for diverse use cases

Target Environment Compatibility

The crate configuration explicitly targets embedded and systems programming environments through its category assignments and dependency choices.

Environment Compatibility Matrix

flowchart TD
subgraph runtime_requirements["Runtime Requirements"]
    no_heap["No Heap Allocation"]
    no_std_lib["No Standard Library"]
    mmio_access["Memory-Mapped I/O"]
    privileged_mode["Privileged Execution"]
end
subgraph compatibility_targets["Compatibility Targets"]
    no_std_env["no_std Environment"]
    embedded_sys["Embedded Systems"]
    bare_metal["Bare Metal Applications"]
    hypervisors["Hypervisors/VMMs"]
end
subgraph architecture_support["Architecture Support"]
    aarch64["aarch64 (ARM64)"]
    cortex_a["Cortex-A Series"]
    armv7["ARMv7 (32-bit)"]
end

aarch64 --> cortex_a
armv7 --> cortex_a
no_std_env --> bare_metal
no_std_env --> embedded_sys
no_std_env --> hypervisors

Target environment compatibility and requirements

Sources: Cargo.toml(L12)  Cargo.toml(L6) 

Category Alignment

The crate's categories directly reflect its intended use cases:

  • embedded: Targeting resource-constrained embedded systems
  • no-std: Compatible with no_std environments without standard library
  • hardware-support: Provides low-level hardware abstraction
  • os: Suitable for operating system and kernel development

Build Configuration Implications

The minimal dependency configuration and feature design have specific implications for build environments and integration scenarios.

Integration Requirements

RequirementConfiguration SourceImplication
Memory-mapped I/O accesstock-registersdependencyRequires privileged execution mode
ARM architecturePackage keywords and categoriesLimited to ARM-based systems
no_stdcompatibilityCategories and dependency choiceSuitable for kernel/bootloader integration
Optional hypervisor supportel2feature flagConfigurable privilege level support

Sources: Cargo.toml(L11 - L12)  Cargo.toml(L14 - L15)  Cargo.toml(L17 - L18) 

Build System and Development Workflow

Relevant source files

This document covers the automated build system, continuous integration pipeline, code quality assurance processes, and development workflow for the arm_gicv2 crate. It details the GitHub Actions configuration, build targets, testing strategies, and documentation generation processes that ensure code quality and maintainability.

For information about crate dependencies and feature configuration, see Crate Configuration and Dependencies.

CI/CD Pipeline Overview

The project uses GitHub Actions for continuous integration and deployment, with a comprehensive pipeline that ensures code quality, compatibility, and automated documentation deployment.

CI/CD Pipeline Architecture

flowchart TD
subgraph DOC_JOB_STEPS["Documentation Job Steps"]
    DOC_CHECKOUT["actions/checkout@v4"]
    DOC_TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    DOC_BUILD["cargo doc --no-deps --all-features"]
    GH_PAGES["JamesIves/github-pages-deploy-action@v4"]
end
subgraph CI_JOB_STEPS["CI Job Steps"]
    CHECKOUT["actions/checkout@v4"]
    TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    VERSION_CHECK["Check rust version"]
    FORMAT["cargo fmt --all -- --check"]
    CLIPPY["cargo clippy --target aarch64-unknown-none-softfloat"]
    BUILD["cargo build --target aarch64-unknown-none-softfloat"]
    TEST["cargo test --target x86_64-unknown-linux-gnu"]
end
PR["Pull Request"]
GHA["GitHub Actions Trigger"]
PUSH["Push to Main"]
CI_JOB["ci job"]
DOC_JOB["doc job"]

BUILD --> TEST
CHECKOUT --> TOOLCHAIN
CI_JOB --> CHECKOUT
CLIPPY --> BUILD
DOC_BUILD --> GH_PAGES
DOC_CHECKOUT --> DOC_TOOLCHAIN
DOC_JOB --> DOC_CHECKOUT
DOC_TOOLCHAIN --> DOC_BUILD
FORMAT --> CLIPPY
GHA --> CI_JOB
GHA --> DOC_JOB
PR --> GHA
PUSH --> GHA
TOOLCHAIN --> VERSION_CHECK
VERSION_CHECK --> FORMAT

Sources: .github/workflows/ci.yml(L1 - L56) 

Job Configuration Matrix

The CI pipeline uses a strategy matrix to test against multiple configurations:

ParameterValues
rust-toolchainnightly
targetsaarch64-unknown-none-softfloat

The pipeline is configured for fail-fast: false, allowing all matrix combinations to complete even if one fails.

Sources: .github/workflows/ci.yml(L7 - L12) 

Build Configuration

Target Platforms

The build system supports multiple target platforms for different use cases:

flowchart TD
subgraph TOOLCHAIN_COMPONENTS["Toolchain Components"]
    RUST_SRC["rust-src"]
    CLIPPY_COMP["clippy"]
    RUSTFMT["rustfmt"]
end
subgraph USE_CASES["Use Cases"]
    EMBEDDED["Embedded Systems"]
    BARE_METAL["Bare Metal Applications"]
    TESTING["Unit Testing"]
end
subgraph BUILD_TARGETS["Build Targets"]
    AARCH64["aarch64-unknown-none-softfloat"]
    X86_TEST["x86_64-unknown-linux-gnu"]
end

AARCH64 --> BARE_METAL
AARCH64 --> EMBEDDED
CLIPPY_COMP --> AARCH64
RUSTFMT --> AARCH64
RUST_SRC --> AARCH64
X86_TEST --> TESTING

Sources: .github/workflows/ci.yml(L12 - L19) 

Primary Build Target

  • aarch64-unknown-none-softfloat: The primary target for ARM64 bare-metal applications
  • Used for clippy linting: cargo clippy --target aarch64-unknown-none-softfloat
  • Used for building: cargo build --target aarch64-unknown-none-softfloat
  • Supports all features: --all-features flag

Testing Target

  • x86_64-unknown-linux-gnu: Used exclusively for unit testing
  • Conditional execution: Only runs when matrix.targets == 'x86_64-unknown-linux-gnu'
  • Command: cargo test --target x86_64-unknown-linux-gnu -- --nocapture

Sources: .github/workflows/ci.yml(L25 - L30) 

Code Quality Assurance

The project implements multiple layers of code quality checks to maintain consistency and catch issues early.

Code Quality Pipeline

sequenceDiagram
    participant Developer as "Developer"
    participant GitRepository as "Git Repository"
    participant CIPipeline as "CI Pipeline"
    participant cargofmt as "cargo fmt"
    participant cargoclippy as "cargo clippy"
    participant cargobuild as "cargo build"

    Developer ->> GitRepository: Push/PR
    GitRepository ->> CIPipeline: Trigger workflow
    CIPipeline ->> cargofmt: Check code format
    cargofmt -->> CIPipeline: Pass/Fail
    CIPipeline ->> cargoclippy: Run linter
    cargoclippy -->> CIPipeline: Pass/Fail (with exceptions)
    CIPipeline ->> cargobuild: Build target
    cargobuild -->> CIPipeline: Pass/Fail

Sources: .github/workflows/ci.yml(L22 - L27) 

Formatting Standards

  • Tool: cargo fmt with --all flag
  • Enforcement: --check flag ensures code is properly formatted
  • Command: cargo fmt --all -- --check
  • Failure Behavior: Pipeline fails if formatting is inconsistent

Linting Configuration

  • Tool: cargo clippy with comprehensive checks
  • Target-specific: Runs against aarch64-unknown-none-softfloat
  • Features: Uses --all-features to lint all code paths
  • Exceptions: Allows clippy::new_without_default lint
  • Command: cargo clippy --target aarch64-unknown-none-softfloat --all-features -- -A clippy::new_without_default

Sources: .github/workflows/ci.yml(L23 - L25) 

Testing Strategy

Test Execution Model

flowchart TD
subgraph REASONS["Why x86_64 for Testing"]
    FAST_EXECUTION["Faster execution on CI runners"]
    STANDARD_LIB["Standard library availability"]
    DEBUG_OUTPUT["Better debugging capabilities"]
end
subgraph TEST_CONDITIONS["Test Execution Conditions"]
    TARGET_CHECK["matrix.targets == 'x86_64-unknown-linux-gnu'"]
    UNIT_TESTS["cargo test"]
    NOCAPTURE["--nocapture flag"]
end

DEBUG_OUTPUT --> NOCAPTURE
FAST_EXECUTION --> TARGET_CHECK
STANDARD_LIB --> TARGET_CHECK
TARGET_CHECK --> UNIT_TESTS
UNIT_TESTS --> NOCAPTURE

Sources: .github/workflows/ci.yml(L28 - L30) 

Test Configuration

  • Conditional Execution: Tests only run for x86_64 target
  • Output Visibility: --nocapture flag ensures test output is visible
  • Target Specification: Explicit target to avoid cross-compilation issues during testing

Documentation System

Documentation Generation Pipeline

The documentation system automatically builds and deploys API documentation to GitHub Pages.

flowchart TD
subgraph DEPLOYMENT["Deployment"]
    GH_PAGES_ACTION["JamesIves/github-pages-deploy-action@v4"]
    SINGLE_COMMIT["single-commit: true"]
    TARGET_FOLDER["folder: target/doc"]
end
subgraph DOC_BUILD["Documentation Build"]
    CARGO_DOC["cargo doc --no-deps --all-features"]
    INDEX_GEN["Generate index.html redirect"]
    RUSTDOC_FLAGS["RUSTDOCFLAGS validation"]
end
subgraph DOC_TRIGGERS["Documentation Triggers"]
    DEFAULT_BRANCH["Push to default branch"]
    PR_EVENT["Pull Request"]
end

CARGO_DOC --> INDEX_GEN
DEFAULT_BRANCH --> CARGO_DOC
GH_PAGES_ACTION --> SINGLE_COMMIT
INDEX_GEN --> RUSTDOC_FLAGS
PR_EVENT --> CARGO_DOC
RUSTDOC_FLAGS --> GH_PAGES_ACTION
SINGLE_COMMIT --> TARGET_FOLDER

Sources: .github/workflows/ci.yml(L32 - L55) 

Documentation Configuration

Build Settings

  • Command: cargo doc --no-deps --all-features
  • Scope: Excludes dependencies, includes all features
  • Output: Generated to target/doc directory

Quality Enforcement

  • Environment Variable: RUSTDOCFLAGS="-D rustdoc::broken_intra_doc_links -D missing-docs"
  • Broken Links: Treats broken documentation links as errors
  • Missing Documentation: Enforces documentation completeness

Index Generation

printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html

Sources: .github/workflows/ci.yml(L40 - L48) 

Deployment Strategy

  • Trigger: Only deploys from the default branch (typically main)
  • Method: GitHub Pages deployment action
  • Configuration:
  • single-commit: true - Keeps deployment history clean
  • branch: gh-pages - Deploys to dedicated documentation branch
  • folder: target/doc - Source folder for deployment

Sources: .github/workflows/ci.yml(L49 - L55) 

Development Environment Setup

Required Toolchain Components

ComponentPurpose
rust-srcSource code for cross-compilation
clippyLinting and code analysis
rustfmtCode formatting

Toolchain Installation

The project uses the dtolnay/rust-toolchain action for consistent toolchain management:

# Equivalent local setup
rustup toolchain install nightly
rustup component add rust-src clippy rustfmt
rustup target add aarch64-unknown-none-softfloat

Sources: .github/workflows/ci.yml(L15 - L19) 

Version Verification

The CI pipeline includes a version check step to ensure toolchain consistency:

  • Command: rustc --version --verbose
  • Purpose: Provides detailed version information for debugging and reproducibility

Sources: .github/workflows/ci.yml(L20 - L21) 

Local Development Workflow

Developers should run these commands locally before pushing:

# Format check
cargo fmt --all -- --check

# Linting
cargo clippy --target aarch64-unknown-none-softfloat --all-features -- -A clippy::new_without_default

# Build verification
cargo build --target aarch64-unknown-none-softfloat --all-features

# Documentation build (optional)
cargo doc --no-deps --all-features

Git Configuration

The project uses standard Git ignore patterns for Rust projects:

PatternPurpose
/targetBuild artifacts
/.vscodeEditor configuration
.DS_StoremacOS system files
Cargo.lockDependency lock file (library crate)

Sources: .gitignore(L1 - L4)