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/// Architectures that do not distinguish between edge and level triggering
71/// can ignore this parameter.
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    /// Sets the guest entry point.
426    fn set_entry(&mut self, entry: GuestPhysAddr) -> VmBackendResult;
427    /// Sets the nested page table selected by AxVM.
428    fn set_nested_page_table(&mut self, config: NestedPagingConfig) -> VmBackendResult;
429    /// Completes architecture-specific setup.
430    fn setup(&mut self, config: Self::SetupConfig) -> VmBackendResult;
431    /// Runs the vCPU until an architecture-specific VM exit.
432    fn run(&mut self) -> VmBackendResult<Self::Exit>;
433    /// Binds the vCPU to the current physical CPU.
434    fn bind(&mut self) -> VmBackendResult;
435    /// Unbinds the vCPU from the current physical CPU.
436    fn unbind(&mut self) -> VmBackendResult;
437    /// Sets a general-purpose register.
438    fn set_gpr(&mut self, reg: usize, val: usize);
439    /// Decodes an architecture-specific memory fault as a legacy normalized
440    /// MMIO event when possible.
441    ///
442    /// This is kept as a transition helper for backends that still route
443    /// device faults through [`VmExit`]. New raw vCPU exits should use
444    /// [`Self::Exit`] and be handled in the architecture-local AxVM adapter.
445    fn decode_mmio_fault(
446        &mut self,
447        _fault_addr: GuestPhysAddr,
448        _access_flags: MappingFlags,
449    ) -> Option<VmExit> {
450        None
451    }
452    /// Injects an interrupt into the vCPU.
453    fn inject_interrupt(&mut self, vector: usize) -> VmBackendResult;
454    /// Injects an interrupt with trigger-mode metadata.
455    fn inject_interrupt_with_trigger(
456        &mut self,
457        vector: usize,
458        trigger: InterruptTriggerMode,
459    ) -> VmBackendResult {
460        debug_assert!(
461            trigger == InterruptTriggerMode::EdgeTriggered,
462            "level-triggered interrupt injection requires an architecture-specific implementation"
463        );
464        self.inject_interrupt(vector)
465    }
466    /// Processes a guest EOI and returns an external EOI vector when needed.
467    fn handle_eoi(&mut self) -> Option<u8> {
468        None
469    }
470    /// Sets the guest return value.
471    fn set_return_value(&mut self, val: usize);
472}
473
474/// Architecture-specific per-CPU virtualization state consumed by AxVM.
475pub trait VmArchPerCpuOps: Sized {
476    /// Creates a new per-CPU state.
477    fn new(cpu_id: usize) -> VmBackendResult<Self>;
478    /// Whether virtualization is enabled on the current CPU.
479    fn is_enabled(&self) -> bool;
480    /// Enables virtualization on the current CPU.
481    fn hardware_enable(&mut self) -> VmBackendResult;
482    /// Disables virtualization on the current CPU.
483    fn hardware_disable(&mut self) -> VmBackendResult;
484    /// Returns the max guest page table levels supported by this architecture.
485    fn max_guest_page_table_levels(&self) -> usize {
486        4
487    }
488    /// Returns the guest physical address width supported by this CPU.
489    fn guest_phys_addr_bits(&self) -> usize {
490        match self.max_guest_page_table_levels() {
491            0..=3 => 39,
492            _ => 48,
493        }
494    }
495}
496
497/// Execution state of an AxVM-owned vCPU wrapper.
498#[derive(Clone, Copy, Debug, PartialEq, Eq)]
499pub enum VmVcpuState {
500    /// Invalid state.
501    Invalid = 0,
502    /// Initial state after vCPU creation.
503    Created = 1,
504    /// vCPU is initialized and free.
505    Free    = 2,
506    /// vCPU is bound and ready to run.
507    Ready   = 3,
508    /// vCPU is currently running.
509    Running = 4,
510    /// vCPU is blocked.
511    Blocked = 5,
512}
513
514/// A part of `AxVMConfig`, which represents guest VM type.
515#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
516pub enum VMType {
517    /// Host VM, used for boot from Linux like Jailhouse do, named "type1.5".
518    VMTHostVM = 0,
519    /// Guest RTOS, generally a simple guest OS with most of the resource passthrough.
520    #[default]
521    VMTRTOS   = 1,
522    /// Guest Linux, generally a full-featured guest OS with complicated device emulation requirements.
523    VMTLinux  = 2,
524}
525
526impl From<usize> for VMType {
527    fn from(value: usize) -> Self {
528        match value {
529            0 => Self::VMTHostVM,
530            1 => Self::VMTRTOS,
531            2 => Self::VMTLinux,
532            _ => Self::default(),
533        }
534    }
535}
536
537impl From<VMType> for usize {
538    fn from(value: VMType) -> Self {
539        value as usize
540    }
541}
542
543/// Guest physical address space population policy.
544#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
545pub enum AddressSpacePolicy {
546    /// Start from an empty guest physical address space and map only explicit
547    /// guest memory, boot-description regions, and explicitly configured
548    /// passthrough resources.
549    #[default]
550    Virtualized,
551    /// Start from a host-physical identity passthrough address space, then
552    /// punch holes for guest memory, boot-description regions, emulated
553    /// devices, and reserved ranges.
554    Passthrough,
555}
556
557/// The type of memory mapping used for VM memory regions.
558#[derive(Debug, Default, Clone, PartialEq, Eq)]
559#[repr(u8)]
560pub enum VmMemMappingType {
561    /// The memory region is allocated by the VM monitor.
562    #[default]
563    MapAlloc     = 0,
564    /// The memory region is identical to the host physical memory region.
565    MapIdentical = 1,
566    /// The memory region is reserved memory for the guest OS.
567    MapReserved  = 2,
568}
569
570/// Configuration for a virtual machine memory region.
571#[derive(Debug, Default, Clone)]
572pub struct VmMemConfig {
573    /// The start address of the memory region in GPA (Guest Physical Address).
574    pub gpa: usize,
575    /// The size of the memory region in bytes.
576    pub size: usize,
577    /// The mappings flags of the memory region.
578    pub flags: usize,
579    /// The type of memory mapping.
580    pub map_type: VmMemMappingType,
581}
582
583/// A part of `AxVMConfig`, which represents the configuration of an emulated device for a virtual machine.
584#[derive(Debug, Default, Clone)]
585pub struct EmulatedDeviceConfig {
586    /// The name of the device.
587    pub name: String,
588    /// The base GPA (Guest Physical Address) of the device.
589    pub base_gpa: usize,
590    /// The address length of the device.
591    pub length: usize,
592    /// The IRQ (Interrupt Request) ID of the device.
593    pub irq_id: usize,
594    /// The type of emulated device.
595    pub emu_type: EmulatedDeviceType,
596    /// The config list of the device.
597    pub cfg_list: Vec<usize>,
598}
599
600/// A part of `AxVMConfig`, which represents the configuration of a pass-through device for a virtual machine.
601#[derive(Debug, Default, Clone, PartialEq)]
602pub struct PassThroughDeviceConfig {
603    /// The name of the device.
604    pub name: String,
605    /// The base GPA (Guest Physical Address) of the device.
606    pub base_gpa: usize,
607    /// The base HPA (Host Physical Address) of the device.
608    pub base_hpa: usize,
609    /// The address length of the device.
610    pub length: usize,
611    /// The IRQ (Interrupt Request) ID of the device.
612    pub irq_id: usize,
613}
614
615/// A part of `AxVMConfig`, which represents the configuration of a pass-through address for a virtual machine.
616#[derive(Debug, Default, Clone, PartialEq)]
617pub struct PassThroughAddressConfig {
618    /// The base GPA (Guest Physical Address).
619    pub base_gpa: usize,
620    /// The address length.
621    pub length: usize,
622}
623
624/// A guest physical address range reserved from default passthrough mapping.
625#[derive(Debug, Default, Clone, PartialEq, Eq)]
626pub struct ReservedAddressConfig {
627    /// The base GPA (Guest Physical Address).
628    pub base_gpa: usize,
629    /// The address length.
630    pub length: usize,
631}
632
633/// A part of `AxVMConfig`, which represents a host I/O port range passed through
634/// to a virtual machine.
635#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
636pub struct PassThroughPortConfig {
637    /// The first host I/O port number.
638    pub base: u16,
639    /// The number of ports in this range.
640    pub length: u16,
641}
642
643/// Describes how a guest VM should enter its boot image.
644#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
645pub enum VMBootProtocol {
646    /// Enter the configured kernel entry directly without a firmware image.
647    #[default]
648    Direct,
649    /// Use the legacy x86 axvm-bios/multiboot trampoline.
650    Multiboot,
651    /// Load an external UEFI firmware image and enter it without multiboot patching.
652    Uefi,
653}
654
655/// Specifies how the VM should handle interrupts and interrupt controllers.
656#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
657pub enum VMInterruptMode {
658    /// The VM will not handle interrupts, and the guest OS should not use interrupts.
659    #[default]
660    NoIrq,
661    /// The VM will use the emulated interrupt controller to handle interrupts.
662    Emulated,
663    /// The VM will use the passthrough interrupt controller (including GPPT) to handle interrupts.
664    Passthrough,
665}
666
667/// The type of emulated device.
668///
669/// Allocation scheme:
670/// - 0x00 - 0x1F: Special devices, and abstract device types that does not specify a concrete
671///   interface or implementation. The device objects created from these types depend on the target
672///   architecture and the specific implementation of the hypervisor.
673/// - 0x20 - 0x7F: Concrete emulated device types.
674///   - 0x20 - 0x2F: Interrupt controller devices.
675///   - 0x30 - 0x3F: Reserved for future use.
676/// - 0x80 - 0xDF: Reserved for future use.
677/// - 0xE0 - 0xEF: Virtio devices.
678/// - 0xF0 - 0xFF: Reserved for future use.
679#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
680#[repr(u8)]
681pub enum EmulatedDeviceType {
682    // Special devices and abstract device types.
683    /// Dummy device type.
684    #[default]
685    Dummy               = 0x0,
686    /// Interrupt controller device, e.g. vGICv2 in aarch64, vLAPIC in x86.
687    InterruptController = 0x1,
688    /// Console (serial) device.
689    Console             = 0x2,
690    /// QEMU fw_cfg MMIO device.
691    FwCfg               = 0x3,
692    /// An emulated device that provides Inter-VM Communication (IVC) channel.
693    ///
694    /// This device is used for communication between different VMs,
695    /// the corresponding memory region of this device should be marked as `Reserved` in
696    /// device tree or ACPI table.
697    IVCChannel          = 0xA,
698
699    // Arch-specific interrupt controller devices.
700    // 0x20 - 0x22: GPPT (GIC Partial Passthrough) devices.
701    /// ARM GIC Partial Passthrough Redistributor device.
702    GPPTRedistributor   = 0x20,
703    /// ARM GIC Partial Passthrough Distributor device.
704    GPPTDistributor     = 0x21,
705    /// ARM GIC Partial Passthrough Interrupt Translation Service device.
706    GPPTITS             = 0x22,
707
708    // 0x23 - 0x24: x86 platform devices.
709    /// x86 virtual IO APIC device.
710    X86IoApic           = 0x23,
711    /// x86 virtual PIT/8254 timer device.
712    X86Pit              = 0x24,
713    /// LoongArch virtual PCH-PIC device.
714    LoongArchPchPic     = 0x25,
715
716    // 0x30: PPPT (PLIC Partial Passthrough) devices.
717    /// RISC-V PLIC Partial Passthrough Global device.
718    PPPTGlobal          = 0x30,
719
720    // Virtio devices.
721    /// Virtio block device.
722    VirtioBlk           = 0xE1,
723    /// Virtio net device.
724    VirtioNet           = 0xE2,
725    /// Virtio console device.
726    VirtioConsole       = 0xE3,
727    // Following are some other emulated devices that are not currently used and removed from the enum temporarily.
728    // /// IOMMU device.
729    // IOMMU = 0x6,
730    // /// Interrupt ICC SRE device.
731    // ICCSRE = 0x7,
732    // /// Interrupt ICC SGIR device.
733    // SGIR = 0x8,
734    // /// Interrupt controller GICR device.
735    // GICR = 0x9,
736}
737
738#[cfg(test)]
739mod tests {
740    use super::*;
741
742    struct MockPerCpu {
743        enabled: bool,
744    }
745
746    impl VmArchPerCpuOps for MockPerCpu {
747        fn new(_cpu_id: usize) -> VmBackendResult<Self> {
748            Ok(Self { enabled: false })
749        }
750
751        fn is_enabled(&self) -> bool {
752            self.enabled
753        }
754
755        fn hardware_enable(&mut self) -> VmBackendResult {
756            self.enabled = true;
757            Ok(())
758        }
759
760        fn hardware_disable(&mut self) -> VmBackendResult {
761            self.enabled = false;
762            Ok(())
763        }
764    }
765
766    #[derive(Debug, PartialEq, Eq)]
767    enum MockExit {
768        SysRegRead { reg: usize },
769    }
770
771    struct MockVcpu;
772
773    impl VmArchVcpuOps for MockVcpu {
774        type CreateConfig = ();
775        type SetupConfig = ();
776        type Exit = MockExit;
777
778        fn new(
779            _vm_id: VMId,
780            _vcpu_id: VCpuId,
781            _config: Self::CreateConfig,
782        ) -> VmBackendResult<Self> {
783            Ok(Self)
784        }
785
786        fn set_entry(&mut self, _entry: GuestPhysAddr) -> VmBackendResult {
787            Ok(())
788        }
789
790        fn set_nested_page_table(&mut self, _config: NestedPagingConfig) -> VmBackendResult {
791            Ok(())
792        }
793
794        fn setup(&mut self, _config: Self::SetupConfig) -> VmBackendResult {
795            Ok(())
796        }
797
798        fn run(&mut self) -> VmBackendResult<Self::Exit> {
799            Ok(MockExit::SysRegRead { reg: 2 })
800        }
801
802        fn bind(&mut self) -> VmBackendResult {
803            Ok(())
804        }
805
806        fn unbind(&mut self) -> VmBackendResult {
807            Ok(())
808        }
809
810        fn set_gpr(&mut self, _reg: usize, _val: usize) {}
811
812        fn inject_interrupt(&mut self, _vector: usize) -> VmBackendResult {
813            Ok(())
814        }
815
816        fn set_return_value(&mut self, _val: usize) {}
817    }
818
819    #[test]
820    fn vcpu_protocol_lives_in_axvm_types() {
821        let mut percpu = MockPerCpu::new(0).unwrap();
822        assert!(!percpu.is_enabled());
823        percpu.hardware_enable().unwrap();
824        assert!(percpu.is_enabled());
825
826        let mut vcpu = MockVcpu::new(1, 0, ()).unwrap();
827        vcpu.set_entry(GuestPhysAddr::from(0x8020_0000)).unwrap();
828        vcpu.set_nested_page_table(NestedPagingConfig::new(
829            HostPhysAddr::from(0x1000),
830            4,
831            48,
832            0,
833        ))
834        .unwrap();
835        vcpu.setup(()).unwrap();
836        assert!(matches!(
837            vcpu.run().unwrap(),
838            MockExit::SysRegRead { reg: 2 }
839        ));
840    }
841
842    #[test]
843    fn vm_exit_keeps_access_width_and_state_types() {
844        let state = VmVcpuState::Created;
845        assert_eq!(state as u8, 1);
846
847        let exit = VmExit::MmioRead {
848            addr: GuestPhysAddr::from(0x1000),
849            width: AccessWidth::Dword,
850            reg: 3,
851            reg_width: AccessWidth::Qword,
852            signed_ext: true,
853        };
854        assert!(matches!(
855            exit,
856            VmExit::MmioRead {
857                width: AccessWidth::Dword,
858                reg: 3,
859                ..
860            }
861        ));
862    }
863}
864
865impl Display for EmulatedDeviceType {
866    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
867        match self {
868            EmulatedDeviceType::Console => write!(f, "console"),
869            EmulatedDeviceType::FwCfg => write!(f, "fw_cfg"),
870            EmulatedDeviceType::InterruptController => write!(f, "interrupt controller"),
871            EmulatedDeviceType::GPPTRedistributor => {
872                write!(f, "gic partial passthrough redistributor")
873            }
874            EmulatedDeviceType::GPPTDistributor => write!(f, "gic partial passthrough distributor"),
875            EmulatedDeviceType::GPPTITS => write!(f, "gic partial passthrough its"),
876            EmulatedDeviceType::X86IoApic => write!(f, "x86 io apic"),
877            EmulatedDeviceType::X86Pit => write!(f, "x86 pit"),
878            EmulatedDeviceType::LoongArchPchPic => write!(f, "loongarch pch pic"),
879            EmulatedDeviceType::PPPTGlobal => write!(f, "plic partial passthrough global"),
880            // EmulatedDeviceType::IOMMU => write!(f, "iommu"),
881            // EmulatedDeviceType::ICCSRE => write!(f, "interrupt icc sre"),
882            // EmulatedDeviceType::SGIR => write!(f, "interrupt icc sgir"),
883            // EmulatedDeviceType::GICR => write!(f, "interrupt controller gicr"),
884            EmulatedDeviceType::IVCChannel => write!(f, "ivc channel"),
885            EmulatedDeviceType::Dummy => write!(f, "meta device"),
886            EmulatedDeviceType::VirtioBlk => write!(f, "virtio block"),
887            EmulatedDeviceType::VirtioNet => write!(f, "virtio net"),
888            EmulatedDeviceType::VirtioConsole => write!(f, "virtio console"),
889        }
890    }
891}
892
893impl EmulatedDeviceType {
894    /// All known emulated device types.
895    pub const ALL: [Self; 15] = [
896        EmulatedDeviceType::Dummy,
897        EmulatedDeviceType::InterruptController,
898        EmulatedDeviceType::Console,
899        EmulatedDeviceType::FwCfg,
900        EmulatedDeviceType::IVCChannel,
901        EmulatedDeviceType::GPPTRedistributor,
902        EmulatedDeviceType::GPPTDistributor,
903        EmulatedDeviceType::GPPTITS,
904        EmulatedDeviceType::X86IoApic,
905        EmulatedDeviceType::X86Pit,
906        EmulatedDeviceType::LoongArchPchPic,
907        EmulatedDeviceType::PPPTGlobal,
908        EmulatedDeviceType::VirtioBlk,
909        EmulatedDeviceType::VirtioNet,
910        EmulatedDeviceType::VirtioConsole,
911    ];
912
913    /// Returns all known emulated device types.
914    pub const fn all() -> &'static [Self] {
915        &Self::ALL
916    }
917
918    /// Returns true if the device is removable.
919    pub fn removable(&self) -> bool {
920        matches!(
921            *self,
922            EmulatedDeviceType::InterruptController
923                // | EmulatedDeviceType::SGIR
924                // | EmulatedDeviceType::ICCSRE
925                | EmulatedDeviceType::GPPTRedistributor
926                | EmulatedDeviceType::X86IoApic
927                | EmulatedDeviceType::X86Pit
928                | EmulatedDeviceType::VirtioBlk
929                | EmulatedDeviceType::VirtioNet
930                // | EmulatedDeviceType::GICR
931                | EmulatedDeviceType::VirtioConsole
932        )
933    }
934
935    /// Converts a `usize` value to an `EmulatedDeviceType`.
936    pub const fn from_usize(value: usize) -> Option<Self> {
937        match value {
938            0x0 => Some(EmulatedDeviceType::Dummy),
939            0x1 => Some(EmulatedDeviceType::InterruptController),
940            0x2 => Some(EmulatedDeviceType::Console),
941            0x3 => Some(EmulatedDeviceType::FwCfg),
942            0xA => Some(EmulatedDeviceType::IVCChannel),
943            0x20 => Some(EmulatedDeviceType::GPPTRedistributor),
944            0x21 => Some(EmulatedDeviceType::GPPTDistributor),
945            0x22 => Some(EmulatedDeviceType::GPPTITS),
946            0x23 => Some(EmulatedDeviceType::X86IoApic),
947            0x24 => Some(EmulatedDeviceType::X86Pit),
948            0x25 => Some(EmulatedDeviceType::LoongArchPchPic),
949            0x30 => Some(EmulatedDeviceType::PPPTGlobal),
950            0xE1 => Some(EmulatedDeviceType::VirtioBlk),
951            0xE2 => Some(EmulatedDeviceType::VirtioNet),
952            0xE3 => Some(EmulatedDeviceType::VirtioConsole),
953            // 0x6 => EmulatedDeviceType::IOMMU,
954            // 0x7 => EmulatedDeviceType::ICCSRE,
955            // 0x8 => EmulatedDeviceType::SGIR,
956            // 0x9 => EmulatedDeviceType::GICR,
957            _ => None,
958        }
959    }
960}