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;
27use core::fmt::{Debug, 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/// Every architecture adapter must explicitly define where this metadata is
71/// consumed, even when its vCPU backend does not distinguish the modes.
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
426 /// Returns the guest-visible MPIDR encoded in a vCPU create config, if any.
427 fn guest_mpidr_from_create_config(_config: &Self::CreateConfig) -> Option<u64> {
428 None
429 }
430
431 /// Sets the guest entry point.
432 fn set_entry(&mut self, entry: GuestPhysAddr) -> VmBackendResult;
433 /// Sets the nested page table selected by AxVM.
434 fn set_nested_page_table(&mut self, config: NestedPagingConfig) -> VmBackendResult;
435 /// Completes architecture-specific setup.
436 fn setup(&mut self, config: Self::SetupConfig) -> VmBackendResult;
437 /// Runs the vCPU until an architecture-specific VM exit.
438 fn run(&mut self) -> VmBackendResult<Self::Exit>;
439 /// Binds the vCPU to the current physical CPU.
440 fn bind(&mut self) -> VmBackendResult;
441 /// Unbinds the vCPU from the current physical CPU.
442 fn unbind(&mut self) -> VmBackendResult;
443 /// Sets a general-purpose register.
444 fn set_gpr(&mut self, reg: usize, val: usize);
445 /// Decodes an architecture-specific memory fault as a legacy normalized
446 /// MMIO event when possible.
447 ///
448 /// This is kept as a transition helper for backends that still route
449 /// device faults through [`VmExit`]. New raw vCPU exits should use
450 /// [`Self::Exit`] and be handled in the architecture-local AxVM adapter.
451 fn decode_mmio_fault(
452 &mut self,
453 _fault_addr: GuestPhysAddr,
454 _access_flags: MappingFlags,
455 ) -> Option<VmExit> {
456 None
457 }
458 /// Injects an interrupt into the vCPU.
459 fn inject_interrupt(&mut self, vector: usize) -> VmBackendResult;
460 /// Injects an interrupt with trigger-mode metadata.
461 ///
462 /// The compatibility default delegates edge-triggered interrupts to
463 /// [`Self::inject_interrupt`]. Backends must override this method to
464 /// support level-triggered injection.
465 fn inject_interrupt_with_trigger(
466 &mut self,
467 vector: usize,
468 trigger: InterruptTriggerMode,
469 ) -> VmBackendResult {
470 match trigger {
471 InterruptTriggerMode::EdgeTriggered => self.inject_interrupt(vector),
472 InterruptTriggerMode::LevelTriggered => Err(VmBackendError::Unsupported),
473 }
474 }
475 /// Processes a guest EOI and returns an external EOI vector when needed.
476 fn handle_eoi(&mut self) -> Option<u8> {
477 None
478 }
479 /// Sets the guest return value.
480 fn set_return_value(&mut self, val: usize);
481}
482
483/// Architecture-specific per-CPU virtualization state consumed by AxVM.
484pub trait VmArchPerCpuOps: Sized {
485 /// Creates a new per-CPU state.
486 fn new(cpu_id: usize) -> VmBackendResult<Self>;
487 /// Whether virtualization is enabled on the current CPU.
488 fn is_enabled(&self) -> bool;
489 /// Enables virtualization on the current CPU.
490 fn hardware_enable(&mut self) -> VmBackendResult;
491 /// Disables virtualization on the current CPU.
492 fn hardware_disable(&mut self) -> VmBackendResult;
493 /// Returns the max guest page table levels supported by this architecture.
494 fn max_guest_page_table_levels(&self) -> usize {
495 4
496 }
497 /// Returns the guest physical address width supported by this CPU.
498 fn guest_phys_addr_bits(&self) -> usize {
499 match self.max_guest_page_table_levels() {
500 0..=3 => 39,
501 _ => 48,
502 }
503 }
504 /// Returns the architectural counter frequency recorded on this CPU.
505 ///
506 /// Architectures without an ARM-style shared counter return `None`.
507 fn timer_frequency_hz(&self) -> Option<u64> {
508 None
509 }
510}
511
512/// Execution state of an AxVM-owned vCPU wrapper.
513#[derive(Clone, Copy, Debug, PartialEq, Eq)]
514pub enum VmVcpuState {
515 /// Invalid state.
516 Invalid = 0,
517 /// Initial state after vCPU creation.
518 Created = 1,
519 /// vCPU is initialized and free.
520 Free = 2,
521 /// vCPU is bound and ready to run.
522 Ready = 3,
523 /// vCPU is currently running.
524 Running = 4,
525 /// vCPU is blocked.
526 Blocked = 5,
527 /// vCPU is reserved by PSCI CPU_ON and not yet runnable.
528 Starting = 6,
529}
530
531/// A part of `AxVMConfig`, which represents guest VM type.
532#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
533pub enum VMType {
534 /// Host VM, used for boot from Linux like Jailhouse do, named "type1.5".
535 VMTHostVM = 0,
536 /// Guest RTOS, generally a simple guest OS with most of the resource passthrough.
537 #[default]
538 VMTRTOS = 1,
539 /// Guest Linux, generally a full-featured guest OS with complicated device emulation requirements.
540 VMTLinux = 2,
541}
542
543impl From<usize> for VMType {
544 fn from(value: usize) -> Self {
545 match value {
546 0 => Self::VMTHostVM,
547 1 => Self::VMTRTOS,
548 2 => Self::VMTLinux,
549 _ => Self::default(),
550 }
551 }
552}
553
554impl From<VMType> for usize {
555 fn from(value: VMType) -> Self {
556 value as usize
557 }
558}
559
560/// Guest physical address space population policy.
561#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
562pub enum AddressSpacePolicy {
563 /// Start from an empty guest physical address space and map only explicit
564 /// guest memory, boot-description regions, and explicitly configured
565 /// passthrough resources.
566 #[default]
567 Virtualized,
568 /// Start from a host-physical identity passthrough address space, then
569 /// punch holes for guest memory, boot-description regions, emulated
570 /// devices, and reserved ranges.
571 Passthrough,
572}
573
574/// The type of memory mapping used for VM memory regions.
575#[derive(Debug, Default, Clone, PartialEq, Eq)]
576#[repr(u8)]
577pub enum VmMemMappingType {
578 /// The memory region is allocated by the VM monitor.
579 #[default]
580 MapAlloc = 0,
581 /// The memory region is identical to the host physical memory region.
582 MapIdentical = 1,
583 /// The memory region is reserved memory for the guest OS.
584 MapReserved = 2,
585}
586
587/// Configuration for a virtual machine memory region.
588#[derive(Debug, Default, Clone)]
589pub struct VmMemConfig {
590 /// The start address of the memory region in GPA (Guest Physical Address).
591 pub gpa: usize,
592 /// The size of the memory region in bytes.
593 pub size: usize,
594 /// The mappings flags of the memory region.
595 pub flags: usize,
596 /// The type of memory mapping.
597 pub map_type: VmMemMappingType,
598}
599
600/// One host-firmware device assignment normalized before VM planning.
601#[derive(Debug, Default, Clone, PartialEq)]
602pub struct HostDeviceAssignment {
603 /// Stable firmware path or architecture-owned assignment name.
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}
612
613/// One architecture-owned host address assignment without a firmware node.
614#[derive(Debug, Default, Clone, PartialEq)]
615pub struct HostAddressAssignment {
616 /// The base GPA (Guest Physical Address).
617 pub base_gpa: usize,
618 /// The address length.
619 pub length: usize,
620}
621
622/// A guest physical address range reserved from default passthrough mapping.
623#[derive(Debug, Default, Clone, PartialEq, Eq)]
624pub struct ReservedAddressConfig {
625 /// The base GPA (Guest Physical Address).
626 pub base_gpa: usize,
627 /// The address length.
628 pub length: usize,
629}
630
631/// One architecture-owned host I/O port assignment.
632#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
633pub struct HostPortAssignment {
634 /// The first host I/O port number.
635 pub base: u16,
636 /// The number of ports in this range.
637 pub length: u16,
638}
639
640/// Describes how a guest VM should enter its boot image.
641#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
642pub enum VMBootProtocol {
643 /// Enter the configured kernel entry directly without a firmware image.
644 #[default]
645 Direct,
646 /// Use the legacy x86 axvm-bios/multiboot trampoline.
647 Multiboot,
648 /// Load an external UEFI firmware image and enter it without multiboot patching.
649 Uefi,
650}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655
656 struct MockPerCpu {
657 enabled: bool,
658 }
659
660 impl VmArchPerCpuOps for MockPerCpu {
661 fn new(_cpu_id: usize) -> VmBackendResult<Self> {
662 Ok(Self { enabled: false })
663 }
664
665 fn is_enabled(&self) -> bool {
666 self.enabled
667 }
668
669 fn hardware_enable(&mut self) -> VmBackendResult {
670 self.enabled = true;
671 Ok(())
672 }
673
674 fn hardware_disable(&mut self) -> VmBackendResult {
675 self.enabled = false;
676 Ok(())
677 }
678 }
679
680 #[derive(Debug, PartialEq, Eq)]
681 enum MockExit {
682 SysRegRead { reg: usize },
683 }
684
685 struct MockVcpu;
686
687 impl VmArchVcpuOps for MockVcpu {
688 type CreateConfig = ();
689 type SetupConfig = ();
690 type Exit = MockExit;
691
692 fn new(
693 _vm_id: VMId,
694 _vcpu_id: VCpuId,
695 _config: Self::CreateConfig,
696 ) -> VmBackendResult<Self> {
697 Ok(Self)
698 }
699
700 fn set_entry(&mut self, _entry: GuestPhysAddr) -> VmBackendResult {
701 Ok(())
702 }
703
704 fn set_nested_page_table(&mut self, _config: NestedPagingConfig) -> VmBackendResult {
705 Ok(())
706 }
707
708 fn setup(&mut self, _config: Self::SetupConfig) -> VmBackendResult {
709 Ok(())
710 }
711
712 fn run(&mut self) -> VmBackendResult<Self::Exit> {
713 Ok(MockExit::SysRegRead { reg: 2 })
714 }
715
716 fn bind(&mut self) -> VmBackendResult {
717 Ok(())
718 }
719
720 fn unbind(&mut self) -> VmBackendResult {
721 Ok(())
722 }
723
724 fn set_gpr(&mut self, _reg: usize, _val: usize) {}
725
726 fn inject_interrupt(&mut self, _vector: usize) -> VmBackendResult {
727 Ok(())
728 }
729
730 fn inject_interrupt_with_trigger(
731 &mut self,
732 _vector: usize,
733 _trigger: InterruptTriggerMode,
734 ) -> VmBackendResult {
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(NestedPagingConfig::new(
751 HostPhysAddr::from(0x1000),
752 4,
753 48,
754 0,
755 ))
756 .unwrap();
757 vcpu.setup(()).unwrap();
758 assert!(matches!(
759 vcpu.run().unwrap(),
760 MockExit::SysRegRead { reg: 2 }
761 ));
762 }
763
764 #[test]
765 fn vm_exit_keeps_access_width_and_state_types() {
766 let state = VmVcpuState::Created;
767 assert_eq!(state as u8, 1);
768
769 let exit = VmExit::MmioRead {
770 addr: GuestPhysAddr::from(0x1000),
771 width: AccessWidth::Dword,
772 reg: 3,
773 reg_width: AccessWidth::Qword,
774 signed_ext: true,
775 };
776 assert!(matches!(
777 exit,
778 VmExit::MmioRead {
779 width: AccessWidth::Dword,
780 reg: 3,
781 ..
782 }
783 ));
784 }
785}