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