Skip to main content

axvm_types/
lib.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Shared base types for AxVM and virtualization capability components.
16//!
17//! This crate intentionally contains only small value types and aliases. It is
18//! not a host capability API and must not depend on any OS-specific crate.
19
20#![no_std]
21
22extern crate alloc;
23
24use alloc::{string::String, vec::Vec};
25use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
26
27use ax_memory_addr::{AddrRange, PhysAddr, VirtAddr, def_usize_addr, def_usize_addr_formatter};
28
29bitflags::bitflags! {
30    /// Generic memory mapping permissions and attributes exchanged between
31    /// AxVM components.
32    #[derive(Clone, Copy, PartialEq, Eq)]
33    pub struct MappingFlags: usize {
34        /// The memory is readable.
35        const READ          = 1 << 0;
36        /// The memory is writable.
37        const WRITE         = 1 << 1;
38        /// The memory is executable.
39        const EXECUTE       = 1 << 2;
40        /// The memory is user accessible.
41        const USER          = 1 << 3;
42        /// The memory is device memory.
43        const DEVICE        = 1 << 4;
44        /// The memory is uncached.
45        const UNCACHED      = 1 << 5;
46    }
47}
48
49impl Debug for MappingFlags {
50    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
51        Debug::fmt(&self.0, f)
52    }
53}
54
55/// Virtual machine identifier.
56pub type VMId = usize;
57
58/// Virtual CPU identifier within a VM.
59pub type VCpuId = usize;
60
61/// Interrupt vector number injected into a guest.
62pub type InterruptVector = u8;
63
64/// Interrupt trigger mode.
65///
66/// Represents the trigger mode of an interrupt in a platform-neutral way.
67/// Architectures that do not distinguish between edge and level triggering
68/// can ignore this parameter.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum InterruptTriggerMode {
71    /// Edge-triggered interrupt.
72    EdgeTriggered,
73    /// Level-triggered interrupt.
74    LevelTriggered,
75}
76
77/// Identifier of an interrupt line within a virtual machine.
78#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub struct IrqLineId(pub usize);
80
81/// The maximum number of virtual CPUs supported in a virtual machine.
82pub const MAX_VCPU_NUM: usize = 64;
83
84/// A set of virtual CPUs.
85pub type VCpuSet = ax_cpumask::CpuMask<MAX_VCPU_NUM>;
86
87/// Host virtual address.
88pub type HostVirtAddr = VirtAddr;
89
90/// Host physical address.
91pub type HostPhysAddr = PhysAddr;
92
93/// Architecture-specific nested paging configuration selected by AxVM.
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub struct NestedPagingConfig {
96    /// Root physical address of the nested page table.
97    pub root_paddr: HostPhysAddr,
98    /// Number of page-table levels.
99    pub levels: usize,
100    /// Guest physical address width in bits.
101    pub gpa_bits: usize,
102    /// Architecture-specific hardware mode encoding.
103    pub mode: usize,
104}
105
106impl NestedPagingConfig {
107    /// Creates a nested paging configuration.
108    pub const fn new(
109        root_paddr: HostPhysAddr,
110        levels: usize,
111        gpa_bits: usize,
112        mode: usize,
113    ) -> Self {
114        Self {
115            root_paddr,
116            levels,
117            gpa_bits,
118            mode,
119        }
120    }
121}
122
123def_usize_addr! {
124    /// Guest virtual address.
125    pub type GuestVirtAddr;
126
127    /// Guest physical address.
128    pub type GuestPhysAddr;
129}
130
131def_usize_addr_formatter! {
132    GuestVirtAddr = "GVA:{}";
133    GuestPhysAddr = "GPA:{}";
134}
135
136/// Guest virtual address range.
137pub type GuestVirtAddrRange = AddrRange<GuestVirtAddr>;
138
139/// Guest physical address range.
140pub type GuestPhysAddrRange = AddrRange<GuestPhysAddr>;
141
142/// Common AxVM result type.
143pub type AxVmResult<T = ()> = ax_errno::AxResult<T>;
144
145/// Common AxVM error type.
146pub type AxVmError = ax_errno::AxError;
147
148/// The width of a guest bus access.
149///
150/// The term "word" follows the x86 convention and means 16 bits.
151#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
152pub enum AccessWidth {
153    /// 8-bit access.
154    Byte,
155    /// 16-bit access.
156    Word,
157    /// 32-bit access.
158    Dword,
159    /// 64-bit access.
160    Qword,
161}
162
163impl TryFrom<usize> for AccessWidth {
164    type Error = ();
165
166    fn try_from(value: usize) -> Result<Self, Self::Error> {
167        match value {
168            1 => Ok(Self::Byte),
169            2 => Ok(Self::Word),
170            4 => Ok(Self::Dword),
171            8 => Ok(Self::Qword),
172            _ => Err(()),
173        }
174    }
175}
176
177impl From<AccessWidth> for usize {
178    fn from(width: AccessWidth) -> usize {
179        match width {
180            AccessWidth::Byte => 1,
181            AccessWidth::Word => 2,
182            AccessWidth::Dword => 4,
183            AccessWidth::Qword => 8,
184        }
185    }
186}
187
188impl AccessWidth {
189    /// Returns the size of this access in bytes.
190    pub fn size(&self) -> usize {
191        (*self).into()
192    }
193
194    /// Returns the bit range covered by this access.
195    pub fn bits_range(&self) -> core::ops::Range<usize> {
196        match self {
197            AccessWidth::Byte => 0..8,
198            AccessWidth::Word => 0..16,
199            AccessWidth::Dword => 0..32,
200            AccessWidth::Qword => 0..64,
201        }
202    }
203}
204
205/// The port number of an x86 I/O operation.
206#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
207pub struct Port(pub u16);
208
209impl Port {
210    /// Creates a new [`Port`].
211    pub const fn new(port: u16) -> Self {
212        Self(port)
213    }
214
215    /// Returns the raw port number.
216    pub const fn number(&self) -> u16 {
217        self.0
218    }
219}
220
221impl LowerHex for Port {
222    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
223        write!(f, "Port({:#x})", self.0)
224    }
225}
226
227impl UpperHex for Port {
228    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
229        write!(f, "Port({:#X})", self.0)
230    }
231}
232
233impl Debug for Port {
234    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
235        write!(f, "Port({})", self.0)
236    }
237}
238
239/// A system register address.
240#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
241pub struct SysRegAddr(pub usize);
242
243impl SysRegAddr {
244    /// Creates a new [`SysRegAddr`].
245    pub const fn new(addr: usize) -> Self {
246        Self(addr)
247    }
248
249    /// Returns the raw register address.
250    pub const fn addr(&self) -> usize {
251        self.0
252    }
253}
254
255impl From<usize> for SysRegAddr {
256    fn from(value: usize) -> Self {
257        Self(value)
258    }
259}
260
261impl LowerHex for SysRegAddr {
262    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
263        write!(f, "SysRegAddr({:#x})", self.0)
264    }
265}
266
267impl UpperHex for SysRegAddr {
268    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
269        write!(f, "SysRegAddr({:#X})", self.0)
270    }
271}
272
273impl Debug for SysRegAddr {
274    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
275        write!(f, "SysRegAddr({})", self.0)
276    }
277}
278
279/// Information about a nested guest page-table fault.
280#[derive(Debug)]
281pub struct NestedPageFaultInfo {
282    /// Access type that caused the nested page fault.
283    pub access_flags: MappingFlags,
284    /// Guest physical address that caused the nested page fault.
285    pub fault_guest_paddr: GuestPhysAddr,
286}
287
288/// Legacy/common normalized VM event.
289///
290/// New AxVM architecture backends should expose their raw VM-exit type through
291/// [`VmArchVcpuOps::Exit`] and handle it inside their `axvm::arch` module.
292/// This enum remains for compatibility and as a transitional normalized event
293/// shape for backends that have not split out an architecture-owned exit enum.
294#[non_exhaustive]
295#[derive(Debug)]
296pub enum VmExit {
297    /// A guest instruction triggered a hypercall to the hypervisor.
298    Hypercall {
299        /// Hypercall number.
300        nr: u64,
301        /// Hypercall arguments.
302        args: [u64; 6],
303    },
304    /// The guest performed an MMIO read.
305    MmioRead {
306        /// Guest physical address being read.
307        addr: GuestPhysAddr,
308        /// Access width.
309        width: AccessWidth,
310        /// Destination guest register.
311        reg: usize,
312        /// Destination register width.
313        reg_width: AccessWidth,
314        /// Whether the value should be sign-extended.
315        signed_ext: bool,
316    },
317    /// The guest performed an MMIO write.
318    MmioWrite {
319        /// Guest physical address being written.
320        addr: GuestPhysAddr,
321        /// Access width.
322        width: AccessWidth,
323        /// Value written by the guest.
324        data: u64,
325    },
326    /// The guest performed a system register read.
327    SysRegRead {
328        /// System register address.
329        addr: SysRegAddr,
330        /// Destination guest register.
331        reg: usize,
332    },
333    /// The guest performed a system register write.
334    SysRegWrite {
335        /// System register address.
336        addr: SysRegAddr,
337        /// Value written by the guest.
338        value: u64,
339    },
340    /// The guest performed an x86 port I/O read.
341    IoRead {
342        /// Port number.
343        port: Port,
344        /// Access width.
345        width: AccessWidth,
346    },
347    /// The guest performed an x86 port I/O write.
348    IoWrite {
349        /// Port number.
350        port: Port,
351        /// Access width.
352        width: AccessWidth,
353        /// Value written by the guest.
354        data: u64,
355    },
356    /// An external interrupt was delivered to the vCPU.
357    ExternalInterrupt {
358        /// Interrupt vector number.
359        vector: u64,
360    },
361    /// A nested page fault occurred during guest execution.
362    NestedPageFault {
363        /// Guest physical address that caused the fault.
364        addr: GuestPhysAddr,
365        /// Access type that caused the fault.
366        access_flags: MappingFlags,
367    },
368    /// The guest halted.
369    Halt,
370    /// The guest reached an idle instruction.
371    Idle,
372    /// The guest requested secondary CPU startup.
373    CpuUp {
374        /// Target CPU identifier in the architecture namespace.
375        target_cpu: u64,
376        /// Secondary entry point.
377        entry_point: GuestPhysAddr,
378        /// Secondary boot argument.
379        arg: u64,
380    },
381    /// The guest powered down one vCPU.
382    CpuDown {
383        /// Architecture power-state payload.
384        _state: u64,
385    },
386    /// The guest requested VM shutdown.
387    SystemDown,
388    /// No VMM action is required.
389    Nothing,
390    /// Hardware virtualization preemption timer expired.
391    PreemptionTimer,
392    /// The guest completed interrupt service with EOI.
393    InterruptEnd {
394        /// EOI vector, when available.
395        vector: Option<u8>,
396    },
397    /// VM entry failed.
398    FailEntry {
399        /// Architecture-specific failure code.
400        hardware_entry_failure_reason: u64,
401    },
402    /// The guest requested an IPI.
403    SendIPI {
404        /// Target CPU identifier in the architecture namespace.
405        target_cpu: u64,
406        /// Auxiliary target selector.
407        target_cpu_aux: u64,
408        /// Whether to broadcast to all CPUs except the sender.
409        send_to_all: bool,
410        /// Whether to target the current vCPU.
411        send_to_self: bool,
412        /// IPI vector.
413        vector: u64,
414    },
415}
416
417/// Architecture-specific vCPU operations consumed by AxVM.
418pub trait VmArchVcpuOps: Sized {
419    /// Architecture-specific creation configuration.
420    type CreateConfig;
421    /// Architecture-specific setup configuration.
422    type SetupConfig;
423    /// Architecture-specific VM-exit type returned by [`Self::run`].
424    type Exit: Debug;
425
426    /// Creates a new architecture-specific vCPU.
427    fn new(vm_id: VMId, vcpu_id: VCpuId, config: Self::CreateConfig) -> AxVmResult<Self>;
428    /// Sets the guest entry point.
429    fn set_entry(&mut self, entry: GuestPhysAddr) -> AxVmResult;
430    /// Sets the nested page table selected by AxVM.
431    fn set_nested_page_table(&mut self, config: NestedPagingConfig) -> AxVmResult;
432    /// Completes architecture-specific setup.
433    fn setup(&mut self, config: Self::SetupConfig) -> AxVmResult;
434    /// Runs the vCPU until an architecture-specific VM exit.
435    fn run(&mut self) -> AxVmResult<Self::Exit>;
436    /// Binds the vCPU to the current physical CPU.
437    fn bind(&mut self) -> AxVmResult;
438    /// Unbinds the vCPU from the current physical CPU.
439    fn unbind(&mut self) -> AxVmResult;
440    /// Sets a general-purpose register.
441    fn set_gpr(&mut self, reg: usize, val: usize);
442    /// Decodes an architecture-specific memory fault as a legacy normalized
443    /// MMIO event when possible.
444    ///
445    /// This is kept as a transition helper for backends that still route
446    /// device faults through [`VmExit`]. New raw vCPU exits should use
447    /// [`Self::Exit`] and be handled in the architecture-local AxVM adapter.
448    fn decode_mmio_fault(
449        &mut self,
450        _fault_addr: GuestPhysAddr,
451        _access_flags: MappingFlags,
452    ) -> Option<VmExit> {
453        None
454    }
455    /// Injects an interrupt into the vCPU.
456    fn inject_interrupt(&mut self, vector: usize) -> AxVmResult;
457    /// Injects an interrupt with trigger-mode metadata.
458    fn inject_interrupt_with_trigger(
459        &mut self,
460        vector: usize,
461        trigger: InterruptTriggerMode,
462    ) -> AxVmResult {
463        debug_assert!(
464            trigger == InterruptTriggerMode::EdgeTriggered,
465            "level-triggered interrupt injection requires an architecture-specific implementation"
466        );
467        self.inject_interrupt(vector)
468    }
469    /// Processes a guest EOI and returns an external EOI vector when needed.
470    fn handle_eoi(&mut self) -> Option<u8> {
471        None
472    }
473    /// Sets the guest return value.
474    fn set_return_value(&mut self, val: usize);
475}
476
477/// Architecture-specific per-CPU virtualization state consumed by AxVM.
478pub trait VmArchPerCpuOps: Sized {
479    /// Creates a new per-CPU state.
480    fn new(cpu_id: usize) -> AxVmResult<Self>;
481    /// Whether virtualization is enabled on the current CPU.
482    fn is_enabled(&self) -> bool;
483    /// Enables virtualization on the current CPU.
484    fn hardware_enable(&mut self) -> AxVmResult;
485    /// Disables virtualization on the current CPU.
486    fn hardware_disable(&mut self) -> AxVmResult;
487    /// Returns the max guest page table levels supported by this architecture.
488    fn max_guest_page_table_levels(&self) -> usize {
489        4
490    }
491    /// Returns the guest physical address width supported by this CPU.
492    fn guest_phys_addr_bits(&self) -> usize {
493        match self.max_guest_page_table_levels() {
494            0..=3 => 39,
495            _ => 48,
496        }
497    }
498}
499
500/// Execution state of an AxVM-owned vCPU wrapper.
501#[derive(Clone, Copy, Debug, PartialEq, Eq)]
502pub enum VmVcpuState {
503    /// Invalid state.
504    Invalid = 0,
505    /// Initial state after vCPU creation.
506    Created = 1,
507    /// vCPU is initialized and free.
508    Free    = 2,
509    /// vCPU is bound and ready to run.
510    Ready   = 3,
511    /// vCPU is currently running.
512    Running = 4,
513    /// vCPU is blocked.
514    Blocked = 5,
515}
516
517/// A part of `AxVMConfig`, which represents guest VM type.
518#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
519pub enum VMType {
520    /// Host VM, used for boot from Linux like Jailhouse do, named "type1.5".
521    VMTHostVM = 0,
522    /// Guest RTOS, generally a simple guest OS with most of the resource passthrough.
523    #[default]
524    VMTRTOS   = 1,
525    /// Guest Linux, generally a full-featured guest OS with complicated device emulation requirements.
526    VMTLinux  = 2,
527}
528
529impl From<usize> for VMType {
530    fn from(value: usize) -> Self {
531        match value {
532            0 => Self::VMTHostVM,
533            1 => Self::VMTRTOS,
534            2 => Self::VMTLinux,
535            _ => Self::default(),
536        }
537    }
538}
539
540impl From<VMType> for usize {
541    fn from(value: VMType) -> Self {
542        value as usize
543    }
544}
545
546/// Guest physical address space population policy.
547#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
548pub enum AddressSpacePolicy {
549    /// Start from an empty guest physical address space and map only explicit
550    /// guest memory, boot-description regions, and explicitly configured
551    /// passthrough resources.
552    #[default]
553    Virtualized,
554    /// Start from a host-physical identity passthrough address space, then
555    /// punch holes for guest memory, boot-description regions, emulated
556    /// devices, and reserved ranges.
557    Passthrough,
558}
559
560/// The type of memory mapping used for VM memory regions.
561#[derive(Debug, Default, Clone, PartialEq, Eq)]
562#[repr(u8)]
563pub enum VmMemMappingType {
564    /// The memory region is allocated by the VM monitor.
565    #[default]
566    MapAlloc     = 0,
567    /// The memory region is identical to the host physical memory region.
568    MapIdentical = 1,
569    /// The memory region is reserved memory for the guest OS.
570    MapReserved  = 2,
571}
572
573/// Configuration for a virtual machine memory region.
574#[derive(Debug, Default, Clone)]
575pub struct VmMemConfig {
576    /// The start address of the memory region in GPA (Guest Physical Address).
577    pub gpa: usize,
578    /// The size of the memory region in bytes.
579    pub size: usize,
580    /// The mappings flags of the memory region.
581    pub flags: usize,
582    /// The type of memory mapping.
583    pub map_type: VmMemMappingType,
584}
585
586/// A part of `AxVMConfig`, which represents the configuration of an emulated device for a virtual machine.
587#[derive(Debug, Default, Clone)]
588pub struct EmulatedDeviceConfig {
589    /// The name of the device.
590    pub name: String,
591    /// The base GPA (Guest Physical Address) of the device.
592    pub base_gpa: usize,
593    /// The address length of the device.
594    pub length: usize,
595    /// The IRQ (Interrupt Request) ID of the device.
596    pub irq_id: usize,
597    /// The type of emulated device.
598    pub emu_type: EmulatedDeviceType,
599    /// The config list of the device.
600    pub cfg_list: Vec<usize>,
601}
602
603/// A part of `AxVMConfig`, which represents the configuration of a pass-through device for a virtual machine.
604#[derive(Debug, Default, Clone, PartialEq)]
605pub struct PassThroughDeviceConfig {
606    /// The name of the device.
607    pub name: String,
608    /// The base GPA (Guest Physical Address) of the device.
609    pub base_gpa: usize,
610    /// The base HPA (Host Physical Address) of the device.
611    pub base_hpa: usize,
612    /// The address length of the device.
613    pub length: usize,
614    /// The IRQ (Interrupt Request) ID of the device.
615    pub irq_id: usize,
616}
617
618/// A part of `AxVMConfig`, which represents the configuration of a pass-through address for a virtual machine.
619#[derive(Debug, Default, Clone, PartialEq)]
620pub struct PassThroughAddressConfig {
621    /// The base GPA (Guest Physical Address).
622    pub base_gpa: usize,
623    /// The address length.
624    pub length: usize,
625}
626
627/// A guest physical address range reserved from default passthrough mapping.
628#[derive(Debug, Default, Clone, PartialEq, Eq)]
629pub struct ReservedAddressConfig {
630    /// The base GPA (Guest Physical Address).
631    pub base_gpa: usize,
632    /// The address length.
633    pub length: usize,
634}
635
636/// A part of `AxVMConfig`, which represents a host I/O port range passed through
637/// to a virtual machine.
638#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
639pub struct PassThroughPortConfig {
640    /// The first host I/O port number.
641    pub base: u16,
642    /// The number of ports in this range.
643    pub length: u16,
644}
645
646/// Describes how a guest VM should enter its boot image.
647#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
648pub enum VMBootProtocol {
649    /// Enter the configured kernel entry directly without a firmware image.
650    #[default]
651    Direct,
652    /// Use the legacy x86 axvm-bios/multiboot trampoline.
653    Multiboot,
654    /// Load an external UEFI firmware image and enter it without multiboot patching.
655    Uefi,
656}
657
658/// Specifies how the VM should handle interrupts and interrupt controllers.
659#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
660pub enum VMInterruptMode {
661    /// The VM will not handle interrupts, and the guest OS should not use interrupts.
662    #[default]
663    NoIrq,
664    /// The VM will use the emulated interrupt controller to handle interrupts.
665    Emulated,
666    /// The VM will use the passthrough interrupt controller (including GPPT) to handle interrupts.
667    Passthrough,
668}
669
670/// The type of emulated device.
671///
672/// Allocation scheme:
673/// - 0x00 - 0x1F: Special devices, and abstract device types that does not specify a concrete
674///   interface or implementation. The device objects created from these types depend on the target
675///   architecture and the specific implementation of the hypervisor.
676/// - 0x20 - 0x7F: Concrete emulated device types.
677///   - 0x20 - 0x2F: Interrupt controller devices.
678///   - 0x30 - 0x3F: Reserved for future use.
679/// - 0x80 - 0xDF: Reserved for future use.
680/// - 0xE0 - 0xEF: Virtio devices.
681/// - 0xF0 - 0xFF: Reserved for future use.
682#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
683#[repr(u8)]
684pub enum EmulatedDeviceType {
685    // Special devices and abstract device types.
686    /// Dummy device type.
687    #[default]
688    Dummy               = 0x0,
689    /// Interrupt controller device, e.g. vGICv2 in aarch64, vLAPIC in x86.
690    InterruptController = 0x1,
691    /// Console (serial) device.
692    Console             = 0x2,
693    /// QEMU fw_cfg MMIO device.
694    FwCfg               = 0x3,
695    /// An emulated device that provides Inter-VM Communication (IVC) channel.
696    ///
697    /// This device is used for communication between different VMs,
698    /// the corresponding memory region of this device should be marked as `Reserved` in
699    /// device tree or ACPI table.
700    IVCChannel          = 0xA,
701
702    // Arch-specific interrupt controller devices.
703    // 0x20 - 0x22: GPPT (GIC Partial Passthrough) devices.
704    /// ARM GIC Partial Passthrough Redistributor device.
705    GPPTRedistributor   = 0x20,
706    /// ARM GIC Partial Passthrough Distributor device.
707    GPPTDistributor     = 0x21,
708    /// ARM GIC Partial Passthrough Interrupt Translation Service device.
709    GPPTITS             = 0x22,
710
711    // 0x23 - 0x24: x86 platform devices.
712    /// x86 virtual IO APIC device.
713    X86IoApic           = 0x23,
714    /// x86 virtual PIT/8254 timer device.
715    X86Pit              = 0x24,
716    /// LoongArch virtual PCH-PIC device.
717    LoongArchPchPic     = 0x25,
718
719    // 0x30: PPPT (PLIC Partial Passthrough) devices.
720    /// RISC-V PLIC Partial Passthrough Global device.
721    PPPTGlobal          = 0x30,
722
723    // Virtio devices.
724    /// Virtio block device.
725    VirtioBlk           = 0xE1,
726    /// Virtio net device.
727    VirtioNet           = 0xE2,
728    /// Virtio console device.
729    VirtioConsole       = 0xE3,
730    // Following are some other emulated devices that are not currently used and removed from the enum temporarily.
731    // /// IOMMU device.
732    // IOMMU = 0x6,
733    // /// Interrupt ICC SRE device.
734    // ICCSRE = 0x7,
735    // /// Interrupt ICC SGIR device.
736    // SGIR = 0x8,
737    // /// Interrupt controller GICR device.
738    // GICR = 0x9,
739}
740
741#[cfg(test)]
742mod tests {
743    use super::*;
744
745    struct MockPerCpu {
746        enabled: bool,
747    }
748
749    impl VmArchPerCpuOps for MockPerCpu {
750        fn new(_cpu_id: usize) -> AxVmResult<Self> {
751            Ok(Self { enabled: false })
752        }
753
754        fn is_enabled(&self) -> bool {
755            self.enabled
756        }
757
758        fn hardware_enable(&mut self) -> AxVmResult {
759            self.enabled = true;
760            Ok(())
761        }
762
763        fn hardware_disable(&mut self) -> AxVmResult {
764            self.enabled = false;
765            Ok(())
766        }
767    }
768
769    #[derive(Debug, PartialEq, Eq)]
770    enum MockExit {
771        SysRegRead { reg: usize },
772    }
773
774    struct MockVcpu;
775
776    impl VmArchVcpuOps for MockVcpu {
777        type CreateConfig = ();
778        type SetupConfig = ();
779        type Exit = MockExit;
780
781        fn new(_vm_id: VMId, _vcpu_id: VCpuId, _config: Self::CreateConfig) -> AxVmResult<Self> {
782            Ok(Self)
783        }
784
785        fn set_entry(&mut self, _entry: GuestPhysAddr) -> AxVmResult {
786            Ok(())
787        }
788
789        fn set_nested_page_table(&mut self, _config: NestedPagingConfig) -> AxVmResult {
790            Ok(())
791        }
792
793        fn setup(&mut self, _config: Self::SetupConfig) -> AxVmResult {
794            Ok(())
795        }
796
797        fn run(&mut self) -> AxVmResult<Self::Exit> {
798            Ok(MockExit::SysRegRead { reg: 2 })
799        }
800
801        fn bind(&mut self) -> AxVmResult {
802            Ok(())
803        }
804
805        fn unbind(&mut self) -> AxVmResult {
806            Ok(())
807        }
808
809        fn set_gpr(&mut self, _reg: usize, _val: usize) {}
810
811        fn inject_interrupt(&mut self, _vector: usize) -> AxVmResult {
812            Ok(())
813        }
814
815        fn set_return_value(&mut self, _val: usize) {}
816    }
817
818    #[test]
819    fn vcpu_protocol_lives_in_axvm_types() {
820        let mut percpu = MockPerCpu::new(0).unwrap();
821        assert!(!percpu.is_enabled());
822        percpu.hardware_enable().unwrap();
823        assert!(percpu.is_enabled());
824
825        let mut vcpu = MockVcpu::new(1, 0, ()).unwrap();
826        vcpu.set_entry(GuestPhysAddr::from(0x8020_0000)).unwrap();
827        vcpu.set_nested_page_table(NestedPagingConfig::new(
828            HostPhysAddr::from(0x1000),
829            4,
830            48,
831            0,
832        ))
833        .unwrap();
834        vcpu.setup(()).unwrap();
835        assert!(matches!(
836            vcpu.run().unwrap(),
837            MockExit::SysRegRead { reg: 2 }
838        ));
839    }
840
841    #[test]
842    fn vm_exit_keeps_access_width_and_state_types() {
843        let state = VmVcpuState::Created;
844        assert_eq!(state as u8, 1);
845
846        let exit = VmExit::MmioRead {
847            addr: GuestPhysAddr::from(0x1000),
848            width: AccessWidth::Dword,
849            reg: 3,
850            reg_width: AccessWidth::Qword,
851            signed_ext: true,
852        };
853        assert!(matches!(
854            exit,
855            VmExit::MmioRead {
856                width: AccessWidth::Dword,
857                reg: 3,
858                ..
859            }
860        ));
861    }
862}
863
864impl Display for EmulatedDeviceType {
865    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
866        match self {
867            EmulatedDeviceType::Console => write!(f, "console"),
868            EmulatedDeviceType::FwCfg => write!(f, "fw_cfg"),
869            EmulatedDeviceType::InterruptController => write!(f, "interrupt controller"),
870            EmulatedDeviceType::GPPTRedistributor => {
871                write!(f, "gic partial passthrough redistributor")
872            }
873            EmulatedDeviceType::GPPTDistributor => write!(f, "gic partial passthrough distributor"),
874            EmulatedDeviceType::GPPTITS => write!(f, "gic partial passthrough its"),
875            EmulatedDeviceType::X86IoApic => write!(f, "x86 io apic"),
876            EmulatedDeviceType::X86Pit => write!(f, "x86 pit"),
877            EmulatedDeviceType::LoongArchPchPic => write!(f, "loongarch pch pic"),
878            EmulatedDeviceType::PPPTGlobal => write!(f, "plic partial passthrough global"),
879            // EmulatedDeviceType::IOMMU => write!(f, "iommu"),
880            // EmulatedDeviceType::ICCSRE => write!(f, "interrupt icc sre"),
881            // EmulatedDeviceType::SGIR => write!(f, "interrupt icc sgir"),
882            // EmulatedDeviceType::GICR => write!(f, "interrupt controller gicr"),
883            EmulatedDeviceType::IVCChannel => write!(f, "ivc channel"),
884            EmulatedDeviceType::Dummy => write!(f, "meta device"),
885            EmulatedDeviceType::VirtioBlk => write!(f, "virtio block"),
886            EmulatedDeviceType::VirtioNet => write!(f, "virtio net"),
887            EmulatedDeviceType::VirtioConsole => write!(f, "virtio console"),
888        }
889    }
890}
891
892impl EmulatedDeviceType {
893    /// All known emulated device types.
894    pub const ALL: [Self; 15] = [
895        EmulatedDeviceType::Dummy,
896        EmulatedDeviceType::InterruptController,
897        EmulatedDeviceType::Console,
898        EmulatedDeviceType::FwCfg,
899        EmulatedDeviceType::IVCChannel,
900        EmulatedDeviceType::GPPTRedistributor,
901        EmulatedDeviceType::GPPTDistributor,
902        EmulatedDeviceType::GPPTITS,
903        EmulatedDeviceType::X86IoApic,
904        EmulatedDeviceType::X86Pit,
905        EmulatedDeviceType::LoongArchPchPic,
906        EmulatedDeviceType::PPPTGlobal,
907        EmulatedDeviceType::VirtioBlk,
908        EmulatedDeviceType::VirtioNet,
909        EmulatedDeviceType::VirtioConsole,
910    ];
911
912    /// Returns all known emulated device types.
913    pub const fn all() -> &'static [Self] {
914        &Self::ALL
915    }
916
917    /// Returns true if the device is removable.
918    pub fn removable(&self) -> bool {
919        matches!(
920            *self,
921            EmulatedDeviceType::InterruptController
922                // | EmulatedDeviceType::SGIR
923                // | EmulatedDeviceType::ICCSRE
924                | EmulatedDeviceType::GPPTRedistributor
925                | EmulatedDeviceType::X86IoApic
926                | EmulatedDeviceType::X86Pit
927                | EmulatedDeviceType::VirtioBlk
928                | EmulatedDeviceType::VirtioNet
929                // | EmulatedDeviceType::GICR
930                | EmulatedDeviceType::VirtioConsole
931        )
932    }
933
934    /// Converts a `usize` value to an `EmulatedDeviceType`.
935    pub const fn from_usize(value: usize) -> Option<Self> {
936        match value {
937            0x0 => Some(EmulatedDeviceType::Dummy),
938            0x1 => Some(EmulatedDeviceType::InterruptController),
939            0x2 => Some(EmulatedDeviceType::Console),
940            0x3 => Some(EmulatedDeviceType::FwCfg),
941            0xA => Some(EmulatedDeviceType::IVCChannel),
942            0x20 => Some(EmulatedDeviceType::GPPTRedistributor),
943            0x21 => Some(EmulatedDeviceType::GPPTDistributor),
944            0x22 => Some(EmulatedDeviceType::GPPTITS),
945            0x23 => Some(EmulatedDeviceType::X86IoApic),
946            0x24 => Some(EmulatedDeviceType::X86Pit),
947            0x25 => Some(EmulatedDeviceType::LoongArchPchPic),
948            0x30 => Some(EmulatedDeviceType::PPPTGlobal),
949            0xE1 => Some(EmulatedDeviceType::VirtioBlk),
950            0xE2 => Some(EmulatedDeviceType::VirtioNet),
951            0xE3 => Some(EmulatedDeviceType::VirtioConsole),
952            // 0x6 => EmulatedDeviceType::IOMMU,
953            // 0x7 => EmulatedDeviceType::ICCSRE,
954            // 0x8 => EmulatedDeviceType::SGIR,
955            // 0x9 => EmulatedDeviceType::GICR,
956            _ => None,
957        }
958    }
959}