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