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
10 use base::error;
11 use base::warn;
12 use base::Error;
13 use base::Result;
14 use once_cell::sync::Lazy;
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 {
new() -> WhpxResult<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 {
drop(&mut self)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> {
from(e: WhpxError) -> Self115 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 {
new() -> Result<Whpx>123 pub fn new() -> Result<Whpx> {
124 Ok(Whpx {})
125 }
126
is_enabled() -> bool127 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
get_capability(cap: WHV_CAPABILITY_CODE) -> WhpxResult<WHV_CAPABILITY>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
check_whpx_feature(feature: WhpxFeature) -> WhpxResult<bool>156 pub fn check_whpx_feature(feature: WhpxFeature) -> WhpxResult<bool> {
157 // use Lazy to cache the results of the get_capability call
158 static FEATURES: Lazy<WhpxResult<WHV_CAPABILITY>> =
159 Lazy::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`.
try_clone(&self) -> Result<Self>167 fn try_clone(&self) -> Result<Self> {
168 Ok(self.clone())
169 }
170
171 /// Checks if a particular `HypervisorCap` is available.
check_capability(&self, cap: HypervisorCap) -> bool172 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`.
cpuid_entry_from_host(function: u32, index: u32) -> CpuIdEntry196 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.
get_supported_cpuid(&self) -> Result<CpuId>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
get_msr_index_list(&self) -> Result<Vec<u32>>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]
new_whpx()243 fn new_whpx() {
244 Whpx::new().expect("failed to instantiate whpx");
245 }
246
247 #[test]
clone_whpx()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]
check_capability()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 assert!(!whpx.check_capability(HypervisorCap::S390UserSigp));
260 }
261 }
262