hyperlight_host/hypervisor/
hyperv_linux.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17#[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        /// Resets the debug information to disable debugging
103        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        /// Get the reason the vCPU has stopped
114        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, &regs)
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/// Determine whether the HyperV for Linux hypervisor API is present
292/// and functional.
293#[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
304/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often
305/// called the Microsoft Hypervisor (MSHV)
306pub(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>, // Initially mapped regions when sandbox is created
318    mmap_regions: Vec<MemoryRegion>,    // Later mapped regions
319
320    #[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    /// Create a new `HypervLinuxDriver`, complete with all registers
333    /// set up to execute a Hyperlight binary inside a HyperV-powered
334    /// sandbox on Linux.
335    ///
336    /// While registers are set up, they will not have been applied to
337    /// the underlying virtual CPU after this function returns. Call the
338    /// `apply_registers` method to do that, or more likely call
339    /// `initialise` to do it for you.
340    #[allow(clippy::too_many_arguments)]
341    // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
342    #[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            // It's important to avoid create_vm() and explicitly use
360            // create_vm_with_args() with an empty arguments structure
361            // here, because otherwise the partition is set up with a SynIC.
362
363            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            // The bellow intercepts make the vCPU exit with the Exception Intercept exit code
381            // Check Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
382            // of Intel 64 and IA-32 Architectures Software Developer's Manual
383            // Install intercept for #DB (1) exception
384            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                    // Exception handler #DB (1)
389                    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            // Install intercept for #BP (3) exception
396            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                    // Exception handler #BP (3)
401                    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        // Send the interrupt handle to the GDB thread if debugging is enabled
467        // This is used to allow the GDB thread to stop the vCPU
468        #[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", &region);
545        }
546        for region in &self.mmap_regions {
547            f.field("Mapped Memory Region", &region);
548        }
549
550        let regs = self.vcpu_fd.get_regs();
551
552        if let Ok(regs) = regs {
553            f.field("Registers", &regs);
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, //bit 1 of rlags is required to be set
604
605            // function args
606            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(&regs)?;
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        // Reset general purpose registers, then set RIP and RSP
664        let regs = StandardRegisters {
665            rip: dispatch_func_addr.into(),
666            rsp: self.orig_rsp.absolute()?,
667            rflags: 2, //bit 1 of rlags is required to be set
668            ..Default::default()
669        };
670        self.vcpu_fd.set_regs(&regs)?;
671
672        // reset fpu state
673        let fpu = FloatingPointUnit {
674            fcw: FP_CONTROL_WORD_DEFAULT,
675            ftwx: FP_TAG_WORD_DEFAULT,
676            mxcsr: MXCSR_DEFAULT,
677            ..Default::default() // zero out the rest
678        };
679        self.vcpu_fd.set_fpu(&fpu)?;
680
681        // run
682        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            // We need to handle the borrow checker issue where we need both:
707            // - &mut SandboxMemoryManager (from self.mem_mgr)
708            // - &mut dyn Hypervisor (from self)
709            // We'll use a temporary approach to extract the mem_mgr temporarily
710            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            // Put the mem_mgr back
722            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        // update rip
741        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        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
765        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
766        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        // Don't run the vcpu if `cancel_requested` is true
783        //
784        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
785        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
786        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                // Store the guest start epoch and cycles to trace the guest execution time
797                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            // Note: if a `InterruptHandle::kill()` called while this thread is **here**
803            // Then the vcpu will run, but we will keep sending signals to this thread
804            // to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
805            // return either normally with an exit reason, or from being "kicked" by out signal handler, with an EINTR error,
806            // both of which are fine.
807            #[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        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
816        // Then signals will be sent to this thread until `running` is set to false.
817        // This is fine since the signal handler is a no-op.
818        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        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
828        // Then `cancel_requested` will be set to true again, which will cancel the **next vcpu run**.
829        // Additionally signals will be sent to this thread until `running` is set to false.
830        // This is fine since the signal handler is a no-op.
831        self.interrupt_handle.clear_running_bit();
832        // At this point, `running` is false so no more signals will be sent to this thread,
833        // but we may still receive async signals that were sent before this point.
834        // To prevent those signals from interrupting subsequent calls to `run()`,
835        // we make sure to check `cancel_requested` before cancelling (see `libc::EINTR` match-arm below).
836        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                // The only case an intercept exit is expected is when debugging is enabled
885                // and the intercepts are installed.
886                // Provide the extra information about the exception to accurately determine
887                // the stop reason
888                #[cfg(gdb)]
889                EXCEPTION_INTERCEPT => {
890                    // Extract exception info from the message so we can figure out
891                    // more information about the vCPU state
892                    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                // we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
916                libc::EINTR => {
917                    // If cancellation was not requested for this specific vm, the vcpu was interrupted because of debug interrupt or
918                    // a stale signal that meant to be delivered to a previous/other vcpu on this same thread, so let's ignore it
919                    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                            // If the vCPU was stopped because of an interrupt, we need to
932                            // return a special exit reason so that the gdb thread can handle it
933                            // and resume execution
934                            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            // Set up the registers for the crash dump
972            regs[0] = vcpu_regs.r15; // r15
973            regs[1] = vcpu_regs.r14; // r14
974            regs[2] = vcpu_regs.r13; // r13
975            regs[3] = vcpu_regs.r12; // r12
976            regs[4] = vcpu_regs.rbp; // rbp
977            regs[5] = vcpu_regs.rbx; // rbx
978            regs[6] = vcpu_regs.r11; // r11
979            regs[7] = vcpu_regs.r10; // r10
980            regs[8] = vcpu_regs.r9; // r9
981            regs[9] = vcpu_regs.r8; // r8
982            regs[10] = vcpu_regs.rax; // rax
983            regs[11] = vcpu_regs.rcx; // rcx
984            regs[12] = vcpu_regs.rdx; // rdx
985            regs[13] = vcpu_regs.rsi; // rsi
986            regs[14] = vcpu_regs.rdi; // rdi
987            regs[15] = 0; // orig rax
988            regs[16] = vcpu_regs.rip; // rip
989            regs[17] = sregs.cs.selector as u64; // cs
990            regs[18] = vcpu_regs.rflags; // eflags
991            regs[19] = vcpu_regs.rsp; // rsp
992            regs[20] = sregs.ss.selector as u64; // ss
993            regs[21] = sregs.fs.base; // fs_base
994            regs[22] = sregs.gs.base; // gs_base
995            regs[23] = sregs.ds.selector as u64; // ds
996            regs[24] = sregs.es.selector as u64; // es
997            regs[25] = sregs.fs.selector as u64; // fs
998            regs[26] = sregs.gs.selector as u64; // gs
999
1000            // Get the filename from the binary path
1001            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            // If the vCPU stopped because of a crash, we need to handle it differently
1032            // We do not want to allow resuming execution or placing breakpoints
1033            // because the guest has crashed.
1034            // We only allow reading registers and memory
1035            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                    // Wait for a message from gdb
1044                    let req = self.recv_dbg_msg()?;
1045
1046                    // Flag to store if we should deny continue or step requests
1047                    let mut deny_continue = false;
1048                    // Flag to store if we should detach from the gdb session
1049                    let mut detach = false;
1050
1051                    let response = match req {
1052                        // Allow the detach request to disable debugging by continuing resuming
1053                        // hypervisor crash error reporting
1054                        DebugMsg::DisableDebug => {
1055                            detach = true;
1056                            DebugResponse::DisableDebug
1057                        }
1058                        // Do not allow continue or step requests
1059                        DebugMsg::Continue | DebugMsg::Step => {
1060                            deny_continue = true;
1061                            DebugResponse::NotAllowed
1062                        }
1063                        // Do not allow adding/removing breakpoints and writing to memory or registers
1064                        DebugMsg::AddHwBreakpoint(_)
1065                        | DebugMsg::AddSwBreakpoint(_)
1066                        | DebugMsg::RemoveHwBreakpoint(_)
1067                        | DebugMsg::RemoveSwBreakpoint(_)
1068                        | DebugMsg::WriteAddr(_, _)
1069                        | DebugMsg::WriteRegisters(_) => DebugResponse::NotAllowed,
1070
1071                        // For all other requests, we will process them normally
1072                        _ => {
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                                    // Treat non fatal errors separately so the guest doesn't fail
1078                                    DebugResponse::ErrorOccurred
1079                                }
1080                                Err(e) => {
1081                                    log::error!("Error processing debug request: {:?}", e);
1082                                    return Err(e);
1083                                }
1084                            }
1085                        }
1086                    };
1087
1088                    // Send the response to the request back to gdb
1089                    self.send_dbg_msg(response)
1090                        .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1091
1092                    // If we are denying continue or step requests, the debugger assumes the
1093                    // execution started so we need to report a stop reason as a crash and let
1094                    // it request to read registers/memory to figure out what happened
1095                    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 we are detaching, we will break the loop and the Hypervisor will continue
1101                    // to handle the Crash reason
1102                    if detach {
1103                        break;
1104                    }
1105                }
1106            }
1107            // If the vCPU stopped because of any other reason except a crash, we can handle it
1108            // normally
1109            _ => {
1110                // Send the stop reason to the gdb thread
1111                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                    // Wait for a message from gdb
1119                    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                        // Treat non fatal errors separately so the guest doesn't fail
1126                        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                    // Check if we should continue execution
1143                    // We continue if the response is one of the following: Step, Continue, or DisableDebug
1144                    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        // safety: all registers that we currently support are 64-bit
1170        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, /* mov $0x3f8, %dx */
1208        0x00, 0xd8, /* add %bl, %al */
1209        0x04, b'0', /* add $'0', %al */
1210        0xee, /* out %al, (%dx) */
1211        /* send a 0 to indicate we're done */
1212        0xb0, b'\0', /* mov $'\0', %al */
1213        0xee, /* out %al, (%dx) */
1214        0xf4, /* HLT */
1215    ];
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}