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