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;
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::open_with_cloexec(true) {
275        Ok(fd) => {
276            unsafe {
277                libc::close(fd);
278            } // must explicitly close fd to avoid a leak
279            true
280        }
281        Err(e) => {
282            log::info!("Error creating MSHV object: {:?}", e);
283            false
284        }
285    }
286}
287
288/// A Hypervisor driver for HyperV-on-Linux. This hypervisor is often
289/// called the Microsoft Hypervisor (MSHV)
290pub(super) struct HypervLinuxDriver {
291    _mshv: Mshv,
292    vm_fd: VmFd,
293    vcpu_fd: VcpuFd,
294    entrypoint: u64,
295    mem_regions: Vec<MemoryRegion>,
296    orig_rsp: GuestPtr,
297
298    #[cfg(gdb)]
299    debug: Option<MshvDebug>,
300    #[cfg(gdb)]
301    gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
302}
303
304impl HypervLinuxDriver {
305    /// Create a new `HypervLinuxDriver`, complete with all registers
306    /// set up to execute a Hyperlight binary inside a HyperV-powered
307    /// sandbox on Linux.
308    ///
309    /// While registers are set up, they will not have been applied to
310    /// the underlying virtual CPU after this function returns. Call the
311    /// `apply_registers` method to do that, or more likely call
312    /// `initialise` to do it for you.
313    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
314    pub(super) fn new(
315        mem_regions: Vec<MemoryRegion>,
316        entrypoint_ptr: GuestPtr,
317        rsp_ptr: GuestPtr,
318        pml4_ptr: GuestPtr,
319        #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
320    ) -> Result<Self> {
321        let mshv = Mshv::new()?;
322        let pr = Default::default();
323        #[cfg(mshv2)]
324        let vm_fd = mshv.create_vm_with_config(&pr)?;
325        #[cfg(mshv3)]
326        let vm_fd = {
327            // It's important to avoid create_vm() and explicitly use
328            // create_vm_with_args() with an empty arguments structure
329            // here, because otherwise the partition is set up with a SynIC.
330
331            let vm_fd = mshv.create_vm_with_args(&pr)?;
332            let features: hv_partition_synthetic_processor_features = Default::default();
333            vm_fd.hvcall_set_partition_property(
334                hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
335                unsafe { features.as_uint64[0] },
336            )?;
337            vm_fd.initialize()?;
338            vm_fd
339        };
340
341        let mut vcpu_fd = vm_fd.create_vcpu(0)?;
342
343        #[cfg(gdb)]
344        let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
345            let mut debug = MshvDebug::new();
346            debug.add_hw_breakpoint(&vcpu_fd, entrypoint_ptr.absolute()?)?;
347
348            // The bellow intercepts make the vCPU exit with the Exception Intercept exit code
349            // Check Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
350            // of Intel 64 and IA-32 Architectures Software Developer's Manual
351            // Install intercept for #DB (1) exception
352            vm_fd
353                .install_intercept(mshv_install_intercept {
354                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
355                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
356                    // Exception handler #DB (1)
357                    intercept_parameter: hv_intercept_parameters {
358                        exception_vector: 0x1,
359                    },
360                })
361                .map_err(|e| new_error!("Cannot install debug exception intercept: {}", e))?;
362
363            // Install intercept for #BP (3) exception
364            vm_fd
365                .install_intercept(mshv_install_intercept {
366                    access_type_mask: HV_INTERCEPT_ACCESS_MASK_EXECUTE,
367                    intercept_type: hv_intercept_type_HV_INTERCEPT_TYPE_EXCEPTION,
368                    // Exception handler #BP (3)
369                    intercept_parameter: hv_intercept_parameters {
370                        exception_vector: 0x3,
371                    },
372                })
373                .map_err(|e| new_error!("Cannot install breakpoint exception intercept: {}", e))?;
374
375            (Some(debug), Some(gdb_conn))
376        } else {
377            (None, None)
378        };
379
380        mem_regions.iter().try_for_each(|region| {
381            let mshv_region = region.to_owned().into();
382            vm_fd.map_user_memory(mshv_region)
383        })?;
384
385        Self::setup_initial_sregs(&mut vcpu_fd, pml4_ptr.absolute()?)?;
386
387        Ok(Self {
388            _mshv: mshv,
389            vm_fd,
390            vcpu_fd,
391            mem_regions,
392            entrypoint: entrypoint_ptr.absolute()?,
393            orig_rsp: rsp_ptr,
394
395            #[cfg(gdb)]
396            debug,
397            #[cfg(gdb)]
398            gdb_conn,
399        })
400    }
401
402    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
403    fn setup_initial_sregs(vcpu: &mut VcpuFd, pml4_addr: u64) -> Result<()> {
404        let sregs = SpecialRegisters {
405            cr0: CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP,
406            cr4: CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT,
407            cr3: pml4_addr,
408            efer: EFER_LME | EFER_LMA | EFER_SCE | EFER_NX,
409            cs: SegmentRegister {
410                type_: 11,
411                present: 1,
412                s: 1,
413                l: 1,
414                ..Default::default()
415            },
416            tr: SegmentRegister {
417                limit: 65535,
418                type_: 11,
419                present: 1,
420                ..Default::default()
421            },
422            ..Default::default()
423        };
424        vcpu.set_sregs(&sregs)?;
425        Ok(())
426    }
427}
428
429impl Debug for HypervLinuxDriver {
430    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
431        let mut f = f.debug_struct("Hyperv Linux Driver");
432
433        f.field("Entrypoint", &self.entrypoint)
434            .field("Original RSP", &self.orig_rsp);
435
436        for region in &self.mem_regions {
437            f.field("Memory Region", &region);
438        }
439
440        let regs = self.vcpu_fd.get_regs();
441
442        if let Ok(regs) = regs {
443            f.field("Registers", &regs);
444        }
445
446        let sregs = self.vcpu_fd.get_sregs();
447
448        if let Ok(sregs) = sregs {
449            f.field("Special Registers", &sregs);
450        }
451
452        f.finish()
453    }
454}
455
456impl Hypervisor for HypervLinuxDriver {
457    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
458    fn initialise(
459        &mut self,
460        peb_addr: RawPtr,
461        seed: u64,
462        page_size: u32,
463        outb_hdl: OutBHandlerWrapper,
464        mem_access_hdl: MemAccessHandlerWrapper,
465        hv_handler: Option<HypervisorHandler>,
466        #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
467    ) -> Result<()> {
468        let regs = StandardRegisters {
469            rip: self.entrypoint,
470            rsp: self.orig_rsp.absolute()?,
471            rflags: 2, //bit 1 of rlags is required to be set
472
473            // function args
474            rcx: peb_addr.into(),
475            rdx: seed,
476            r8: page_size.into(),
477            r9: self.get_max_log_level().into(),
478
479            ..Default::default()
480        };
481        self.vcpu_fd.set_regs(&regs)?;
482
483        VirtualCPU::run(
484            self.as_mut_hypervisor(),
485            hv_handler,
486            outb_hdl,
487            mem_access_hdl,
488            #[cfg(gdb)]
489            dbg_mem_access_fn,
490        )?;
491
492        Ok(())
493    }
494
495    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
496    fn dispatch_call_from_host(
497        &mut self,
498        dispatch_func_addr: RawPtr,
499        outb_handle_fn: OutBHandlerWrapper,
500        mem_access_fn: MemAccessHandlerWrapper,
501        hv_handler: Option<HypervisorHandler>,
502        #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
503    ) -> Result<()> {
504        // Reset general purpose registers, then set RIP and RSP
505        let regs = StandardRegisters {
506            rip: dispatch_func_addr.into(),
507            rsp: self.orig_rsp.absolute()?,
508            rflags: 2, //bit 1 of rlags is required to be set
509            ..Default::default()
510        };
511        self.vcpu_fd.set_regs(&regs)?;
512
513        // reset fpu state
514        let fpu = FloatingPointUnit {
515            fcw: FP_CONTROL_WORD_DEFAULT,
516            ftwx: FP_TAG_WORD_DEFAULT,
517            mxcsr: MXCSR_DEFAULT,
518            ..Default::default() // zero out the rest
519        };
520        self.vcpu_fd.set_fpu(&fpu)?;
521
522        // run
523        VirtualCPU::run(
524            self.as_mut_hypervisor(),
525            hv_handler,
526            outb_handle_fn,
527            mem_access_fn,
528            #[cfg(gdb)]
529            dbg_mem_access_fn,
530        )?;
531
532        Ok(())
533    }
534
535    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
536    fn handle_io(
537        &mut self,
538        port: u16,
539        data: Vec<u8>,
540        rip: u64,
541        instruction_length: u64,
542        outb_handle_fn: OutBHandlerWrapper,
543    ) -> Result<()> {
544        let payload = data[..8].try_into()?;
545        outb_handle_fn
546            .try_lock()
547            .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
548            .call(port, u64::from_le_bytes(payload))?;
549
550        // update rip
551        self.vcpu_fd.set_reg(&[hv_register_assoc {
552            name: hv_register_name_HV_X64_REGISTER_RIP,
553            value: hv_register_value {
554                reg64: rip + instruction_length,
555            },
556            ..Default::default()
557        }])?;
558        Ok(())
559    }
560
561    #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
562    fn run(&mut self) -> Result<super::HyperlightExit> {
563        const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
564        const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
565            hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
566        const UNMAPPED_GPA_MESSAGE: hv_message_type = hv_message_type_HVMSG_UNMAPPED_GPA;
567        const INVALID_GPA_ACCESS_MESSAGE: hv_message_type = hv_message_type_HVMSG_GPA_INTERCEPT;
568        #[cfg(gdb)]
569        const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
570
571        #[cfg(mshv2)]
572        let run_result = {
573            let hv_message: hv_message = Default::default();
574            &self.vcpu_fd.run(hv_message)
575        };
576        #[cfg(mshv3)]
577        let run_result = &self.vcpu_fd.run();
578
579        let result = match run_result {
580            Ok(m) => match m.header.message_type {
581                HALT_MESSAGE => {
582                    crate::debug!("mshv - Halt Details : {:#?}", &self);
583                    HyperlightExit::Halt()
584                }
585                IO_PORT_INTERCEPT_MESSAGE => {
586                    let io_message = m.to_ioport_info()?;
587                    let port_number = io_message.port_number;
588                    let rip = io_message.header.rip;
589                    let rax = io_message.rax;
590                    let instruction_length = io_message.header.instruction_length() as u64;
591                    crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
592                    HyperlightExit::IoOut(
593                        port_number,
594                        rax.to_le_bytes().to_vec(),
595                        rip,
596                        instruction_length,
597                    )
598                }
599                UNMAPPED_GPA_MESSAGE => {
600                    let mimo_message = m.to_memory_info()?;
601                    let addr = mimo_message.guest_physical_address;
602                    crate::debug!(
603                        "mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
604                        addr,
605                        &self
606                    );
607                    HyperlightExit::Mmio(addr)
608                }
609                INVALID_GPA_ACCESS_MESSAGE => {
610                    let mimo_message = m.to_memory_info()?;
611                    let gpa = mimo_message.guest_physical_address;
612                    let access_info = MemoryRegionFlags::try_from(mimo_message)?;
613                    crate::debug!(
614                        "mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
615                        gpa,
616                        &self
617                    );
618                    match self.get_memory_access_violation(
619                        gpa as usize,
620                        &self.mem_regions,
621                        access_info,
622                    ) {
623                        Some(access_info_violation) => access_info_violation,
624                        None => HyperlightExit::Mmio(gpa),
625                    }
626                }
627                // The only case an intercept exit is expected is when debugging is enabled
628                // and the intercepts are installed
629                #[cfg(gdb)]
630                EXCEPTION_INTERCEPT => match self.get_stop_reason() {
631                    Ok(reason) => HyperlightExit::Debug(reason),
632                    Err(e) => {
633                        log_then_return!("Error getting stop reason: {:?}", e);
634                    }
635                },
636                other => {
637                    crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
638                    log_then_return!("unknown Hyper-V run message type {:?}", other);
639                }
640            },
641            Err(e) => match e.errno() {
642                // we send a signal to the thread to cancel execution this results in EINTR being returned by KVM so we return Cancelled
643                libc::EINTR => HyperlightExit::Cancelled(),
644                libc::EAGAIN => HyperlightExit::Retry(),
645                _ => {
646                    crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
647                    log_then_return!("Error running VCPU {:?}", e);
648                }
649            },
650        };
651        Ok(result)
652    }
653
654    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
655    fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
656        self as &mut dyn Hypervisor
657    }
658
659    #[cfg(crashdump)]
660    fn get_memory_regions(&self) -> &[MemoryRegion] {
661        &self.mem_regions
662    }
663
664    #[cfg(gdb)]
665    fn handle_debug(
666        &mut self,
667        dbg_mem_access_fn: std::sync::Arc<
668            std::sync::Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>,
669        >,
670        stop_reason: super::gdb::VcpuStopReason,
671    ) -> Result<()> {
672        self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
673            .map_err(|e| new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e))?;
674
675        loop {
676            log::debug!("Debug wait for event to resume vCPU");
677
678            // Wait for a message from gdb
679            let req = self.recv_dbg_msg()?;
680
681            let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
682
683            let response = match result {
684                Ok(response) => response,
685                // Treat non fatal errors separately so the guest doesn't fail
686                Err(HyperlightError::TranslateGuestAddress(_)) => DebugResponse::ErrorOccurred,
687                Err(e) => {
688                    return Err(e);
689                }
690            };
691
692            // If the command was either step or continue, we need to run the vcpu
693            let cont = matches!(
694                response,
695                DebugResponse::Step | DebugResponse::Continue | DebugResponse::DisableDebug
696            );
697
698            self.send_dbg_msg(response)
699                .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
700
701            if cont {
702                break;
703            }
704        }
705
706        Ok(())
707    }
708}
709
710impl Drop for HypervLinuxDriver {
711    #[instrument(skip_all, parent = Span::current(), level = "Trace")]
712    fn drop(&mut self) {
713        for region in &self.mem_regions {
714            let mshv_region: mshv_user_mem_region = region.to_owned().into();
715            match self.vm_fd.unmap_user_memory(mshv_region) {
716                Ok(_) => (),
717                Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
718            }
719        }
720    }
721}
722
723#[cfg(test)]
724mod tests {
725    use super::*;
726    use crate::mem::memory_region::MemoryRegionVecBuilder;
727    use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
728
729    #[rustfmt::skip]
730    const CODE: [u8; 12] = [
731        0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
732        0x00, 0xd8, /* add %bl, %al */
733        0x04, b'0', /* add $'0', %al */
734        0xee, /* out %al, (%dx) */
735        /* send a 0 to indicate we're done */
736        0xb0, b'\0', /* mov $'\0', %al */
737        0xee, /* out %al, (%dx) */
738        0xf4, /* HLT */
739    ];
740
741    fn shared_mem_with_code(
742        code: &[u8],
743        mem_size: usize,
744        load_offset: usize,
745    ) -> Result<Box<ExclusiveSharedMemory>> {
746        if load_offset > mem_size {
747            log_then_return!(
748                "code load offset ({}) > memory size ({})",
749                load_offset,
750                mem_size
751            );
752        }
753        let mut shared_mem = ExclusiveSharedMemory::new(mem_size)?;
754        shared_mem.copy_from_slice(code, load_offset)?;
755        Ok(Box::new(shared_mem))
756    }
757
758    #[test]
759    fn create_driver() {
760        if !super::is_hypervisor_present() {
761            return;
762        }
763        const MEM_SIZE: usize = 0x3000;
764        let gm = shared_mem_with_code(CODE.as_slice(), MEM_SIZE, 0).unwrap();
765        let rsp_ptr = GuestPtr::try_from(0).unwrap();
766        let pml4_ptr = GuestPtr::try_from(0).unwrap();
767        let entrypoint_ptr = GuestPtr::try_from(0).unwrap();
768        let mut regions = MemoryRegionVecBuilder::new(0, gm.base_addr());
769        regions.push_page_aligned(
770            MEM_SIZE,
771            MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE,
772            crate::mem::memory_region::MemoryRegionType::Code,
773        );
774        super::HypervLinuxDriver::new(
775            regions.build(),
776            entrypoint_ptr,
777            rsp_ptr,
778            pml4_ptr,
779            #[cfg(gdb)]
780            None,
781        )
782        .unwrap();
783    }
784}