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}