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}