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::ptr::{GuestPtr, RawPtr};
79use crate::mem::shared_mem::HostSharedMemory;
80use crate::sandbox::SandboxConfiguration;
81#[cfg(feature = "trace_guest")]
82use crate::sandbox::TraceInfo;
83use crate::sandbox::host_funcs::FunctionRegistry;
84use crate::sandbox::mem_mgr::MemMgrWrapper;
85use crate::sandbox::outb::handle_outb;
86#[cfg(crashdump)]
87use crate::sandbox::uninitialized::SandboxRuntimeConfig;
88use crate::{Result, log_then_return, new_error};
89
90#[cfg(gdb)]
91mod debug {
92    use std::sync::{Arc, Mutex};
93
94    use super::mshv_bindings::hv_x64_exception_intercept_message;
95    use super::{HypervLinuxDriver, *};
96    use crate::hypervisor::gdb::{DebugMsg, DebugResponse, VcpuStopReason, X86_64Regs};
97    use crate::mem::shared_mem::HostSharedMemory;
98    use crate::sandbox::mem_mgr::MemMgrWrapper;
99    use crate::{Result, new_error};
100
101    impl HypervLinuxDriver {
102        /// 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<MemMgrWrapper<HostSharedMemory>>>,
130        ) -> Result<DebugResponse> {
131            if let Some(debug) = self.debug.as_mut() {
132                match req {
133                    DebugMsg::AddHwBreakpoint(addr) => Ok(DebugResponse::AddHwBreakpoint(
134                        debug
135                            .add_hw_breakpoint(&self.vcpu_fd, addr)
136                            .map_err(|e| {
137                                log::error!("Failed to add hw breakpoint: {:?}", e);
138
139                                e
140                            })
141                            .is_ok(),
142                    )),
143                    DebugMsg::AddSwBreakpoint(addr) => Ok(DebugResponse::AddSwBreakpoint(
144                        debug
145                            .add_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
146                            .map_err(|e| {
147                                log::error!("Failed to add sw breakpoint: {:?}", e);
148
149                                e
150                            })
151                            .is_ok(),
152                    )),
153                    DebugMsg::Continue => {
154                        debug.set_single_step(&self.vcpu_fd, false).map_err(|e| {
155                            log::error!("Failed to continue execution: {:?}", e);
156
157                            e
158                        })?;
159
160                        Ok(DebugResponse::Continue)
161                    }
162                    DebugMsg::DisableDebug => {
163                        self.disable_debug().map_err(|e| {
164                            log::error!("Failed to disable debugging: {:?}", e);
165
166                            e
167                        })?;
168
169                        Ok(DebugResponse::DisableDebug)
170                    }
171                    DebugMsg::GetCodeSectionOffset => {
172                        let offset = dbg_mem_access_fn
173                            .try_lock()
174                            .map_err(|e| {
175                                new_error!("Error locking at {}:{}: {}", file!(), line!(), e)
176                            })?
177                            .unwrap_mgr()
178                            .layout
179                            .get_guest_code_address();
180
181                        Ok(DebugResponse::GetCodeSectionOffset(offset as u64))
182                    }
183                    DebugMsg::ReadAddr(addr, len) => {
184                        let mut data = vec![0u8; len];
185
186                        debug
187                            .read_addrs(&self.vcpu_fd, addr, &mut data, dbg_mem_access_fn)
188                            .map_err(|e| {
189                                log::error!("Failed to read from address: {:?}", e);
190
191                                e
192                            })?;
193
194                        Ok(DebugResponse::ReadAddr(data))
195                    }
196                    DebugMsg::ReadRegisters => {
197                        let mut regs = X86_64Regs::default();
198
199                        debug
200                            .read_regs(&self.vcpu_fd, &mut regs)
201                            .map_err(|e| {
202                                log::error!("Failed to read registers: {:?}", e);
203
204                                e
205                            })
206                            .map(|_| DebugResponse::ReadRegisters(regs))
207                    }
208                    DebugMsg::RemoveHwBreakpoint(addr) => Ok(DebugResponse::RemoveHwBreakpoint(
209                        debug
210                            .remove_hw_breakpoint(&self.vcpu_fd, addr)
211                            .map_err(|e| {
212                                log::error!("Failed to remove hw breakpoint: {:?}", e);
213
214                                e
215                            })
216                            .is_ok(),
217                    )),
218                    DebugMsg::RemoveSwBreakpoint(addr) => Ok(DebugResponse::RemoveSwBreakpoint(
219                        debug
220                            .remove_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
221                            .map_err(|e| {
222                                log::error!("Failed to remove sw breakpoint: {:?}", e);
223
224                                e
225                            })
226                            .is_ok(),
227                    )),
228                    DebugMsg::Step => {
229                        debug.set_single_step(&self.vcpu_fd, true).map_err(|e| {
230                            log::error!("Failed to enable step instruction: {:?}", e);
231
232                            e
233                        })?;
234
235                        Ok(DebugResponse::Step)
236                    }
237                    DebugMsg::WriteAddr(addr, data) => {
238                        debug
239                            .write_addrs(&self.vcpu_fd, addr, &data, dbg_mem_access_fn)
240                            .map_err(|e| {
241                                log::error!("Failed to write to address: {:?}", e);
242
243                                e
244                            })?;
245
246                        Ok(DebugResponse::WriteAddr)
247                    }
248                    DebugMsg::WriteRegisters(regs) => debug
249                        .write_regs(&self.vcpu_fd, &regs)
250                        .map_err(|e| {
251                            log::error!("Failed to write registers: {:?}", e);
252
253                            e
254                        })
255                        .map(|_| DebugResponse::WriteRegisters),
256                }
257            } else {
258                Err(new_error!("Debugging is not enabled"))
259            }
260        }
261
262        pub(crate) fn recv_dbg_msg(&mut self) -> Result<DebugMsg> {
263            let gdb_conn = self
264                .gdb_conn
265                .as_mut()
266                .ok_or_else(|| new_error!("Debug is not enabled"))?;
267
268            gdb_conn.recv().map_err(|e| {
269                new_error!(
270                    "Got an error while waiting to receive a
271                    message: {:?}",
272                    e
273                )
274            })
275        }
276
277        pub(crate) fn send_dbg_msg(&mut self, cmd: DebugResponse) -> Result<()> {
278            log::debug!("Sending {:?}", cmd);
279
280            let gdb_conn = self
281                .gdb_conn
282                .as_mut()
283                .ok_or_else(|| new_error!("Debug is not enabled"))?;
284
285            gdb_conn
286                .send(cmd)
287                .map_err(|e| new_error!("Got an error while sending a response message {:?}", e))
288        }
289    }
290}
291
292/// Determine whether the HyperV for Linux hypervisor API is present
293/// and functional.
294#[instrument(skip_all, parent = Span::current(), level = "Trace")]
295pub(crate) fn is_hypervisor_present() -> bool {
296    match Mshv::new() {
297        Ok(_) => true,
298        Err(_) => {
299            log::info!("MSHV is not available on this system");
300            false
301        }
302    }
303}
304
305/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often
306/// called the Microsoft Hypervisor (MSHV)
307pub(crate) struct HypervLinuxDriver {
308    _mshv: Mshv,
309    page_size: usize,
310    vm_fd: VmFd,
311    vcpu_fd: VcpuFd,
312    orig_rsp: GuestPtr,
313    entrypoint: u64,
314    interrupt_handle: Arc<LinuxInterruptHandle>,
315    mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
316    host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
317
318    sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
319    mmap_regions: Vec<MemoryRegion>,    // Later mapped regions
320
321    #[cfg(gdb)]
322    debug: Option<MshvDebug>,
323    #[cfg(gdb)]
324    gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
325    #[cfg(crashdump)]
326    rt_cfg: SandboxRuntimeConfig,
327    #[cfg(feature = "trace_guest")]
328    #[allow(dead_code)]
329    trace_info: TraceInfo,
330}
331
332impl HypervLinuxDriver {
333    /// Create a new `HypervLinuxDriver`, complete with all registers
334    /// set up to execute a Hyperlight binary inside a HyperV-powered
335    /// sandbox on Linux.
336    ///
337    /// While registers are set up, they will not have been applied to
338    /// the underlying virtual CPU after this function returns. Call the
339    /// `apply_registers` method to do that, or more likely call
340    /// `initialise` to do it for you.
341    #[allow(clippy::too_many_arguments)]
342    // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
343    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
344    pub(crate) fn new(
345        mem_regions: Vec<MemoryRegion>,
346        entrypoint_ptr: GuestPtr,
347        rsp_ptr: GuestPtr,
348        pml4_ptr: GuestPtr,
349        config: &SandboxConfiguration,
350        #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
351        #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig,
352        #[cfg(feature = "trace_guest")] trace_info: TraceInfo,
353    ) -> Result<Self> {
354        let mshv = Mshv::new()?;
355        let pr = Default::default();
356        #[cfg(mshv2)]
357        let vm_fd = mshv.create_vm_with_config(&pr)?;
358        #[cfg(mshv3)]
359        let vm_fd = {
360            // It's important to avoid create_vm() and explicitly use
361            // create_vm_with_args() with an empty arguments structure
362            // here, because otherwise the partition is set up with a SynIC.
363
364            let vm_fd = mshv.create_vm_with_args(&pr)?;
365            let features: hv_partition_synthetic_processor_features = Default::default();
366            vm_fd.hvcall_set_partition_property(
367                hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
368                unsafe { features.as_uint64[0] },
369            )?;
370            vm_fd.initialize()?;
371            vm_fd
372        };
373
374        let mut vcpu_fd = vm_fd.create_vcpu(0)?;
375
376        #[cfg(gdb)]
377        let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
378            let mut debug = MshvDebug::new();
379            debug.add_hw_breakpoint(&vcpu_fd, entrypoint_ptr.absolute()?)?;
380
381            // The bellow intercepts make the vCPU exit with the Exception Intercept exit code
382            // Check Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
383            // of Intel 64 and IA-32 Architectures Software Developer's Manual
384            // Install intercept for #DB (1) exception
385            vm_fd
386                .install_intercept(mshv_install_intercept {
387                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
388                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
389                    // Exception handler #DB (1)
390                    intercept_parameter: hv_intercept_parameters {
391                        exception_vector: 0x1,
392                    },
393                })
394                .map_err(|e| new_error!("Cannot install debug exception intercept: {}", e))?;
395
396            // Install intercept for #BP (3) exception
397            vm_fd
398                .install_intercept(mshv_install_intercept {
399                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
400                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
401                    // Exception handler #BP (3)
402                    intercept_parameter: hv_intercept_parameters {
403                        exception_vector: 0x3,
404                    },
405                })
406                .map_err(|e| new_error!("Cannot install breakpoint exception intercept: {}", e))?;
407
408            (Some(debug), Some(gdb_conn))
409        } else {
410            (None, None)
411        };
412
413        mem_regions.iter().try_for_each(|region| {
414            let mshv_region = region.to_owned().into();
415            vm_fd.map_user_memory(mshv_region)
416        })?;
417
418        Self::setup_initial_sregs(&mut vcpu_fd, pml4_ptr.absolute()?)?;
419
420        let interrupt_handle = Arc::new(LinuxInterruptHandle {
421            running: AtomicU64::new(0),
422            cancel_requested: AtomicBool::new(false),
423            #[cfg(gdb)]
424            debug_interrupt: AtomicBool::new(false),
425            #[cfg(all(
426                target_arch = "x86_64",
427                target_vendor = "unknown",
428                target_os = "linux",
429                target_env = "musl"
430            ))]
431            tid: AtomicU64::new(unsafe { libc::pthread_self() as u64 }),
432            #[cfg(not(all(
433                target_arch = "x86_64",
434                target_vendor = "unknown",
435                target_os = "linux",
436                target_env = "musl"
437            )))]
438            tid: AtomicU64::new(unsafe { libc::pthread_self() }),
439            retry_delay: config.get_interrupt_retry_delay(),
440            sig_rt_min_offset: config.get_interrupt_vcpu_sigrtmin_offset(),
441            dropped: AtomicBool::new(false),
442        });
443
444        #[allow(unused_mut)]
445        let mut hv = Self {
446            _mshv: mshv,
447            page_size: 0,
448            vm_fd,
449            vcpu_fd,
450            sandbox_regions: mem_regions,
451            mmap_regions: Vec::new(),
452            entrypoint: entrypoint_ptr.absolute()?,
453            orig_rsp: rsp_ptr,
454            interrupt_handle: interrupt_handle.clone(),
455            mem_mgr: None,
456            host_funcs: None,
457            #[cfg(gdb)]
458            debug,
459            #[cfg(gdb)]
460            gdb_conn,
461            #[cfg(crashdump)]
462            rt_cfg,
463            #[cfg(feature = "trace_guest")]
464            trace_info,
465        };
466
467        // Send the interrupt handle to the GDB thread if debugging is enabled
468        // This is used to allow the GDB thread to stop the vCPU
469        #[cfg(gdb)]
470        if hv.debug.is_some() {
471            hv.send_dbg_msg(DebugResponse::InterruptHandle(interrupt_handle))?;
472        }
473
474        Ok(hv)
475    }
476
477    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
478    fn setup_initial_sregs(vcpu: &mut VcpuFd, _pml4_addr: u64) -> Result<()> {
479        #[cfg(feature = "init-paging")]
480        let sregs = SpecialRegisters {
481            cr0: CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP,
482            cr4: CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT,
483            cr3: _pml4_addr,
484            efer: EFER_LME | EFER_LMA | EFER_SCE | EFER_NX,
485            cs: SegmentRegister {
486                type_: 11,
487                present: 1,
488                s: 1,
489                l: 1,
490                ..Default::default()
491            },
492            tr: SegmentRegister {
493                limit: 65535,
494                type_: 11,
495                present: 1,
496                ..Default::default()
497            },
498            ..Default::default()
499        };
500
501        #[cfg(not(feature = "init-paging"))]
502        let sregs = SpecialRegisters {
503            cs: SegmentRegister {
504                base: 0,
505                selector: 0,
506                limit: 0xFFFF,
507                type_: 11,
508                present: 1,
509                s: 1,
510                ..Default::default()
511            },
512            ds: SegmentRegister {
513                base: 0,
514                selector: 0,
515                limit: 0xFFFF,
516                type_: 3,
517                present: 1,
518                s: 1,
519                ..Default::default()
520            },
521            tr: SegmentRegister {
522                base: 0,
523                selector: 0,
524                limit: 0xFFFF,
525                type_: 11,
526                present: 1,
527                s: 0,
528                ..Default::default()
529            },
530            ..Default::default()
531        };
532        vcpu.set_sregs(&sregs)?;
533        Ok(())
534    }
535}
536
537impl Debug for HypervLinuxDriver {
538    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
539        let mut f = f.debug_struct("Hyperv Linux Driver");
540
541        f.field("Entrypoint", &self.entrypoint)
542            .field("Original RSP", &self.orig_rsp);
543
544        for region in &self.sandbox_regions {
545            f.field("Sandbox Memory Region", &region);
546        }
547        for region in &self.mmap_regions {
548            f.field("Mapped Memory Region", &region);
549        }
550
551        let regs = self.vcpu_fd.get_regs();
552
553        if let Ok(regs) = regs {
554            f.field("Registers", &regs);
555        }
556
557        let sregs = self.vcpu_fd.get_sregs();
558
559        if let Ok(sregs) = sregs {
560            f.field("Special Registers", &sregs);
561        }
562
563        f.finish()
564    }
565}
566
567#[cfg(feature = "trace_guest")]
568impl From<TraceRegister> for hv_register_name {
569    fn from(r: TraceRegister) -> Self {
570        match r {
571            TraceRegister::RAX => hv_register_name_HV_X64_REGISTER_RAX,
572            TraceRegister::RCX => hv_register_name_HV_X64_REGISTER_RCX,
573            TraceRegister::RIP => hv_register_name_HV_X64_REGISTER_RIP,
574            TraceRegister::RSP => hv_register_name_HV_X64_REGISTER_RSP,
575            TraceRegister::RBP => hv_register_name_HV_X64_REGISTER_RBP,
576        }
577    }
578}
579
580impl Hypervisor for HypervLinuxDriver {
581    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
582    fn initialise(
583        &mut self,
584        peb_addr: RawPtr,
585        seed: u64,
586        page_size: u32,
587        mem_mgr: MemMgrWrapper<HostSharedMemory>,
588        host_funcs: Arc<Mutex<FunctionRegistry>>,
589        max_guest_log_level: Option<LevelFilter>,
590        #[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<MemMgrWrapper<HostSharedMemory>>>,
591    ) -> Result<()> {
592        self.mem_mgr = Some(mem_mgr);
593        self.host_funcs = Some(host_funcs);
594        self.page_size = page_size as usize;
595
596        let max_guest_log_level: u64 = match max_guest_log_level {
597            Some(level) => level as u64,
598            None => self.get_max_log_level().into(),
599        };
600
601        let regs = StandardRegisters {
602            rip: self.entrypoint,
603            rsp: self.orig_rsp.absolute()?,
604            rflags: 2, //bit 1 of rlags is required to be set
605
606            // function args
607            rdi: peb_addr.into(),
608            rsi: seed,
609            rdx: page_size.into(),
610            rcx: max_guest_log_level,
611
612            ..Default::default()
613        };
614        self.vcpu_fd.set_regs(&regs)?;
615
616        VirtualCPU::run(
617            self.as_mut_hypervisor(),
618            #[cfg(gdb)]
619            dbg_mem_access_fn,
620        )
621    }
622
623    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
624    unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
625        if [
626            rgn.guest_region.start,
627            rgn.guest_region.end,
628            rgn.host_region.start,
629            rgn.host_region.end,
630        ]
631        .iter()
632        .any(|x| x % self.page_size != 0)
633        {
634            log_then_return!("region is not page-aligned");
635        }
636        let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
637        self.vm_fd.map_user_memory(mshv_region)?;
638        self.mmap_regions.push(rgn.to_owned());
639        Ok(())
640    }
641
642    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
643    unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
644        if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
645            let removed_region = self.mmap_regions.remove(pos);
646            let mshv_region: mshv_user_mem_region = removed_region.into();
647            self.vm_fd.unmap_user_memory(mshv_region)?;
648            Ok(())
649        } else {
650            Err(new_error!("Tried to unmap region that is not mapped"))
651        }
652    }
653
654    fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
655        Box::new(self.mmap_regions.iter())
656    }
657
658    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
659    fn dispatch_call_from_host(
660        &mut self,
661        dispatch_func_addr: RawPtr,
662        #[cfg(gdb)] dbg_mem_access_fn: Arc<Mutex<MemMgrWrapper<HostSharedMemory>>>,
663    ) -> Result<()> {
664        // Reset general purpose registers, then set RIP and RSP
665        let regs = StandardRegisters {
666            rip: dispatch_func_addr.into(),
667            rsp: self.orig_rsp.absolute()?,
668            rflags: 2, //bit 1 of rlags is required to be set
669            ..Default::default()
670        };
671        self.vcpu_fd.set_regs(&regs)?;
672
673        // reset fpu state
674        let fpu = FloatingPointUnit {
675            fcw: FP_CONTROL_WORD_DEFAULT,
676            ftwx: FP_TAG_WORD_DEFAULT,
677            mxcsr: MXCSR_DEFAULT,
678            ..Default::default() // zero out the rest
679        };
680        self.vcpu_fd.set_fpu(&fpu)?;
681
682        // run
683        VirtualCPU::run(
684            self.as_mut_hypervisor(),
685            #[cfg(gdb)]
686            dbg_mem_access_fn,
687        )?;
688
689        Ok(())
690    }
691
692    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
693    fn handle_io(
694        &mut self,
695        port: u16,
696        data: Vec<u8>,
697        rip: u64,
698        instruction_length: u64,
699    ) -> Result<()> {
700        let mut padded = [0u8; 4];
701        let copy_len = data.len().min(4);
702        padded[..copy_len].copy_from_slice(&data[..copy_len]);
703        let val = u32::from_le_bytes(padded);
704
705        #[cfg(feature = "trace_guest")]
706        {
707            // We need to handle the borrow checker issue where we need both:
708            // - &mut MemMgrWrapper (from self.mem_mgr.as_mut())
709            // - &mut dyn Hypervisor (from self)
710            // We'll use a temporary approach to extract the mem_mgr temporarily
711            let mem_mgr_option = self.mem_mgr.take();
712            let mut mem_mgr = mem_mgr_option
713                .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
714            let host_funcs = self
715                .host_funcs
716                .as_ref()
717                .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
718                .clone();
719
720            handle_outb(&mut mem_mgr, host_funcs, self, port, val)?;
721
722            // Put the mem_mgr back
723            self.mem_mgr = Some(mem_mgr);
724        }
725
726        #[cfg(not(feature = "trace_guest"))]
727        {
728            let mem_mgr = self
729                .mem_mgr
730                .as_mut()
731                .ok_or_else(|| new_error!("mem_mgr should be initialized before handling IO"))?;
732            let host_funcs = self
733                .host_funcs
734                .as_ref()
735                .ok_or_else(|| new_error!("host_funcs should be initialized before handling IO"))?
736                .clone();
737
738            handle_outb(mem_mgr, host_funcs, port, val)?;
739        }
740
741        // update rip
742        self.vcpu_fd.set_reg(&[hv_register_assoc {
743            name: hv_register_name_HV_X64_REGISTER_RIP,
744            value: hv_register_value {
745                reg64: rip + instruction_length,
746            },
747            ..Default::default()
748        }])?;
749        Ok(())
750    }
751
752    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
753    fn run(&mut self) -> Result<super::HyperlightExit> {
754        const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
755        const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
756            hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
757        const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
758        const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
759        #[cfg(gdb)]
760        const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
761
762        self.interrupt_handle
763            .tid
764            .store(unsafe { libc::pthread_self() as u64 }, Ordering::Relaxed);
765        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
766        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
767        self.interrupt_handle
768            .set_running_and_increment_generation()
769            .map_err(|e| {
770                new_error!(
771                    "Error setting running state and incrementing generation: {}",
772                    e
773                )
774            })?;
775        #[cfg(not(gdb))]
776        let debug_interrupt = false;
777        #[cfg(gdb)]
778        let debug_interrupt = self
779            .interrupt_handle
780            .debug_interrupt
781            .load(Ordering::Relaxed);
782
783        // Don't run the vcpu if `cancel_requested` is true
784        //
785        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
786        // Then this is fine since `cancel_requested` is set to true, so we will skip the `VcpuFd::run()` call
787        let exit_reason = if self
788            .interrupt_handle
789            .cancel_requested
790            .load(Ordering::Relaxed)
791            || debug_interrupt
792        {
793            Err(mshv_ioctls::MshvError::from(libc::EINTR))
794        } else {
795            #[cfg(feature = "trace_guest")]
796            if self.trace_info.guest_start_epoch.is_none() {
797                // Store the guest start epoch and cycles to trace the guest execution time
798                crate::debug!("MSHV - Guest Start Epoch set");
799                self.trace_info.guest_start_tsc =
800                    Some(hyperlight_guest_tracing::invariant_tsc::read_tsc());
801                self.trace_info.guest_start_epoch = Some(std::time::Instant::now());
802            }
803            // Note: if a `InterruptHandle::kill()` called while this thread is **here**
804            // Then the vcpu will run, but we will keep sending signals to this thread
805            // to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will
806            // return either normally with an exit reason, or from being "kicked" by out signal handler, with an EINTR error,
807            // both of which are fine.
808            #[cfg(mshv2)]
809            {
810                let hv_message: hv_message = Default::default();
811                self.vcpu_fd.run(hv_message)
812            }
813            #[cfg(mshv3)]
814            self.vcpu_fd.run()
815        };
816        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
817        // Then signals will be sent to this thread until `running` is set to false.
818        // This is fine since the signal handler is a no-op.
819        let cancel_requested = self
820            .interrupt_handle
821            .cancel_requested
822            .load(Ordering::Relaxed);
823        #[cfg(gdb)]
824        let debug_interrupt = self
825            .interrupt_handle
826            .debug_interrupt
827            .load(Ordering::Relaxed);
828        // Note: if a `InterruptHandle::kill()` called while this thread is **here**
829        // Then `cancel_requested` will be set to true again, which will cancel the **next vcpu run**.
830        // Additionally signals will be sent to this thread until `running` is set to false.
831        // This is fine since the signal handler is a no-op.
832        self.interrupt_handle.clear_running_bit();
833        // At this point, `running` is false so no more signals will be sent to this thread,
834        // but we may still receive async signals that were sent before this point.
835        // To prevent those signals from interrupting subsequent calls to `run()`,
836        // we make sure to check `cancel_requested` before cancelling (see `libc::EINTR` match-arm below).
837        let result = match exit_reason {
838            Ok(m) => match m.header.message_type {
839                HALT_MESSAGE => {
840                    crate::debug!("mshv - Halt Details : {:#?}", &self);
841                    HyperlightExit::Halt()
842                }
843                IO_PORT_INTERCEPT_MESSAGE => {
844                    let io_message = m.to_ioport_info().map_err(mshv_ioctls::MshvError::from)?;
845                    let port_number = io_message.port_number;
846                    let rip = io_message.header.rip;
847                    let rax = io_message.rax;
848                    let instruction_length = io_message.header.instruction_length() as u64;
849                    crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
850                    HyperlightExit::IoOut(
851                        port_number,
852                        rax.to_le_bytes().to_vec(),
853                        rip,
854                        instruction_length,
855                    )
856                }
857                UNMAPPED_GPA_MESSAGE => {
858                    let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
859                    let addr = mimo_message.guest_physical_address;
860                    crate::debug!(
861                        "mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
862                        addr,
863                        &self
864                    );
865                    HyperlightExit::Mmio(addr)
866                }
867                INVALID_GPA_ACCESS_MESSAGE => {
868                    let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
869                    let gpa = mimo_message.guest_physical_address;
870                    let access_info = MemoryRegionFlags::try_from(mimo_message)?;
871                    crate::debug!(
872                        "mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
873                        gpa,
874                        &self
875                    );
876                    match get_memory_access_violation(
877                        gpa as usize,
878                        self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
879                        access_info,
880                    ) {
881                        Some(access_info_violation) => access_info_violation,
882                        None => HyperlightExit::Mmio(gpa),
883                    }
884                }
885                // The only case an intercept exit is expected is when debugging is enabled
886                // and the intercepts are installed.
887                // Provide the extra information about the exception to accurately determine
888                // the stop reason
889                #[cfg(gdb)]
890                EXCEPTION_INTERCEPT => {
891                    // Extract exception info from the message so we can figure out
892                    // more information about the vCPU state
893                    let ex_info = match m.to_exception_info().map_err(mshv_ioctls::MshvError::from)
894                    {
895                        Ok(info) => info,
896                        Err(e) => {
897                            log_then_return!("Error converting to exception info: {:?}", e);
898                        }
899                    };
900
901                    match self.get_stop_reason(ex_info) {
902                        Ok(reason) => HyperlightExit::Debug(reason),
903                        Err(e) => {
904                            log_then_return!("Error getting stop reason: {:?}", e);
905                        }
906                    }
907                }
908                other => {
909                    crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
910                    #[cfg(crashdump)]
911                    let _ = crashdump::generate_crashdump(self);
912                    log_then_return!("unknown Hyper-V run message type {:?}", other);
913                }
914            },
915            Err(e) => match e.errno() {
916                // we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
917                libc::EINTR => {
918                    // If cancellation was not requested for this specific vm, the vcpu was interrupted because of debug interrupt or
919                    // a stale signal that meant to be delivered to a previous/other vcpu on this same thread, so let's ignore it
920                    if cancel_requested {
921                        self.interrupt_handle
922                            .cancel_requested
923                            .store(false, Ordering::Relaxed);
924                        HyperlightExit::Cancelled()
925                    } else {
926                        #[cfg(gdb)]
927                        if debug_interrupt {
928                            self.interrupt_handle
929                                .debug_interrupt
930                                .store(false, Ordering::Relaxed);
931
932                            // If the vCPU was stopped because of an interrupt, we need to
933                            // return a special exit reason so that the gdb thread can handle it
934                            // and resume execution
935                            HyperlightExit::Debug(VcpuStopReason::Interrupt)
936                        } else {
937                            HyperlightExit::Retry()
938                        }
939
940                        #[cfg(not(gdb))]
941                        HyperlightExit::Retry()
942                    }
943                }
944                libc::EAGAIN => HyperlightExit::Retry(),
945                _ => {
946                    crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
947                    log_then_return!("Error running VCPU {:?}", e);
948                }
949            },
950        };
951        Ok(result)
952    }
953
954    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
955    fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
956        self as &mut dyn Hypervisor
957    }
958
959    fn interrupt_handle(&self) -> Arc<dyn InterruptHandle> {
960        self.interrupt_handle.clone()
961    }
962
963    #[cfg(crashdump)]
964    fn crashdump_context(&self) -> Result<Option<super::crashdump::CrashDumpContext>> {
965        if self.rt_cfg.guest_core_dump {
966            let mut regs = [0; 27];
967
968            let vcpu_regs = self.vcpu_fd.get_regs()?;
969            let sregs = self.vcpu_fd.get_sregs()?;
970            let xsave = self.vcpu_fd.get_xsave()?;
971
972            // Set up the registers for the crash dump
973            regs[0] = vcpu_regs.r15; // r15
974            regs[1] = vcpu_regs.r14; // r14
975            regs[2] = vcpu_regs.r13; // r13
976            regs[3] = vcpu_regs.r12; // r12
977            regs[4] = vcpu_regs.rbp; // rbp
978            regs[5] = vcpu_regs.rbx; // rbx
979            regs[6] = vcpu_regs.r11; // r11
980            regs[7] = vcpu_regs.r10; // r10
981            regs[8] = vcpu_regs.r9; // r9
982            regs[9] = vcpu_regs.r8; // r8
983            regs[10] = vcpu_regs.rax; // rax
984            regs[11] = vcpu_regs.rcx; // rcx
985            regs[12] = vcpu_regs.rdx; // rdx
986            regs[13] = vcpu_regs.rsi; // rsi
987            regs[14] = vcpu_regs.rdi; // rdi
988            regs[15] = 0; // orig rax
989            regs[16] = vcpu_regs.rip; // rip
990            regs[17] = sregs.cs.selector as u64; // cs
991            regs[18] = vcpu_regs.rflags; // eflags
992            regs[19] = vcpu_regs.rsp; // rsp
993            regs[20] = sregs.ss.selector as u64; // ss
994            regs[21] = sregs.fs.base; // fs_base
995            regs[22] = sregs.gs.base; // gs_base
996            regs[23] = sregs.ds.selector as u64; // ds
997            regs[24] = sregs.es.selector as u64; // es
998            regs[25] = sregs.fs.selector as u64; // fs
999            regs[26] = sregs.gs.selector as u64; // gs
1000
1001            // Get the filename from the binary path
1002            let filename = self.rt_cfg.binary_path.clone().and_then(|path| {
1003                Path::new(&path)
1004                    .file_name()
1005                    .and_then(|name| name.to_os_string().into_string().ok())
1006            });
1007
1008            Ok(Some(crashdump::CrashDumpContext::new(
1009                &self.sandbox_regions,
1010                regs,
1011                xsave.buffer.to_vec(),
1012                self.entrypoint,
1013                self.rt_cfg.binary_path.clone(),
1014                filename,
1015            )))
1016        } else {
1017            Ok(None)
1018        }
1019    }
1020
1021    #[cfg(gdb)]
1022    fn handle_debug(
1023        &mut self,
1024        dbg_mem_access_fn: Arc<Mutex<MemMgrWrapper<HostSharedMemory>>>,
1025        stop_reason: VcpuStopReason,
1026    ) -> Result<()> {
1027        if self.debug.is_none() {
1028            return Err(new_error!("Debugging is not enabled"));
1029        }
1030
1031        match stop_reason {
1032            // If the vCPU stopped because of a crash, we need to handle it differently
1033            // We do not want to allow resuming execution or placing breakpoints
1034            // because the guest has crashed.
1035            // We only allow reading registers and memory
1036            VcpuStopReason::Crash => {
1037                self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1038                    .map_err(|e| {
1039                        new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1040                    })?;
1041
1042                loop {
1043                    log::debug!("Debug wait for event to resume vCPU");
1044                    // Wait for a message from gdb
1045                    let req = self.recv_dbg_msg()?;
1046
1047                    // Flag to store if we should deny continue or step requests
1048                    let mut deny_continue = false;
1049                    // Flag to store if we should detach from the gdb session
1050                    let mut detach = false;
1051
1052                    let response = match req {
1053                        // Allow the detach request to disable debugging by continuing resuming
1054                        // hypervisor crash error reporting
1055                        DebugMsg::DisableDebug => {
1056                            detach = true;
1057                            DebugResponse::DisableDebug
1058                        }
1059                        // Do not allow continue or step requests
1060                        DebugMsg::Continue | DebugMsg::Step => {
1061                            deny_continue = true;
1062                            DebugResponse::NotAllowed
1063                        }
1064                        // Do not allow adding/removing breakpoints and writing to memory or registers
1065                        DebugMsg::AddHwBreakpoint(_)
1066                        | DebugMsg::AddSwBreakpoint(_)
1067                        | DebugMsg::RemoveHwBreakpoint(_)
1068                        | DebugMsg::RemoveSwBreakpoint(_)
1069                        | DebugMsg::WriteAddr(_, _)
1070                        | DebugMsg::WriteRegisters(_) => DebugResponse::NotAllowed,
1071
1072                        // For all other requests, we will process them normally
1073                        _ => {
1074                            let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1075                            match result {
1076                                Ok(response) => response,
1077                                Err(HyperlightError::TranslateGuestAddress(_)) => {
1078                                    // Treat non fatal errors separately so the guest doesn't fail
1079                                    DebugResponse::ErrorOccurred
1080                                }
1081                                Err(e) => {
1082                                    log::error!("Error processing debug request: {:?}", e);
1083                                    return Err(e);
1084                                }
1085                            }
1086                        }
1087                    };
1088
1089                    // Send the response to the request back to gdb
1090                    self.send_dbg_msg(response)
1091                        .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1092
1093                    // If we are denying continue or step requests, the debugger assumes the
1094                    // execution started so we need to report a stop reason as a crash and let
1095                    // it request to read registers/memory to figure out what happened
1096                    if deny_continue {
1097                        self.send_dbg_msg(DebugResponse::VcpuStopped(VcpuStopReason::Crash))
1098                            .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1099                    }
1100
1101                    // If we are detaching, we will break the loop and the Hypervisor will continue
1102                    // to handle the Crash reason
1103                    if detach {
1104                        break;
1105                    }
1106                }
1107            }
1108            // If the vCPU stopped because of any other reason except a crash, we can handle it
1109            // normally
1110            _ => {
1111                // Send the stop reason to the gdb thread
1112                self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
1113                    .map_err(|e| {
1114                        new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e)
1115                    })?;
1116
1117                loop {
1118                    log::debug!("Debug wait for event to resume vCPU");
1119                    // Wait for a message from gdb
1120                    let req = self.recv_dbg_msg()?;
1121
1122                    let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
1123
1124                    let response = match result {
1125                        Ok(response) => response,
1126                        // Treat non fatal errors separately so the guest doesn't fail
1127                        Err(HyperlightError::TranslateGuestAddress(_)) => {
1128                            DebugResponse::ErrorOccurred
1129                        }
1130                        Err(e) => {
1131                            return Err(e);
1132                        }
1133                    };
1134
1135                    let cont = matches!(
1136                        response,
1137                        DebugResponse::Continue | DebugResponse::Step | DebugResponse::DisableDebug
1138                    );
1139
1140                    self.send_dbg_msg(response)
1141                        .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
1142
1143                    // Check if we should continue execution
1144                    // We continue if the response is one of the following: Step, Continue, or DisableDebug
1145                    if cont {
1146                        break;
1147                    }
1148                }
1149            }
1150        }
1151
1152        Ok(())
1153    }
1154
1155    fn check_stack_guard(&self) -> Result<bool> {
1156        if let Some(mgr) = self.mem_mgr.as_ref() {
1157            mgr.check_stack_guard()
1158        } else {
1159            Err(new_error!("Memory manager is not initialized"))
1160        }
1161    }
1162
1163    #[cfg(feature = "trace_guest")]
1164    fn read_trace_reg(&self, reg: TraceRegister) -> Result<u64> {
1165        let mut assoc = [hv_register_assoc {
1166            name: reg.into(),
1167            ..Default::default()
1168        }];
1169        self.vcpu_fd.get_reg(&mut assoc)?;
1170        // safety: all registers that we currently support are 64-bit
1171        unsafe { Ok(assoc[0].value.reg64) }
1172    }
1173
1174    #[cfg(feature = "trace_guest")]
1175    fn trace_info_as_ref(&self) -> &TraceInfo {
1176        &self.trace_info
1177    }
1178    #[cfg(feature = "trace_guest")]
1179    fn trace_info_as_mut(&mut self) -> &mut TraceInfo {
1180        &mut self.trace_info
1181    }
1182}
1183
1184impl Drop for HypervLinuxDriver {
1185    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
1186    fn drop(&mut self) {
1187        self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
1188        for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
1189            let mshv_region: mshv_user_mem_region = region.to_owned().into();
1190            match self.vm_fd.unmap_user_memory(mshv_region) {
1191                Ok(_) => (),
1192                Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
1193            }
1194        }
1195    }
1196}
1197
1198#[cfg(test)]
1199mod tests {
1200    use super::*;
1201    #[cfg(feature = "unwind_guest")]
1202    use crate::mem::exe::DummyUnwindInfo;
1203    use crate::mem::memory_region::MemoryRegionVecBuilder;
1204    use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
1205
1206    #[rustfmt::skip]
1207    const CODE: [u8; 12] = [
1208        0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
1209        0x00, 0xd8, /* add %bl, %al */
1210        0x04, b'0', /* add $'0', %al */
1211        0xee, /* out %al, (%dx) */
1212        /* send a 0 to indicate we're done */
1213        0xb0, b'\0', /* mov $'\0', %al */
1214        0xee, /* out %al, (%dx) */
1215        0xf4, /* HLT */
1216    ];
1217
1218    fn shared_mem_with_code(
1219        code: &[u8],
1220        mem_size: usize,
1221        load_offset: usize,
1222    ) -> Result<Box<ExclusiveSharedMemory>> {
1223        if load_offset > mem_size {
1224            log_then_return!(
1225                "code load offset ({}) > memory size ({})",
1226                load_offset,
1227                mem_size
1228            );
1229        }
1230        let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
1231        shared_mem.copy_from_slice(code, load_offset)?;
1232        Ok(Box::new(shared_mem))
1233    }
1234
1235    #[test]
1236    fn create_driver() {
1237        if !super::is_hypervisor_present() {
1238            return;
1239        }
1240        const MEM_SIZE: usize = 0x3000;
1241        let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
1242        let rsp_ptr = GuestPtr::try_from(0).unwrap();
1243        let pml4_ptr = GuestPtr::try_from(0).unwrap();
1244        let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
1245        let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
1246        regions.push_page_aligned(
1247            MEM_SIZE,
1248            MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
1249            crate::mem::memory_region::MemoryRegionType::Code,
1250        );
1251        let config: SandboxConfiguration = Default::default();
1252
1253        super::HypervLinuxDriver::new(
1254            regions.build(),
1255            entrypoint_ptr,
1256            rsp_ptr,
1257            pml4_ptr,
1258            &config,
1259            #[cfg(gdb)]
1260            None,
1261            #[cfg(crashdump)]
1262            SandboxRuntimeConfig {
1263                #[cfg(crashdump)]
1264                binary_path: None,
1265                #[cfg(gdb)]
1266                debug_info: None,
1267                #[cfg(crashdump)]
1268                guest_core_dump: true,
1269            },
1270            #[cfg(feature = "trace_guest")]
1271            TraceInfo::new(
1272                #[cfg(feature = "unwind_guest")]
1273                Arc::new(DummyUnwindInfo {}),
1274            )
1275            .unwrap(),
1276        )
1277        .unwrap();
1278    }
1279}