virt/x86/
mod.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! x86-specific state.
5
6pub mod apic_software_device;
7pub mod vm;
8pub mod vp;
9
10use crate::state::StateElement;
11use inspect::Inspect;
12use mesh_protobuf::Protobuf;
13use std::fmt::Debug;
14use vm_topology::processor::ProcessorTopology;
15use vm_topology::processor::x86::ApicMode;
16use vm_topology::processor::x86::X86Topology;
17use vm_topology::processor::x86::X86VpInfo;
18use x86defs::cpuid::CpuidFunction;
19use x86defs::cpuid::SgxCpuidSubleafEax;
20use x86defs::cpuid::Vendor;
21use x86defs::xsave::XSAVE_VARIABLE_OFFSET;
22
23/// VP state that can be set for initial boot.
24#[derive(Debug, PartialEq, Eq, Protobuf)]
25pub struct X86InitialRegs {
26    /// Register state to be set on the BSP.
27    pub registers: vp::Registers,
28    /// MTRR state to be set on all processors.
29    pub mtrrs: vp::Mtrrs,
30    /// PAT state to be set on all processors.
31    pub pat: vp::Pat,
32}
33
34impl X86InitialRegs {
35    pub fn at_reset(caps: &X86PartitionCapabilities, bsp: &X86VpInfo) -> Self {
36        Self {
37            registers: vp::Registers::at_reset(caps, bsp),
38            mtrrs: vp::Mtrrs::at_reset(caps, bsp),
39            pat: vp::Pat::at_reset(caps, bsp),
40        }
41    }
42}
43
44/// Partition capabilities, used to determine which state is active on a
45/// partition and what the reset state should be.
46#[derive(Debug, Inspect)]
47pub struct X86PartitionCapabilities {
48    /// The processor vendor.
49    #[inspect(display)]
50    pub vendor: Vendor,
51    /// The MS hypervisor is available.
52    pub hv1: bool,
53    /// The reference TSC page is available.
54    pub hv1_reference_tsc_page: bool,
55    /// Xsave information.
56    pub xsave: XsaveCapabilities,
57    /// X2apic is supported.
58    pub x2apic: bool,
59    /// X2apic is enabled at boot.
60    pub x2apic_enabled: bool,
61    /// The initial value for rdx.
62    #[inspect(hex)]
63    pub reset_rdx: u64,
64    /// CET is supported.
65    pub cet: bool,
66    /// CET-SS is supported.
67    pub cet_ss: bool,
68    /// SGX is enabled.
69    pub sgx: bool,
70    /// TSC_AUX is supported
71    pub tsc_aux: bool,
72    /// The address of the virtual top of memory, for encrypted VMs.
73    ///
74    /// This is computed from the Hyper-V isolation leaf. It is guaranteed to be
75    /// a power of 2, if present.
76    #[inspect(hex)]
77    pub vtom: Option<u64>,
78    /// The physical address width of the CPU, as reported by CPUID.
79    pub physical_address_width: u8,
80
81    /// The hypervisor can freeze time across state manipulation.
82    pub can_freeze_time: bool,
83    /// The hypervisor has a broken implementation querying xsave state, where
84    /// supervisor states are not correctly set in xstate_bv.
85    pub xsaves_state_bv_broken: bool,
86    /// The hypervisor has a broken implementation setting dr6, where bit 16 is
87    /// forced on even if the processor supports TSX.
88    pub dr6_tsx_broken: bool,
89    /// EFER.NXE is forced on. This is set for TDX 1.5 partitions, which require
90    /// this.
91    pub nxe_forced_on: bool,
92}
93
94impl X86PartitionCapabilities {
95    pub fn from_cpuid(
96        processor_topology: &ProcessorTopology<X86Topology>,
97        f: &mut dyn FnMut(u32, u32) -> [u32; 4],
98    ) -> Self {
99        let mut this = Self {
100            vendor: Vendor([0; 12]),
101            hv1: false,
102            hv1_reference_tsc_page: false,
103            xsave: XsaveCapabilities {
104                features: 0,
105                supervisor_features: 0,
106                standard_len: XSAVE_VARIABLE_OFFSET as u32,
107                compact_len: XSAVE_VARIABLE_OFFSET as u32,
108                feature_info: [Default::default(); 63],
109            },
110            x2apic: false,
111            x2apic_enabled: false,
112            reset_rdx: 0,
113            cet: false,
114            cet_ss: false,
115            sgx: false,
116            tsc_aux: false,
117            vtom: None,
118            physical_address_width: max_physical_address_size_from_cpuid(&mut *f),
119            can_freeze_time: false,
120            xsaves_state_bv_broken: false,
121            dr6_tsx_broken: false,
122            nxe_forced_on: false,
123        };
124
125        let max_function = {
126            let [eax, ebx, ecx, edx] = f(CpuidFunction::VendorAndMaxFunction.0, 0);
127            this.vendor = Vendor::from_ebx_ecx_edx(ebx, ecx, edx);
128            eax
129        };
130
131        let mut hypervisor = false;
132        let mut xsave = false;
133        if max_function >= CpuidFunction::VersionAndFeatures.0 {
134            let result = f(CpuidFunction::VersionAndFeatures.0, 0);
135            this.reset_rdx = result[0].into();
136            let features = result[2] as u64 | ((result[3] as u64) << 32);
137            this.x2apic = features & (1 << 21) != 0;
138            xsave = features & (1 << 26) != 0;
139            hypervisor = features & (1 << 31) != 0;
140        }
141
142        let extended_features = if max_function >= CpuidFunction::ExtendedFeatures.0 {
143            f(CpuidFunction::ExtendedFeatures.0, 0)
144        } else {
145            Default::default()
146        };
147
148        if max_function >= CpuidFunction::ExtendedFeatures.0 {
149            if extended_features[2] & (1 << 7) != 0 {
150                this.cet = true;
151                this.cet_ss = true;
152            }
153            if extended_features[3] & (1 << 20) != 0 {
154                this.cet = true;
155            }
156        }
157
158        if max_function >= CpuidFunction::SgxEnumeration.0 {
159            let sgx_result: SgxCpuidSubleafEax =
160                SgxCpuidSubleafEax::from(f(CpuidFunction::SgxEnumeration.0, 2)[0]);
161            if sgx_result.sgx_type() != 0 {
162                this.sgx = true;
163            }
164        }
165
166        if xsave {
167            let result = f(CpuidFunction::ExtendedStateEnumeration.0, 0);
168            this.xsave.features = result[0] as u64 | ((result[3] as u64) << 32);
169            let standard_len = result[2];
170
171            let result = f(CpuidFunction::ExtendedStateEnumeration.0, 1);
172            this.xsave.supervisor_features = result[2] as u64 | ((result[3] as u64) << 32);
173
174            let mut n = (this.xsave.features | this.xsave.supervisor_features) & !3;
175            while n != 0 {
176                let i = n.trailing_zeros();
177                n -= 1 << i;
178                let result = f(CpuidFunction::ExtendedStateEnumeration.0, i);
179                let feature = XsaveFeature {
180                    offset: result[1],
181                    len: result[0],
182                    align: result[2] & 2 != 0,
183                };
184                this.xsave.feature_info[i as usize] = feature;
185            }
186            this.xsave.compact_len = this.xsave.compact_len_for(!0);
187            this.xsave.standard_len = this.xsave.standard_len_for(!0);
188            assert!(
189                this.xsave.standard_len <= standard_len,
190                "advertised xsave length ({}) too small for features, requires ({}) bytes",
191                standard_len,
192                this.xsave.standard_len
193            );
194        }
195
196        // Hypervisor info.
197        if hypervisor {
198            let hv_max = f(hvdef::HV_CPUID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION, 0)[0];
199            if hv_max >= hvdef::HV_CPUID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION
200                && f(hvdef::HV_CPUID_FUNCTION_HV_INTERFACE, 0)[0] == u32::from_le_bytes(*b"Hv#1")
201            {
202                this.hv1 = true;
203                let result = f(hvdef::HV_CPUID_FUNCTION_MS_HV_FEATURES, 0);
204                let privs = hvdef::HvPartitionPrivilege::from(
205                    result[0] as u64 | ((result[1] as u64) << 32),
206                );
207                this.hv1_reference_tsc_page = privs.access_partition_reference_tsc();
208                if privs.isolation()
209                    && hv_max >= hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION
210                {
211                    let [eax, ebx, ecx, edx] =
212                        f(hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION, 0);
213                    let config = hvdef::HvIsolationConfiguration::from(
214                        eax as u128
215                            | ((ebx as u128) << 32)
216                            | ((ecx as u128) << 64)
217                            | ((edx as u128) << 96),
218                    );
219                    if config.shared_gpa_boundary_active() {
220                        this.vtom = Some(1 << config.shared_gpa_boundary_bits());
221                    }
222                }
223            }
224        }
225
226        match processor_topology.apic_mode() {
227            ApicMode::XApic => assert!(
228                !this.x2apic,
229                "x2apic disabled in topology, enabled in cpuid"
230            ),
231            ApicMode::X2ApicSupported => {
232                assert!(this.x2apic, "x2apic enabled in topology, disabled in cpuid")
233            }
234            ApicMode::X2ApicEnabled => {
235                assert!(this.x2apic, "x2apic enabled in topology, disabled in cpuid");
236                this.x2apic_enabled = true;
237            }
238        }
239
240        this.tsc_aux = {
241            let rdtscp = {
242                let extended_max_function = f(CpuidFunction::ExtendedMaxFunction.0, 0)[0];
243                if extended_max_function >= CpuidFunction::ExtendedVersionAndFeatures.0 {
244                    x86defs::cpuid::ExtendedVersionAndFeaturesEdx::from(
245                        f(CpuidFunction::ExtendedVersionAndFeatures.0, 0)[3],
246                    )
247                    .rdtscp()
248                } else {
249                    false
250                }
251            };
252
253            let rdpid =
254                x86defs::cpuid::ExtendedFeatureSubleaf0Ecx::from(extended_features[2]).rd_pid();
255
256            rdtscp || rdpid
257        };
258
259        this
260    }
261}
262
263#[derive(Debug, Copy, Clone, Inspect)]
264pub struct XsaveCapabilities {
265    pub features: u64,
266    pub supervisor_features: u64,
267    pub standard_len: u32,
268    pub compact_len: u32,
269    #[inspect(skip)] // TODO
270    pub feature_info: [XsaveFeature; 63],
271}
272
273#[derive(Default, Debug, Copy, Clone)]
274pub struct XsaveFeature {
275    pub offset: u32,
276    pub len: u32,
277    pub align: bool,
278}
279
280impl XsaveCapabilities {
281    pub fn standard_len_for(&self, xfem: u64) -> u32 {
282        let mut len = XSAVE_VARIABLE_OFFSET as u32;
283        for i in 2..63 {
284            if xfem & (1 << i) != 0 {
285                let feature = &self.feature_info[i as usize];
286                len = len.max(feature.offset + feature.len);
287            }
288        }
289        len
290    }
291
292    pub fn compact_len_for(&self, xfem: u64) -> u32 {
293        let mut len = XSAVE_VARIABLE_OFFSET as u32;
294        for i in 2..63 {
295            if xfem & (1 << i) != 0 {
296                let feature = &self.feature_info[i as usize];
297                if feature.align {
298                    len = (len + 63) & !63;
299                }
300                len += feature.len;
301            }
302        }
303        len
304    }
305}
306
307#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Protobuf, Inspect)]
308#[mesh(package = "virt.x86")]
309pub struct TableRegister {
310    #[inspect(hex)]
311    #[mesh(1)]
312    pub base: u64,
313    #[inspect(hex)]
314    #[mesh(2)]
315    pub limit: u16,
316}
317
318impl From<hvdef::HvX64TableRegister> for TableRegister {
319    fn from(table: hvdef::HvX64TableRegister) -> Self {
320        Self {
321            base: table.base,
322            limit: table.limit,
323        }
324    }
325}
326
327impl From<TableRegister> for hvdef::HvX64TableRegister {
328    fn from(table: TableRegister) -> Self {
329        Self {
330            base: table.base,
331            limit: table.limit,
332            pad: [0; 3],
333        }
334    }
335}
336
337#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Protobuf, Inspect)]
338#[mesh(package = "virt.x86")]
339pub struct SegmentRegister {
340    #[inspect(hex)]
341    #[mesh(1)]
342    pub base: u64,
343    #[inspect(hex)]
344    #[mesh(2)]
345    pub limit: u32,
346    #[inspect(hex)]
347    #[mesh(3)]
348    pub selector: u16,
349    #[inspect(hex)]
350    #[mesh(4)]
351    pub attributes: u16,
352}
353
354impl From<x86defs::SegmentRegister> for SegmentRegister {
355    fn from(seg: x86defs::SegmentRegister) -> Self {
356        Self {
357            base: seg.base,
358            limit: seg.limit,
359            selector: seg.selector,
360            attributes: seg.attributes.into(),
361        }
362    }
363}
364
365impl From<SegmentRegister> for x86defs::SegmentRegister {
366    fn from(seg: SegmentRegister) -> Self {
367        Self {
368            base: seg.base,
369            limit: seg.limit,
370            selector: seg.selector,
371            attributes: seg.attributes.into(),
372        }
373    }
374}
375
376impl From<hvdef::HvX64SegmentRegister> for SegmentRegister {
377    fn from(seg: hvdef::HvX64SegmentRegister) -> Self {
378        Self {
379            base: seg.base,
380            limit: seg.limit,
381            selector: seg.selector,
382            attributes: seg.attributes,
383        }
384    }
385}
386
387impl From<SegmentRegister> for hvdef::HvX64SegmentRegister {
388    fn from(seg: SegmentRegister) -> Self {
389        Self {
390            base: seg.base,
391            limit: seg.limit,
392            selector: seg.selector,
393            attributes: seg.attributes,
394        }
395    }
396}
397
398/// Guest debugging state, for gdbstub or similar use cases.
399#[derive(Debug, Copy, Clone, Protobuf)]
400pub struct DebugState {
401    /// Single step the VP.
402    pub single_step: bool,
403    /// Hardware breakpoints/watchpoints.
404    pub breakpoints: [Option<HardwareBreakpoint>; 4],
405}
406
407#[derive(Debug, Copy, Clone, Protobuf, PartialEq, Eq)]
408pub struct HardwareBreakpoint {
409    /// The address to watch.
410    pub address: u64,
411    /// The breakpoint type.
412    pub ty: BreakpointType,
413    /// The size of the memory location to watch.
414    pub size: BreakpointSize,
415}
416
417impl HardwareBreakpoint {
418    /// Parses the hardware breakpoint from DR7, the address of the breakpoint,
419    /// and the debug register index (0-3).
420    pub fn from_dr7(dr7: u64, address: u64, reg: usize) -> Self {
421        let v = dr7 >> (16 + reg * 4);
422        let ty = match v & 3 {
423            0 => BreakpointType::Execute,
424            1 => BreakpointType::Invalid,
425            2 => BreakpointType::Write,
426            3 => BreakpointType::ReadOrWrite,
427            _ => unreachable!(),
428        };
429        let size = match (v >> 2) & 3 {
430            0 => BreakpointSize::Byte,
431            1 => BreakpointSize::Word,
432            2 => BreakpointSize::QWord,
433            3 => BreakpointSize::DWord,
434            _ => unreachable!(),
435        };
436        Self { address, ty, size }
437    }
438
439    /// Returns a value to OR into DR7 to enable this breakpoint.
440    pub fn dr7_bits(&self, reg: usize) -> u64 {
441        ((self.ty as u64 | ((self.size as u64) << 2)) << (16 + reg * 4)) | (1 << (1 + reg * 2))
442    }
443}
444
445/// A hardware breakpoint type.
446#[derive(Debug, Copy, Clone, Protobuf, PartialEq, Eq)]
447pub enum BreakpointType {
448    /// Break on execute. Size should be [`BreakpointSize::Byte`].
449    Execute = 0,
450    /// Invalid type, not used on x86.
451    Invalid = 1,
452    /// Break on write.
453    Write = 2,
454    /// Break on read or write.
455    ReadOrWrite = 3,
456}
457
458/// The size of the debug breakpoint.
459#[derive(Debug, Copy, Clone, Protobuf, PartialEq, Eq)]
460pub enum BreakpointSize {
461    /// 1 byte.
462    Byte = 0,
463    /// 2 bytes.
464    Word = 1,
465    /// 4 bytes.
466    DWord = 3,
467    /// 8 bytes.
468    QWord = 2,
469}
470
471/// The requested breakpoint size is not supported.
472#[derive(Debug)]
473pub struct UnsupportedBreakpointSize;
474
475impl TryFrom<usize> for BreakpointSize {
476    type Error = UnsupportedBreakpointSize;
477
478    fn try_from(value: usize) -> Result<Self, Self::Error> {
479        Ok(match value {
480            1 => BreakpointSize::Byte,
481            2 => BreakpointSize::Word,
482            4 => BreakpointSize::DWord,
483            8 => BreakpointSize::QWord,
484            _ => return Err(UnsupportedBreakpointSize),
485        })
486    }
487}
488
489/// Query the max physical address size of the system.
490pub fn max_physical_address_size_from_cpuid(mut cpuid: impl FnMut(u32, u32) -> [u32; 4]) -> u8 {
491    const DEFAULT_PHYSICAL_ADDRESS_SIZE: u8 = 32;
492
493    let max_extended = {
494        let result = cpuid(CpuidFunction::ExtendedMaxFunction.0, 0);
495        result[0]
496    };
497
498    if max_extended >= CpuidFunction::ExtendedAddressSpaceSizes.0 {
499        let result = cpuid(CpuidFunction::ExtendedAddressSpaceSizes.0, 0);
500        (result[0] & 0xFF) as u8
501    } else {
502        DEFAULT_PHYSICAL_ADDRESS_SIZE
503    }
504}
505
506/// Error returned by MSR routines.
507#[derive(Debug)]
508pub enum MsrError {
509    /// The MSR is not implemented. Depending on the configuration, this should
510    /// either be ignored (returning 0 for reads) or should result in a #GP.
511    Unknown,
512    /// The MSR is implemented but this is an invalid read or write and should
513    /// always result in a #GP.
514    InvalidAccess,
515}
516
517/// Extension trait to chain MSR accesses together.
518pub trait MsrErrorExt: Sized {
519    /// Calls `f` if `self` is `Err(Msr::Unknown)`.
520    fn or_else_if_unknown(self, f: impl FnOnce() -> Self) -> Self;
521}
522
523impl<T> MsrErrorExt for Result<T, MsrError> {
524    fn or_else_if_unknown(self, f: impl FnOnce() -> Self) -> Self {
525        match self {
526            Err(MsrError::Unknown) => f(),
527            r => r,
528        }
529    }
530}