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};
28
29use log::{error, LevelFilter};
30#[cfg(mshv2)]
31use mshv_bindings::hv_message;
32#[cfg(gdb)]
33use mshv_bindings::{
34 hv_intercept_parameters, hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
35 hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT, mshv_install_intercept,
36 HV_INTERCEPT_ACCESS_MASK_EXECUTE,
37};
38use mshv_bindings::{
39 hv_message_type, hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
40 hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT, hv_register_assoc,
41 hv_register_name_HV_X64_REGISTER_RIP, hv_register_value, mshv_user_mem_region,
42 FloatingPointUnit, SegmentRegister, SpecialRegisters, StandardRegisters,
43};
44#[cfg(mshv3)]
45use mshv_bindings::{
46 hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
47 hv_partition_synthetic_processor_features,
48};
49use mshv_ioctls::{Mshv, VcpuFd, VmFd};
50use tracing::{instrument, Span};
51
52use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
53#[cfg(gdb)]
54use super::gdb::{DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, MshvDebug};
55#[cfg(gdb)]
56use super::handlers::DbgMemAccessHandlerWrapper;
57use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
58use super::{
59 Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR,
60 CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
61};
62use crate::hypervisor::hypervisor_handler::HypervisorHandler;
63use crate::hypervisor::HyperlightExit;
64use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
65use crate::mem::ptr::{GuestPtr, RawPtr};
66#[cfg(gdb)]
67use crate::HyperlightError;
68use crate::{log_then_return, new_error, Result};
69
70#[cfg(gdb)]
71mod debug {
72 use std::sync::{Arc, Mutex};
73
74 use super::mshv_bindings::hv_x64_exception_intercept_message;
75 use super::{HypervLinuxDriver, *};
76 use crate::hypervisor::gdb::{DebugMsg, DebugResponse, VcpuStopReason, X86_64Regs};
77 use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
78 use crate::{new_error, Result};
79
80 impl HypervLinuxDriver {
81 fn disable_debug(&mut self) -> Result<()> {
83 let mut debug = MshvDebug::default();
84
85 debug.set_single_step(&self.vcpu_fd, false)?;
86
87 self.debug = Some(debug);
88
89 Ok(())
90 }
91
92 pub(crate) fn get_stop_reason(
94 &mut self,
95 ex_info: hv_x64_exception_intercept_message,
96 ) -> Result<VcpuStopReason> {
97 let debug = self
98 .debug
99 .as_mut()
100 .ok_or_else(|| new_error!("Debug is not enabled"))?;
101
102 debug.get_stop_reason(&self.vcpu_fd, ex_info.exception_vector, self.entrypoint)
103 }
104
105 pub(crate) fn process_dbg_request(
106 &mut self,
107 req: DebugMsg,
108 dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
109 ) -> Result<DebugResponse> {
110 if let Some(debug) = self.debug.as_mut() {
111 match req {
112 DebugMsg::AddHwBreakpoint(addr) => Ok(DebugResponse::AddHwBreakpoint(
113 debug
114 .add_hw_breakpoint(&self.vcpu_fd, addr)
115 .map_err(|e| {
116 log::error!("Failed to add hw breakpoint: {:?}", e);
117
118 e
119 })
120 .is_ok(),
121 )),
122 DebugMsg::AddSwBreakpoint(addr) => Ok(DebugResponse::AddSwBreakpoint(
123 debug
124 .add_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
125 .map_err(|e| {
126 log::error!("Failed to add sw breakpoint: {:?}", e);
127
128 e
129 })
130 .is_ok(),
131 )),
132 DebugMsg::Continue => {
133 debug.set_single_step(&self.vcpu_fd, false).map_err(|e| {
134 log::error!("Failed to continue execution: {:?}", e);
135
136 e
137 })?;
138
139 Ok(DebugResponse::Continue)
140 }
141 DebugMsg::DisableDebug => {
142 self.disable_debug().map_err(|e| {
143 log::error!("Failed to disable debugging: {:?}", e);
144
145 e
146 })?;
147
148 Ok(DebugResponse::DisableDebug)
149 }
150 DebugMsg::GetCodeSectionOffset => {
151 let offset = dbg_mem_access_fn
152 .try_lock()
153 .map_err(|e| {
154 new_error!("Error locking at {}:{}: {}", file!(), line!(), e)
155 })?
156 .get_code_offset()
157 .map_err(|e| {
158 log::error!("Failed to get code offset: {:?}", e);
159
160 e
161 })?;
162
163 Ok(DebugResponse::GetCodeSectionOffset(offset as u64))
164 }
165 DebugMsg::ReadAddr(addr, len) => {
166 let mut data = vec![0u8; len];
167
168 debug
169 .read_addrs(&self.vcpu_fd, addr, &mut data, dbg_mem_access_fn)
170 .map_err(|e| {
171 log::error!("Failed to read from address: {:?}", e);
172
173 e
174 })?;
175
176 Ok(DebugResponse::ReadAddr(data))
177 }
178 DebugMsg::ReadRegisters => {
179 let mut regs = X86_64Regs::default();
180
181 debug
182 .read_regs(&self.vcpu_fd, &mut regs)
183 .map_err(|e| {
184 log::error!("Failed to read registers: {:?}", e);
185
186 e
187 })
188 .map(|_| DebugResponse::ReadRegisters(regs))
189 }
190 DebugMsg::RemoveHwBreakpoint(addr) => Ok(DebugResponse::RemoveHwBreakpoint(
191 debug
192 .remove_hw_breakpoint(&self.vcpu_fd, addr)
193 .map_err(|e| {
194 log::error!("Failed to remove hw breakpoint: {:?}", e);
195
196 e
197 })
198 .is_ok(),
199 )),
200 DebugMsg::RemoveSwBreakpoint(addr) => Ok(DebugResponse::RemoveSwBreakpoint(
201 debug
202 .remove_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
203 .map_err(|e| {
204 log::error!("Failed to remove sw breakpoint: {:?}", e);
205
206 e
207 })
208 .is_ok(),
209 )),
210 DebugMsg::Step => {
211 debug.set_single_step(&self.vcpu_fd, true).map_err(|e| {
212 log::error!("Failed to enable step instruction: {:?}", e);
213
214 e
215 })?;
216
217 Ok(DebugResponse::Step)
218 }
219 DebugMsg::WriteAddr(addr, data) => {
220 debug
221 .write_addrs(&self.vcpu_fd, addr, &data, dbg_mem_access_fn)
222 .map_err(|e| {
223 log::error!("Failed to write to address: {:?}", e);
224
225 e
226 })?;
227
228 Ok(DebugResponse::WriteAddr)
229 }
230 DebugMsg::WriteRegisters(regs) => debug
231 .write_regs(&self.vcpu_fd, ®s)
232 .map_err(|e| {
233 log::error!("Failed to write registers: {:?}", e);
234
235 e
236 })
237 .map(|_| DebugResponse::WriteRegisters),
238 }
239 } else {
240 Err(new_error!("Debugging is not enabled"))
241 }
242 }
243
244 pub(crate) fn recv_dbg_msg(&mut self) -> Result<DebugMsg> {
245 let gdb_conn = self
246 .gdb_conn
247 .as_mut()
248 .ok_or_else(|| new_error!("Debug is not enabled"))?;
249
250 gdb_conn.recv().map_err(|e| {
251 new_error!(
252 "Got an error while waiting to receive a
253 message: {:?}",
254 e
255 )
256 })
257 }
258
259 pub(crate) fn send_dbg_msg(&mut self, cmd: DebugResponse) -> Result<()> {
260 log::debug!("Sending {:?}", cmd);
261
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
268 .send(cmd)
269 .map_err(|e| new_error!("Got an error while sending a response message {:?}", e))
270 }
271 }
272}
273
274#[instrument(skip_all, parent = Span::current(), level = "Trace")]
277pub(crate) fn is_hypervisor_present() -> bool {
278 match Mshv::new() {
279 Ok(_) => true,
280 Err(_) => {
281 log::info!("MSHV is not available on this system");
282 false
283 }
284 }
285}
286
287pub(super) struct HypervLinuxDriver {
290 _mshv: Mshv,
291 vm_fd: VmFd,
292 vcpu_fd: VcpuFd,
293 entrypoint: u64,
294 mem_regions: Vec<MemoryRegion>,
295 orig_rsp: GuestPtr,
296
297 #[cfg(gdb)]
298 debug: Option<MshvDebug>,
299 #[cfg(gdb)]
300 gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
301}
302
303impl HypervLinuxDriver {
304 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
313 pub(super) fn new(
314 mem_regions: Vec<MemoryRegion>,
315 entrypoint_ptr: GuestPtr,
316 rsp_ptr: GuestPtr,
317 pml4_ptr: GuestPtr,
318 #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
319 ) -> Result<Self> {
320 let mshv = Mshv::new()?;
321 let pr = Default::default();
322 #[cfg(mshv2)]
323 let vm_fd = mshv.create_vm_with_config(&pr)?;
324 #[cfg(mshv3)]
325 let vm_fd = {
326 let vm_fd = mshv.create_vm_with_args(&pr)?;
331 let features: hv_partition_synthetic_processor_features = Default::default();
332 vm_fd.hvcall_set_partition_property(
333 hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
334 unsafe { features.as_uint64[0] },
335 )?;
336 vm_fd.initialize()?;
337 vm_fd
338 };
339
340 let mut vcpu_fd = vm_fd.create_vcpu(0)?;
341
342 #[cfg(gdb)]
343 let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
344 let mut debug = MshvDebug::new();
345 debug.add_hw_breakpoint(&vcpu_fd, entrypoint_ptr.absolute()?)?;
346
347 vm_fd
352 .install_intercept(mshv_install_intercept {
353 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
354 intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
355 intercept_parameter: hv_intercept_parameters {
357 exception_vector: 0x1,
358 },
359 })
360 .map_err(|e| new_error!("Cannot install debug exception intercept: {}", e))?;
361
362 vm_fd
364 .install_intercept(mshv_install_intercept {
365 access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
366 intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
367 intercept_parameter: hv_intercept_parameters {
369 exception_vector: 0x3,
370 },
371 })
372 .map_err(|e| new_error!("Cannot install breakpoint exception intercept: {}", e))?;
373
374 (Some(debug), Some(gdb_conn))
375 } else {
376 (None, None)
377 };
378
379 mem_regions.iter().try_for_each(|region| {
380 let mshv_region = region.to_owned().into();
381 vm_fd.map_user_memory(mshv_region)
382 })?;
383
384 Self::setup_initial_sregs(&mut vcpu_fd, pml4_ptr.absolute()?)?;
385
386 Ok(Self {
387 _mshv: mshv,
388 vm_fd,
389 vcpu_fd,
390 mem_regions,
391 entrypoint: entrypoint_ptr.absolute()?,
392 orig_rsp: rsp_ptr,
393
394 #[cfg(gdb)]
395 debug,
396 #[cfg(gdb)]
397 gdb_conn,
398 })
399 }
400
401 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
402 fn setup_initial_sregs(vcpu: &mut VcpuFd, pml4_addr: u64) -> Result<()> {
403 let sregs = SpecialRegisters {
404 cr0: CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP,
405 cr4: CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT,
406 cr3: pml4_addr,
407 efer: EFER_LME | EFER_LMA | EFER_SCE | EFER_NX,
408 cs: SegmentRegister {
409 type_: 11,
410 present: 1,
411 s: 1,
412 l: 1,
413 ..Default::default()
414 },
415 tr: SegmentRegister {
416 limit: 65535,
417 type_: 11,
418 present: 1,
419 ..Default::default()
420 },
421 ..Default::default()
422 };
423 vcpu.set_sregs(&sregs)?;
424 Ok(())
425 }
426}
427
428impl Debug for HypervLinuxDriver {
429 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
430 let mut f = f.debug_struct("Hyperv Linux Driver");
431
432 f.field("Entrypoint", &self.entrypoint)
433 .field("Original RSP", &self.orig_rsp);
434
435 for region in &self.mem_regions {
436 f.field("Memory Region", ®ion);
437 }
438
439 let regs = self.vcpu_fd.get_regs();
440
441 if let Ok(regs) = regs {
442 f.field("Registers", ®s);
443 }
444
445 let sregs = self.vcpu_fd.get_sregs();
446
447 if let Ok(sregs) = sregs {
448 f.field("Special Registers", &sregs);
449 }
450
451 f.finish()
452 }
453}
454
455impl Hypervisor for HypervLinuxDriver {
456 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
457 fn initialise(
458 &mut self,
459 peb_addr: RawPtr,
460 seed: u64,
461 page_size: u32,
462 outb_hdl: OutBHandlerWrapper,
463 mem_access_hdl: MemAccessHandlerWrapper,
464 hv_handler: Option<HypervisorHandler>,
465 max_guest_log_level: Option<LevelFilter>,
466 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
467 ) -> Result<()> {
468 let max_guest_log_level: u64 = match max_guest_log_level {
469 Some(level) => level as u64,
470 None => self.get_max_log_level().into(),
471 };
472
473 let regs = StandardRegisters {
474 rip: self.entrypoint,
475 rsp: self.orig_rsp.absolute()?,
476 rflags: 2, rdi: peb_addr.into(),
480 rsi: seed,
481 rdx: page_size.into(),
482 rcx: max_guest_log_level,
483
484 ..Default::default()
485 };
486 self.vcpu_fd.set_regs(®s)?;
487
488 VirtualCPU::run(
489 self.as_mut_hypervisor(),
490 hv_handler,
491 outb_hdl,
492 mem_access_hdl,
493 #[cfg(gdb)]
494 dbg_mem_access_fn,
495 )?;
496
497 Ok(())
498 }
499
500 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
501 fn dispatch_call_from_host(
502 &mut self,
503 dispatch_func_addr: RawPtr,
504 outb_handle_fn: OutBHandlerWrapper,
505 mem_access_fn: MemAccessHandlerWrapper,
506 hv_handler: Option<HypervisorHandler>,
507 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
508 ) -> Result<()> {
509 let regs = StandardRegisters {
511 rip: dispatch_func_addr.into(),
512 rsp: self.orig_rsp.absolute()?,
513 rflags: 2, ..Default::default()
515 };
516 self.vcpu_fd.set_regs(®s)?;
517
518 let fpu = FloatingPointUnit {
520 fcw: FP_CONTROL_WORD_DEFAULT,
521 ftwx: FP_TAG_WORD_DEFAULT,
522 mxcsr: MXCSR_DEFAULT,
523 ..Default::default() };
525 self.vcpu_fd.set_fpu(&fpu)?;
526
527 VirtualCPU::run(
529 self.as_mut_hypervisor(),
530 hv_handler,
531 outb_handle_fn,
532 mem_access_fn,
533 #[cfg(gdb)]
534 dbg_mem_access_fn,
535 )?;
536
537 Ok(())
538 }
539
540 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
541 fn handle_io(
542 &mut self,
543 port: u16,
544 data: Vec<u8>,
545 rip: u64,
546 instruction_length: u64,
547 outb_handle_fn: OutBHandlerWrapper,
548 ) -> Result<()> {
549 let mut padded = [0u8; 4];
550 let copy_len = data.len().min(4);
551 padded[..copy_len].copy_from_slice(&data[..copy_len]);
552 let val = u32::from_le_bytes(padded);
553
554 outb_handle_fn
555 .try_lock()
556 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
557 .call(port, val)?;
558
559 self.vcpu_fd.set_reg(&[hv_register_assoc {
561 name: hv_register_name_HV_X64_REGISTER_RIP,
562 value: hv_register_value {
563 reg64: rip + instruction_length,
564 },
565 ..Default::default()
566 }])?;
567 Ok(())
568 }
569
570 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
571 fn run(&mut self) -> Result<super::HyperlightExit> {
572 const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
573 const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
574 hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
575 const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
576 const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
577 #[cfg(gdb)]
578 const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
579
580 #[cfg(mshv2)]
581 let run_result = {
582 let hv_message: hv_message = Default::default();
583 &self.vcpu_fd.run(hv_message)
584 };
585 #[cfg(mshv3)]
586 let run_result = &self.vcpu_fd.run();
587
588 let result = match run_result {
589 Ok(m) => match m.header.message_type {
590 HALT_MESSAGE => {
591 crate::debug!("mshv - Halt Details : {:#?}", &self);
592 HyperlightExit::Halt()
593 }
594 IO_PORT_INTERCEPT_MESSAGE => {
595 let io_message = m.to_ioport_info()?;
596 let port_number = io_message.port_number;
597 let rip = io_message.header.rip;
598 let rax = io_message.rax;
599 let instruction_length = io_message.header.instruction_length() as u64;
600 crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
601 HyperlightExit::IoOut(
602 port_number,
603 rax.to_le_bytes().to_vec(),
604 rip,
605 instruction_length,
606 )
607 }
608 UNMAPPED_GPA_MESSAGE => {
609 let mimo_message = m.to_memory_info()?;
610 let addr = mimo_message.guest_physical_address;
611 crate::debug!(
612 "mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
613 addr,
614 &self
615 );
616 HyperlightExit::Mmio(addr)
617 }
618 INVALID_GPA_ACCESS_MESSAGE => {
619 let mimo_message = m.to_memory_info()?;
620 let gpa = mimo_message.guest_physical_address;
621 let access_info = MemoryRegionFlags::try_from(mimo_message)?;
622 crate::debug!(
623 "mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
624 gpa,
625 &self
626 );
627 match self.get_memory_access_violation(
628 gpa as usize,
629 &self.mem_regions,
630 access_info,
631 ) {
632 Some(access_info_violation) => access_info_violation,
633 None => HyperlightExit::Mmio(gpa),
634 }
635 }
636 #[cfg(gdb)]
641 EXCEPTION_INTERCEPT => {
642 let ex_info = match m.to_exception_info() {
645 Ok(info) => info,
646 Err(e) => {
647 log_then_return!("Error converting to exception info: {:?}", e);
648 }
649 };
650
651 match self.get_stop_reason(ex_info) {
652 Ok(reason) => HyperlightExit::Debug(reason),
653 Err(e) => {
654 log_then_return!("Error getting stop reason: {:?}", e);
655 }
656 }
657 }
658 other => {
659 crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
660 log_then_return!("unknown Hyper-V run message type {:?}", other);
661 }
662 },
663 Err(e) => match e.errno() {
664 libc::EINTR => HyperlightExit::Cancelled(),
666 libc::EAGAIN => HyperlightExit::Retry(),
667 _ => {
668 crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
669 log_then_return!("Error running VCPU {:?}", e);
670 }
671 },
672 };
673 Ok(result)
674 }
675
676 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
677 fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
678 self as &mut dyn Hypervisor
679 }
680
681 #[cfg(crashdump)]
682 fn get_memory_regions(&self) -> &[MemoryRegion] {
683 &self.mem_regions
684 }
685
686 #[cfg(gdb)]
687 fn handle_debug(
688 &mut self,
689 dbg_mem_access_fn: std::sync::Arc<
690 std::sync::Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>,
691 >,
692 stop_reason: super::gdb::VcpuStopReason,
693 ) -> Result<()> {
694 self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
695 .map_err(|e| new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e))?;
696
697 loop {
698 log::debug!("Debug wait for event to resume vCPU");
699
700 let req = self.recv_dbg_msg()?;
702
703 let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
704
705 let response = match result {
706 Ok(response) => response,
707 Err(HyperlightError::TranslateGuestAddress(_)) => DebugResponse::ErrorOccurred,
709 Err(e) => {
710 return Err(e);
711 }
712 };
713
714 let cont = matches!(
716 response,
717 DebugResponse::Step | DebugResponse::Continue | DebugResponse::DisableDebug
718 );
719
720 self.send_dbg_msg(response)
721 .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
722
723 if cont {
724 break;
725 }
726 }
727
728 Ok(())
729 }
730}
731
732impl Drop for HypervLinuxDriver {
733 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
734 fn drop(&mut self) {
735 for region in &self.mem_regions {
736 let mshv_region: mshv_user_mem_region = region.to_owned().into();
737 match self.vm_fd.unmap_user_memory(mshv_region) {
738 Ok(_) => (),
739 Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
740 }
741 }
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use super::*;
748 use crate::mem::memory_region::MemoryRegionVecBuilder;
749 use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
750
751 #[rustfmt::skip]
752 const CODE: [u8; 12] = [
753 0xba, 0xf8, 0x03, 0x00, 0xd8, 0x04, b'0', 0xee, 0xb0, b'\0', 0xee, 0xf4, ];
762
763 fn shared_mem_with_code(
764 code: &[u8],
765 mem_size: usize,
766 load_offset: usize,
767 ) -> Result<Box<ExclusiveSharedMemory>> {
768 if load_offset > mem_size {
769 log_then_return!(
770 "code load offset ({}) > memory size ({})",
771 load_offset,
772 mem_size
773 );
774 }
775 let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
776 shared_mem.copy_from_slice(code, load_offset)?;
777 Ok(Box::new(shared_mem))
778 }
779
780 #[test]
781 fn create_driver() {
782 if !super::is_hypervisor_present() {
783 return;
784 }
785 const MEM_SIZE: usize = 0x3000;
786 let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
787 let rsp_ptr = GuestPtr::try_from(0).unwrap();
788 let pml4_ptr = GuestPtr::try_from(0).unwrap();
789 let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
790 let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
791 regions.push_page_aligned(
792 MEM_SIZE,
793 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
794 crate::mem::memory_region::MemoryRegionType::Code,
795 );
796 super::HypervLinuxDriver::new(
797 regions.build(),
798 entrypoint_ptr,
799 rsp_ptr,
800 pml4_ptr,
801 #[cfg(gdb)]
802 None,
803 )
804 .unwrap();
805 }
806}