Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/hypervisor/src/haxm.rs
5394 views
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.
46
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.
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 {
60
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.
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 {
75
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`.
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 {
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.
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]
299
fn new_haxm() {
300
Haxm::new().expect("failed to instantiate HAXM");
301
}
302
303
#[test]
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]
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]
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
368