hyperlight_host/hypervisor/
mod.rs1use log::LevelFilter;
18use tracing::{instrument, Span};
19
20use crate::error::HyperlightError::ExecutionCanceledByHost;
21use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
22use crate::metrics::METRIC_GUEST_CANCELLATION;
23use crate::{log_then_return, new_error, HyperlightError, Result};
24
25#[cfg(any(kvm, mshv, target_os = "windows"))]
27pub mod fpu;
28pub mod handlers;
30#[cfg(mshv)]
32pub mod hyperv_linux;
33#[cfg(target_os = "windows")]
34pub(crate) mod hyperv_windows;
36pub(crate) mod hypervisor_handler;
37
38#[cfg(gdb)]
40mod gdb;
41
42#[cfg(kvm)]
43pub mod kvm;
45#[cfg(target_os = "windows")]
46pub(crate) mod surrogate_process;
48#[cfg(target_os = "windows")]
49pub(crate) mod surrogate_process_manager;
51#[cfg(target_os = "windows")]
53pub(crate) mod windows_hypervisor_platform;
54#[cfg(target_os = "windows")]
56pub(crate) mod wrappers;
57
58#[cfg(crashdump)]
59pub(crate) mod crashdump;
60
61use std::fmt::Debug;
62use std::str::FromStr;
63use std::sync::{Arc, Mutex};
64
65#[cfg(gdb)]
66use gdb::VcpuStopReason;
67
68#[cfg(gdb)]
69use self::handlers::{DbgMemAccessHandlerCaller, DbgMemAccessHandlerWrapper};
70use self::handlers::{
71 MemAccessHandlerCaller, MemAccessHandlerWrapper, OutBHandlerCaller, OutBHandlerWrapper,
72};
73use crate::hypervisor::hypervisor_handler::HypervisorHandler;
74use crate::mem::ptr::RawPtr;
75
76pub(crate) const CR4_PAE: u64 = 1 << 5;
77pub(crate) const CR4_OSFXSR: u64 = 1 << 9;
78pub(crate) const CR4_OSXMMEXCPT: u64 = 1 << 10;
79pub(crate) const CR0_PE: u64 = 1;
80pub(crate) const CR0_MP: u64 = 1 << 1;
81pub(crate) const CR0_ET: u64 = 1 << 4;
82pub(crate) const CR0_NE: u64 = 1 << 5;
83pub(crate) const CR0_WP: u64 = 1 << 16;
84pub(crate) const CR0_AM: u64 = 1 << 18;
85pub(crate) const CR0_PG: u64 = 1 << 31;
86pub(crate) const EFER_LME: u64 = 1 << 8;
87pub(crate) const EFER_LMA: u64 = 1 << 10;
88pub(crate) const EFER_SCE: u64 = 1;
89pub(crate) const EFER_NX: u64 = 1 << 11;
90
91pub enum HyperlightExit {
94 #[cfg(gdb)]
95 Debug(VcpuStopReason),
97 Halt(),
99 IoOut(u16, Vec<u8>, u64, u64),
101 Mmio(u64),
103 AccessViolation(u64, MemoryRegionFlags, MemoryRegionFlags),
105 Cancelled(),
107 Unknown(String),
109 Retry(),
111}
112
113pub(crate) trait Hypervisor: Debug + Sync + Send {
119 #[allow(clippy::too_many_arguments)]
122 fn initialise(
123 &mut self,
124 peb_addr: RawPtr,
125 seed: u64,
126 page_size: u32,
127 outb_handle_fn: OutBHandlerWrapper,
128 mem_access_fn: MemAccessHandlerWrapper,
129 hv_handler: Option<HypervisorHandler>,
130 guest_max_log_level: Option<LevelFilter>,
131 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
132 ) -> Result<()>;
133
134 fn dispatch_call_from_host(
142 &mut self,
143 dispatch_func_addr: RawPtr,
144 outb_handle_fn: OutBHandlerWrapper,
145 mem_access_fn: MemAccessHandlerWrapper,
146 hv_handler: Option<HypervisorHandler>,
147 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
148 ) -> Result<()>;
149
150 fn handle_io(
152 &mut self,
153 port: u16,
154 data: Vec<u8>,
155 rip: u64,
156 instruction_length: u64,
157 outb_handle_fn: OutBHandlerWrapper,
158 ) -> Result<()>;
159
160 fn run(&mut self) -> Result<HyperlightExit>;
162
163 fn get_memory_access_violation(
166 &self,
167 gpa: usize,
168 mem_regions: &[MemoryRegion],
169 access_info: MemoryRegionFlags,
170 ) -> Option<HyperlightExit> {
171 let region = mem_regions
173 .iter()
174 .find(|region| region.guest_region.contains(&gpa));
175
176 if let Some(region) = region {
177 if !region.flags.contains(access_info)
178 || region.flags.contains(MemoryRegionFlags::STACK_GUARD)
179 {
180 return Some(HyperlightExit::AccessViolation(
181 gpa as u64,
182 access_info,
183 region.flags,
184 ));
185 }
186 }
187 None
188 }
189
190 fn get_max_log_level(&self) -> u32 {
192 let val = std::env::var("RUST_LOG").unwrap_or_default();
201
202 let level = if val.contains("hyperlight_guest") {
203 val.split(',')
204 .find(|s| s.contains("hyperlight_guest"))
205 .unwrap_or("")
206 .split('=')
207 .nth(1)
208 .unwrap_or("")
209 } else if val.contains("hyperlight_host") {
210 val.split(',')
211 .find(|s| s.contains("hyperlight_host"))
212 .unwrap_or("")
213 .split('=')
214 .nth(1)
215 .unwrap_or("")
216 } else {
217 val.split(',').find(|s| !s.contains("=")).unwrap_or("")
219 };
220
221 log::info!("Determined guest log level: {}", level);
222 LevelFilter::from_str(level).unwrap_or(LevelFilter::Error) as u32
225 }
226
227 fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor;
229
230 #[cfg(target_os = "windows")]
232 fn get_partition_handle(&self) -> windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE;
233
234 #[cfg(crashdump)]
235 fn get_memory_regions(&self) -> &[MemoryRegion];
236
237 #[cfg(gdb)]
238 fn handle_debug(
240 &mut self,
241 _dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
242 _stop_reason: VcpuStopReason,
243 ) -> Result<()> {
244 unimplemented!()
245 }
246}
247
248pub struct VirtualCPU {}
250
251impl VirtualCPU {
252 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
254 pub fn run(
255 hv: &mut dyn Hypervisor,
256 hv_handler: Option<HypervisorHandler>,
257 outb_handle_fn: Arc<Mutex<dyn OutBHandlerCaller>>,
258 mem_access_fn: Arc<Mutex<dyn MemAccessHandlerCaller>>,
259 #[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
260 ) -> Result<()> {
261 loop {
262 match hv.run() {
263 #[cfg(gdb)]
264 Ok(HyperlightExit::Debug(stop_reason)) => {
265 if let Err(e) = hv.handle_debug(dbg_mem_access_fn.clone(), stop_reason) {
266 log_then_return!(e);
267 }
268 }
269
270 Ok(HyperlightExit::Halt()) => {
271 break;
272 }
273 Ok(HyperlightExit::IoOut(port, data, rip, instruction_length)) => {
274 hv.handle_io(port, data, rip, instruction_length, outb_handle_fn.clone())?
275 }
276 Ok(HyperlightExit::Mmio(addr)) => {
277 #[cfg(crashdump)]
278 crashdump::crashdump_to_tempfile(hv)?;
279
280 mem_access_fn
281 .clone()
282 .try_lock()
283 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
284 .call()?;
285
286 log_then_return!("MMIO access address {:#x}", addr);
287 }
288 Ok(HyperlightExit::AccessViolation(addr, tried, region_permission)) => {
289 #[cfg(crashdump)]
290 crashdump::crashdump_to_tempfile(hv)?;
291
292 if region_permission.intersects(MemoryRegionFlags::STACK_GUARD) {
293 return Err(HyperlightError::StackOverflow());
294 }
295 log_then_return!(HyperlightError::MemoryAccessViolation(
296 addr,
297 tried,
298 region_permission
299 ));
300 }
301 Ok(HyperlightExit::Cancelled()) => {
302 if let Some(hvh) = hv_handler {
305 hvh.set_running(false);
308 #[cfg(target_os = "linux")]
309 hvh.set_run_cancelled(true);
310 }
311 metrics::counter!(METRIC_GUEST_CANCELLATION).increment(1);
312 log_then_return!(ExecutionCanceledByHost());
313 }
314 Ok(HyperlightExit::Unknown(reason)) => {
315 #[cfg(crashdump)]
316 crashdump::crashdump_to_tempfile(hv)?;
317
318 log_then_return!("Unexpected VM Exit {:?}", reason);
319 }
320 Ok(HyperlightExit::Retry()) => continue,
321 Err(e) => {
322 #[cfg(crashdump)]
323 crashdump::crashdump_to_tempfile(hv)?;
324
325 return Err(e);
326 }
327 }
328 }
329
330 Ok(())
331 }
332}
333
334#[cfg(all(test, any(target_os = "windows", kvm)))]
335pub(crate) mod tests {
336 use std::path::Path;
337 use std::sync::{Arc, Mutex};
338 use std::time::Duration;
339
340 use hyperlight_testing::dummy_guest_as_string;
341
342 #[cfg(gdb)]
343 use super::handlers::DbgMemAccessHandlerWrapper;
344 use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
345 use crate::hypervisor::hypervisor_handler::{
346 HvHandlerConfig, HypervisorHandler, HypervisorHandlerAction,
347 };
348 use crate::mem::ptr::RawPtr;
349 use crate::sandbox::uninitialized::GuestBinary;
350 use crate::sandbox::{SandboxConfiguration, UninitializedSandbox};
351 use crate::{new_error, Result};
352
353 pub(crate) fn test_initialise(
354 outb_hdl: OutBHandlerWrapper,
355 mem_access_hdl: MemAccessHandlerWrapper,
356 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
357 ) -> Result<()> {
358 let filename = dummy_guest_as_string().map_err(|e| new_error!("{}", e))?;
359 if !Path::new(&filename).exists() {
360 return Err(new_error!(
361 "test_initialise: file {} does not exist",
362 filename
363 ));
364 }
365
366 let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(filename.clone()), None)?;
367 let (hshm, gshm) = sandbox.mgr.build();
368 drop(hshm);
369
370 let hv_handler_config = HvHandlerConfig {
371 outb_handler: outb_hdl,
372 mem_access_handler: mem_access_hdl,
373 #[cfg(gdb)]
374 dbg_mem_access_handler: dbg_mem_access_fn,
375 seed: 1234567890,
376 page_size: 4096,
377 peb_addr: RawPtr::from(0x230000),
378 dispatch_function_addr: Arc::new(Mutex::new(None)),
379 max_init_time: Duration::from_millis(
380 SandboxConfiguration::DEFAULT_MAX_INITIALIZATION_TIME as u64,
381 ),
382 max_exec_time: Duration::from_millis(
383 SandboxConfiguration::DEFAULT_MAX_EXECUTION_TIME as u64,
384 ),
385 max_wait_for_cancellation: Duration::from_millis(
386 SandboxConfiguration::DEFAULT_MAX_WAIT_FOR_CANCELLATION as u64,
387 ),
388 max_guest_log_level: None,
389 };
390
391 let mut hv_handler = HypervisorHandler::new(hv_handler_config);
392
393 hv_handler.start_hypervisor_handler(
407 gshm,
408 #[cfg(gdb)]
409 None,
410 )?;
411
412 hv_handler.execute_hypervisor_handler_action(HypervisorHandlerAction::Initialise)
413 }
414}