hyperlight_host/hypervisor/
hyperv_linux.rs

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