Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/hypervisor/src/whpx.rs
5394 views
1
// Copyright 2022 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
//! Implementation for WHPX hypervisor aka Windows Hyper-V platform.
6
7
use core::ffi::c_void;
8
use std::arch::x86_64::__cpuid_count;
9
use std::sync::LazyLock;
10
11
use base::error;
12
use base::warn;
13
use base::Error;
14
use base::Result;
15
use thiserror::Error as ThisError;
16
use winapi::shared::winerror::S_OK;
17
18
use crate::CpuId;
19
use crate::CpuIdEntry;
20
use crate::Hypervisor;
21
use crate::HypervisorCap;
22
use crate::HypervisorX86_64;
23
24
#[macro_export]
25
macro_rules! check_whpx {
26
($x: expr) => {{
27
match $x {
28
S_OK => Ok(()),
29
_ => Err(Error::new($x)),
30
}
31
}};
32
}
33
34
mod types;
35
mod vcpu;
36
pub use vcpu::*;
37
mod vm;
38
pub use vm::*;
39
pub mod whpx_sys;
40
pub use whpx_sys::*;
41
42
// used by both the vm and vcpu
43
struct SafePartition {
44
partition: WHV_PARTITION_HANDLE,
45
}
46
47
// we can send the partition over safely even though it is void*, it can be sent
48
// in another thread safely.
49
unsafe impl Send for SafePartition {}
50
unsafe impl Sync for SafePartition {}
51
52
impl SafePartition {
53
fn new() -> WhpxResult<SafePartition> {
54
let mut partition_handle: WHV_PARTITION_HANDLE = std::ptr::null_mut();
55
// safe because we pass in the partition handle for the system to fill in.
56
check_whpx!(unsafe { WHvCreatePartition(&mut partition_handle) })
57
.map_err(WhpxError::CreatePartitionError)?;
58
59
Ok(SafePartition {
60
partition: partition_handle,
61
})
62
}
63
}
64
65
impl Drop for SafePartition {
66
fn drop(&mut self) {
67
// safe because we own this partition
68
check_whpx!(unsafe { WHvDeletePartition(self.partition) }).unwrap();
69
}
70
}
71
72
#[derive(Clone)]
73
pub struct Whpx {
74
// there is no general whpx object, the vm contains the partition.
75
}
76
77
/// Enum of WHPX Features. Similar to WHV_CAPABILITY_FEATURES but allows us to avoid making the
78
/// whpx_sys crate pub.
79
#[derive(Debug)]
80
pub enum WhpxFeature {
81
PartialUnmap = 0x0,
82
LocalApicEmulation = 0x1,
83
Xsave = 0x2,
84
DirtyPageTracking = 0x4,
85
SpeculationControl = 0x8,
86
ApicRemoteRead = 0x10,
87
IdleSuspend = 0x20,
88
}
89
90
#[derive(ThisError, Debug, Clone, Copy)]
91
pub enum WhpxError {
92
#[error("failed to create WHPX partition: {0}")]
93
CreatePartitionError(base::Error),
94
#[error("failed to get WHPX capability {0}: {1}")]
95
GetCapability(WHV_CAPABILITY_CODE, base::Error),
96
#[error("WHPX local apic emulation is not supported on this host")]
97
LocalApicEmulationNotSupported,
98
#[error("failed to map guest physical address range: {0}")]
99
MapGpaRange(base::Error),
100
#[error("failed to set WHPX partition processor count: {0}")]
101
SetProcessorCount(base::Error),
102
#[error("failed to set WHPX partition cpuid result list: {0}")]
103
SetCpuidResultList(base::Error),
104
#[error("failed to set WHPX partition cpuid exit list: {0}")]
105
SetCpuidExitList(base::Error),
106
#[error("failed to set WHPX partition extended vm exits: {0}")]
107
SetExtendedVmExits(base::Error),
108
#[error("failed to set WHPX partition local apic emulation mode: {0}")]
109
SetLocalApicEmulationMode(base::Error),
110
#[error("failed to setup WHPX partition: {0}")]
111
SetupPartition(base::Error),
112
}
113
114
impl From<WhpxError> for Box<dyn std::error::Error + Send> {
115
fn from(e: WhpxError) -> Self {
116
Box::new(e)
117
}
118
}
119
120
type WhpxResult<T> = std::result::Result<T, WhpxError>;
121
122
impl Whpx {
123
pub fn new() -> Result<Whpx> {
124
Ok(Whpx {})
125
}
126
127
pub fn is_enabled() -> bool {
128
let res = Whpx::get_capability(WHV_CAPABILITY_CODE_WHvCapabilityCodeHypervisorPresent);
129
match res {
130
Ok(cap_code) => {
131
// safe because we trust the kernel to fill in hypervisor present in the union
132
unsafe { cap_code.HypervisorPresent > 0 }
133
}
134
_ => {
135
warn!("error checking if whpx was enabled. Assuming whpx is disabled");
136
false
137
}
138
}
139
}
140
141
fn get_capability(cap: WHV_CAPABILITY_CODE) -> WhpxResult<WHV_CAPABILITY> {
142
let mut capability: WHV_CAPABILITY = Default::default();
143
let mut written_size = 0;
144
check_whpx!(unsafe {
145
WHvGetCapability(
146
cap,
147
&mut capability as *mut _ as *mut c_void,
148
std::mem::size_of::<WHV_CAPABILITY>() as u32,
149
&mut written_size,
150
)
151
})
152
.map_err(|e| WhpxError::GetCapability(cap, e))?;
153
Ok(capability)
154
}
155
156
pub fn check_whpx_feature(feature: WhpxFeature) -> WhpxResult<bool> {
157
// use LazyLock to cache the results of the get_capability call
158
static FEATURES: LazyLock<WhpxResult<WHV_CAPABILITY>> =
159
LazyLock::new(|| Whpx::get_capability(WHV_CAPABILITY_CODE_WHvCapabilityCodeFeatures));
160
161
Ok((unsafe { (*FEATURES)?.Features.AsUINT64 } & feature as u64) != 0)
162
}
163
}
164
165
impl Hypervisor for Whpx {
166
/// Makes a shallow clone of this `Hypervisor`.
167
fn try_clone(&self) -> Result<Self> {
168
Ok(self.clone())
169
}
170
171
/// Checks if a particular `HypervisorCap` is available.
172
fn check_capability(&self, cap: HypervisorCap) -> bool {
173
// whpx supports immediate exit, user memory, and the xcr0 state
174
match cap {
175
HypervisorCap::ImmediateExit => true,
176
HypervisorCap::UserMemory => true,
177
HypervisorCap::Xcrs => {
178
Whpx::check_whpx_feature(WhpxFeature::Xsave).unwrap_or_else(|e| {
179
error!(
180
"failed to check whpx feature {:?}: {}",
181
WhpxFeature::Xsave,
182
e
183
);
184
false
185
})
186
}
187
// under whpx, guests rely on this leaf to calibrate their clocksource.
188
HypervisorCap::CalibratedTscLeafRequired => true,
189
_ => false,
190
}
191
}
192
}
193
194
/// Build a CpuIdEntry for a given `function`/`index` from the host results for that
195
/// `function`/`index`.
196
fn cpuid_entry_from_host(function: u32, index: u32) -> CpuIdEntry {
197
// Safe because arguments are passed by value
198
let result = unsafe { __cpuid_count(function, index) };
199
CpuIdEntry {
200
function,
201
index,
202
flags: 0,
203
cpuid: result,
204
}
205
}
206
207
impl HypervisorX86_64 for Whpx {
208
/// Get the system supported CPUID values.
209
///
210
/// WHPX does not have an API for getting this information, so we just return the host values
211
/// instead. This is not technically accurate, since WHPX does modify the contents of various
212
/// leaves, but in practice this is fine because this function is only used for pre-setting
213
/// the contents of certain leaves that we can safely base off of the host value.
214
fn get_supported_cpuid(&self) -> Result<CpuId> {
215
Ok(CpuId {
216
cpu_id_entries: vec![
217
// Leaf 0 just contains information about the max leaf. WHPX seems to set this to
218
// a value lower than the host value but we want it to be at least 0x15. We set it
219
// to the host value here assuming that the leaves above 0x15 probably won't hurt
220
// the guest.
221
cpuid_entry_from_host(0, 0),
222
// crosvm overrides the entirety of leaves 2, 0x80000005, and 0x80000006 to the
223
// host value, so we just return the host value here.
224
cpuid_entry_from_host(2, 0),
225
cpuid_entry_from_host(0x80000005, 0),
226
cpuid_entry_from_host(0x80000006, 0),
227
],
228
})
229
}
230
231
/// Gets the list of supported MSRs.
232
/// TODO: this is only used by the plugin
233
fn get_msr_index_list(&self) -> Result<Vec<u32>> {
234
Ok(vec![])
235
}
236
}
237
238
#[cfg(test)]
239
mod tests {
240
use super::*;
241
242
#[test]
243
fn new_whpx() {
244
Whpx::new().expect("failed to instantiate whpx");
245
}
246
247
#[test]
248
fn clone_whpx() {
249
let whpx = Whpx::new().expect("failed to instantiate whpx");
250
let _whpx_clone = whpx.try_clone().unwrap();
251
}
252
253
#[test]
254
fn check_capability() {
255
let whpx = Whpx::new().expect("failed to instantiate whpx");
256
assert!(whpx.check_capability(HypervisorCap::UserMemory));
257
assert!(whpx.check_capability(HypervisorCap::Xcrs));
258
assert!(whpx.check_capability(HypervisorCap::ImmediateExit));
259
}
260
}
261
262