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