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