Skip to main content

axvcpu/
exit.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
15use axaddrspace::{
16    GuestPhysAddr, MappingFlags,
17    device::{AccessWidth, Port, SysRegAddr},
18};
19
20#[allow(unused_imports)] // used in doc
21use super::AxArchVCpu;
22
23/// Reasons for VM-Exits returned by [AxArchVCpu::run].
24///
25/// When a guest virtual CPU executes, various conditions can cause control to be
26/// transferred back to the hypervisor. This enum represents all possible exit reasons
27/// that can occur during VCpu execution.
28///
29/// # VM Exit Categories
30///
31/// - **I/O Operations**: MMIO reads/writes, port I/O, system register access
32/// - **System Events**: Hypercalls, interrupts, nested page faults
33/// - **Power Management**: CPU power state changes, system shutdown
34/// - **Multiprocessing**: IPI sending, secondary CPU bring-up
35/// - **Error Conditions**: Entry failures, invalid states
36///
37/// # Compatibility Note
38///
39/// This enum draws inspiration from [kvm-ioctls](https://github.com/rust-vmm/kvm-ioctls/blob/main/src/ioctls/vcpu.rs)
40/// for consistency with existing virtualization frameworks.
41#[non_exhaustive]
42#[derive(Debug)]
43pub enum AxVCpuExitReason {
44    /// A guest instruction triggered a hypercall to the hypervisor.
45    ///
46    /// Hypercalls are a mechanism for the guest OS to request services from
47    /// the hypervisor, similar to system calls in a traditional OS.
48    Hypercall {
49        /// The hypercall number identifying the requested service
50        nr: u64,
51        /// Arguments passed to the hypercall (up to 6 parameters)
52        args: [u64; 6],
53    },
54
55    /// The guest performed a Memory-Mapped I/O (MMIO) read operation.
56    ///
57    /// MMIO reads occur when the guest accesses device registers or other
58    /// hardware-mapped memory regions that require hypervisor emulation.
59    MmioRead {
60        /// Guest physical address being read from
61        addr: GuestPhysAddr,
62        /// Width/size of the memory access (8, 16, 32, or 64 bits)
63        width: AccessWidth,
64        /// Index of the guest register that will receive the read value
65        reg: usize,
66        /// Width of the destination register  
67        reg_width: AccessWidth,
68        /// Whether to sign-extend the read value to fill the register
69        signed_ext: bool,
70    },
71
72    /// The guest performed a Memory-Mapped I/O (MMIO) write operation.
73    ///
74    /// MMIO writes occur when the guest writes to device registers or other
75    /// hardware-mapped memory regions that require hypervisor emulation.
76    MmioWrite {
77        /// Guest physical address being written to
78        addr: GuestPhysAddr,
79        /// Width/size of the memory access (8, 16, 32, or 64 bits)
80        width: AccessWidth,
81        /// Data being written to the memory location
82        data: u64,
83    },
84
85    /// The guest performed a system register read operation.
86    ///
87    /// System registers are architecture-specific control and status registers:
88    /// - **x86_64**: Model-Specific Registers (MSRs)
89    /// - **RISC-V**: Control and Status Registers (CSRs)
90    /// - **AArch64**: System registers accessible via MRS instruction
91    SysRegRead {
92        /// Address/identifier of the system register being read
93        ///
94        /// - **x86_64/RISC-V**: Direct register address
95        /// - **AArch64**: ESR_EL2.ISS format (`<op0><op2><op1><CRn>00000<CRm>0`)
96        ///   compatible with the `aarch64_sysreg` crate numbering scheme
97        addr: SysRegAddr,
98        /// Index of the guest register that will receive the read value
99        ///
100        /// **Note**: Unused on x86_64 where the result is always stored in `[edx:eax]`
101        reg: usize,
102    },
103
104    /// The guest performed a system register write operation.
105    ///
106    /// System registers are architecture-specific control and status registers:
107    /// - **x86_64**: Model-Specific Registers (MSRs)
108    /// - **RISC-V**: Control and Status Registers (CSRs)
109    /// - **AArch64**: System registers accessible via MSR instruction  
110    SysRegWrite {
111        /// Address/identifier of the system register being written
112        ///
113        /// - **x86_64/RISC-V**: Direct register address
114        /// - **AArch64**: ESR_EL2.ISS format (`<op0><op2><op1><CRn>00000<CRm>0`)
115        ///   compatible with the `aarch64_sysreg` crate numbering scheme
116        addr: SysRegAddr,
117        /// Data being written to the system register
118        value: u64,
119    },
120
121    /// The guest performed a port-based I/O read operation.
122    ///
123    /// **Architecture**: x86-specific (other architectures don't have port I/O)
124    ///
125    /// The destination register is implicitly `al`/`ax`/`eax` based on the access width.
126    IoRead {
127        /// I/O port number being read from
128        port: Port,
129        /// Width of the I/O access (8, 16, or 32 bits)
130        width: AccessWidth,
131    },
132
133    /// The guest performed a port-based I/O write operation.
134    ///
135    /// **Architecture**: x86-specific (other architectures don't have port I/O)
136    ///
137    /// The source register is implicitly `al`/`ax`/`eax` based on the access width.
138    IoWrite {
139        /// I/O port number being written to
140        port: Port,
141        /// Width of the I/O access (8, 16, or 32 bits)
142        width: AccessWidth,
143        /// Data being written to the I/O port
144        data: u64,
145    },
146
147    /// An external interrupt was delivered to the VCpu.
148    ///
149    /// This represents hardware interrupts from external devices that need
150    /// to be processed by the guest or hypervisor.
151    ///
152    /// **Note**: This enum may be extended with additional fields in the future.
153    /// Use `..` in pattern matching to ensure forward compatibility.
154    ExternalInterrupt {
155        /// Hardware interrupt vector number
156        vector: u64,
157    },
158
159    /// A nested page fault occurred during guest memory access.
160    ///
161    /// Also known as EPT violations on x86. These faults occur when:
162    /// - Guest accesses unmapped memory regions
163    /// - Access permissions are violated (e.g., writing to read-only pages)
164    /// - Page table entries need to be populated or updated
165    ///
166    /// **Note**: This enum may be extended with additional fields in the future.
167    /// Use `..` in pattern matching to ensure forward compatibility.
168    NestedPageFault {
169        /// Guest physical address that caused the fault
170        addr: GuestPhysAddr,
171        /// Type of access that was attempted (read/write/execute)
172        access_flags: MappingFlags,
173    },
174
175    /// The guest VCpu has executed a halt instruction and is now idle.
176    ///
177    /// This typically occurs when the guest OS has no work to do and is
178    /// waiting for interrupts or other events to wake it up.
179    Halt,
180
181    /// Request to bring up a secondary CPU core.
182    ///
183    /// This exit reason is used during the multi-core VM boot process when
184    /// the primary CPU requests that a secondary CPU be started. The specific
185    /// mechanism varies by architecture:
186    ///
187    /// - **ARM**: PSCI (Power State Coordination Interface) calls
188    /// - **x86**: SIPI (Startup Inter-Processor Interrupt)
189    /// - **RISC-V**: SBI (Supervisor Binary Interface) calls
190    CpuUp {
191        /// Target CPU identifier to be started
192        ///
193        /// Format varies by architecture:
194        /// - **AArch64**: MPIDR register affinity fields  
195        /// - **x86_64**: APIC ID of the target CPU
196        /// - **RISC-V**: Hart ID of the target CPU
197        target_cpu: u64,
198        /// Guest physical address where the secondary CPU should begin execution
199        entry_point: GuestPhysAddr,
200        /// Argument to pass to the secondary CPU
201        ///
202        /// - **AArch64**: Value to set in `x0` register at startup
203        /// - **RISC-V**: Value to set in `a1` register (`a0` gets the hartid)
204        /// - **x86_64**: Currently unused
205        arg: u64,
206    },
207
208    /// The guest VCpu has been powered down.
209    ///
210    /// This indicates the VCpu has executed a power-down instruction or
211    /// hypercall and should be suspended. The VCpu may be resumed later.
212    CpuDown {
213        /// Power state information (currently unused)
214        ///
215        /// Reserved for future use with PSCI_POWER_STATE or similar mechanisms
216        _state: u64,
217    },
218
219    /// The guest has requested system-wide shutdown.
220    ///
221    /// This indicates the entire virtual machine should be powered off,
222    /// not just the current VCpu.
223    SystemDown,
224
225    /// No special handling required - the VCpu handled the exit internally.
226    ///
227    /// This provides an opportunity for the hypervisor to:
228    /// - Check virtual device states
229    /// - Process pending interrupts  
230    /// - Handle background tasks
231    /// - Perform scheduling decisions
232    ///
233    /// The VCpu can typically be resumed immediately after these checks.
234    Nothing,
235
236    /// VM entry failed due to invalid VCpu state or configuration.
237    ///
238    /// This corresponds to `KVM_EXIT_FAIL_ENTRY` in KVM and indicates that
239    /// the hardware virtualization layer could not successfully enter the guest.
240    ///
241    /// The failure reason contains architecture-specific diagnostic information.
242    FailEntry {
243        /// Hardware-specific failure reason code
244        ///
245        /// Interpretation depends on the underlying virtualization technology
246        /// and CPU architecture. Consult architecture documentation for details.
247        hardware_entry_failure_reason: u64,
248    },
249
250    /// The guest is attempting to send an Inter-Processor Interrupt (IPI).
251    ///
252    /// IPIs are used for inter-CPU communication in multi-core systems.
253    /// This does **not** include Startup IPIs (SIPI), which are handled
254    /// by the [`AxVCpuExitReason::CpuUp`] variant.
255    SendIPI {
256        /// Target CPU identifier to receive the IPI
257        ///
258        /// This field is invalid if `send_to_all` or `send_to_self` is true.
259        target_cpu: u64,
260        /// Auxiliary field for complex target CPU specifications
261        ///
262        /// Currently used only on AArch64 where:
263        /// - `target_cpu` contains `Aff3.Aff2.Aff1.0`
264        /// - `target_cpu_aux` contains a bitmask for `Aff0` values
265        target_cpu_aux: u64,
266        /// Whether to broadcast the IPI to all CPUs except the sender
267        send_to_all: bool,
268        /// Whether to send the IPI to the current CPU (self-IPI)
269        send_to_self: bool,
270        /// IPI vector/interrupt number to deliver
271        vector: u64,
272    },
273}