1#[cfg(mshv2)]
18extern crate mshv_bindings2 as mshv_bindings;
19#[cfg(mshv2)]
20extern crate mshv_ioctls2 as mshv_ioctls;
21
22#[cfg(mshv3)]
23extern crate mshv_bindings3 as mshv_bindings;
24#[cfg(mshv3)]
25extern crate mshv_ioctls3 as mshv_ioctls;
26
27use std::fmt::{Debug, Formatter};
28use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
29use std::sync::{Arc, Mutex};
30
31use log::{LevelFilter, error};
32#[cfg(mshv2)]
33use mshv_bindings::hv_message;
34use mshv_bindings::{
35 FloatingPointUnit, SegmentRegister, SpecialRegisters, StandardRegisters, hv_message_type,
36 hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
37 hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT, hv_register_assoc,
38 hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
39};
40#[cfg(gdb)]
41use mshv_bindings::{
42 HV_INTERCEPT_ACCESS_MASK_EXECUTE, hv_intercept_parameters,
43 hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT,
44 mshv_install_intercept,
45};
46#[cfg(mshv3)]
47use mshv_bindings::{
48 hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
49 hv_partition_synthetic_processor_features,
50};
51#[cfg(feature = "trace_guest")]
52use mshv_bindings::{
53 hv_register_name, hv_register_name_HV_X64_REGISTER_RAX, hv_register_name_HV_X64_REGISTER_RBP,
54 hv_register_name_HV_X64_REGISTER_RCX, hv_register_name_HV_X64_REGISTER_RSP,
55};
56use mshv_ioctls::{Mshv, VcpuFd, VmFd};
57use tracing::{Span, instrument};
58#[cfg(crashdump)]
59use {super::crashdump, std::path::Path};
60
61#[cfg(feature = "trace_guest")]
62use super::TraceRegister;
63use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
64#[cfg(gdb)]
65use super::gdb::{
66 DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, MshvDebug, VcpuStopReason,
67};
68#[cfg(gdb)]
69use super::handlers::DbgMemAccessHandlerWrapper;
70#[cfg(feature = "init-paging")]
71use super::{
72 CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE,
73 EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
74};
75use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
76#[cfg(gdb)]
77use crate::HyperlightError;
78use crate::hypervisor::get_memory_access_violation;
79use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
80use crate::mem::ptr::{GuestPtr, RawPtr};
81use crate::mem::shared_mem::HostSharedMemory;
82use crate::sandbox::SandboxConfiguration;
83#[cfg(feature = "trace_guest")]
84use crate::sandbox::TraceInfo;
85use crate::sandbox::host_funcs::FunctionRegistry;
86use crate::sandbox::mem_mgr::MemMgrWrapper;
87use crate::sandbox::outb::handle_outb;
88#[cfg(crashdump)]
89use crate::sandbox::uninitialized::SandboxRuntimeConfig;
90use crate::{Result, log_then_return, new_error};
91
92#[cfg(gdb)]
93mod debug {
94 use std::sync::{Arc, Mutex};
95
96 use super::mshv_bindings::hv_x64_exception_intercept_message;
97 use super::{HypervLinuxDriver, *};
98 use crate::hypervisor::gdb::{DebugMsg, DebugResponse, VcpuStopReason, X86_64Regs};
99 use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
100 use crate::{Result, new_error};
101
102 impl HypervLinuxDriver {
103 fn disable_debug(&mut self) -> Result<()> {
105 let mut debug = MshvDebug::default();
106
107 debug.set_single_step(&self.vcpu_fd, false)?;
108
109 self.debug = Some(debug);
110
111 Ok(())
112 }
113
114 pub(crate) fn get_stop_reason(
116 &mut self,
117 ex_info: hv_x64_exception_intercept_message,
118 ) -> Result<VcpuStopReason> {
119 let debug = self
120 .debug
121 .as_mut()
122 .ok_or_else(|| new_error!("Debug is not enabled"))?;
123
124 debug.get_stop_reason(&self.vcpu_fd, ex_info.exception_vector, self.entrypoint)
125 }
126
127 pub(crate) fn process_dbg_request(
128 &mut self,
129 req: DebugMsg,
130 dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
131 ) -> Result<DebugResponse> {
132 if let Some(debug) = self.debug.as_mut() {
133 match req {
134 DebugMsg::AddHwBreakpoint(addr) => Ok(DebugResponse::AddHwBreakpoint(
135 debug
136 .add_hw_breakpoint(&self.vcpu_fd, addr)
137 .map_err(|e| {
138 log::error!("Failed to add hw breakpoint: {:?}", e);
139
140 e
141 })
142 .is_ok(),
143 )),
144 DebugMsg::AddSwBreakpoint(addr) => Ok(DebugResponse::AddSwBreakpoint(
145 debug
146 .add_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
147 .map_err(|e| {
148 log::error!("Failed to add sw breakpoint: {:?}", e);
149
150 e
151 })
152 .is_ok(),
153 )),
154 DebugMsg::Continue => {
155 debug.set_single_step(&self.vcpu_fd, false).map_err(|e| {
156 log::error!("Failed to continue execution: {:?}", e);
157
158 e
159 })?;
160
161 Ok(DebugResponse::Continue)
162 }
163 DebugMsg::DisableDebug => {
164 self.disable_debug().map_err(|e| {
165 log::error!("Failed to disable debugging: {:?}", e);
166
167 e
168 })?;
169
170 Ok(DebugResponse::DisableDebug)
171 }
172 DebugMsg::GetCodeSectionOffset => {
173 let offset = dbg_mem_access_fn
174 .try_lock()
175 .map_err(|e| {
176 new_error!("Error locking at {}:{}: {}", file!(), line!(), e)
177 })?
178 .get_code_offset()
179 .map_err(|e| {
180 log::error!("Failed to get code offset: {:?}", e);
181
182 e
183 })?;
184
185 Ok(DebugResponse::GetCodeSectionOffset(offset as u64))
186 }
187 DebugMsg::ReadAddr(addr, len) => {
188 let mut data = vec![0u8; len];
189
190 debug
191 .read_addrs(&self.vcpu_fd, addr, &mut data, dbg_mem_access_fn)
192 .map_err(|e| {
193 log::error!("Failed to read from address: {:?}", e);
194
195 e
196 })?;
197
198 Ok(DebugResponse::ReadAddr(data))
199 }
200 DebugMsg::ReadRegisters => {
201 let mut regs = X86_64Regs::default();
202
203 debug
204 .read_regs(&self.vcpu_fd, &mut regs)
205 .map_err(|e| {
206 log::error!("Failed to read registers: {:?}", e);
207
208 e
209 })
210 .map(|_| DebugResponse::ReadRegisters(regs))
211 }
212 DebugMsg::RemoveHwBreakpoint(addr) => Ok(DebugResponse::RemoveHwBreakpoint(
213 debug
214 .remove_hw_breakpoint(&self.vcpu_fd, addr)
215 .map_err(|e| {
216 log::error!("Failed to remove hw breakpoint: {:?}", e);
217
218 e
219 })
220 .is_ok(),
221 )),
222 DebugMsg::RemoveSwBreakpoint(addr) => Ok(DebugResponse::RemoveSwBreakpoint(
223 debug
224 .remove_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
225 .map_err(|e| {
226 log::error!("Failed to remove sw breakpoint: {:?}", e);
227
228 e
229 })
230 .is_ok(),
231 )),
232 DebugMsg::Step => {
233 debug.set_single_step(&self.vcpu_fd, true).map_err(|e| {
234 log::error!("Failed to enable step instruction: {:?}", e);
235
236 e
237 })?;
238
239 Ok(DebugResponse::Step)
240 }
241 DebugMsg::WriteAddr(addr, data) => {
242 debug
243 .write_addrs(&self.vcpu_fd, addr, &data, dbg_mem_access_fn)
244 .map_err(|e| {
245 log::error!("Failed to write to address: {:?}", e);
246
247 e
248 })?;
249
250 Ok(DebugResponse::WriteAddr)
251 }
252 DebugMsg::WriteRegisters(regs) => debug
253 .write_regs(&self.vcpu_fd, ®s)
254 .map_err(|e| {
255 log::error!("Failed to write registers: {:?}", e);
256
257 e
258 })
259 .map(|_| DebugResponse::WriteRegisters),
260 }
261 } else {
262 Err(new_error!("Debugging is not enabled"))
263 }
264 }
265
266 pub(crate) fn recv_dbg_msg(&mut self) -> Result<DebugMsg> {
267 let gdb_conn = self
268 .gdb_conn
269 .as_mut()
270 .ok_or_else(|| new_error!("Debug is not enabled"))?;
271
272 gdb_conn.recv().map_err(|e| {
273 new_error!(
274 "Got an error while waiting to receive a
275 message: {:?}",
276 e
277 )
278 })
279 }
280
281 pub(crate) fn send_dbg_msg(&mut self, cmd: DebugResponse) -> Result<()> {
282 log::debug!("Sending {:?}", cmd);
283
284 let gdb_conn = self
285 .gdb_conn
286 .as_mut()
287 .ok_or_else(|| new_error!("Debug is not enabled"))?;
288
289 gdb_conn
290 .send(cmd)
291 .map_err(|e| new_error!("Got an error while sending a response message {:?}", e))
292 }
293 }
294}
295
296#[instrument(skip_all, parent = Span::current(), level = "Trace")]
299pub(crate) fn is_hypervisor_present() -> bool {
300 match Mshv::new() {
301 Ok(_) => true,
302 Err(_) => {
303 log::info!("MSHV is not available on this system");
304 false
305 }
306 }
307}
308
309pub(crate) struct HypervLinuxDriver {
312 _mshv: Mshv,
313 page_size: usize,
314 vm_fd: VmFd,
315 vcpu_fd: VcpuFd,
316 orig_rsp: GuestPtr,
317 entrypoint: u64,
318 interrupt_handle: Arc<LinuxInterruptHandle>,
319 mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
320 host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
321
322 sandbox_regions: Vec<MemoryRegion>, mmap_regions: Vec<MemoryRegion>, #[cfg(gdb)]
326 debug: Option<MshvDebug>,
327 #[cfg(gdb)]
328 gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
329 #[cfg(crashdump)]
330 rt_cfg: SandboxRuntimeConfig,
331 #[cfg(feature = "trace_guest")]
332 #[allow(dead_code)]
333 trace_info: TraceInfo,
334}
335
336impl HypervLinuxDriver {
337 #[allow(clippy::too_many_arguments)]
346 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
348 pub(crate) fn new(
349 mem_regions: Vec<MemoryRegion>,
350 entrypoint_ptr: GuestPtr,
351 rsp_ptr: GuestPtr,
352 pml4_ptr: GuestPtr,
353 config: &SandboxConfiguration,
354 #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
355 #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
356 #[cfg(feature = "trace_guest")] trace_info: TraceInfo,
357 ) -> Result<Self> {
358 let mshv = Mshv::new()?;
359 let pr = Default::default();
360 #[cfg(mshv2)]
361 let vm_fd = mshv.create_vm_with_config(&pr)?;
362 #[cfg(mshv3)]
363 let vm_fd = {
364 let vm_fd = mshv.create_vm_with_args(&pr)?;
369 let features: hv_partition_synthetic_processor_features = Default::default();
370 vm_fd.hvcall_set_partition_property(
371 hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
372 unsafe { features.as_uint64[0] },
373 )?;
374 vm_fd.initialize()?;
375 vm_fd
376 };
377
378 let mut vcpu_fd = vm_fd.create_vcpu(0)?;
379
380 #[cfg(gdb)]
381 let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
382 let mut debug = MshvDebug::new();
383 debug.add_hw_breakpoint(&vcpu_fd, entrypoint_ptr.absolute()?)?;
384
385 vm_fd
390 .install_intercept(mshv_install_intercept {
391 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
392 intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
393 intercept_parameter: hv_intercept_parameters {
395 exception_vector: 0x1,
396 },
397 })
398 .map_err(|e| new_error!("Cannot install debug exception intercept: {}", e))?;
399
400 vm_fd
402 .install_intercept(mshv_install_intercept {
403 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
404 intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
405 intercept_parameter: hv_intercept_parameters {
407 exception_vector: 0x3,
408 },
409 })
410 .map_err(|e| new_error!("Cannot install breakpoint exception intercept: {}", e))?;
411
412 (Some(debug), Some(gdb_conn))
413 } else {
414 (None, None)
415 };
416
417 mem_regions.iter().try_for_each(|region| {
418 let mshv_region = region.to_owned().into();
419 vm_fd.map_user_memory(mshv_region)
420 })?;
421
422 Self::setup_initial_sregs(&mut vcpu_fd, pml4_ptr.absolute()?)?;
423
424 let interrupt_handle = Arc::new(LinuxInterruptHandle {
425 running: AtomicU64::new(0),
426 cancel_requested: AtomicBool::new(false),
427 #[cfg(gdb)]
428 debug_interrupt: AtomicBool::new(false),
429 #[cfg(all(
430 target_arch = "x86_64",
431 target_vendor = "unknown",
432 target_os = "linux",
433 target_env = "musl"
434 ))]
435 tid: AtomicU64::new(unsafe { libc::pthread_self() as u64 }),
436 #[cfg(not(all(
437 target_arch = "x86_64",
438 target_vendor = "unknown",
439 target_os = "linux",
440 target_env = "musl"
441 )))]
442 tid: AtomicU64::new(unsafe { libc::pthread_self() }),
443 retry_delay: config.get_interrupt_retry_delay(),
444 sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
445 dropped: AtomicBool::new(false),
446 });
447
448 #[allow(unused_mut)]
449 let mut hv = Self {
450 _mshv: mshv,
451 page_size: 0,
452 vm_fd,
453 vcpu_fd,
454 sandbox_regions: mem_regions,
455 mmap_regions: Vec::new(),
456 entrypoint: entrypoint_ptr.absolute()?,
457 orig_rsp: rsp_ptr,
458 interrupt_handle: interrupt_handle.clone(),
459 mem_mgr: None,
460 host_funcs: None,
461 #[cfg(gdb)]
462 debug,
463 #[cfg(gdb)]
464 gdb_conn,
465 #[cfg(crashdump)]
466 rt_cfg,
467 #[cfg(feature = "trace_guest")]
468 trace_info,
469 };
470
471 #[cfg(gdb)]
474 if hv.debug.is_some() {
475 hv.send_dbg_msg(DebugResponse::InterruptHandle(interrupt_handle))?;
476 }
477
478 Ok(hv)
479 }
480
481 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
482 fn setup_initial_sregs(vcpu: &mut VcpuFd, _pml4_addr: u64) -> Result<()> {
483 #[cfg(feature = "init-paging")]
484 let sregs = SpecialRegisters {
485 cr0: CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP,
486 cr4: CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT,
487 cr3: _pml4_addr,
488 efer: EFER_LME | EFER_LMA | EFER_SCE | EFER_NX,
489 cs: SegmentRegister {
490 type_: 11,
491 present: 1,
492 s: 1,
493 l: 1,
494 ..Default::default()
495 },
496 tr: SegmentRegister {
497 limit: 65535,
498 type_: 11,
499 present: 1,
500 ..Default::default()
501 },
502 ..Default::default()
503 };
504
505 #[cfg(not(feature = "init-paging"))]
506 let sregs = SpecialRegisters {
507 cs: SegmentRegister {
508 base: 0,
509 selector: 0,
510 limit: 0xFFFF,
511 type_: 11,
512 present: 1,
513 s: 1,
514 ..Default::default()
515 },
516 ds: SegmentRegister {
517 base: 0,
518 selector: 0,
519 limit: 0xFFFF,
520 type_: 3,
521 present: 1,
522 s: 1,
523 ..Default::default()
524 },
525 tr: SegmentRegister {
526 base: 0,
527 selector: 0,
528 limit: 0xFFFF,
529 type_: 11,
530 present: 1,
531 s: 0,
532 ..Default::default()
533 },
534 ..Default::default()
535 };
536 vcpu.set_sregs(&sregs)?;
537 Ok(())
538 }
539}
540
541impl Debug for HypervLinuxDriver {
542 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
543 let mut f = f.debug_struct("Hyperv Linux Driver");
544
545 f.field("Entrypoint", &self.entrypoint)
546 .field("Original RSP", &self.orig_rsp);
547
548 for region in &self.sandbox_regions {
549 f.field("Sandbox Memory Region", ®ion);
550 }
551 for region in &self.mmap_regions {
552 f.field("Mapped Memory Region", ®ion);
553 }
554
555 let regs = self.vcpu_fd.get_regs();
556
557 if let Ok(regs) = regs {
558 f.field("Registers", ®s);
559 }
560
561 let sregs = self.vcpu_fd.get_sregs();
562
563 if let Ok(sregs) = sregs {
564 f.field("Special Registers", &sregs);
565 }
566
567 f.finish()
568 }
569}
570
571#[cfg(feature = "trace_guest")]
572impl From<TraceRegister> for hv_register_name {
573 fn from(r: TraceRegister) -> Self {
574 match r {
575 TraceRegister::RAX => hv_register_name_HV_X64_REGISTER_RAX,
576 TraceRegister::RCX => hv_register_name_HV_X64_REGISTER_RCX,
577 TraceRegister::RIP => hv_register_name_HV_X64_REGISTER_RIP,
578 TraceRegister::RSP => hv_register_name_HV_X64_REGISTER_RSP,
579 TraceRegister::RBP => hv_register_name_HV_X64_REGISTER_RBP,
580 }
581 }
582}
583
584impl Hypervisor for HypervLinuxDriver {
585 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
586 fn initialise(
587 &mut self,
588 peb_addr: RawPtr,
589 seed: u64,
590 page_size: u32,
591 mem_mgr: MemMgrWrapper<HostSharedMemory>,
592 host_funcs: Arc<Mutex<FunctionRegistry>>,
593 max_guest_log_level: Option<LevelFilter>,
594 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
595 ) -> Result<()> {
596 self.mem_mgr = Some(mem_mgr);
597 self.host_funcs = Some(host_funcs);
598 self.page_size = page_size as usize;
599
600 let max_guest_log_level: u64 = match max_guest_log_level {
601 Some(level) => level as u64,
602 None => self.get_max_log_level().into(),
603 };
604
605 let regs = StandardRegisters {
606 rip: self.entrypoint,
607 rsp: self.orig_rsp.absolute()?,
608 rflags: 2, rdi: peb_addr.into(),
612 rsi: seed,
613 rdx: page_size.into(),
614 rcx: max_guest_log_level,
615
616 ..Default::default()
617 };
618 self.vcpu_fd.set_regs(®s)?;
619
620 VirtualCPU::run(
621 self.as_mut_hypervisor(),
622 #[cfg(gdb)]
623 dbg_mem_access_fn,
624 )
625 }
626
627 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
628 unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
629 if [
630 rgn.guest_region.start,
631 rgn.guest_region.end,
632 rgn.host_region.start,
633 rgn.host_region.end,
634 ]
635 .iter()
636 .any(|x| x % self.page_size != 0)
637 {
638 log_then_return!("region is not page-aligned");
639 }
640 let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
641 self.vm_fd.map_user_memory(mshv_region)?;
642 self.mmap_regions.push(rgn.to_owned());
643 Ok(())
644 }
645
646 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
647 unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
648 if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
649 let removed_region = self.mmap_regions.remove(pos);
650 let mshv_region: mshv_user_mem_region = removed_region.into();
651 self.vm_fd.unmap_user_memory(mshv_region)?;
652 Ok(())
653 } else {
654 Err(new_error!("Tried to unmap region that is not mapped"))
655 }
656 }
657
658 fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
659 Box::new(self.mmap_regions.iter())
660 }
661
662 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
663 fn dispatch_call_from_host(
664 &mut self,
665 dispatch_func_addr: RawPtr,
666 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
667 ) -> Result<()> {
668 let regs = StandardRegisters {
670 rip: dispatch_func_addr.into(),
671 rsp: self.orig_rsp.absolute()?,
672 rflags: 2, ..Default::default()
674 };
675 self.vcpu_fd.set_regs(®s)?;
676
677 let fpu = FloatingPointUnit {
679 fcw: FP_CONTROL_WORD_DEFAULT,
680 ftwx: FP_TAG_WORD_DEFAULT,
681 mxcsr: MXCSR_DEFAULT,
682 ..Default::default() };
684 self.vcpu_fd.set_fpu(&fpu)?;
685
686 VirtualCPU::run(
688 self.as_mut_hypervisor(),
689 #[cfg(gdb)]
690 dbg_mem_access_fn,
691 )?;
692
693 Ok(())
694 }
695
696 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
697 fn handle_io(
698 &mut self,
699 port: u16,
700 data: Vec<u8>,
701 rip: u64,
702 instruction_length: u64,
703 ) -> Result<()> {
704 let mut padded = [0u8; 4];
705 let copy_len = data.len().min(4);
706 padded[..copy_len].copy_from_slice(&data[..copy_len]);
707 let val = u32::from_le_bytes(padded);
708
709 #[cfg(feature = "trace_guest")]
710 {
711 let mem_mgr_option = self.mem_mgr.take();
716 let mut mem_mgr = mem_mgr_option
717 .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
718 let host_funcs = self
719 .host_funcs
720 .as_ref()
721 .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
722 .clone();
723
724 handle_outb(&mut mem_mgr, host_funcs, self, port, val)?;
725
726 self.mem_mgr = Some(mem_mgr);
728 }
729
730 #[cfg(not(feature = "trace_guest"))]
731 {
732 let mem_mgr = self
733 .mem_mgr
734 .as_mut()
735 .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
736 let host_funcs = self
737 .host_funcs
738 .as_ref()
739 .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
740 .clone();
741
742 handle_outb(mem_mgr, host_funcs, port, val)?;
743 }
744
745 self.vcpu_fd.set_reg(&[hv_register_assoc {
747 name: hv_register_name_HV_X64_REGISTER_RIP,
748 value: hv_register_value {
749 reg64: rip + instruction_length,
750 },
751 ..Default::default()
752 }])?;
753 Ok(())
754 }
755
756 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
757 fn run(&mut self) -> Result<super::HyperlightExit> {
758 const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
759 const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
760 hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
761 const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
762 const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
763 #[cfg(gdb)]
764 const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
765
766 self.interrupt_handle
767 .tid
768 .store(unsafe { libc::pthread_self() as u64 }, Ordering::Relaxed);
769 self.interrupt_handle
772 .set_running_and_increment_generation()
773 .map_err(|e| {
774 new_error!(
775 "Error setting running state and incrementing generation: {}",
776 e
777 )
778 })?;
779 #[cfg(not(gdb))]
780 let debug_interrupt = false;
781 #[cfg(gdb)]
782 let debug_interrupt = self
783 .interrupt_handle
784 .debug_interrupt
785 .load(Ordering::Relaxed);
786
787 let exit_reason = if self
792 .interrupt_handle
793 .cancel_requested
794 .load(Ordering::Relaxed)
795 || debug_interrupt
796 {
797 Err(mshv_ioctls::MshvError::from(libc::EINTR))
798 } else {
799 #[cfg(feature = "trace_guest")]
800 if self.trace_info.guest_start_epoch.is_none() {
801 crate::debug!("MSHV - Guest Start Epoch set");
803 self.trace_info.guest_start_tsc =
804 Some(hyperlight_guest_tracing::invariant_tsc::read_tsc());
805 self.trace_info.guest_start_epoch = Some(std::time::Instant::now());
806 }
807 #[cfg(mshv2)]
813 {
814 let hv_message: hv_message = Default::default();
815 self.vcpu_fd.run(hv_message)
816 }
817 #[cfg(mshv3)]
818 self.vcpu_fd.run()
819 };
820 let cancel_requested = self
824 .interrupt_handle
825 .cancel_requested
826 .load(Ordering::Relaxed);
827 #[cfg(gdb)]
828 let debug_interrupt = self
829 .interrupt_handle
830 .debug_interrupt
831 .load(Ordering::Relaxed);
832 self.interrupt_handle.clear_running_bit();
837 let result = match exit_reason {
842 Ok(m) => match m.header.message_type {
843 HALT_MESSAGE => {
844 crate::debug!("mshv - Halt Details : {:#?}", &self);
845 HyperlightExit::Halt()
846 }
847 IO_PORT_INTERCEPT_MESSAGE => {
848 let io_message = m.to_ioport_info().map_err(mshv_ioctls::MshvError::from)?;
849 let port_number = io_message.port_number;
850 let rip = io_message.header.rip;
851 let rax = io_message.rax;
852 let instruction_length = io_message.header.instruction_length() as u64;
853 crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
854 HyperlightExit::IoOut(
855 port_number,
856 rax.to_le_bytes().to_vec(),
857 rip,
858 instruction_length,
859 )
860 }
861 UNMAPPED_GPA_MESSAGE => {
862 let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
863 let addr = mimo_message.guest_physical_address;
864 crate::debug!(
865 "mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
866 addr,
867 &self
868 );
869 HyperlightExit::Mmio(addr)
870 }
871 INVALID_GPA_ACCESS_MESSAGE => {
872 let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
873 let gpa = mimo_message.guest_physical_address;
874 let access_info = MemoryRegionFlags::try_from(mimo_message)?;
875 crate::debug!(
876 "mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
877 gpa,
878 &self
879 );
880 match get_memory_access_violation(
881 gpa as usize,
882 self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
883 access_info,
884 ) {
885 Some(access_info_violation) => access_info_violation,
886 None => HyperlightExit::Mmio(gpa),
887 }
888 }
889 #[cfg(gdb)]
894 EXCEPTION_INTERCEPT => {
895 let ex_info = match m.to_exception_info().map_err(mshv_ioctls::MshvError::from)
898 {
899 Ok(info) => info,
900 Err(e) => {
901 log_then_return!("Error converting to exception info: {:?}", e);
902 }
903 };
904
905 match self.get_stop_reason(ex_info) {
906 Ok(reason) => HyperlightExit::Debug(reason),
907 Err(e) => {
908 log_then_return!("Error getting stop reason: {:?}", e);
909 }
910 }
911 }
912 other => {
913 crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
914 #[cfg(crashdump)]
915 let _ = crashdump::generate_crashdump(self);
916 log_then_return!("unknown Hyper-V run message type {:?}", other);
917 }
918 },
919 Err(e) => match e.errno() {
920 libc::EINTR => {
922 if cancel_requested {
925 self.interrupt_handle
926 .cancel_requested
927 .store(false, Ordering::Relaxed);
928 HyperlightExit::Cancelled()
929 } else {
930 #[cfg(gdb)]
931 if debug_interrupt {
932 self.interrupt_handle
933 .debug_interrupt
934 .store(false, Ordering::Relaxed);
935
936 HyperlightExit::Debug(VcpuStopReason::Interrupt)
940 } else {
941 HyperlightExit::Retry()
942 }
943
944 #[cfg(not(gdb))]
945 HyperlightExit::Retry()
946 }
947 }
948 libc::EAGAIN => HyperlightExit::Retry(),
949 _ => {
950 crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
951 log_then_return!("Error running VCPU {:?}", e);
952 }
953 },
954 };
955 Ok(result)
956 }
957
958 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
959 fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
960 self as &mut dyn Hypervisor
961 }
962
963 fn interrupt_handle(&self) -> Arc<dyn InterruptHandle> {
964 self.interrupt_handle.clone()
965 }
966
967 #[cfg(crashdump)]
968 fn crashdump_context(&self) -> Result<Option<super::crashdump::CrashDumpContext>> {
969 if self.rt_cfg.guest_core_dump {
970 let mut regs = [0; 27];
971
972 let vcpu_regs = self.vcpu_fd.get_regs()?;
973 let sregs = self.vcpu_fd.get_sregs()?;
974 let xsave = self.vcpu_fd.get_xsave()?;
975
976 regs[0] = vcpu_regs.r15; regs[1] = vcpu_regs.r14; regs[2] = vcpu_regs.r13; regs[3] = vcpu_regs.r12; regs[4] = vcpu_regs.rbp; regs[5] = vcpu_regs.rbx; regs[6] = vcpu_regs.r11; regs[7] = vcpu_regs.r10; regs[8] = vcpu_regs.r9; regs[9] = vcpu_regs.r8; regs[10] = vcpu_regs.rax; regs[11] = vcpu_regs.rcx; regs[12] = vcpu_regs.rdx; regs[13] = vcpu_regs.rsi; regs[14] = vcpu_regs.rdi; regs[15] = 0; regs[16] = vcpu_regs.rip; regs[17] = sregs.cs.selector as u64; regs[18] = vcpu_regs.rflags; regs[19] = vcpu_regs.rsp; regs[20] = sregs.ss.selector as u64; regs[21] = sregs.fs.base; regs[22] = sregs.gs.base; regs[23] = sregs.ds.selector as u64; regs[24] = sregs.es.selector as u64; regs[25] = sregs.fs.selector as u64; regs[26] = sregs.gs.selector as u64; let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
1007 Path::new(&path)
1008 .file_name()
1009 .and_then(|name| name.to_os_string().into_string().ok())
1010 });
1011
1012 Ok(Some(crashdump::CrashDumpContext::new(
1013 &self.sandbox_regions,
1014 regs,
1015 xsave.buffer.to_vec(),
1016 self.entrypoint,
1017 self.rt_cfg.binary_path.clone(),
1018 filename,
1019 )))
1020 } else {
1021 Ok(None)
1022 }
1023 }
1024
1025 #[cfg(gdb)]
1026 fn handle_debug(
1027 &mut self,
1028 dbg_mem_access_fn: std::sync::Arc<
1029 std::sync::Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>,
1030 >,
1031 stop_reason: VcpuStopReason,
1032 ) -> Result<()> {
1033 if self.debug.is_none() {
1034 return Err(new_error!("Debugging is not enabled"));
1035 }
1036
1037 match stop_reason {
1038 VcpuStopReason::Crash => {
1043 self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1044 .map_err(|e| {
1045 new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1046 })?;
1047
1048 loop {
1049 log::debug!("Debug wait for event to resume vCPU");
1050 let req = self.recv_dbg_msg()?;
1052
1053 let mut deny_continue = false;
1055 let mut detach = false;
1057
1058 let response = match req {
1059 DebugMsg::DisableDebug => {
1062 detach = true;
1063 DebugResponse::DisableDebug
1064 }
1065 DebugMsg::Continue | DebugMsg::Step => {
1067 deny_continue = true;
1068 DebugResponse::NotAllowed
1069 }
1070 DebugMsg::AddHwBreakpoint(_)
1072 | DebugMsg::AddSwBreakpoint(_)
1073 | DebugMsg::RemoveHwBreakpoint(_)
1074 | DebugMsg::RemoveSwBreakpoint(_)
1075 | DebugMsg::WriteAddr(_, _)
1076 | DebugMsg::WriteRegisters(_) => DebugResponse::NotAllowed,
1077
1078 _ => {
1080 let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1081 match result {
1082 Ok(response) => response,
1083 Err(HyperlightError::TranslateGuestAddress(_)) => {
1084 DebugResponse::ErrorOccurred
1086 }
1087 Err(e) => {
1088 log::error!("Error processing debug request: {:?}", e);
1089 return Err(e);
1090 }
1091 }
1092 }
1093 };
1094
1095 self.send_dbg_msg(response)
1097 .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1098
1099 if deny_continue {
1103 self.send_dbg_msg(DebugResponse::VcpuStopped(VcpuStopReason::Crash))
1104 .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1105 }
1106
1107 if detach {
1110 break;
1111 }
1112 }
1113 }
1114 _ => {
1117 self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1119 .map_err(|e| {
1120 new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1121 })?;
1122
1123 loop {
1124 log::debug!("Debug wait for event to resume vCPU");
1125 let req = self.recv_dbg_msg()?;
1127
1128 let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1129
1130 let response = match result {
1131 Ok(response) => response,
1132 Err(HyperlightError::TranslateGuestAddress(_)) => {
1134 DebugResponse::ErrorOccurred
1135 }
1136 Err(e) => {
1137 return Err(e);
1138 }
1139 };
1140
1141 let cont = matches!(
1142 response,
1143 DebugResponse::Continue | DebugResponse::Step | DebugResponse::DisableDebug
1144 );
1145
1146 self.send_dbg_msg(response)
1147 .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1148
1149 if cont {
1152 break;
1153 }
1154 }
1155 }
1156 }
1157
1158 Ok(())
1159 }
1160
1161 fn check_stack_guard(&self) -> Result<bool> {
1162 if let Some(mgr) = self.mem_mgr.as_ref() {
1163 mgr.check_stack_guard()
1164 } else {
1165 Err(new_error!("Memory manager is not initialized"))
1166 }
1167 }
1168
1169 #[cfg(feature = "trace_guest")]
1170 fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
1171 let mut assoc = [hv_register_assoc {
1172 name: reg.into(),
1173 ..Default::default()
1174 }];
1175 self.vcpu_fd.get_reg(&mut assoc)?;
1176 unsafe { Ok(assoc[0].value.reg64) }
1178 }
1179
1180 #[cfg(feature = "trace_guest")]
1181 fn trace_info_as_ref(&self) -> &TraceInfo {
1182 &self.trace_info
1183 }
1184 #[cfg(feature = "trace_guest")]
1185 fn trace_info_as_mut(&mut self) -> &mut TraceInfo {
1186 &mut self.trace_info
1187 }
1188}
1189
1190impl Drop for HypervLinuxDriver {
1191 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
1192 fn drop(&mut self) {
1193 self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
1194 for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
1195 let mshv_region: mshv_user_mem_region = region.to_owned().into();
1196 match self.vm_fd.unmap_user_memory(mshv_region) {
1197 Ok(_) => (),
1198 Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
1199 }
1200 }
1201 }
1202}
1203
1204#[cfg(test)]
1205mod tests {
1206 use super::*;
1207 #[cfg(feature = "unwind_guest")]
1208 use crate::mem::exe::DummyUnwindInfo;
1209 use crate::mem::memory_region::MemoryRegionVecBuilder;
1210 use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
1211
1212 #[rustfmt::skip]
1213 const CODE: [u8; 12] = [
1214 0xba, 0xf8, 0x03, 0x00, 0xd8, 0x04, b'0', 0xee, 0xb0, b'\0', 0xee, 0xf4, ];
1223
1224 fn shared_mem_with_code(
1225 code: &[u8],
1226 mem_size: usize,
1227 load_offset: usize,
1228 ) -> Result<Box<ExclusiveSharedMemory>> {
1229 if load_offset > mem_size {
1230 log_then_return!(
1231 "code load offset ({}) > memory size ({})",
1232 load_offset,
1233 mem_size
1234 );
1235 }
1236 let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
1237 shared_mem.copy_from_slice(code, load_offset)?;
1238 Ok(Box::new(shared_mem))
1239 }
1240
1241 #[test]
1242 fn create_driver() {
1243 if !super::is_hypervisor_present() {
1244 return;
1245 }
1246 const MEM_SIZE: usize = 0x3000;
1247 let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
1248 let rsp_ptr = GuestPtr::try_from(0).unwrap();
1249 let pml4_ptr = GuestPtr::try_from(0).unwrap();
1250 let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
1251 let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
1252 regions.push_page_aligned(
1253 MEM_SIZE,
1254 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
1255 crate::mem::memory_region::MemoryRegionType::Code,
1256 );
1257 let config: SandboxConfiguration = Default::default();
1258
1259 super::HypervLinuxDriver::new(
1260 regions.build(),
1261 entrypoint_ptr,
1262 rsp_ptr,
1263 pml4_ptr,
1264 &config,
1265 #[cfg(gdb)]
1266 None,
1267 #[cfg(crashdump)]
1268 SandboxRuntimeConfig {
1269 #[cfg(crashdump)]
1270 binary_path: None,
1271 #[cfg(gdb)]
1272 debug_info: None,
1273 #[cfg(crashdump)]
1274 guest_core_dump: true,
1275 },
1276 #[cfg(feature = "trace_guest")]
1277 TraceInfo::new(
1278 #[cfg(feature = "unwind_guest")]
1279 Arc::new(DummyUnwindInfo {}),
1280 )
1281 .unwrap(),
1282 )
1283 .unwrap();
1284 }
1285}