diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8247110..5d66dd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - rust-toolchain: [nightly-2024-12-25, nightly] + rust-toolchain: [nightly-2025-05-20, nightly] targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] steps: - uses: actions/checkout@v4 @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly with: - toolchain: nightly-2024-12-25 + toolchain: nightly-2025-05-20 - name: Build docs continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} run: | diff --git a/Cargo.toml b/Cargo.toml index 2d86aed..9758b63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,16 @@ name = "axvcpu" authors = ["aarkegz "] version = "0.1.0" edition = "2024" +categories = ["Virtualization", "no-std"] +description = "Virtual CPU abstraction for ArceOS hypervisor" +repository = "https://github.com/arceos-hypervisor/axvcpu" +keywords = ["vcpu", "hypervisor", "arceos"] +license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0" # MulanPubL2 is not included in SPDX [dependencies] axerrno = "0.1.0" memory_addr = "0.4" percpu = "0.2.0" -axaddrspace = { git = "https://github.com/arceos-hypervisor/axaddrspace.git" } -axvisor_api = { git = "https://github.com/arceos-hypervisor/axvisor_api.git" } +axaddrspace = "=0.1.0" +axvisor_api = "=0.1.0" diff --git a/README.md b/README.md index 1d3b6b3..408173e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,167 @@ # AxVCpu -Virtual CPU interface and wrapper for [`ArceOS`](https://github.com/arceos-org/arceos). \ No newline at end of file +[![CI](https://github.com/arceos-hypervisor/x86_vcpu/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/x86_vcpu/actions/workflows/ci.yml) + +AxVCpu is a virtual CPU abstraction library for ArceOS hypervisors, providing a unified, architecture-independent interface for managing virtual CPUs in hypervisor environments. + +## Features + +- **Architecture Agnostic**: Unified interface supporting multiple architectures (x86_64, ARM64, RISC-V) +- **State Management**: Robust VCpu lifecycle management with clear state transitions +- **Per-CPU Virtualization**: Efficient per-CPU state management and resource isolation +- **Hardware Abstraction**: Clean separation between architecture-specific and common operations +- **CPU Affinity**: Support for CPU binding and affinity management +- **Exit Handling**: Comprehensive VM exit reason handling and processing + +## Architecture + +AxVCpu follows a layered architecture design: + +``` +┌─────────────────────────────────────────┐ +│ Application Layer │ ← Hypervisor/VMM +├─────────────────────────────────────────┤ +│ AxVCpu Core Interface │ ← Main API +├─────────────────────────────────────────┤ +│ Architecture Abstraction │ ← AxArchVCpu trait +├─────────────────────────────────────────┤ +│ Hardware Abstraction Layer │ ← AxVCpuHal trait +├─────────────────────────────────────────┤ +│ Architecture-Specific Backends │ ← x86_64, ARM64, etc. +└─────────────────────────────────────────┘ +``` + +## Core Components + +### VCpu State Machine + +``` +Created → Free → Ready → Running → Blocked + ↓ ↓ ↓ ↓ ↓ + └───────┴──────┴────────┴────────┘ + Invalid +``` + +- **Created**: Initial state after VCpu creation +- **Free**: Initialized and ready to be bound to a physical CPU +- **Ready**: Bound to a physical CPU and ready for execution +- **Running**: Currently executing on a physical CPU +- **Blocked**: Execution blocked (waiting for I/O, etc.) +- **Invalid**: Error state when transitions fail + +### Key Traits + +- `AxArchVCpu`: Architecture-specific VCpu implementation interface +- `AxVCpuHal`: Hardware abstraction layer for hypervisor operations +- `AxVCpuExitReason`: VM exit reason enumeration and handling + +## Quick Start + +Add AxVCpu to your `Cargo.toml`: + +```toml +[dependencies] +axvcpu = "0.1.0" +``` + +### Basic Usage + +```rust +use axvcpu::{AxVCpu, VCpuState}; + +// Mock implementation for example +struct MyArchVCpu; +impl AxArchVCpu for MyArchVCpu { + // Implement required methods... +} + +// Create a new virtual CPU +let vcpu = AxVCpu::::new( + vm_id, // VM identifier + vcpu_id, // VCpu identifier + favor_cpu, // Preferred physical CPU + cpu_set, // CPU affinity mask + config // Architecture-specific config +)?; + +// Check VCpu state +assert_eq!(vcpu.state(), VCpuState::Created); + +// Setup the VCpu +vcpu.setup(entry_addr, ept_root, setup_config)?; + +// Bind to current physical CPU and run +vcpu.bind()?; +let exit_reason = vcpu.run()?; + +// Handle VM exit +match exit_reason { + AxVCpuExitReason::Halt => { + println!("Guest halted"); + }, + AxVCpuExitReason::Io { port, is_write, .. } => { + println!("I/O access on port {}", port); + }, + // ... handle other exit reasons +} +``` + +## Architecture Implementation + +To implement AxVCpu for a new architecture: + +```rust +use axvcpu::AxArchVCpu; + +struct MyArchVCpu { + // Architecture-specific fields +} + +impl AxArchVCpu for MyArchVCpu { + type CreateConfig = MyCreateConfig; + type SetupConfig = MySetupConfig; + + fn new(vm_id: VMId, vcpu_id: VCpuId, config: Self::CreateConfig) -> AxResult { + // Initialize architecture-specific VCpu + Ok(Self { /* ... */ }) + } + + fn set_entry(&mut self, entry: GuestPhysAddr) -> AxResult { + // Set guest entry point + Ok(()) + } + + fn set_ept_root(&mut self, ept_root: HostPhysAddr) -> AxResult { + // Configure memory virtualization + Ok(()) + } + + fn setup(&mut self, config: Self::SetupConfig) -> AxResult { + // Complete VCpu initialization + Ok(()) + } + + fn run(&mut self) -> AxResult { + // Execute guest code until VM exit + Ok(AxVCpuExitReason::Halt) + } + + // Implement other required methods... +} +``` + +## Related Projects + +- [ArceOS](https://github.com/arceos-org/arceos) - A component-based OS kernel +- [AxVisor](https://github.com/arceos-hypervisor/axvisor) - A hypervisor implemented based on the ArceOS unikernel framework. + +## License + +This project is licensed under multiple licenses. You may choose to use this project under any of the following licenses: + +- **[GPL-3.0-or-later](LICENSE.GPLv3)** - GNU General Public License v3.0 or later +- **[Apache-2.0](LICENSE.Apache2)** - Apache License 2.0 +- **[MulanPubL-2.0](LICENSE.MulanPubL2)** - Mulan Public License 2.0 +- **[MulanPSL2](LICENSE.MulanPSL2)** - Mulan Permissive Software License v2 + +You may use this software under the terms of any of these licenses at your option. diff --git a/src/arch_vcpu.rs b/src/arch_vcpu.rs index 28347ff..c5bff4d 100644 --- a/src/arch_vcpu.rs +++ b/src/arch_vcpu.rs @@ -4,55 +4,76 @@ use axvisor_api::vmm::{VCpuId, VMId}; use crate::exit::AxVCpuExitReason; -/// A trait for architecture-specific vcpu. +/// Architecture-specific virtual CPU trait definition. /// -/// This trait is an abstraction for virtual CPUs of different architectures. +/// This trait provides an abstraction layer for implementing virtual CPUs across +/// different hardware architectures (x86_64, ARM64, RISC-V, etc.). Each architecture +/// must implement this trait to provide the necessary low-level virtualization primitives. pub trait AxArchVCpu: Sized { - /// The configuration for creating a new [`AxArchVCpu`]. Used by [`AxArchVCpu::new`]. + /// Architecture-specific configuration for VCpu creation. + /// + /// This associated type allows each architecture to define its own + /// configuration parameters needed during VCpu initialization. type CreateConfig; - /// The configuration for setting up a created [`AxArchVCpu`]. Used by [`AxArchVCpu::setup`]. + + /// Architecture-specific configuration for VCpu setup. + /// + /// This associated type allows each architecture to specify additional + /// configuration parameters needed after basic VCpu creation but before execution. type SetupConfig; - /// Create a new `AxArchVCpu`. + /// Creates a new architecture-specific VCpu instance. fn new(vm_id: VMId, vcpu_id: VCpuId, config: Self::CreateConfig) -> AxResult; - /// Set the entry point of the vcpu. + /// Sets the guest entry point where VCpu execution will begin. /// /// It's guaranteed that this function is called only once, before [`AxArchVCpu::setup`] being called. fn set_entry(&mut self, entry: GuestPhysAddr) -> AxResult; - /// Set the EPT root of the vcpu. + /// Sets the Extended Page Table (EPT) root for memory translation. /// - /// It's guaranteed that this function is called only once, before [`AxArchVCpu::setup`] being called. + /// The EPT root defines the top-level page table used for guest-to-host + /// physical address translation in hardware virtualization. fn set_ept_root(&mut self, ept_root: HostPhysAddr) -> AxResult; - /// Setup the vcpu. + /// Completes VCpu initialization and prepares it for execution. /// - /// It's guaranteed that this function is called only once, after [`AxArchVCpu::set_entry`] and [`AxArchVCpu::set_ept_root`] being called. + /// This method performs any final architecture-specific setup needed + /// before the VCpu can be bound and executed. fn setup(&mut self, config: Self::SetupConfig) -> AxResult; - /// Run the vcpu until a vm-exit occurs. + /// Executes the VCpu until a VM exit occurs. + /// + /// This is the core execution method that transfers control to the guest VCpu + /// and runs until the guest triggers a VM exit condition that requires + /// hypervisor intervention. fn run(&mut self) -> AxResult; - /// Bind the vcpu to the current physical CPU. + /// Binds the VCpu to the current physical CPU for execution. + /// + /// This method performs any necessary architecture-specific initialization + /// to prepare the VCpu for execution on the current physical CPU. fn bind(&mut self) -> AxResult; - /// Unbind the vcpu from the current physical CPU. + /// Unbinds the VCpu from the current physical CPU. + /// + /// This method performs cleanup and state preservation when moving + /// the VCpu away from the current physical CPU. fn unbind(&mut self) -> AxResult; - /// Set the value of a general-purpose register according to the given index. + /// Sets the value of a general-purpose register. fn set_gpr(&mut self, reg: usize, val: usize); - /// Inject an interrupt to the vcpu. + /// Inject an interrupt to the VCpu. /// /// It's guaranteed (for implementors, and required for callers) that this function is called - /// on the physical CPU where the vcpu is running or queueing. + /// on the physical CPU where the VCpu is running or queueing. /// - /// It's not guaranteed that the vcpu is running or bound to the current physical CPU when this + /// It's not guaranteed that the VCpu is running or bound to the current physical CPU when this /// function is called. It means sometimes an irq queue is necessary to buffer the interrupts - /// until the vcpu is running. + /// until the VCpu is running. fn inject_interrupt(&mut self, vector: usize) -> AxResult; - /// Set return value of the vcpu. + /// Sets the return value that will be delivered to the guest. fn set_return_value(&mut self, val: usize); } diff --git a/src/exit.rs b/src/exit.rs index 72fde58..701b494 100644 --- a/src/exit.rs +++ b/src/exit.rs @@ -6,171 +6,254 @@ use axaddrspace::{ #[allow(unused_imports)] // used in doc use super::AxArchVCpu; -/// The result of [`AxArchVCpu::run`]. -/// Can we reference or directly reuse content from [kvm-ioctls](https://github.com/rust-vmm/kvm-ioctls/blob/main/src/ioctls/vcpu.rs) ? +/// Reasons for VM-Exits returned by [AxArchVCpu::run]. +/// +/// When a guest virtual CPU executes, various conditions can cause control to be +/// transferred back to the hypervisor. This enum represents all possible exit reasons +/// that can occur during VCpu execution. +/// +/// # VM Exit Categories +/// +/// - **I/O Operations**: MMIO reads/writes, port I/O, system register access +/// - **System Events**: Hypercalls, interrupts, nested page faults +/// - **Power Management**: CPU power state changes, system shutdown +/// - **Multiprocessing**: IPI sending, secondary CPU bring-up +/// - **Error Conditions**: Entry failures, invalid states +/// +/// # Compatibility Note +/// +/// This enum draws inspiration from [kvm-ioctls](https://github.com/rust-vmm/kvm-ioctls/blob/main/src/ioctls/vcpu.rs) +/// for consistency with existing virtualization frameworks. #[non_exhaustive] #[derive(Debug)] pub enum AxVCpuExitReason { - /// The instruction executed by the vcpu performs a hypercall. + /// A guest instruction triggered a hypercall to the hypervisor. + /// + /// Hypercalls are a mechanism for the guest OS to request services from + /// the hypervisor, similar to system calls in a traditional OS. Hypercall { - /// The hypercall number. + /// The hypercall number identifying the requested service nr: u64, - /// The arguments for the hypercall. + /// Arguments passed to the hypercall (up to 6 parameters) args: [u64; 6], }, - /// The instruction executed by the vcpu performs a MMIO read operation. + + /// The guest performed a Memory-Mapped I/O (MMIO) read operation. + /// + /// MMIO reads occur when the guest accesses device registers or other + /// hardware-mapped memory regions that require hypervisor emulation. MmioRead { - /// The physical address of the MMIO read. + /// Guest physical address being read from addr: GuestPhysAddr, - /// The width of the MMIO read. + /// Width/size of the memory access (8, 16, 32, or 64 bits) width: AccessWidth, - /// The index of reg to be read + /// Index of the guest register that will receive the read value reg: usize, - /// The width of the reg to be read + /// Width of the destination register reg_width: AccessWidth, - /// Sign-extend the read value if true. + /// Whether to sign-extend the read value to fill the register signed_ext: bool, }, - /// The instruction executed by the vcpu performs a MMIO write operation. + + /// The guest performed a Memory-Mapped I/O (MMIO) write operation. + /// + /// MMIO writes occur when the guest writes to device registers or other + /// hardware-mapped memory regions that require hypervisor emulation. MmioWrite { - /// The physical address of the MMIO write. + /// Guest physical address being written to addr: GuestPhysAddr, - /// The width of the MMIO write. + /// Width/size of the memory access (8, 16, 32, or 64 bits) width: AccessWidth, - /// The data to be written. + /// Data being written to the memory location data: u64, }, - /// The instruction executed by the vcpu performs a system register read operation. + + /// The guest performed a system register read operation. /// - /// System register here refers `MSR`s in x86, `CSR`s in RISC-V, and `System registers` in Aarch64. + /// System registers are architecture-specific control and status registers: + /// - **x86_64**: Model-Specific Registers (MSRs) + /// - **RISC-V**: Control and Status Registers (CSRs) + /// - **AArch64**: System registers accessible via MRS instruction SysRegRead { - /// The address of the system register to be read. - /// - /// Under x86_64 and RISC-V, this field is the address. + /// Address/identifier of the system register being read /// - /// Under Aarch64, this field follows the ESR_EL2.ISS format: `000000`, - /// which is consistent with the numbering scheme in the `aarch64_sysreg` crate. + /// - **x86_64/RISC-V**: Direct register address + /// - **AArch64**: ESR_EL2.ISS format (`000000`) + /// compatible with the `aarch64_sysreg` crate numbering scheme addr: SysRegAddr, - /// The index of the GPR (general purpose register) where the value should be stored. + /// Index of the guest register that will receive the read value /// - /// Note that in x86_64, the destination register is always `[edx:eax]`, so this field is unused. + /// **Note**: Unused on x86_64 where the result is always stored in `[edx:eax]` reg: usize, }, - /// The instruction executed by the vcpu performs a system register write operation. + + /// The guest performed a system register write operation. /// - /// System register here refers `MSR`s in x86, `CSR`s in RISC-V, and `System registers` in Aarch64. + /// System registers are architecture-specific control and status registers: + /// - **x86_64**: Model-Specific Registers (MSRs) + /// - **RISC-V**: Control and Status Registers (CSRs) + /// - **AArch64**: System registers accessible via MSR instruction SysRegWrite { - /// The address of the system register to be written. - /// - /// Under x86_64 and RISC-V, this field is the address. + /// Address/identifier of the system register being written /// - /// Under Aarch64, this field follows the ESR_EL2.ISS format: `000000`, - /// which is consistent with the numbering scheme in the `aarch64_sysreg` crate. + /// - **x86_64/RISC-V**: Direct register address + /// - **AArch64**: ESR_EL2.ISS format (`000000`) + /// compatible with the `aarch64_sysreg` crate numbering scheme addr: SysRegAddr, - /// Data to be written. + /// Data being written to the system register value: u64, }, - /// The instruction executed by the vcpu performs a I/O read operation. + + /// The guest performed a port-based I/O read operation. + /// + /// **Architecture**: x86-specific (other architectures don't have port I/O) /// - /// It's unnecessary to specify the destination register because it's always `al`, `ax`, or `eax` (as port-I/O exists only in x86). + /// The destination register is implicitly `al`/`ax`/`eax` based on the access width. IoRead { - /// The port number of the I/O read. + /// I/O port number being read from port: Port, - /// The width of the I/O read. + /// Width of the I/O access (8, 16, or 32 bits) width: AccessWidth, }, - /// The instruction executed by the vcpu performs a I/O write operation. + + /// The guest performed a port-based I/O write operation. /// - /// It's unnecessary to specify the source register because it's always `al`, `ax`, or `eax` (as port-I/O exists only in x86). + /// **Architecture**: x86-specific (other architectures don't have port I/O) + /// + /// The source register is implicitly `al`/`ax`/`eax` based on the access width. IoWrite { - /// The port number of the I/O write. + /// I/O port number being written to port: Port, - /// The width of the I/O write. + /// Width of the I/O access (8, 16, or 32 bits) width: AccessWidth, - /// The data to be written. + /// Data being written to the I/O port data: u64, }, - /// An external interrupt happened. + + /// An external interrupt was delivered to the VCpu. /// - /// Note that fields may be added in the future, use `..` to handle them. + /// This represents hardware interrupts from external devices that need + /// to be processed by the guest or hypervisor. + /// + /// **Note**: This enum may be extended with additional fields in the future. + /// Use `..` in pattern matching to ensure forward compatibility. ExternalInterrupt { - /// The interrupt vector. + /// Hardware interrupt vector number vector: u64, }, - /// A nested page fault happened. (EPT violation in x86) + + /// A nested page fault occurred during guest memory access. /// - /// Note that fields may be added in the future, use `..` to handle them. + /// Also known as EPT violations on x86. These faults occur when: + /// - Guest accesses unmapped memory regions + /// - Access permissions are violated (e.g., writing to read-only pages) + /// - Page table entries need to be populated or updated + /// + /// **Note**: This enum may be extended with additional fields in the future. + /// Use `..` in pattern matching to ensure forward compatibility. NestedPageFault { - /// The guest physical address of the fault. + /// Guest physical address that caused the fault addr: GuestPhysAddr, - /// The access flags of the fault. + /// Type of access that was attempted (read/write/execute) access_flags: MappingFlags, }, - /// The vcpu is halted. + + /// The guest VCpu has executed a halt instruction and is now idle. + /// + /// This typically occurs when the guest OS has no work to do and is + /// waiting for interrupts or other events to wake it up. Halt, - /// Try to bring up a secondary CPU. - /// - /// This is used to notify the hypervisor that the target vcpu - /// is powered on and ready to run, generally used in the boot process - /// of a multi-core VM. - /// This VM exit reason is architecture-specific, may be triggered by - /// * a PSCI call in ARM - /// * a SIPI in x86 - /// * a sbi call in RISC-V + + /// Request to bring up a secondary CPU core. + /// + /// This exit reason is used during the multi-core VM boot process when + /// the primary CPU requests that a secondary CPU be started. The specific + /// mechanism varies by architecture: + /// + /// - **ARM**: PSCI (Power State Coordination Interface) calls + /// - **x86**: SIPI (Startup Inter-Processor Interrupt) + /// - **RISC-V**: SBI (Supervisor Binary Interface) calls CpuUp { - /// The target vcpu id that is to be started. - /// * for aarch64, it contains the affinity fields of the MPIDR register, - /// * for x86_64, it contains the APIC ID of the secondary CPU, - /// * for RISC-V, it contains the hartid of the secondary CPU. + /// Target CPU identifier to be started + /// + /// Format varies by architecture: + /// - **AArch64**: MPIDR register affinity fields + /// - **x86_64**: APIC ID of the target CPU + /// - **RISC-V**: Hart ID of the target CPU target_cpu: u64, - /// Runtime-specified physical address of the secondary CPU's entry point, where the vcpu can start executing. + /// Guest physical address where the secondary CPU should begin execution entry_point: GuestPhysAddr, - /// This argument passed as the first argument to the secondary CPU's. - /// * for aarch64, it is the `arg` value that will be set in the `x0` register when the vcpu starts executing at `entry_point`. - /// * for RISC-V, it will be set in the `a1` register when the hart starts executing at `entry_point`, and the `a0` register will be set to the hartid. - /// * for x86_64, it is currently unused. + /// Argument to pass to the secondary CPU + /// + /// - **AArch64**: Value to set in `x0` register at startup + /// - **RISC-V**: Value to set in `a1` register (`a0` gets the hartid) + /// - **x86_64**: Currently unused arg: u64, }, - /// The vcpu is powered off. + + /// The guest VCpu has been powered down. /// - /// This vcpu may be resumed later. + /// This indicates the VCpu has executed a power-down instruction or + /// hypercall and should be suspended. The VCpu may be resumed later. CpuDown { - /// Currently unused. - /// Maybe used for `PSCI_POWER_STATE` in the future. + /// Power state information (currently unused) + /// + /// Reserved for future use with PSCI_POWER_STATE or similar mechanisms _state: u64, }, - /// The system should be powered off. + + /// The guest has requested system-wide shutdown. /// - /// This is used to notify the hypervisor that the whole system should be powered off. + /// This indicates the entire virtual machine should be powered off, + /// not just the current VCpu. SystemDown, - /// Nothing special happened, the vcpu has handled the exit itself. + + /// No special handling required - the VCpu handled the exit internally. + /// + /// This provides an opportunity for the hypervisor to: + /// - Check virtual device states + /// - Process pending interrupts + /// - Handle background tasks + /// - Perform scheduling decisions /// - /// This exists to allow the caller to have a chance to check virtual devices/physical devices/virtual interrupts. + /// The VCpu can typically be resumed immediately after these checks. Nothing, - /// Something bad happened during VM entry, the vcpu could not be run due to unknown reasons. - /// Further architecture-specific information is available in hardware_entry_failure_reason. - /// Corresponds to `KVM_EXIT_FAIL_ENTRY`. + + /// VM entry failed due to invalid VCpu state or configuration. + /// + /// This corresponds to `KVM_EXIT_FAIL_ENTRY` in KVM and indicates that + /// the hardware virtualization layer could not successfully enter the guest. + /// + /// The failure reason contains architecture-specific diagnostic information. FailEntry { - /// Architecture related VM entry failure reasons. + /// Hardware-specific failure reason code + /// + /// Interpretation depends on the underlying virtualization technology + /// and CPU architecture. Consult architecture documentation for details. hardware_entry_failure_reason: u64, }, - /// The vcpu is trying to send an Inter-Processor Interrupt (IPI) to another CPU. + + /// The guest is attempting to send an Inter-Processor Interrupt (IPI). /// - /// This does not include SIPI, which is handled by [`AxVCpuExitReason::CpuUp`]. + /// IPIs are used for inter-CPU communication in multi-core systems. + /// This does **not** include Startup IPIs (SIPI), which are handled + /// by the [`AxVCpuExitReason::CpuUp`] variant. SendIPI { - /// The target CPU to send the IPI to. Invalid if any of `send_to_all` or `send_to_self` is - /// true. + /// Target CPU identifier to receive the IPI + /// + /// This field is invalid if `send_to_all` or `send_to_self` is true. target_cpu: u64, - /// The auxiliary field for the target CPU list. Used currently only in aarch64. + /// Auxiliary field for complex target CPU specifications /// - /// In aarch64, `target_cpu` contains Aff3.Aff2.Aff1.0, while this field contains a bitmask - /// specifying the Aff0 values of the target CPUs. + /// Currently used only on AArch64 where: + /// - `target_cpu` contains `Aff3.Aff2.Aff1.0` + /// - `target_cpu_aux` contains a bitmask for `Aff0` values target_cpu_aux: u64, - /// Specifies whether the IPI should be sent to all CPUs except the current one. + /// Whether to broadcast the IPI to all CPUs except the sender send_to_all: bool, - /// Specifies whether the IPI should be sent to the current CPU. + /// Whether to send the IPI to the current CPU (self-IPI) send_to_self: bool, - /// The IPI vector to be sent. + /// IPI vector/interrupt number to deliver vector: u64, }, } diff --git a/src/hal.rs b/src/hal.rs index 41c8200..ccd7502 100644 --- a/src/hal.rs +++ b/src/hal.rs @@ -1,18 +1,31 @@ -/// The interfaces which the underlying software (kernel or hypervisor) must implement. +/// Hardware abstraction layer interfaces for VCpu operations. +/// +/// This trait defines the interfaces that the underlying software (kernel or hypervisor) +/// must implement to support VCpu operations such as interrupt handling and memory management. pub trait AxVCpuHal { - /// Memory management interfaces. + /// Memory management interfaces required by the VCpu subsystem. + /// Must implement the AxMmHal trait from the axaddrspace crate. type MmHal: axaddrspace::AxMmHal; - /// Fetches current interrupt (IRQ) number. - /// - /// # Returns - /// - /// * `usize` - The current IRQ number. + /// Fetches the current interrupt (IRQ) number from the hardware. fn irq_fetch() -> usize { 0 } - /// Dispatch an interrupt request (IRQ) to the underlying host OS. + /// Dispatches an interrupt request (IRQ) to the underlying host OS. + /// + /// This function should handle the actual interrupt processing and delegation + /// to the appropriate interrupt handler in the host system. + /// + /// # Implementation Required + /// + /// The default implementation panics as this function **must** be implemented + /// by the underlying hypervisor or kernel to provide proper interrupt handling. + /// + /// # Safety + /// + /// Implementations should ensure proper interrupt handling and avoid + /// reentrancy issues during interrupt processing. fn irq_hanlder() { unimplemented!("irq_handler is not implemented"); } diff --git a/src/lib.rs b/src/lib.rs index a5aaeea..d723e06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,34 @@ -//! This crate provides a simple virtual CPU abstraction for hypervisors. +//! AxVCpu - Virtual CPU abstraction for ArceOS hypervisors. //! +//! This crate provides a unified, architecture-independent interface for managing virtual CPUs +//! in hypervisor environments. It delegates architecture-specific operations to implementations +//! of the `AxArchVCpu` trait while providing common functionality like state management, +//! CPU binding, and execution control. +//! +//! # Features +//! +//! - Architecture-agnostic virtual CPU management +//! - State machine for VCpu lifecycle (Created → Free → Ready → Running) +//! - Per-CPU virtualization state management +//! - Hardware abstraction layer for hypervisor operations +//! - Support for interrupt injection and register manipulation #![no_std] #[macro_use] extern crate alloc; -mod arch_vcpu; -mod exit; -mod hal; -mod percpu; -mod vcpu; - -pub use arch_vcpu::AxArchVCpu; -pub use hal::AxVCpuHal; -pub use percpu::*; -pub use vcpu::*; +// Core modules +mod arch_vcpu; // Architecture-specific VCpu trait definition +mod exit; // VM exit reason enumeration and handling +mod hal; // Hardware abstraction layer interfaces +mod percpu; // Per-CPU virtualization state management +mod test; // Unit tests for VCpu functionality +mod vcpu; // Main VCpu implementation and state management +// Public API exports +pub use arch_vcpu::AxArchVCpu; // Architecture-specific VCpu trait pub use exit::AxVCpuExitReason; +pub use hal::AxVCpuHal; // Hardware abstraction layer trait +pub use percpu::*; // Per-CPU state management types +pub use vcpu::*; // Main VCpu types and functions // VM exit reasons diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..e897a1b --- /dev/null +++ b/src/test.rs @@ -0,0 +1,454 @@ +#![cfg_attr(not(test), no_std)] + +extern crate alloc; + +#[cfg(test)] +mod tests { + use crate::{AxArchVCpu, AxVCpu, VCpuState, exit::AxVCpuExitReason}; + use alloc::{ + rc::Rc, + string::{String, ToString}, + vec::Vec, + }; + use axaddrspace::{GuestPhysAddr, HostPhysAddr}; + use axerrno::{AxError, AxResult}; + use axvisor_api::vmm::{VCpuId, VMId}; + use core::cell::RefCell; + + // Mock architecture implementation for testing + #[derive(Debug)] + struct MockArchVCpu { + vm_id: VMId, + vcpu_id: VCpuId, + entry: Option, + ept_root: Option, + is_setup: bool, + is_bound: bool, + registers: [usize; 16], + pending_interrupts: Vec, + return_value: usize, + // Track method calls for testing + call_log: Rc>>, + } + + #[derive(Debug, Clone)] + struct MockCreateConfig { + call_log: Rc>>, + } + + #[derive(Debug)] + struct MockSetupConfig; + + impl AxArchVCpu for MockArchVCpu { + type CreateConfig = MockCreateConfig; + type SetupConfig = MockSetupConfig; + + fn new(vm_id: VMId, vcpu_id: VCpuId, config: Self::CreateConfig) -> AxResult { + config.call_log.borrow_mut().push("new".to_string()); + Ok(Self { + vm_id, + vcpu_id, + entry: None, + ept_root: None, + is_setup: false, + is_bound: false, + registers: [0; 16], + pending_interrupts: Vec::new(), + return_value: 0, + call_log: config.call_log, + }) + } + + fn set_entry(&mut self, entry: GuestPhysAddr) -> AxResult { + self.call_log.borrow_mut().push("set_entry".to_string()); + self.entry = Some(entry); + Ok(()) + } + + fn set_ept_root(&mut self, ept_root: HostPhysAddr) -> AxResult { + self.call_log.borrow_mut().push("set_ept_root".to_string()); + self.ept_root = Some(ept_root); + Ok(()) + } + + fn setup(&mut self, _config: Self::SetupConfig) -> AxResult { + self.call_log.borrow_mut().push("setup".to_string()); + if self.entry.is_none() || self.ept_root.is_none() { + return Err(AxError::InvalidInput); + } + self.is_setup = true; + Ok(()) + } + + fn run(&mut self) -> AxResult { + self.call_log.borrow_mut().push("run".to_string()); + if !self.is_bound { + return Err(AxError::BadState); + } + // Simulate a simple halt exit + Ok(AxVCpuExitReason::Halt) + } + + fn bind(&mut self) -> AxResult { + self.call_log.borrow_mut().push("bind".to_string()); + if !self.is_setup { + return Err(AxError::BadState); + } + self.is_bound = true; + Ok(()) + } + + fn unbind(&mut self) -> AxResult { + self.call_log.borrow_mut().push("unbind".to_string()); + self.is_bound = false; + Ok(()) + } + + fn set_gpr(&mut self, reg: usize, val: usize) { + self.call_log + .borrow_mut() + .push(format!("set_gpr({}, {})", reg, val)); + if reg < self.registers.len() { + self.registers[reg] = val; + } + } + + fn inject_interrupt(&mut self, vector: usize) -> AxResult { + self.call_log + .borrow_mut() + .push(format!("inject_interrupt({})", vector)); + self.pending_interrupts.push(vector); + Ok(()) + } + + fn set_return_value(&mut self, val: usize) { + self.call_log + .borrow_mut() + .push(format!("set_return_value({})", val)); + self.return_value = val; + } + } + + fn create_mock_vcpu() -> (AxVCpu, Rc>>) { + let call_log = Rc::new(RefCell::new(Vec::new())); + let config = MockCreateConfig { + call_log: call_log.clone(), + }; + let vcpu = AxVCpu::new(1, 0, 0, None, config).unwrap(); + (vcpu, call_log) + } + + #[test] + fn test_vcpu_creation() { + let (vcpu, call_log) = create_mock_vcpu(); + + assert_eq!(vcpu.id(), 0); + assert_eq!(vcpu.favor_phys_cpu(), 0); + assert_eq!(vcpu.phys_cpu_set(), None); + assert_eq!(vcpu.state(), VCpuState::Created); + assert!(vcpu.is_bsp()); + + assert_eq!(call_log.borrow().len(), 1); + assert_eq!(call_log.borrow()[0], "new"); + } + + #[test] + fn test_vcpu_setup_lifecycle() { + let (vcpu, call_log) = create_mock_vcpu(); + + // Test individual setup methods instead of the high-level setup() + // This avoids the percpu-related code in manipulate_arch_vcpu + + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + // Test direct arch_vcpu access + let arch_vcpu = vcpu.get_arch_vcpu(); + + // Test set_entry + let result = arch_vcpu.set_entry(entry); + assert!(result.is_ok()); + + // Test set_ept_root + let result = arch_vcpu.set_ept_root(ept_root); + assert!(result.is_ok()); + + // Test setup + let result = arch_vcpu.setup(MockSetupConfig); + assert!(result.is_ok()); + + // Check call order + let calls = call_log.borrow(); + assert_eq!(calls.len(), 4); + assert_eq!(calls[0], "new"); + assert_eq!(calls[1], "set_entry"); + assert_eq!(calls[2], "set_ept_root"); + assert_eq!(calls[3], "setup"); + + // Note: State transitions are not tested here due to percpu limitations + } + + #[test] + fn test_vcpu_state_transitions() { + let (vcpu, _) = create_mock_vcpu(); + + // Created -> Free + assert_eq!(vcpu.state(), VCpuState::Created); + let result = vcpu.transition_state(VCpuState::Created, VCpuState::Free); + assert!(result.is_ok()); + assert_eq!(vcpu.state(), VCpuState::Free); + + // Free -> Ready + let result = vcpu.transition_state(VCpuState::Free, VCpuState::Ready); + assert!(result.is_ok()); + assert_eq!(vcpu.state(), VCpuState::Ready); + + // Invalid transition should fail + let result = vcpu.transition_state(VCpuState::Running, VCpuState::Free); + assert!(result.is_err()); + assert_eq!(vcpu.state(), VCpuState::Invalid); + } + + #[test] + fn test_vcpu_bind_unbind() { + let (vcpu, call_log) = create_mock_vcpu(); + + // Test direct arch_vcpu operations instead of high-level methods + // to avoid percpu-related code + + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + // Setup arch_vcpu directly + let arch_vcpu = vcpu.get_arch_vcpu(); + arch_vcpu.set_entry(entry).unwrap(); + arch_vcpu.set_ept_root(ept_root).unwrap(); + arch_vcpu.setup(MockSetupConfig).unwrap(); + + // Test bind/unbind + let result = arch_vcpu.bind(); + assert!(result.is_ok()); + + let result = arch_vcpu.unbind(); + assert!(result.is_ok()); + + let calls = call_log.borrow(); + assert!(calls.contains(&"bind".to_string())); + assert!(calls.contains(&"unbind".to_string())); + } + + #[test] + fn test_vcpu_run() { + let (vcpu, call_log) = create_mock_vcpu(); + + // Setup arch_vcpu directly to avoid percpu code + let arch_vcpu = vcpu.get_arch_vcpu(); + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + arch_vcpu.set_entry(entry).unwrap(); + arch_vcpu.set_ept_root(ept_root).unwrap(); + arch_vcpu.setup(MockSetupConfig).unwrap(); + arch_vcpu.bind().unwrap(); + + // Test run + let result = arch_vcpu.run(); + assert!(result.is_ok()); + if let Ok(AxVCpuExitReason::Halt) = result { + // Expected + } else { + panic!("Expected Halt exit reason"); + } + + assert!(call_log.borrow().contains(&"run".to_string())); + } + + #[test] + fn test_vcpu_register_operations() { + let (vcpu, call_log) = create_mock_vcpu(); + + vcpu.set_gpr(5, 0xdeadbeef); + vcpu.set_return_value(42); + + // Check that values were actually set in the mock struct + let arch_vcpu = vcpu.get_arch_vcpu(); + assert_eq!(arch_vcpu.registers[5], 0xdeadbeef); + assert_eq!(arch_vcpu.return_value, 42); + + let calls = call_log.borrow(); + assert!(calls.contains(&"set_gpr(5, 3735928559)".to_string())); + assert!(calls.contains(&"set_return_value(42)".to_string())); + } + + #[test] + fn test_vcpu_interrupt_injection() { + let (vcpu, call_log) = create_mock_vcpu(); + + let result = vcpu.inject_interrupt(32); + assert!(result.is_ok()); + + assert!( + call_log + .borrow() + .contains(&"inject_interrupt(32)".to_string()) + ); + } + + #[test] + fn test_exit_reason_debug_format() { + let exit_mmio = AxVCpuExitReason::MmioRead { + addr: GuestPhysAddr::from(0x1000), + width: axaddrspace::device::AccessWidth::Dword, + reg: 5, + reg_width: axaddrspace::device::AccessWidth::Qword, + signed_ext: false, + }; + + let debug_str = format!("{:?}", exit_mmio); + assert!(debug_str.contains("MmioRead")); + assert!(debug_str.contains("0x1000")); + } + + #[test] + fn test_exit_reason_hypercall() { + let exit_hypercall = AxVCpuExitReason::Hypercall { + nr: 42, + args: [1, 2, 3, 4, 5, 6], + }; + + if let AxVCpuExitReason::Hypercall { nr, args } = exit_hypercall { + assert_eq!(nr, 42); + assert_eq!(args[0], 1); + assert_eq!(args[5], 6); + } else { + panic!("Expected Hypercall variant"); + } + } + + #[test] + fn test_vcpu_state_display() { + assert_eq!(VCpuState::Created as u8, 1); + assert_eq!(VCpuState::Free as u8, 2); + assert_eq!(VCpuState::Ready as u8, 3); + assert_eq!(VCpuState::Running as u8, 4); + assert_eq!(VCpuState::Blocked as u8, 5); + assert_eq!(VCpuState::Invalid as u8, 0); + } + + #[test] + fn test_vcpu_setup_wrong_state() { + let (vcpu, _) = create_mock_vcpu(); + + // Test state transition without using high-level setup method + assert_eq!(vcpu.state(), VCpuState::Created); + + // Transition to wrong state first + vcpu.transition_state(VCpuState::Created, VCpuState::Ready) + .unwrap(); + assert_eq!(vcpu.state(), VCpuState::Ready); + + // Test that arch_vcpu setup works regardless of vCPU state + // (since we're bypassing the state machine) + let arch_vcpu = vcpu.get_arch_vcpu(); + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + // These should work at arch level even if vCPU state is wrong + assert!(arch_vcpu.set_entry(entry).is_ok()); + assert!(arch_vcpu.set_ept_root(ept_root).is_ok()); + assert!(arch_vcpu.setup(MockSetupConfig).is_ok()); + } + + #[test] + fn test_vcpu_run_without_bind() { + let (vcpu, _) = create_mock_vcpu(); + + // Setup arch_vcpu but don't bind + let arch_vcpu = vcpu.get_arch_vcpu(); + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + arch_vcpu.set_entry(entry).unwrap(); + arch_vcpu.set_ept_root(ept_root).unwrap(); + arch_vcpu.setup(MockSetupConfig).unwrap(); + + // Run should fail without binding (according to mock implementation) + let result = arch_vcpu.run(); + assert!(result.is_err()); + } + + #[test] + fn test_vcpu_bsp_identification() { + let call_log = Rc::new(RefCell::new(Vec::new())); + let config = MockCreateConfig { + call_log: call_log.clone(), + }; + + let vcpu0 = AxVCpu::::new(1, 0, 0, None, config.clone()).unwrap(); + let vcpu1 = AxVCpu::::new(1, 1, 0, None, config).unwrap(); + + assert!(vcpu0.is_bsp()); + assert!(!vcpu1.is_bsp()); + } + + #[test] + fn test_vcpu_cpu_affinity() { + let call_log = Rc::new(RefCell::new(Vec::new())); + let config = MockCreateConfig { call_log }; + + let vcpu = AxVCpu::::new(1, 0, 2, Some(0b1010), config).unwrap(); + + assert_eq!(vcpu.favor_phys_cpu(), 2); + assert_eq!(vcpu.phys_cpu_set(), Some(0b1010)); + } + + #[test] + fn test_vcpu_creation_failure() { + // Test creation with invalid config + let call_log = Rc::new(RefCell::new(Vec::new())); + let config = MockCreateConfig { call_log }; + + // This should succeed with our mock implementation + let result = AxVCpu::::new(1, 0, 0, None, config); + assert!(result.is_ok()); + } + + // Integration test - simplified to avoid percpu issues + #[test] + fn test_arch_vcpu_lifecycle() { + let (vcpu, call_log) = create_mock_vcpu(); + + // Test the arch_vcpu lifecycle directly + let arch_vcpu = vcpu.get_arch_vcpu(); + + // Setup phase + let entry = GuestPhysAddr::from(0x1000); + let ept_root = HostPhysAddr::from(0x2000); + + assert!(arch_vcpu.set_entry(entry).is_ok()); + assert!(arch_vcpu.set_ept_root(ept_root).is_ok()); + assert!(arch_vcpu.setup(MockSetupConfig).is_ok()); + + // Bind and run + assert!(arch_vcpu.bind().is_ok()); + + let exit_reason = arch_vcpu.run().unwrap(); + assert!(matches!(exit_reason, AxVCpuExitReason::Halt)); + + // Unbind + assert!(arch_vcpu.unbind().is_ok()); + + // Verify all methods were called + let calls = call_log.borrow(); + assert!(calls.contains(&"set_entry".to_string())); + assert!(calls.contains(&"set_ept_root".to_string())); + assert!(calls.contains(&"setup".to_string())); + assert!(calls.contains(&"bind".to_string())); + assert!(calls.contains(&"run".to_string())); + assert!(calls.contains(&"unbind".to_string())); + } + + // Note: Per-CPU tests are omitted due to percpu crate linking conflicts in test environment. + // The percpu crate requires kernel-space linking which is incompatible with cargo test. + // In a real hypervisor environment, AxPerCpu would be tested differently. +} diff --git a/src/vcpu.rs b/src/vcpu.rs index bfbea24..d4600ca 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -6,67 +6,93 @@ use axvisor_api::vmm::{VCpuId, VMId}; use super::{AxArchVCpu, AxVCpuExitReason}; -/// The constant part of `AxVCpu`. +/// Immutable configuration data for a virtual CPU. +/// +/// This structure contains the constant properties of a VCpu that don't change +/// after creation, such as CPU affinity settings and identifiers. struct AxVCpuInnerConst { - /// The id of the VM this vcpu belongs to. + /// Unique identifier of the VM this VCpu belongs to vm_id: VMId, - /// The id of the vcpu. + /// Unique identifier of this VCpu within its VM vcpu_id: VCpuId, - /// The id of the physical CPU who has the priority to run this vcpu. + /// Physical CPU ID that has priority to run this VCpu + /// Used for CPU affinity optimization favor_phys_cpu: usize, - /// The set of physical CPUs who can run this vcpu. - /// If `None`, the vcpu can run on any physical CPU. - /// Refer to [CPU_SET](https://man7.org/linux/man-pages/man3/CPU_SET.3.html) in Linux. + /// Bitmask of physical CPUs that can run this VCpu + /// If `None`, the VCpu can run on any available physical CPU + /// Similar to Linux CPU_SET functionality phys_cpu_set: Option, } -/// The state of a virtual CPU. +/// Represents the current execution state of a virtual CPU. +/// +/// The VCpu follows a strict state machine: +/// Created → Free → Ready → Running +/// +/// Invalid state is used when errors occur during state transitions. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum VCpuState { - /// An invalid state. + /// Invalid state - indicates an error occurred during state transition Invalid = 0, - /// The vcpu is created but not initialized yet. + /// Initial state after VCpu creation, not yet initialized Created = 1, - /// The vcpu is already initialized and can be bound to a physical CPU. + /// VCpu is initialized and ready to be bound to a physical CPU Free = 2, - /// The vcpu is bound to a physical CPU and ready to run. + /// VCpu is bound to a physical CPU and ready for execution Ready = 3, - /// The vcpu is bound to a physical CPU and running. + /// VCpu is currently executing on a physical CPU Running = 4, - /// The vcpu is blocked. + /// VCpu execution is blocked (waiting for I/O, etc.) Blocked = 5, } -/// The mutable part of [`AxVCpu`]. +/// Mutable runtime state of a virtual CPU. +/// +/// This structure contains data that changes during VCpu execution, +/// protected by RefCell for interior mutability. pub struct AxVCpuInnerMut { - /// The state of the vcpu. + /// Current execution state of the VCpu state: VCpuState, } -/// A virtual CPU with architecture-independent interface. +/// Architecture-independent virtual CPU implementation. /// -/// By delegating the architecture-specific operations to a struct implementing [`AxArchVCpu`], this struct provides -/// a unified interface and state management model for virtual CPUs of different architectures. -/// -/// The architecture-specific operations are delegated to a struct implementing [`AxArchVCpu`]. +/// This is the main VCpu abstraction that provides a unified interface for +/// managing virtual CPUs across different architectures. It delegates +/// architecture-specific operations to implementations of the `AxArchVCpu` trait. /// /// Note that: /// - This struct handles internal mutability itself, almost all the methods are `&self`. /// - This struct is not thread-safe. It's caller's responsibility to ensure the safety. pub struct AxVCpu { - /// The constant part of the vcpu. + /// Immutable VCpu configuration (VM ID, CPU affinity, etc.) inner_const: AxVCpuInnerConst, - /// The mutable part of the vcpu. + /// Mutable VCpu state protected by RefCell for safe interior mutability inner_mut: RefCell, - /// The architecture-specific state of the vcpu. + /// Architecture-specific VCpu implementation /// - /// `UnsafeCell` is used to allow interior mutability. Note that `RefCell` or `Mutex` is not suitable here - /// because it's not possible to drop the guard when launching a vcpu. + /// Uses UnsafeCell instead of RefCell because RefCell guards cannot be + /// dropped during VCpu execution (when control is transferred to guest) arch_vcpu: UnsafeCell, } impl AxVCpu { - /// Create a new [`AxVCpu`]. + /// Creates a new virtual CPU instance. + /// + /// Initializes a VCpu with the given configuration and creates the underlying + /// architecture-specific implementation. The VCpu starts in the `Created` state. + /// + /// # Arguments + /// + /// * `vm_id` - Unique identifier of the VM this VCpu belongs to + /// * `vcpu_id` - Unique identifier for this VCpu within the VM + /// * `favor_phys_cpu` - Physical CPU ID that should preferentially run this VCpu + /// * `phys_cpu_set` - Optional bitmask of allowed physical CPUs (None = no restriction) + /// * `arch_config` - Architecture-specific configuration for VCpu creation + /// + /// # Returns + /// + /// Returns `Ok(AxVCpu)` on success, or an error if architecture-specific creation fails. pub fn new( vm_id: VMId, vcpu_id: VCpuId, @@ -88,7 +114,10 @@ impl AxVCpu { }) } - /// Setup the vcpu. + /// Sets up the VCpu for execution. + /// + /// Configures the VCpu's entry point, memory management (EPT root), and any + /// architecture-specific setup. Transitions the VCpu from `Created` to `Free` state. pub fn setup( &self, entry: GuestPhysAddr, @@ -103,34 +132,43 @@ impl AxVCpu { }) } - /// Get the id of the vcpu. + /// Returns the unique identifier of this VCpu. pub const fn id(&self) -> VCpuId { self.inner_const.vcpu_id } - /// Get the id of the physical CPU who has the priority to run this vcpu. - /// Currently unused. + /// Returns the preferred physical CPU for this VCpu. + /// + /// This is used for CPU affinity optimization - the scheduler should + /// preferentially run this VCpu on the returned physical CPU ID. + /// + /// # Note + /// + /// Currently unused in the implementation but reserved for future + /// scheduler optimizations. pub const fn favor_phys_cpu(&self) -> usize { self.inner_const.favor_phys_cpu } - /// Get the set of physical CPUs who can run this vcpu. - /// If `None`, this vcpu has no limitation and can be scheduled on any physical CPU. + /// Returns the set of physical CPUs that can run this VCpu. pub const fn phys_cpu_set(&self) -> Option { self.inner_const.phys_cpu_set } - /// Get whether the vcpu is the BSP. We always assume the first vcpu (vcpu with id #0) is the BSP. + /// Checks if this VCpu is the Bootstrap Processor (BSP). + /// + /// By convention, the VCpu with ID 0 is always considered the BSP, + /// which is responsible for system initialization in multi-core VMs. pub const fn is_bsp(&self) -> bool { self.inner_const.vcpu_id == 0 } - /// Get the state of the vcpu. + /// Gets the current execution state of the VCpu. pub fn state(&self) -> VCpuState { self.inner_mut.borrow().state } - /// Set the state of the vcpu. + /// Set the state of the VCpu. /// # Safety /// This method is unsafe because it may break the state transition model. /// Use it with caution. @@ -138,7 +176,7 @@ impl AxVCpu { self.inner_mut.borrow_mut().state = state; } - /// Execute a block with the state of the vcpu transitioned from `from` to `to`. If the current state is not `from`, return an error. + /// Execute a block with the state of the VCpu transitioned from `from` to `to`. If the current state is not `from`, return an error. /// /// The state will be set to [`VCpuState::Invalid`] if an error occurs (including the case that the current state is not `from`). /// @@ -165,7 +203,7 @@ impl AxVCpu { } } - /// Execute a block with the current vcpu set to `&self`. + /// Execute a block with the current VCpu set to `&self`. pub fn with_current_cpu_set(&self, f: F) -> T where F: FnOnce() -> T, @@ -184,7 +222,7 @@ impl AxVCpu { } } - /// Execute an operation on the architecture-specific vcpu, with the state transitioned from `from` to `to` and the current vcpu set to `&self`. + /// Execute an operation on the architecture-specific VCpu, with the state transitioned from `from` to `to` and the current VCpu set to `&self`. /// /// This method is a combination of [`AxVCpu::with_state_transition`] and [`AxVCpu::with_current_cpu_set`]. pub fn manipulate_arch_vcpu(&self, from: VCpuState, to: VCpuState, f: F) -> AxResult @@ -196,18 +234,18 @@ impl AxVCpu { }) } - /// Transition the state of the vcpu. If the current state is not `from`, return an error. + /// Transition the state of the VCpu. If the current state is not `from`, return an error. pub fn transition_state(&self, from: VCpuState, to: VCpuState) -> AxResult { self.with_state_transition(from, to, || Ok(())) } - /// Get the architecture-specific vcpu. + /// Get the architecture-specific VCpu. #[allow(clippy::mut_from_ref)] pub fn get_arch_vcpu(&self) -> &mut A { unsafe { &mut *self.arch_vcpu.get() } } - /// Run the vcpu. + /// Run the VCpu. pub fn run(&self) -> AxResult { self.transition_state(VCpuState::Ready, VCpuState::Running)?; self.manipulate_arch_vcpu(VCpuState::Running, VCpuState::Ready, |arch_vcpu| { @@ -215,21 +253,21 @@ impl AxVCpu { }) } - /// Bind the vcpu to the current physical CPU. + /// Bind the VCpu to the current physical CPU. pub fn bind(&self) -> AxResult { self.manipulate_arch_vcpu(VCpuState::Free, VCpuState::Ready, |arch_vcpu| { arch_vcpu.bind() }) } - /// Unbind the vcpu from the current physical CPU. + /// Unbind the VCpu from the current physical CPU. pub fn unbind(&self) -> AxResult { self.manipulate_arch_vcpu(VCpuState::Ready, VCpuState::Free, |arch_vcpu| { arch_vcpu.unbind() }) } - /// Sets the entry address of the vcpu. + /// Sets the entry address of the VCpu. pub fn set_entry(&self, entry: GuestPhysAddr) -> AxResult { self.get_arch_vcpu().set_entry(entry) } @@ -239,12 +277,12 @@ impl AxVCpu { self.get_arch_vcpu().set_gpr(reg, val); } - /// Inject an interrupt to the vcpu. + /// Inject an interrupt to the VCpu. pub fn inject_interrupt(&self, vector: usize) -> AxResult { self.get_arch_vcpu().inject_interrupt(vector) } - /// Sets the return value of the vcpu. + /// Sets the return value of the VCpu. pub fn set_return_value(&self, val: usize) { self.get_arch_vcpu().set_return_value(val); } @@ -253,9 +291,9 @@ impl AxVCpu { #[percpu::def_percpu] static mut CURRENT_VCPU: Option<*mut u8> = None; -/// Get the current vcpu on the current physical CPU. +/// Get the current VCpu on the current physical CPU. /// -/// It's guaranteed that each time before a method of [`AxArchVCpu`] is called, the current vcpu is set to the corresponding [`AxVCpu`]. +/// It's guaranteed that each time before a method of [`AxArchVCpu`] is called, the current VCpu is set to the corresponding [`AxVCpu`]. /// So methods of [`AxArchVCpu`] can always get the [`AxVCpu`] containing itself by calling this method. pub fn get_current_vcpu<'a, A: AxArchVCpu>() -> Option<&'a AxVCpu> { unsafe { @@ -267,7 +305,7 @@ pub fn get_current_vcpu<'a, A: AxArchVCpu>() -> Option<&'a AxVCpu> { } } -/// Get a mutable reference to the current vcpu on the current physical CPU. +/// Get a mutable reference to the current VCpu on the current physical CPU. /// /// See [`get_current_vcpu`] for more details. pub fn get_current_vcpu_mut<'a, A: AxArchVCpu>() -> Option<&'a mut AxVCpu> { @@ -280,7 +318,7 @@ pub fn get_current_vcpu_mut<'a, A: AxArchVCpu>() -> Option<&'a mut AxVCpu> { } } -/// Set the current vcpu on the current physical CPU. +/// Set the current VCpu on the current physical CPU. /// /// # Safety /// This method is marked as unsafe because it may result in unexpected behavior if not used properly.