xref: /aosp_15_r20/external/crosvm/hypervisor/src/haxm.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::arch::x86_64::CpuidResult;
6 use std::arch::x86_64::__cpuid;
7 use std::sync::atomic::AtomicBool;
8 use std::sync::atomic::Ordering;
9 
10 use base::AsRawDescriptor;
11 use base::RawDescriptor;
12 use base::Result;
13 use base::SafeDescriptor;
14 
15 use crate::CpuId;
16 use crate::CpuIdEntry;
17 use crate::Hypervisor;
18 use crate::HypervisorCap;
19 use crate::HypervisorX86_64;
20 
21 mod haxm_sys;
22 // This is a HAXM-specific capability, so it's not present in the VmCap enum, and the
23 // register_vm_log function does not exist on the Vm trait. But windows.rs will use it when
24 // creating Haxm Vm instances, so we expose the cap constant here.
25 pub use haxm_sys::HAX_CAP_VM_LOG;
26 use haxm_sys::*;
27 
28 mod vcpu;
29 pub use vcpu::*;
30 mod vm;
31 pub use vm::*;
32 
33 #[cfg(windows)]
34 mod win;
35 #[cfg(windows)]
36 use win::*;
37 
38 #[cfg(any(target_os = "android", target_os = "linux"))]
39 mod linux;
40 #[cfg(any(target_os = "android", target_os = "linux"))]
41 use linux::*;
42 
43 static USE_GHAXM: AtomicBool = AtomicBool::new(true);
44 
45 /// Returns whether ghaxm variant is in use vs. the upstream haxm variant.
get_use_ghaxm() -> bool46 pub fn get_use_ghaxm() -> bool {
47     USE_GHAXM.load(Ordering::Relaxed)
48 }
49 
50 /// Sets whether to use ghaxm variant vs. the upstream haxm variant.
set_use_ghaxm(use_ghaxm: bool)51 pub fn set_use_ghaxm(use_ghaxm: bool) {
52     USE_GHAXM.store(use_ghaxm, Ordering::Relaxed);
53 }
54 
55 pub struct Haxm {
56     haxm: SafeDescriptor,
57 }
58 
59 impl AsRawDescriptor for Haxm {
as_raw_descriptor(&self) -> RawDescriptor60     fn as_raw_descriptor(&self) -> RawDescriptor {
61         self.haxm.as_raw_descriptor()
62     }
63 }
64 
65 impl Haxm {
66     /// Opens HAXM device and returns a Haxm object on success.
new() -> Result<Haxm>67     pub fn new() -> Result<Haxm> {
68         Ok(Haxm {
69             haxm: open_haxm_device(get_use_ghaxm())?,
70         })
71     }
72 }
73 
74 impl Hypervisor for Haxm {
check_capability(&self, cap: HypervisorCap) -> bool75     fn check_capability(&self, cap: HypervisorCap) -> bool {
76         // under haxm, guests rely on this leaf to calibrate their
77         // clocksource.
78         matches!(
79             cap,
80             HypervisorCap::UserMemory | HypervisorCap::CalibratedTscLeafRequired
81         )
82     }
83 
84     /// Makes a shallow clone of this `Hypervisor`.
try_clone(&self) -> Result<Self>85     fn try_clone(&self) -> Result<Self> {
86         Ok(Haxm {
87             haxm: self.haxm.try_clone()?,
88         })
89     }
90 }
91 
92 impl HypervisorX86_64 for Haxm {
get_supported_cpuid(&self) -> Result<CpuId>93     fn get_supported_cpuid(&self) -> Result<CpuId> {
94         // Start with cpuids that HAXM supports from
95         // https://github.com/intel/haxm/blob/v7.6.1/core/cpuid.c#L170
96         let mut supported_features_1_ecx = (Feature1Ecx::SSE3
97             | Feature1Ecx::SSSE3
98             | Feature1Ecx::PCID
99             | Feature1Ecx::SSE41
100             | Feature1Ecx::SSE42
101             | Feature1Ecx::CMPXCHG16B
102             | Feature1Ecx::MOVBE
103             | Feature1Ecx::AESNI
104             | Feature1Ecx::PCLMULQDQ
105             | Feature1Ecx::POPCNT
106             | Feature1Ecx::FMA
107             | Feature1Ecx::XSAVE
108             | Feature1Ecx::OSXSAVE
109             | Feature1Ecx::AVX
110             | Feature1Ecx::F16C
111             | Feature1Ecx::RDRAND)
112             .bits();
113         let mut supported_features_1_edx = (Feature1Edx::PAT
114             | Feature1Edx::FPU
115             | Feature1Edx::VME
116             | Feature1Edx::DE
117             | Feature1Edx::TSC
118             | Feature1Edx::MSR
119             | Feature1Edx::PAE
120             | Feature1Edx::MCE
121             | Feature1Edx::CX8
122             | Feature1Edx::APIC
123             | Feature1Edx::SEP
124             | Feature1Edx::MTRR
125             | Feature1Edx::PGE
126             | Feature1Edx::MCA
127             | Feature1Edx::CMOV
128             | Feature1Edx::CLFSH
129             | Feature1Edx::MMX
130             | Feature1Edx::FXSR
131             | Feature1Edx::SSE
132             | Feature1Edx::SSE2
133             | Feature1Edx::SS
134             | Feature1Edx::PSE
135             | Feature1Edx::HTT)
136             .bits();
137 
138         let mut supported_features_80000001_edx = (Feature80000001Edx::NX
139             | Feature80000001Edx::SYSCALL
140             | Feature80000001Edx::RDTSCP
141             | Feature80000001Edx::EM64T)
142             .bits();
143 
144         let mut supported_features_80000001_ecx =
145             (Feature80000001Ecx::LAHF | Feature80000001Ecx::ABM | Feature80000001Ecx::PREFETCHW)
146                 .bits();
147 
148         // SAFETY: trivially safe
149         let result = unsafe { __cpuid(0x1) };
150 
151         // Filter HAXM supported cpuids by host-supported cpuids
152         supported_features_1_ecx &= result.ecx;
153         supported_features_1_edx &= result.edx;
154 
155         // SAFETY: trivially safe
156         let result = unsafe { __cpuid(0x80000001) };
157 
158         supported_features_80000001_edx &= result.edx;
159         supported_features_80000001_ecx &= result.ecx;
160 
161         // SAFETY: trivially safe
162         let cpuid_7 = unsafe { __cpuid(0x7) };
163         // SAFETY: trivially safe
164         let cpuid_15 = unsafe { __cpuid(0x15) };
165         // SAFETY: trivially safe
166         let cpuid_16 = unsafe { __cpuid(0x16) };
167 
168         Ok(CpuId {
169             cpu_id_entries: vec![
170                 CpuIdEntry {
171                     function: 0x1,
172                     index: 0,
173                     flags: 0,
174                     cpuid: CpuidResult {
175                         eax: 0,
176                         ebx: 0,
177                         ecx: supported_features_1_ecx,
178                         edx: supported_features_1_edx,
179                     },
180                 },
181                 CpuIdEntry {
182                     function: 0x7,
183                     index: 0,
184                     flags: 0,
185                     cpuid: CpuidResult {
186                         eax: cpuid_7.eax,
187                         ebx: cpuid_7.ebx,
188                         ecx: cpuid_7.ecx,
189                         edx: cpuid_7.edx,
190                     },
191                 },
192                 CpuIdEntry {
193                     function: 0x15,
194                     index: 0,
195                     flags: 0,
196                     cpuid: CpuidResult {
197                         eax: cpuid_15.eax,
198                         ebx: cpuid_15.ebx,
199                         ecx: cpuid_15.ecx,
200                         edx: cpuid_15.edx,
201                     },
202                 },
203                 CpuIdEntry {
204                     function: 0x16,
205                     index: 0,
206                     flags: 0,
207                     cpuid: CpuidResult {
208                         eax: cpuid_16.eax,
209                         ebx: cpuid_16.ebx,
210                         ecx: cpuid_16.ecx,
211                         edx: cpuid_16.edx,
212                     },
213                 },
214                 CpuIdEntry {
215                     function: 0x80000001,
216                     index: 0,
217                     flags: 0,
218                     cpuid: CpuidResult {
219                         eax: 0,
220                         ebx: 0,
221                         ecx: supported_features_80000001_ecx,
222                         edx: supported_features_80000001_edx,
223                     },
224                 },
225             ],
226         })
227     }
228 
229     /// Gets the list of supported MSRs.
get_msr_index_list(&self) -> Result<Vec<u32>>230     fn get_msr_index_list(&self) -> Result<Vec<u32>> {
231         // HAXM supported MSRs come from
232         // https://github.com/intel/haxm/blob/v7.6.1/core/vcpu.c#L3296
233         let mut msrs = vec![
234             IA32_TSC,
235             IA32_FEATURE_CONTROL,
236             IA32_PLATFORM_ID,
237             IA32_APIC_BASE,
238             IA32_EFER,
239             IA32_STAR,
240             IA32_LSTAR,
241             IA32_CSTAR,
242             IA32_SF_MASK,
243             IA32_KERNEL_GS_BASE,
244             IA32_TSC_AUX,
245             IA32_FS_BASE,
246             IA32_GS_BASE,
247             IA32_SYSENTER_CS,
248             IA32_SYSENTER_ESP,
249             IA32_SYSENTER_EIP,
250             IA32_MTRRCAP,
251             MTRRFIX64K_00000,
252             IA32_CR_PAT,
253             IA32_MTRR_DEF_TYPE,
254             IA32_MCG_CAP,
255             IA32_MCG_STATUS,
256             IA32_MCG_CTL,
257             IA32_MC0_CTL,
258             IA32_P5_MC_TYPE,
259             IA32_MC0_STATUS,
260             IA32_P5_MC_ADDR,
261             IA32_MC0_ADDR,
262             IA32_MC0_MISC,
263             IA32_THERM_DIODE_OFFSET,
264             IA32_FSB_FREQ,
265             IA32_TEMP_TARGET,
266             IA32_BBL_CR_CTL3,
267             IA32_DEBUGCTL,
268             IA32_CPUID_FEATURE_MASK,
269             IA32_EBC_FREQUENCY_ID,
270             IA32_EBC_HARD_POWERON,
271             IA32_EBC_SOFT_POWERON,
272             IA32_BIOS_SIGN_ID,
273             IA32_MISC_ENABLE,
274             IA32_PERF_CAPABILITIES,
275             IA32_PMC0,
276             IA32_PMC1,
277             IA32_PMC2,
278             IA32_PMC3,
279             IA32_PERFEVTSEL0,
280             IA32_PERFEVTSEL1,
281             IA32_PERFEVTSEL2,
282             IA32_PERFEVTSEL3,
283         ];
284         msrs.extend(IA32_MTRR_PHYSBASE0..IA32_MTRR_PHYSMASK9);
285         msrs.extend(MTRRFIX16K_80000..MTRRFIX16K_A0000);
286         msrs.extend(MTRRFIX4K_C0000..MTRRFIX4K_F8000);
287         msrs.extend(IA32_MC0_CTL2..IA32_MC8_CTL2);
288         Ok(msrs)
289     }
290 }
291 
292 // TODO(b:241252288): Enable tests disabled with dummy feature flag - enable_haxm_tests.
293 #[cfg(test)]
294 #[cfg(feature = "enable_haxm_tests")]
295 mod tests {
296     use super::*;
297 
298     #[test]
new_haxm()299     fn new_haxm() {
300         Haxm::new().expect("failed to instantiate HAXM");
301     }
302 
303     #[test]
check_capability()304     fn check_capability() {
305         let haxm = Haxm::new().expect("failed to instantiate HAXM");
306         assert!(haxm.check_capability(HypervisorCap::UserMemory));
307         assert!(!haxm.check_capability(HypervisorCap::ImmediateExit));
308     }
309 
310     #[test]
check_supported_cpuid()311     fn check_supported_cpuid() {
312         let haxm = Haxm::new().expect("failed to instantiate HAXM");
313         let cpuid = haxm
314             .get_supported_cpuid()
315             .expect("failed to get supported cupid");
316 
317         // HAXM returns five leaves: 0x1, 0x7, 0x15, 0x16, and 0x80000001
318         assert_eq!(cpuid.cpu_id_entries.len(), 5);
319 
320         // Check some simple cpuids that we know should be enabled
321         let entry = cpuid
322             .cpu_id_entries
323             .iter()
324             .find(|entry| entry.function == 0x1)
325             .expect("failed to get leaf 0x1");
326 
327         // FPU should definitely exist
328         assert_ne!(entry.cpuid.edx & Feature1Edx::FPU.bits(), 0);
329         // SSSE3 almost certainly set
330         assert_ne!(entry.cpuid.ecx & Feature1Ecx::SSSE3.bits(), 0);
331         // VMX should not be set, HAXM does not support nested virt
332         assert_eq!(entry.cpuid.ecx & Feature1Ecx::VMX.bits(), 0);
333 
334         // Check that leaf 0x15 is in the results
335         cpuid
336             .cpu_id_entries
337             .iter()
338             .find(|entry| entry.function == 0x15)
339             .expect("failed to get leaf 0x15");
340 
341         // Check that leaf 0x16 is in the results
342         cpuid
343             .cpu_id_entries
344             .iter()
345             .find(|entry| entry.function == 0x16)
346             .expect("failed to get leaf 0x16");
347 
348         let entry = cpuid
349             .cpu_id_entries
350             .iter()
351             .find(|entry| entry.function == 0x80000001)
352             .expect("failed to get leaf 0x80000001");
353         // NX should be set, Windows 8+ and HAXM require it
354         assert_ne!(entry.cpuid.edx & Feature80000001Edx::NX.bits(), 0);
355     }
356 
357     #[test]
check_msr_index_list()358     fn check_msr_index_list() {
359         let haxm = Haxm::new().expect("failed to instantiate HAXM");
360         let msr_index_list = haxm
361             .get_msr_index_list()
362             .expect("failed to get HAXM msr index list");
363 
364         assert!(msr_index_list.contains(&IA32_TSC));
365     }
366 }
367