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