hv1_emulator/
cpuid.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Provides the synthetic hypervisor cpuid leaves matching this hv1 emulator's
5//! capabilities.
6
7#![cfg(guest_arch = "x86_64")]
8
9use hvdef::HvEnlightenmentInformation;
10use hvdef::HvFeatures;
11use hvdef::HvHardwareFeatures;
12use hvdef::HvIsolationConfiguration;
13use hvdef::HvPartitionPrivilege;
14use std::arch::x86_64::CpuidResult;
15use virt::CpuidLeaf;
16use x86defs::cpuid::CpuidFunction;
17
18/// The partition privileges that this emulator supports.
19pub const SUPPORTED_PRIVILEGES: HvPartitionPrivilege = HvPartitionPrivilege::new()
20    .with_access_partition_reference_counter(true)
21    .with_access_hypercall_msrs(true)
22    .with_access_vp_index(true)
23    .with_access_synic_msrs(true)
24    .with_access_synthetic_timer_msrs(true)
25    .with_access_partition_reference_tsc(true);
26
27/// The hypervisor features that this emulator supports.
28pub const SUPPORTED_FEATURES: HvFeatures = HvFeatures::new()
29    .with_privileges(SUPPORTED_PRIVILEGES)
30    .with_direct_synthetic_timers(true);
31
32const fn split_u128(x: u128) -> CpuidResult {
33    let bytes: [u32; 4] = zerocopy::transmute!(x);
34    CpuidResult {
35        eax: bytes[0],
36        ebx: bytes[1],
37        ecx: bytes[2],
38        edx: bytes[3],
39    }
40}
41
42/// Converts the given features and enlightenment information into a set of
43/// synthetic cpuid leaves.
44pub fn make_hv_cpuid_leaves(
45    features: HvFeatures,
46    enlightenments: HvEnlightenmentInformation,
47    max_cpus: u32,
48) -> [(CpuidFunction, CpuidResult); 3] {
49    const fn split_u128(x: u128) -> CpuidResult {
50        let bytes: [u32; 4] = zerocopy::transmute!(x);
51        CpuidResult {
52            eax: bytes[0],
53            ebx: bytes[1],
54            ecx: bytes[2],
55            edx: bytes[3],
56        }
57    }
58
59    [
60        (CpuidFunction(hvdef::HV_CPUID_FUNCTION_MS_HV_FEATURES), {
61            split_u128(features.into_bits())
62        }),
63        (
64            CpuidFunction(hvdef::HV_CPUID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION),
65            split_u128(enlightenments.into_bits()),
66        ),
67        (
68            CpuidFunction(hvdef::HV_CPUID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS),
69            CpuidResult {
70                eax: max_cpus,
71                ebx: max_cpus,
72                ecx: 0,
73                edx: 0,
74            },
75        ),
76    ]
77}
78
79/// Converts the given features and enlightenment information into a set of
80/// synthetic cpuid leaves for isolated VMs.
81pub fn make_isolated_hv_cpuid_leaves(
82    hardware_features: HvHardwareFeatures,
83    isolation_config: HvIsolationConfiguration,
84) -> [(CpuidFunction, CpuidResult); 2] {
85    [
86        (
87            CpuidFunction(hvdef::HV_CPUID_FUNCTION_MS_HV_HARDWARE_FEATURES),
88            split_u128(hardware_features.into_bits()),
89        ),
90        (
91            CpuidFunction(hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION),
92            split_u128(isolation_config.into_bits()),
93        ),
94    ]
95}
96
97/// Adds the standard hypervisor leaves and version information, and filters out
98/// information we shouldn't expose if we're hiding isolation.
99pub fn process_hv_cpuid_leaves(
100    leaves: &mut Vec<CpuidLeaf>,
101    hide_isolation: bool,
102    hv_version: [u32; 4],
103) {
104    // Add the standard leaves.
105    leaves.push(CpuidLeaf::new(
106        hvdef::HV_CPUID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION,
107        [
108            if hide_isolation {
109                hvdef::HV_CPUID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS
110            } else {
111                hvdef::HV_CPUID_FUNCTION_MS_HV_ISOLATION_CONFIGURATION
112            },
113            u32::from_le_bytes(*b"Micr"),
114            u32::from_le_bytes(*b"osof"),
115            u32::from_le_bytes(*b"t Hv"),
116        ],
117    ));
118    leaves.push(CpuidLeaf::new(
119        hvdef::HV_CPUID_FUNCTION_HV_INTERFACE,
120        [u32::from_le_bytes(*b"Hv#1"), 0, 0, 0],
121    ));
122    leaves.push(CpuidLeaf::new(
123        hvdef::HV_CPUID_FUNCTION_MS_HV_VERSION,
124        hv_version,
125    ));
126
127    // If we're hiding isolation, remove any HV leaves above the lowered limit.
128    if hide_isolation {
129        leaves.retain(|leaf| {
130            if leaf.function & 0xF0000000 == hvdef::HV_CPUID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION {
131                leaf.function <= hvdef::HV_CPUID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS
132            } else {
133                true
134            }
135        });
136
137        // And don't report that we're isolated.
138        let isolated_mask =
139            HvFeatures::new().with_privileges(HvPartitionPrivilege::new().with_isolation(true));
140        leaves.push(
141            CpuidLeaf::new(hvdef::HV_CPUID_FUNCTION_MS_HV_FEATURES, [0, 0, 0, 0])
142                .masked(zerocopy::transmute!(isolated_mask)),
143        );
144    }
145}