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