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