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