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