ostd/arch/x86/vm/exit.rs
1// SPDX-License-Identifier: MPL-2.0
2
3use int_to_c_enum::TryFromInt;
4use x86::vmx::vmcs::ro;
5
6use super::vmx::vmcs::CurrentVmcs;
7use crate::{Error, prelude::*, vm::Gpaddr};
8
9/// A VM exit that needs handling by the kernel client.
10#[derive(Clone, Copy, Debug)]
11pub struct GuestExitInfo {
12 /// The basic VMX exit reason.
13 pub exit_reason: u32,
14 /// The length of the exiting instruction, or unspecified for non-instruction exits.
15 pub instruction_len: u32,
16 /// Architecture-specific exit qualification.
17 pub exit_qualification: u64,
18 /// The guest physical address for an EPT exit, or zero otherwise.
19 pub guest_phys_addr: Gpaddr,
20 /// VM-exit interruption information for an exception or NMI, or zero otherwise.
21 pub interruption_info: u32,
22 /// The exception error code, if indicated by `interruption_info`.
23 pub interruption_error_code: u32,
24 /// IDT-vectoring information for an interrupted event delivery.
25 pub idt_vectoring_info: u32,
26 /// The exception error code, if indicated by `idt_vectoring_info`.
27 pub idt_vectoring_error_code: u32,
28}
29
30/// VMX basic exit reasons.
31///
32/// See Intel SDM, Vol. 3D, Appendix C, Table C-1
33#[expect(non_camel_case_types)]
34#[repr(u32)]
35#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromInt)]
36pub enum VmxExitReason {
37 /// An exception selected by the exception bitmap, or an intercepted NMI.
38 EXCEPTION_NMI = 0,
39 /// An external interrupt with external-interrupt exiting enabled.
40 EXTERNAL_INTERRUPT = 1,
41 /// An exception during double-fault delivery that was not intercepted.
42 TRIPLE_FAULT = 2,
43 /// Receipt of an INIT signal.
44 INIT = 3,
45 /// Receipt of a startup IPI while waiting for SIPI.
46 SIPI = 4,
47 /// An SMM VM exit caused by an SMI immediately after an I/O instruction retires.
48 SMI = 5,
49 /// An SMM VM exit caused by an SMI not immediately following I/O instruction retirement.
50 OTHER_SMI = 6,
51 /// An interrupt window with `RFLAGS.IF` set, no `STI` or `MOV SS` blocking,
52 /// and interrupt-window exiting enabled.
53 INTERRUPT_WINDOW = 7,
54 /// An NMI window with no virtual-NMI or `MOV SS` blocking and NMI-window exiting enabled.
55 NMI_WINDOW = 8,
56 /// A task switch attempted by the guest.
57 TASK_SWITCH = 9,
58 /// An attempt to execute `CPUID`.
59 CPUID = 10,
60 /// An attempt to execute `GETSEC`.
61 GETSEC = 11,
62 /// An intercepted `HLT` instruction.
63 HLT = 12,
64 /// An attempt to execute `INVD`.
65 INVD = 13,
66 /// An intercepted `INVLPG` instruction.
67 INVLPG = 14,
68 /// An intercepted `RDPMC` instruction.
69 RDPMC = 15,
70 /// An intercepted `RDTSC` instruction.
71 RDTSC = 16,
72 /// An attempt to execute `RSM` in SMM.
73 RSM = 17,
74 /// A `VMCALL` from the guest or the executive monitor.
75 VMCALL = 18,
76 /// An attempt to execute `VMCLEAR`.
77 VMCLEAR = 19,
78 /// An attempt to execute `VMLAUNCH`.
79 VMLAUNCH = 20,
80 /// An attempt to execute `VMPTRLD`.
81 VMPTRLD = 21,
82 /// An attempt to execute `VMPTRST`.
83 VMPTRST = 22,
84 /// An attempt to execute `VMREAD`.
85 VMREAD = 23,
86 /// An attempt to execute `VMRESUME`.
87 VMRESUME = 24,
88 /// An attempt to execute `VMWRITE`.
89 VMWRITE = 25,
90 /// An attempt to execute `VMXOFF`.
91 VMOFF = 26,
92 /// An attempt to execute `VMXON`.
93 VMON = 27,
94 /// An intercepted access to `CR0`, `CR3`, `CR4`, or `CR8` via `CLTS`, `LMSW`, or `MOV CR`.
95 CR_ACCESS = 28,
96 /// An intercepted `MOV` to or from a debug register.
97 DR_ACCESS = 29,
98 /// An I/O instruction intercepted by I/O exiting controls or the I/O bitmaps.
99 IO_INSTRUCTION = 30,
100 /// An intercepted `RDMSR` instruction.
101 MSR_READ = 31,
102 /// An intercepted `WRMSR` or `WRMSRNS` instruction.
103 MSR_WRITE = 32,
104 /// A VM-entry failure caused by invalid guest state.
105 INVALID_GUEST_STATE = 33,
106 /// A VM-entry failure while loading guest MSRs.
107 MSR_LOAD_FAIL = 34,
108 /// An intercepted `MWAIT` instruction.
109 MWAIT_INSTRUCTION = 36,
110 /// A monitor-trap-flag exit enabled by VM controls or injected on VM entry.
111 MONITOR_TRAP_FLAG = 37,
112 /// An intercepted `MONITOR` instruction.
113 MONITOR_INSTRUCTION = 39,
114 /// An intercepted `PAUSE` instruction or a pause loop exceeding `PLE_Window`.
115 PAUSE_INSTRUCTION = 40,
116 /// A machine-check event during VM entry.
117 MCE_DURING_VMENTRY = 41,
118 /// A virtual TPR priority below the configured threshold while TPR shadowing is enabled.
119 TPR_BELOW_THRESHOLD = 43,
120 /// An intercepted memory access to the APIC-access page.
121 APIC_ACCESS = 44,
122 /// A virtual EOI for an interrupt selected by the EOI-exit bitmap.
123 VIRTUALIZED_EOI = 45,
124 /// An intercepted `LGDT`, `LIDT`, `SGDT`, or `SIDT` instruction.
125 GDTR_IDTR = 46,
126 /// An intercepted `LLDT`, `LTR`, `SLDT`, or `STR` instruction.
127 LDTR_TR = 47,
128 /// A guest physical memory access denied by the EPT paging structures.
129 EPT_VIOLATION = 48,
130 /// A guest physical memory access encountering an invalid EPT entry.
131 EPT_MISCONFIG = 49,
132 /// An attempt to execute `INVEPT`.
133 INVEPT = 50,
134 /// An intercepted `RDTSCP` instruction.
135 RDTSCP = 51,
136 /// Expiration of the VMX preemption timer.
137 PREEMPTION_TIMER = 52,
138 /// An attempt to execute `INVVPID`.
139 INVVPID = 53,
140 /// An intercepted `WBINVD` or `WBNOINVD` instruction.
141 WBINVD = 54,
142 /// An attempt to execute `XSETBV`.
143 XSETBV = 55,
144 /// A completed write to the virtual-APIC page requiring VMM emulation.
145 APIC_WRITE = 56,
146 /// An intercepted `RDRAND` instruction.
147 RDRAND = 57,
148 /// An intercepted `INVPCID` instruction.
149 INVPCID = 58,
150 /// A `VMFUNC` for a disabled function or a function-specific exit condition.
151 VMFUNC = 59,
152 /// An `ENCLS` leaf intercepted by the ENCLS-exiting bitmap.
153 ENCLS = 60,
154 /// An intercepted `RDSEED` instruction.
155 RDSEED = 61,
156 /// An attempted page-modification log update with the PML index outside `0..=511`.
157 PML_FULL = 62,
158 /// An `XSAVES` request for state selected by `IA32_XSS` and the XSS-exiting bitmap.
159 XSAVES = 63,
160 /// An `XRSTORS` request for state selected by `IA32_XSS` and the XSS-exiting bitmap.
161 XRSTORS = 64,
162 /// A `PCONFIG` leaf intercepted by the PCONFIG-exiting bitmap.
163 PCONFIG = 65,
164 /// A miss or misconfiguration while looking up sub-page write permissions.
165 SPP_EVENT = 66,
166 /// An intercepted `UMWAIT` instruction.
167 UMWAIT = 67,
168 /// An intercepted `TPAUSE` instruction.
169 TPAUSE = 68,
170 /// An intercepted `LOADIWKEY` instruction.
171 LOADIWKEY = 69,
172}
173
174/// Reads the current VMCS's exit information.
175pub(super) fn exit_info(vmcs: &CurrentVmcs<'_>) -> Result<GuestExitInfo> {
176 let raw_reason = vmcs.read(ro::EXIT_REASON)?;
177 // A late VM-entry failure reaches the VM-exit handler instead of setting
178 // CF/ZF at VMLAUNCH/VMRESUME (Intel SDM, Vol. 3C, Section 26.7).
179 if raw_reason & (1 << 31) != 0 {
180 return Err(Error::InvalidArgs);
181 }
182 let reason = (raw_reason & 0xffff) as u32;
183
184 let is_ept = reason == VmxExitReason::EPT_VIOLATION as u32
185 || reason == VmxExitReason::EPT_MISCONFIG as u32;
186 let guest_phys_addr = if is_ept {
187 vmcs.read(ro::GUEST_PHYSICAL_ADDR_FULL)?
188 } else {
189 0
190 };
191
192 const ERROR_CODE_VALID: u32 = (1 << 31) | (1 << 11);
193
194 let interruption_info = if reason == VmxExitReason::EXCEPTION_NMI as u32 {
195 vmcs.read(ro::VMEXIT_INTERRUPTION_INFO)? as u32
196 } else {
197 0
198 };
199 let interruption_error_code = if interruption_info & ERROR_CODE_VALID == ERROR_CODE_VALID {
200 vmcs.read(ro::VMEXIT_INTERRUPTION_ERR_CODE)? as u32
201 } else {
202 0
203 };
204
205 let idt_vectoring_info = vmcs.read(ro::IDT_VECTORING_INFO)? as u32;
206 let idt_vectoring_error_code = if idt_vectoring_info & ERROR_CODE_VALID == ERROR_CODE_VALID {
207 vmcs.read(ro::IDT_VECTORING_ERR_CODE)? as u32
208 } else {
209 0
210 };
211
212 Ok(GuestExitInfo {
213 exit_reason: reason,
214 instruction_len: vmcs.read(ro::VMEXIT_INSTRUCTION_LEN)? as u32,
215 exit_qualification: vmcs.read(ro::EXIT_QUALIFICATION)? as u64,
216 guest_phys_addr,
217 interruption_info,
218 interruption_error_code,
219 idt_vectoring_info,
220 idt_vectoring_error_code,
221 })
222}