Skip to main content

x86defs/
vmx.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Intel VMX specific definitions.
5
6// TODO: move VMX defs somewhere?
7
8use crate::ApicRegisterValue;
9use bitfield_struct::bitfield;
10use open_enum::open_enum;
11use static_assertions::const_assert_eq;
12use zerocopy::FromBytes;
13use zerocopy::Immutable;
14use zerocopy::IntoBytes;
15use zerocopy::KnownLayout;
16
17open_enum! {
18    /// VMX basic exit reason
19    pub enum VmxExitBasic: u16 {
20        EXCEPTION = 0x0,
21        HW_INTERRUPT = 0x1,
22        TRIPLE_FAULT = 0x2,
23        SMI_INTR = 0x6,
24        INTERRUPT_WINDOW = 0x7,
25        NMI_WINDOW = 0x8,
26        PAUSE_INSTRUCTION = 0x28,
27        CPUID = 0xA,
28        HLT_INSTRUCTION = 0xC,
29        VMCALL_INSTRUCTION = 0x12,
30        CR_ACCESS = 0x1C,
31        IO_INSTRUCTION = 0x1E,
32        MSR_READ = 0x1F,
33        MSR_WRITE = 0x20,
34        BAD_GUEST_STATE = 0x21,
35        TPR_BELOW_THRESHOLD = 0x2B,
36        GDTR_OR_IDTR = 0x2E,
37        LDTR_OR_TR = 0x2F,
38        EPT_VIOLATION = 0x30,
39        TIMER_EXPIRED = 0x34,
40        WBINVD_INSTRUCTION = 0x36,
41        XSETBV = 0x37,
42        TDCALL = 0x4D,
43    }
44}
45
46impl VmxExitBasic {
47    pub(crate) const fn from_bits(value: u64) -> Self {
48        Self(value as u16)
49    }
50
51    pub(crate) const fn into_bits(self) -> u64 {
52        self.0 as u64
53    }
54}
55
56/// VMX exit reason
57#[bitfield(u32)]
58#[derive(PartialEq, Eq)]
59pub struct VmxExit {
60    #[bits(16)]
61    pub basic_reason: VmxExitBasic,
62    #[bits(10)]
63    rsvd_0: u64,
64    #[bits(1)]
65    pub bus_lock_preempted: bool,
66    #[bits(1)]
67    pub enclave_interruption: bool,
68    #[bits(1)]
69    pub pending_mtf: bool,
70    #[bits(2)]
71    rsvd_1: u8,
72    #[bits(1)]
73    pub vm_enter_failed: bool,
74}
75
76pub const VMX_ENTRY_CONTROL_LONG_MODE_GUEST: u32 = 0x00000200;
77
78/// IA32_FEATURE_CONTROL MSR (0x3A) layout.
79#[bitfield(u64)]
80pub struct Ia32FeatureControl {
81    /// Lock bit. Once set, further writes to IA32_FEATURE_CONTROL are ignored
82    /// until the next reset.
83    pub locked: bool,
84    /// Enable VMX inside SMX operation.
85    pub vmx_enabled_inside_smx: bool,
86    /// Enable VMX outside SMX operation.
87    pub vmx_enabled_outside_smx: bool,
88    #[bits(5)]
89    _reserved0: u64,
90    /// SENTER local function enables (bits 8..14).
91    #[bits(7)]
92    pub senter_local_enables: u8,
93    /// SENTER global enable.
94    pub senter_global_enable: bool,
95    #[bits(1)]
96    _reserved1: u64,
97    /// SGX launch control enable.
98    pub sgx_launch_control_enable: bool,
99    /// SGX global enable.
100    pub sgx_global_enable: bool,
101    #[bits(1)]
102    _reserved2: u64,
103    /// LMCE (Local Machine Check Exception) on.
104    pub lmce_on: bool,
105    #[bits(43)]
106    _reserved3: u64,
107}
108
109#[repr(u32)]
110#[derive(Debug, PartialEq, Eq)]
111pub enum FieldWidth {
112    Width16 = 0,
113    Width32 = 2,
114    Width64 = 1,
115    WidthNatural = 3, // 32 on X86, 64 on X64.
116}
117
118impl FieldWidth {
119    const fn from_bits(value: u32) -> Self {
120        match value {
121            0 => FieldWidth::Width16,
122            2 => FieldWidth::Width32,
123            1 => FieldWidth::Width64,
124            3 => FieldWidth::WidthNatural,
125            _ => panic!("Invalid field width"),
126        }
127    }
128
129    const fn into_bits(self) -> u32 {
130        self as u32
131    }
132}
133
134#[bitfield(u32)]
135pub struct VmcsField {
136    #[bits(1)]
137    pub access_high: u32,
138    #[bits(9)]
139    pub index: u32,
140    #[bits(2)]
141    pub typ: u32,
142    #[bits(1)]
143    pub reserved: u32,
144    #[bits(2)]
145    pub field_width: FieldWidth,
146    #[bits(17)]
147    pub reserved2: u32,
148}
149
150impl VmcsField {
151    pub const VMX_VMCS_ENTRY_CONTROLS: Self = Self(0x00004012);
152
153    pub const VMX_VMCS_GUEST_CR0: Self = Self(0x00006800);
154    pub const VMX_VMCS_GUEST_CR3: Self = Self(0x00006802);
155    pub const VMX_VMCS_GUEST_CR4: Self = Self(0x00006804);
156    pub const VMX_VMCS_GUEST_DR7: Self = Self(0x0000681A);
157
158    pub const VMX_VMCS_GUEST_ES_SELECTOR: Self = Self(0x00000800);
159    pub const VMX_VMCS_GUEST_ES_BASE: Self = Self(0x00006806);
160    pub const VMX_VMCS_GUEST_ES_LIMIT: Self = Self(0x00004800);
161    pub const VMX_VMCS_GUEST_ES_AR: Self = Self(0x00004814);
162
163    pub const VMX_VMCS_GUEST_CS_SELECTOR: Self = Self(0x00000802);
164    pub const VMX_VMCS_GUEST_CS_BASE: Self = Self(0x00006808);
165    pub const VMX_VMCS_GUEST_CS_LIMIT: Self = Self(0x00004802);
166    pub const VMX_VMCS_GUEST_CS_AR: Self = Self(0x00004816);
167
168    pub const VMX_VMCS_GUEST_SS_SELECTOR: Self = Self(0x00000804);
169    pub const VMX_VMCS_GUEST_SS_BASE: Self = Self(0x0000680A);
170    pub const VMX_VMCS_GUEST_SS_LIMIT: Self = Self(0x00004804);
171    pub const VMX_VMCS_GUEST_SS_AR: Self = Self(0x00004818);
172
173    pub const VMX_VMCS_GUEST_DS_SELECTOR: Self = Self(0x00000806);
174    pub const VMX_VMCS_GUEST_DS_BASE: Self = Self(0x0000680C);
175    pub const VMX_VMCS_GUEST_DS_LIMIT: Self = Self(0x00004806);
176    pub const VMX_VMCS_GUEST_DS_AR: Self = Self(0x0000481A);
177
178    pub const VMX_VMCS_GUEST_FS_SELECTOR: Self = Self(0x00000808);
179    pub const VMX_VMCS_GUEST_FS_BASE: Self = Self(0x0000680E);
180    pub const VMX_VMCS_GUEST_FS_LIMIT: Self = Self(0x00004808);
181    pub const VMX_VMCS_GUEST_FS_AR: Self = Self(0x0000481C);
182
183    pub const VMX_VMCS_GUEST_GS_SELECTOR: Self = Self(0x0000080A);
184    pub const VMX_VMCS_GUEST_GS_BASE: Self = Self(0x00006810);
185    pub const VMX_VMCS_GUEST_GS_LIMIT: Self = Self(0x0000480A);
186    pub const VMX_VMCS_GUEST_GS_AR: Self = Self(0x0000481E);
187
188    pub const VMX_VMCS_GUEST_LDTR_SELECTOR: Self = Self(0x0000080C);
189    pub const VMX_VMCS_GUEST_LDTR_BASE: Self = Self(0x00006812);
190    pub const VMX_VMCS_GUEST_LDTR_LIMIT: Self = Self(0x0000480C);
191    pub const VMX_VMCS_GUEST_LDTR_AR: Self = Self(0x00004820);
192
193    pub const VMX_VMCS_GUEST_TR_SELECTOR: Self = Self(0x0000080E);
194    pub const VMX_VMCS_GUEST_TR_BASE: Self = Self(0x00006814);
195    pub const VMX_VMCS_GUEST_TR_LIMIT: Self = Self(0x0000480E);
196    pub const VMX_VMCS_GUEST_TR_AR: Self = Self(0x00004822);
197
198    pub const VMX_VMCS_GUEST_GDTR_BASE: Self = Self(0x00006816);
199    pub const VMX_VMCS_GUEST_GDTR_LIMIT: Self = Self(0x00004810);
200
201    pub const VMX_VMCS_GUEST_IDTR_BASE: Self = Self(0x00006818);
202    pub const VMX_VMCS_GUEST_IDTR_LIMIT: Self = Self(0x00004812);
203
204    pub const VMX_VMCS_GUEST_PAT: Self = Self(0x00002804);
205    pub const VMX_VMCS_GUEST_EFER: Self = Self(0x00002806);
206
207    pub const VMX_VMCS_VIRTUAL_APIC_PAGE: Self = Self(0x00002012);
208
209    pub const VMX_VMCS_EOI_EXIT_0: Self = Self(0x0000201C);
210    pub const VMX_VMCS_EOI_EXIT_1: Self = Self(0x0000201E);
211    pub const VMX_VMCS_EOI_EXIT_2: Self = Self(0x00002020);
212    pub const VMX_VMCS_EOI_EXIT_3: Self = Self(0x00002022);
213
214    pub const VMX_VMCS_PROCESSOR_CONTROLS: Self = Self(0x00004002);
215    pub const VMX_VMCS_EXCEPTION_BITMAP: Self = Self(0x00004004);
216    pub const VMX_VMCS_ENTRY_INTERRUPT_INFO: Self = Self(0x00004016);
217    pub const VMX_VMCS_ENTRY_EXCEPTION_ERROR_CODE: Self = Self(0x00004018);
218    pub const VMX_VMCS_ENTRY_INSTRUCTION_LENGTH: Self = Self(0x0000401A);
219    pub const VMX_VMCS_TPR_THRESHOLD: Self = Self(0x0000401C);
220    pub const VMX_VMCS_SECONDARY_PROCESSOR_CONTROLS: Self = Self(0x0000401E);
221
222    pub const VMX_VMCS_CR0_GUEST_HOST_MASK: Self = Self(0x00006000);
223    pub const VMX_VMCS_CR4_GUEST_HOST_MASK: Self = Self(0x00006002);
224    pub const VMX_VMCS_CR0_READ_SHADOW: Self = Self(0x00006004);
225    pub const VMX_VMCS_CR4_READ_SHADOW: Self = Self(0x00006006);
226
227    pub const VMX_VMCS_GUEST_INTERRUPTIBILITY: Self = Self(0x00004824);
228
229    pub const VMX_VMCS_GUEST_SYSENTER_CS_MSR: Self = Self(0x0000482A);
230    pub const VMX_VMCS_GUEST_SYSENTER_ESP_MSR: Self = Self(0x00006824);
231    pub const VMX_VMCS_GUEST_SYSENTER_EIP_MSR: Self = Self(0x00006826);
232}
233
234#[bitfield(u32)]
235pub struct VmxSegmentAttributes {
236    #[bits(4)]
237    pub typ: u32,
238    #[bits(1)]
239    pub user: u32,
240    #[bits(2)]
241    pub dpl: u32,
242    pub present: bool,
243    #[bits(4)]
244    pub reserved1: u32,
245    pub available: bool,
246    #[bits(1)]
247    pub long_mode: u32,
248    #[bits(1)]
249    pub default_size: u32,
250    #[bits(1)]
251    pub granularity: u32,
252    pub null: bool,
253    #[bits(15)]
254    pub reserved: u32,
255}
256
257pub const IO_SIZE_8_BIT: u8 = 0;
258pub const IO_SIZE_16_BIT: u8 = 1;
259pub const IO_SIZE_32_BIT: u8 = 3;
260
261#[bitfield(u32)]
262pub struct ExitQualificationIo {
263    #[bits(2)]
264    pub access_size: u8,
265    #[bits(1)]
266    pub reserved1: u8,
267    pub is_in: bool,
268    pub is_string: bool,
269    pub rep_prefix: bool,
270    pub immediate_operand: bool,
271    #[bits(9)]
272    pub reserved2: u32,
273    pub port: u16,
274}
275
276#[bitfield(u64)]
277pub struct VmxEptExitQualification {
278    #[bits(3)]
279    pub access_mask: u8,
280    #[bits(4)]
281    pub ept_access_mask: u8,
282    pub gva_valid: bool,
283    pub caused_by_gpa_access: bool,
284    pub gva_user: bool,
285    pub gva_read_write: bool,
286    pub gva_no_execute: bool,
287    pub nmi_unmasking_due_to_iret: bool,
288    pub shadow_stack: bool,
289    pub ept_supervisor_shadow_stack: bool,
290    #[bits(49)]
291    pub reserved: u64,
292}
293
294#[bitfield(u64)]
295pub struct CrAccessQualification {
296    #[bits(4)]
297    pub cr: u8,
298    #[bits(2)]
299    pub access_type: u8,
300    pub lmsw_is_memory: bool,
301    #[bits(1)]
302    _reserved1: u8,
303    #[bits(4)]
304    pub gp_register: u8,
305    #[bits(4)]
306    _reserved2: u8,
307    pub lmsw_source_data: u16,
308    _reserved3: u32,
309}
310
311pub const CR_ACCESS_TYPE_MOV_TO_CR: u8 = 0;
312pub const CR_ACCESS_TYPE_MOV_FROM_CR: u8 = 1;
313pub const CR_ACCESS_TYPE_CLTS: u8 = 2;
314pub const CR_ACCESS_TYPE_LMSW: u8 = 3;
315
316#[bitfield(u32)]
317pub struct InterruptionInformation {
318    #[bits(8)]
319    pub vector: u8,
320    #[bits(3)]
321    pub interruption_type: u8,
322    pub deliver_error_code: bool,
323    #[bits(19)]
324    pub reserved: u32,
325    pub valid: bool,
326}
327
328#[bitfield(u32)]
329pub struct GdtrOrIdtrInstructionInfo {
330    #[bits(2)]
331    pub scaling: u8,
332    #[bits(5)]
333    _reserved1: u8,
334    #[bits(3)]
335    pub address_size: u8,
336    _reserved2: bool,
337    pub operand_size: bool,
338    #[bits(3)]
339    _reserved3: u8,
340    #[bits(3)]
341    pub segment_register: u8,
342    #[bits(4)]
343    pub index_register: u8,
344    pub index_register_invalid: bool,
345    #[bits(4)]
346    pub base_register: u8,
347    pub base_register_invalid: bool,
348    #[bits(2)]
349    pub instruction: GdtrOrIdtrInstruction,
350    #[bits(2)]
351    _reserved4: u8,
352}
353
354#[derive(Copy, Clone, Debug, PartialEq)]
355pub enum GdtrOrIdtrInstruction {
356    Sgdt = 0,
357    Sidt = 1,
358    Lgdt = 2,
359    Lidt = 3,
360}
361
362impl GdtrOrIdtrInstruction {
363    const fn from_bits(value: u8) -> Self {
364        match value {
365            0 => GdtrOrIdtrInstruction::Sgdt,
366            1 => GdtrOrIdtrInstruction::Sidt,
367            2 => GdtrOrIdtrInstruction::Lgdt,
368            3 => GdtrOrIdtrInstruction::Lidt,
369            _ => unreachable!(),
370        }
371    }
372
373    const fn into_bits(self) -> u8 {
374        self as u8
375    }
376
377    pub const fn is_load(self) -> bool {
378        match self {
379            GdtrOrIdtrInstruction::Lgdt | GdtrOrIdtrInstruction::Lidt => true,
380            GdtrOrIdtrInstruction::Sgdt | GdtrOrIdtrInstruction::Sidt => false,
381        }
382    }
383}
384
385#[bitfield(u32)]
386pub struct LdtrOrTrInstructionInfo {
387    #[bits(2)]
388    pub scaling: u8,
389    _reserved1: bool,
390    #[bits(4)]
391    pub register_1: u8,
392    #[bits(3)]
393    pub address_size: u8,
394    /// 0 - Memory, 1 - Register
395    pub memory_or_register: bool,
396    #[bits(4)]
397    _reserved2: u8,
398    #[bits(3)]
399    pub segment_register: u8,
400    #[bits(4)]
401    pub index_register: u8,
402    pub index_register_invalid: bool,
403    #[bits(4)]
404    pub base_register: u8,
405    pub base_register_invalid: bool,
406    #[bits(2)]
407    pub instruction: LdtrOrTrInstruction,
408    #[bits(2)]
409    _reserved4: u8,
410}
411
412#[derive(Copy, Clone, Debug, PartialEq)]
413pub enum LdtrOrTrInstruction {
414    Sldt = 0,
415    Str = 1,
416    Lldt = 2,
417    Ltr = 3,
418}
419
420impl LdtrOrTrInstruction {
421    const fn from_bits(value: u8) -> Self {
422        match value {
423            0 => LdtrOrTrInstruction::Sldt,
424            1 => LdtrOrTrInstruction::Str,
425            2 => LdtrOrTrInstruction::Lldt,
426            3 => LdtrOrTrInstruction::Ltr,
427            _ => unreachable!(),
428        }
429    }
430
431    const fn into_bits(self) -> u8 {
432        self as u8
433    }
434
435    pub const fn is_load(self) -> bool {
436        match self {
437            LdtrOrTrInstruction::Lldt | LdtrOrTrInstruction::Ltr => true,
438            LdtrOrTrInstruction::Sldt | LdtrOrTrInstruction::Str => false,
439        }
440    }
441}
442
443pub const INTERRUPT_TYPE_EXTERNAL: u8 = 0;
444pub const INTERRUPT_TYPE_NMI: u8 = 2;
445pub const INTERRUPT_TYPE_HARDWARE_EXCEPTION: u8 = 3;
446pub const INTERRUPT_TYPE_SOFTWARE_INTERRUPT: u8 = 4;
447pub const INTERRUPT_TYPE_PRIVILEGED_SOFTWARE_INTERRUPT: u8 = 5;
448pub const INTERRUPT_TYPE_SOFTWARE_EXCEPTION: u8 = 6;
449
450#[bitfield(u32)]
451pub struct Interruptibility {
452    pub blocked_by_sti: bool,
453    pub blocked_by_movss: bool,
454    pub blocked_by_smi: bool,
455    pub blocked_by_nmi: bool,
456    #[bits(28)]
457    _reserved: u32,
458}
459
460#[bitfield(u32)]
461#[derive(PartialEq, Eq)]
462pub struct ProcessorControls {
463    #[bits(2)]
464    _reserved: u32,
465    pub interrupt_window_exiting: bool,
466    pub use_tsc_offsetting: bool,
467    #[bits(3)]
468    _reserved2: u32,
469    pub hlt_exiting: bool,
470    _reserved3: bool,
471    pub invlpg_exiting: bool,
472    pub mwait_exiting: bool,
473    pub rdpmc_exiting: bool,
474    pub rdtsc_exiting: bool,
475    #[bits(2)]
476    _reserved4: u32,
477    pub cr3_load_exiting: bool,
478    pub cr3_store_exiting: bool,
479    pub activate_tertiary_controls: bool,
480    _reserved5: bool,
481    pub cr8_load_exiting: bool,
482    pub cr8_store_exiting: bool,
483    pub use_tpr_shadow: bool,
484    pub nmi_window_exiting: bool,
485    pub mov_dr_exiting: bool,
486    pub unconditional_io_exiting: bool,
487    pub use_io_bitmaps: bool,
488    _reserved6: bool,
489    pub monitor_trap_flag: bool,
490    pub use_msr_bitmaps: bool,
491    pub monitor_exiting: bool,
492    pub pause_exiting: bool,
493    pub activate_secondary_controls: bool,
494}
495
496#[bitfield(u32)]
497pub struct SecondaryProcessorControls {
498    pub virtualize_apic_accesses: bool,
499    pub enable_ept: bool,
500    pub descriptor_table_exiting: bool,
501    pub enable_rdtscp: bool,
502    pub virtualize_x2apic_mode: bool,
503    pub enable_vpid: bool,
504    pub wbinvd_exiting: bool,
505    pub unrestricted_guest: bool,
506    pub apic_register_virtualization: bool,
507    pub virtual_interrupt_delivery: bool,
508    pub pause_loop_exiting: bool,
509    pub rdrand_exiting: bool,
510    pub enable_invpcid: bool,
511    pub enable_vmfunc: bool,
512    pub vmcs_shadowing: bool,
513    pub enable_encls_exiting: bool,
514    pub rdseed_exiting: bool,
515    pub enable_pml: bool,
516    pub ept_violation_ve: bool,
517    pub conceal_vmx_from_pt: bool,
518    pub enable_xsaves_xrstors: bool,
519    pub pasid_translation: bool,
520    pub mode_based_execute_control: bool,
521    pub sub_page_write_permissions: bool,
522    pub pt_uses_guest_physical_addresses: bool,
523    pub use_tsc_scaling: bool,
524    pub enable_user_wait_and_pause: bool,
525    pub enable_pconfig: bool,
526    pub enable_enclv_exiting: bool,
527    _reserved: bool,
528    pub vmm_bus_lock_detection: bool,
529    pub instruction_timeout: bool,
530}
531
532#[repr(C)]
533#[derive(Debug, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)]
534pub struct VmxApicPage {
535    pub reserved_0: [ApicRegisterValue; 2],
536    pub id: ApicRegisterValue,
537    pub version: ApicRegisterValue,
538    pub reserved_4: [ApicRegisterValue; 4],
539    pub tpr: ApicRegisterValue,
540    pub apr: ApicRegisterValue,
541    pub ppr: ApicRegisterValue,
542    pub eoi: ApicRegisterValue,
543    pub rrd: ApicRegisterValue,
544    pub ldr: ApicRegisterValue,
545    pub dfr: ApicRegisterValue,
546    pub svr: ApicRegisterValue,
547    pub isr: [ApicRegisterValue; 8],
548    pub tmr: [ApicRegisterValue; 8],
549    pub irr: [ApicRegisterValue; 8],
550    pub esr: ApicRegisterValue,
551    pub reserved_29: [ApicRegisterValue; 6],
552    pub lvt_cmci: ApicRegisterValue,
553    pub icr: [ApicRegisterValue; 2],
554    pub lvt_timer: ApicRegisterValue,
555    pub lvt_thermal: ApicRegisterValue,
556    pub lvt_pmc: ApicRegisterValue,
557    pub lvt_lint0: ApicRegisterValue,
558    pub lvt_lint1: ApicRegisterValue,
559    pub lvt_error: ApicRegisterValue,
560    pub timer_icr: ApicRegisterValue,
561    pub timer_ccr: ApicRegisterValue,
562    pub reserved_3a: [ApicRegisterValue; 4],
563    pub timer_dcr: ApicRegisterValue,
564    pub reserved_3f: ApicRegisterValue,
565    pub reserved_40: [ApicRegisterValue; 0xc0],
566}
567
568const_assert_eq!(size_of::<VmxApicPage>(), 4096);