hyperlight_host/hypervisor/
mod.rs1use tracing::{instrument, Span};
18
19use crate::error::HyperlightError::ExecutionCanceledByHost;
20use crate::hypervisor::metrics::HypervisorMetric::NumberOfCancelledGuestExecutions;
21use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
22use crate::{int_counter_inc, log_then_return, new_error, HyperlightError, Result};
23
24#[cfg(any(kvm, mshv, target_os = "windows"))]
26pub mod fpu;
27pub mod handlers;
29#[cfg(mshv)]
31pub mod hyperv_linux;
32#[cfg(target_os = "windows")]
33pub(crate) mod hyperv_windows;
35pub(crate) mod hypervisor_handler;
36
37#[cfg(gdb)]
39mod gdb;
40
41#[cfg(inprocess)]
43pub mod inprocess;
44#[cfg(kvm)]
45pub mod kvm;
47mod metrics;
49#[cfg(target_os = "windows")]
50pub(crate) mod surrogate_process;
52#[cfg(target_os = "windows")]
53pub(crate) mod surrogate_process_manager;
55#[cfg(target_os = "windows")]
57pub(crate) mod windows_hypervisor_platform;
58#[cfg(target_os = "windows")]
60pub(crate) mod wrappers;
61
62#[cfg(crashdump)]
63pub(crate) mod crashdump;
64
65use std::fmt::Debug;
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 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
134 ) -> Result<()>;
135
136 fn dispatch_call_from_host(
144 &mut self,
145 dispatch_func_addr: RawPtr,
146 outb_handle_fn: OutBHandlerWrapper,
147 mem_access_fn: MemAccessHandlerWrapper,
148 hv_handler: Option<HypervisorHandler>,
149 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
150 ) -> Result<()>;
151
152 fn handle_io(
154 &mut self,
155 port: u16,
156 data: Vec<u8>,
157 rip: u64,
158 instruction_length: u64,
159 outb_handle_fn: OutBHandlerWrapper,
160 ) -> Result<()>;
161
162 fn run(&mut self) -> Result<HyperlightExit>;
164
165 fn get_memory_access_violation(
168 &self,
169 gpa: usize,
170 mem_regions: &[MemoryRegion],
171 access_info: MemoryRegionFlags,
172 ) -> Option<HyperlightExit> {
173 let region = mem_regions
175 .iter()
176 .find(|region| region.guest_region.contains(&gpa));
177
178 if let Some(region) = region {
179 if !region.flags.contains(access_info)
180 || region.flags.contains(MemoryRegionFlags::STACK_GUARD)
181 {
182 return Some(HyperlightExit::AccessViolation(
183 gpa as u64,
184 access_info,
185 region.flags,
186 ));
187 }
188 }
189 None
190 }
191
192 fn get_max_log_level(&self) -> u32 {
194 log::max_level() as u32
195 }
196
197 fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor;
199
200 #[cfg(target_os = "windows")]
202 fn get_partition_handle(&self) -> windows::Win32::System::Hypervisor::WHV_PARTITION_HANDLE;
203
204 #[cfg(crashdump)]
205 fn get_memory_regions(&self) -> &[MemoryRegion];
206
207 #[cfg(gdb)]
208 fn handle_debug(
210 &mut self,
211 _dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
212 _stop_reason: VcpuStopReason,
213 ) -> Result<()> {
214 unimplemented!()
215 }
216}
217
218pub struct VirtualCPU {}
220
221impl VirtualCPU {
222 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
224 pub fn run(
225 hv: &mut dyn Hypervisor,
226 hv_handler: Option<HypervisorHandler>,
227 outb_handle_fn: Arc<Mutex<dyn OutBHandlerCaller>>,
228 mem_access_fn: Arc<Mutex<dyn MemAccessHandlerCaller>>,
229 #[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
230 ) -> Result<()> {
231 loop {
232 match hv.run() {
233 #[cfg(gdb)]
234 Ok(HyperlightExit::Debug(stop_reason)) => {
235 if let Err(e) = hv.handle_debug(dbg_mem_access_fn.clone(), stop_reason) {
236 log_then_return!(e);
237 }
238 }
239
240 Ok(HyperlightExit::Halt()) => {
241 break;
242 }
243 Ok(HyperlightExit::IoOut(port, data, rip, instruction_length)) => {
244 hv.handle_io(port, data, rip, instruction_length, outb_handle_fn.clone())?
245 }
246 Ok(HyperlightExit::Mmio(addr)) => {
247 #[cfg(crashdump)]
248 crashdump::crashdump_to_tempfile(hv)?;
249
250 mem_access_fn
251 .clone()
252 .try_lock()
253 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
254 .call()?;
255
256 log_then_return!("MMIO access address {:#x}", addr);
257 }
258 Ok(HyperlightExit::AccessViolation(addr, tried, region_permission)) => {
259 #[cfg(crashdump)]
260 crashdump::crashdump_to_tempfile(hv)?;
261
262 if region_permission.intersects(MemoryRegionFlags::STACK_GUARD) {
263 return Err(HyperlightError::StackOverflow());
264 }
265 log_then_return!(HyperlightError::MemoryAccessViolation(
266 addr,
267 tried,
268 region_permission
269 ));
270 }
271 Ok(HyperlightExit::Cancelled()) => {
272 if let Some(hvh) = hv_handler {
275 hvh.set_running(false);
278 #[cfg(target_os = "linux")]
279 hvh.set_run_cancelled(true);
280 }
281 int_counter_inc!(&NumberOfCancelledGuestExecutions);
282 log_then_return!(ExecutionCanceledByHost());
283 }
284 Ok(HyperlightExit::Unknown(reason)) => {
285 #[cfg(crashdump)]
286 crashdump::crashdump_to_tempfile(hv)?;
287
288 log_then_return!("Unexpected VM Exit {:?}", reason);
289 }
290 Ok(HyperlightExit::Retry()) => continue,
291 Err(e) => {
292 #[cfg(crashdump)]
293 crashdump::crashdump_to_tempfile(hv)?;
294
295 return Err(e);
296 }
297 }
298 }
299
300 Ok(())
301 }
302}
303
304#[cfg(all(test, any(target_os = "windows", kvm)))]
305pub(crate) mod tests {
306 use std::path::Path;
307 use std::sync::{Arc, Mutex};
308 use std::time::Duration;
309
310 use hyperlight_testing::dummy_guest_as_string;
311
312 #[cfg(gdb)]
313 use super::handlers::DbgMemAccessHandlerWrapper;
314 use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
315 use crate::hypervisor::hypervisor_handler::{
316 HvHandlerConfig, HypervisorHandler, HypervisorHandlerAction,
317 };
318 use crate::mem::ptr::RawPtr;
319 use crate::sandbox::uninitialized::GuestBinary;
320 use crate::sandbox::{SandboxConfiguration, UninitializedSandbox};
321 use crate::{new_error, Result};
322
323 pub(crate) fn test_initialise(
324 outb_hdl: OutBHandlerWrapper,
325 mem_access_hdl: MemAccessHandlerWrapper,
326 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
327 ) -> Result<()> {
328 let filename = dummy_guest_as_string().map_err(|e| new_error!("{}", e))?;
329 if !Path::new(&filename).exists() {
330 return Err(new_error!(
331 "test_initialise: file {} does not exist",
332 filename
333 ));
334 }
335
336 let sandbox =
337 UninitializedSandbox::new(GuestBinary::FilePath(filename.clone()), None, None, None)?;
338 let (hshm, gshm) = sandbox.mgr.build();
339 drop(hshm);
340
341 let hv_handler_config = HvHandlerConfig {
342 outb_handler: outb_hdl,
343 mem_access_handler: mem_access_hdl,
344 #[cfg(gdb)]
345 dbg_mem_access_handler: dbg_mem_access_fn,
346 seed: 1234567890,
347 page_size: 4096,
348 peb_addr: RawPtr::from(0x230000),
349 dispatch_function_addr: Arc::new(Mutex::new(None)),
350 max_init_time: Duration::from_millis(
351 SandboxConfiguration::DEFAULT_MAX_INITIALIZATION_TIME as u64,
352 ),
353 max_exec_time: Duration::from_millis(
354 SandboxConfiguration::DEFAULT_MAX_EXECUTION_TIME as u64,
355 ),
356 max_wait_for_cancellation: Duration::from_millis(
357 SandboxConfiguration::DEFAULT_MAX_WAIT_FOR_CANCELLATION as u64,
358 ),
359 };
360
361 let mut hv_handler = HypervisorHandler::new(hv_handler_config);
362
363 hv_handler.start_hypervisor_handler(
377 gshm,
378 #[cfg(gdb)]
379 None,
380 )?;
381
382 hv_handler.execute_hypervisor_handler_action(HypervisorHandlerAction::Initialise)
383 }
384}