Skip to main content

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