Skip to content

Commit 3954d0a

Browse files
committed
Implement run, handle_io, memory mapping
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 0b8b212 commit 3954d0a

File tree

9 files changed

+491
-689
lines changed

9 files changed

+491
-689
lines changed

src/hyperlight_host/src/hypervisor/gdb/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
mod arch;
17+
pub(crate) mod arch;
1818
mod event_loop;
1919
#[cfg(target_os = "windows")]
2020
mod hyperv_debug;

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 344 additions & 15 deletions
Large diffs are not rendered by default.

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 45 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ use super::gdb::{
4545
DebugCommChannel, DebugMemoryAccess, DebugMsg, DebugResponse, GuestDebug, MshvDebug,
4646
VcpuStopReason,
4747
};
48-
use super::{HyperlightExit, Hypervisor, LinuxInterruptHandle, VirtualCPU};
48+
use super::{Hypervisor, LinuxInterruptHandle};
4949
#[cfg(gdb)]
5050
use crate::HyperlightError;
5151
use crate::hypervisor::regs::CommonFpu;
52-
use crate::hypervisor::{InterruptHandle, InterruptHandleImpl, get_memory_access_violation};
52+
use crate::hypervisor::{InterruptHandle, InterruptHandleImpl, HyperlightExit};
5353
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
5454
use crate::mem::mgr::SandboxMemoryManager;
5555
use crate::mem::ptr::{GuestPtr, RawPtr};
@@ -273,9 +273,6 @@ pub(crate) struct HypervLinuxDriver {
273273
entrypoint: u64,
274274
interrupt_handle: Arc<dyn InterruptHandleImpl>,
275275

276-
sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
277-
mmap_regions: Vec<MemoryRegion>, // Later mapped regions
278-
279276
#[cfg(gdb)]
280277
debug: Option<MshvDebug>,
281278
#[cfg(gdb)]
@@ -299,7 +296,6 @@ impl HypervLinuxDriver {
299296
// TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg
300297
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
301298
pub(crate) fn new(
302-
mem_regions: Vec<MemoryRegion>,
303299
entrypoint_ptr: GuestPtr,
304300
rsp_ptr: GuestPtr,
305301
pml4_ptr: GuestPtr,
@@ -365,11 +361,6 @@ impl HypervLinuxDriver {
365361
(None, None)
366362
};
367363

368-
mem_regions.iter().try_for_each(|region| {
369-
let mshv_region = region.to_owned().into();
370-
vm_fd.map_user_memory(mshv_region)
371-
})?;
372-
373364
let interrupt_handle: Arc<dyn InterruptHandleImpl> = Arc::new(LinuxInterruptHandle {
374365
state: AtomicU8::new(0),
375366
#[cfg(all(
@@ -396,8 +387,6 @@ impl HypervLinuxDriver {
396387
page_size: 0,
397388
vm_fd,
398389
vcpu_fd,
399-
sandbox_regions: mem_regions,
400-
mmap_regions: Vec::new(),
401390
entrypoint: entrypoint_ptr.absolute()?,
402391
orig_rsp: rsp_ptr,
403392
interrupt_handle: interrupt_handle.clone(),
@@ -431,13 +420,6 @@ impl Debug for HypervLinuxDriver {
431420
f.field("Entrypoint", &self.entrypoint)
432421
.field("Original RSP", &self.orig_rsp);
433422

434-
for region in &self.sandbox_regions {
435-
f.field("Sandbox Memory Region", &region);
436-
}
437-
for region in &self.mmap_regions {
438-
f.field("Mapped Memory Region", &region);
439-
}
440-
441423
let regs = self.vcpu_fd.get_regs();
442424

443425
if let Ok(regs) = regs {
@@ -488,51 +470,22 @@ impl Hypervisor for HypervLinuxDriver {
488470
};
489471
self.vcpu_fd.set_regs(&regs)?;
490472

491-
let interrupt_handle = self.interrupt_handle.clone();
492-
493-
VirtualCPU::run(
494-
self.as_mut_hypervisor(),
495-
interrupt_handle,
496-
mem_mgr,
497-
host_funcs,
498-
#[cfg(gdb)]
499-
dbg_mem_access_fn,
500-
)
473+
Ok(())
501474
}
502475

503-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
504-
unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
505-
if [
506-
rgn.guest_region.start,
507-
rgn.guest_region.end,
508-
rgn.host_region.start,
509-
rgn.host_region.end,
510-
]
511-
.iter()
512-
.any(|x| x % self.page_size != 0)
513-
{
514-
log_then_return!("region is not page-aligned");
515-
}
516-
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
476+
/// # Safety
477+
/// The caller must ensure that the memory region is valid and points to valid memory,
478+
/// and lives long enough for the VM to use it.
479+
unsafe fn map_memory(&mut self, (_slot, region): (u32, &MemoryRegion)) -> Result<()> {
480+
let mshv_region: mshv_user_mem_region = region.into();
517481
self.vm_fd.map_user_memory(mshv_region)?;
518-
self.mmap_regions.push(rgn.to_owned());
519482
Ok(())
520483
}
521484

522-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
523-
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
524-
if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
525-
let removed_region = self.mmap_regions.remove(pos);
526-
let mshv_region: mshv_user_mem_region = removed_region.into();
527-
self.vm_fd.unmap_user_memory(mshv_region)?;
528-
Ok(())
529-
} else {
530-
Err(new_error!("Tried to unmap region that is not mapped"))
531-
}
532-
}
533-
534-
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
535-
Box::new(self.mmap_regions.iter())
485+
fn unmap_memory(&mut self, (_slot, region): (u32, &MemoryRegion)) -> Result<()> {
486+
let mshv_region: mshv_user_mem_region = region.into();
487+
self.vm_fd.unmap_user_memory(mshv_region)?;
488+
Ok(())
536489
}
537490

538491
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -555,61 +508,11 @@ impl Hypervisor for HypervLinuxDriver {
555508
// reset fpu state
556509
self.set_fpu(&CommonFpu::default())?;
557510

558-
let interrupt_handle = self.interrupt_handle.clone();
559-
560511
// run
561-
VirtualCPU::run(
562-
self.as_mut_hypervisor(),
563-
interrupt_handle,
564-
mem_mgr,
565-
host_funcs,
566-
#[cfg(gdb)]
567-
dbg_mem_access_fn,
568-
)
569-
}
570-
571-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
572-
fn handle_io(
573-
&mut self,
574-
port: u16,
575-
data: Vec<u8>,
576-
rip: u64,
577-
instruction_length: u64,
578-
mem_mgr: &mut SandboxMemoryManager<HostSharedMemory>,
579-
host_funcs: &Arc<Mutex<FunctionRegistry>>,
580-
) -> Result<()> {
581-
let mut padded = [0u8; 4];
582-
let copy_len = data.len().min(4);
583-
padded[..copy_len].copy_from_slice(&data[..copy_len]);
584-
let val = u32::from_le_bytes(padded);
585-
586-
#[cfg(feature = "mem_profile")]
587-
{
588-
let regs = self.regs()?;
589-
let trace_info = self.trace_info_mut();
590-
handle_outb(mem_mgr, host_funcs, port, val, &regs, trace_info)?;
591-
}
592-
#[cfg(not(feature = "mem_profile"))]
593-
{
594-
handle_outb(mem_mgr, host_funcs, port, val)?;
595-
}
596-
597-
// update rip
598-
self.vcpu_fd.set_reg(&[hv_register_assoc {
599-
name: hv_register_name_HV_X64_REGISTER_RIP,
600-
value: hv_register_value {
601-
reg64: rip + instruction_length,
602-
},
603-
..Default::default()
604-
}])?;
605512
Ok(())
606513
}
607514

608-
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
609-
fn run(
610-
&mut self,
611-
#[cfg(feature = "trace_guest")] tc: &mut crate::sandbox::trace::TraceContext,
612-
) -> Result<super::HyperlightExit> {
515+
fn run_vcpu(&mut self) -> Result<HyperlightExit> {
613516
const HALT_MESSAGE: hv_message_type = hv_message_type_HVMSG_X64_HALT;
614517
const IO_PORT_INTERCEPT_MESSAGE: hv_message_type =
615518
hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT;
@@ -618,97 +521,67 @@ impl Hypervisor for HypervLinuxDriver {
618521
#[cfg(gdb)]
619522
const EXCEPTION_INTERCEPT: hv_message_type = hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT;
620523

621-
#[cfg(feature = "trace_guest")]
622-
tc.setup_guest_trace(Span::current().context());
623-
624524
let exit_reason = self.vcpu_fd.run();
625525

626526
let result = match exit_reason {
627527
Ok(m) => match m.header.message_type {
628-
HALT_MESSAGE => {
629-
crate::debug!("mshv - Halt Details : {:#?}", &self);
630-
HyperlightExit::Halt()
631-
}
528+
HALT_MESSAGE => HyperlightExit::Halt(),
632529
IO_PORT_INTERCEPT_MESSAGE => {
633530
let io_message = m.to_ioport_info().map_err(mshv_ioctls::MshvError::from)?;
634531
let port_number = io_message.port_number;
635532
let rip = io_message.header.rip;
636533
let rax = io_message.rax;
637534
let instruction_length = io_message.header.instruction_length() as u64;
638-
crate::debug!("mshv IO Details : \nPort : {}\n{:#?}", port_number, &self);
639-
HyperlightExit::IoOut(
640-
port_number,
641-
rax.to_le_bytes().to_vec(),
642-
rip,
643-
instruction_length,
644-
)
535+
536+
// mshv, unlike kvm, does not automatically increment RIP
537+
self.vcpu_fd.set_reg(&[hv_register_assoc {
538+
name: hv_register_name_HV_X64_REGISTER_RIP,
539+
value: hv_register_value {
540+
reg64: rip + instruction_length,
541+
},
542+
..Default::default()
543+
}])?;
544+
HyperlightExit::IoOut(port_number, rax.to_le_bytes().to_vec())
645545
}
646546
UNMAPPED_GPA_MESSAGE => {
647547
let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
648548
let addr = mimo_message.guest_physical_address;
649-
crate::debug!(
650-
"mshv MMIO unmapped GPA -Details: Address: {} \n {:#?}",
651-
addr,
652-
&self
653-
);
654-
HyperlightExit::Mmio(addr)
549+
match MemoryRegionFlags::try_from(mimo_message)? {
550+
MemoryRegionFlags::READ => HyperlightExit::MmioRead(addr),
551+
MemoryRegionFlags::WRITE => HyperlightExit::MmioWrite(addr),
552+
_ => HyperlightExit::Unknown("Unknown MMIO access".to_string()),
553+
}
655554
}
656555
INVALID_GPA_ACCESS_MESSAGE => {
657556
let mimo_message = m.to_memory_info().map_err(mshv_ioctls::MshvError::from)?;
658557
let gpa = mimo_message.guest_physical_address;
659558
let access_info = MemoryRegionFlags::try_from(mimo_message)?;
660-
crate::debug!(
661-
"mshv MMIO invalid GPA access -Details: Address: {} \n {:#?}",
662-
gpa,
663-
&self
664-
);
665-
match get_memory_access_violation(
666-
gpa as usize,
667-
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
668-
access_info,
669-
) {
670-
Some(access_info_violation) => access_info_violation,
671-
None => HyperlightExit::Mmio(gpa),
559+
match access_info {
560+
MemoryRegionFlags::READ => HyperlightExit::MmioRead(gpa),
561+
MemoryRegionFlags::WRITE => HyperlightExit::MmioWrite(gpa),
562+
_ => HyperlightExit::Unknown("Unknown MMIO access".to_string()),
672563
}
673564
}
674-
// The only case an intercept exit is expected is when debugging is enabled
675-
// and the intercepts are installed.
676-
// Provide the extra information about the exception to accurately determine
677-
// the stop reason
678565
#[cfg(gdb)]
679566
EXCEPTION_INTERCEPT => {
680-
// Extract exception info from the message so we can figure out
681-
// more information about the vCPU state
682-
let ex_info = match m.to_exception_info().map_err(mshv_ioctls::MshvError::from)
683-
{
684-
Ok(info) => info,
685-
Err(e) => {
686-
log_then_return!("Error converting to exception info: {:?}", e);
687-
}
688-
};
689-
690-
match self.get_stop_reason(ex_info) {
691-
Ok(reason) => HyperlightExit::Debug(reason),
692-
Err(e) => {
693-
log_then_return!("Error getting stop reason: {:?}", e);
694-
}
567+
use mshv_bindings::DebugRegisters;
568+
569+
let ex_info = m
570+
.to_exception_info()
571+
.map_err(mshv_ioctls::MshvError::from)?;
572+
let DebugRegisters { dr6, .. } = self.vcpu_fd.get_debug_regs()?;
573+
HyperlightExit::Debug {
574+
dr6,
575+
exception: ex_info.exception_vector as u32,
695576
}
696577
}
697-
other => {
698-
crate::debug!("mshv Other Exit: Exit: {:#?} \n {:#?}", other, &self);
699-
#[cfg(crashdump)]
700-
let _ = crashdump::generate_crashdump(self);
701-
log_then_return!("unknown Hyper-V run message type {:?}", other);
702-
}
578+
other => HyperlightExit::Unknown(format!("Unknown MSHV VCPU exit: {:?}", other)),
703579
},
704580
Err(e) => match e.errno() {
705-
// We send a signal (SIGRTMIN+offset) to interrupt the vcpu, which causes EINTR
581+
// InterruptHandle::kill() sends a signal (SIGRTMIN+offset) to interrupt the vcpu, which causes EINTR
706582
libc::EINTR => HyperlightExit::Cancelled(),
707583
libc::EAGAIN => HyperlightExit::Retry(),
708-
_ => {
709-
crate::debug!("mshv Error - Details: Error: {} \n {:#?}", e, &self);
710-
log_then_return!("Error running VCPU {:?}", e);
711-
}
584+
_ => HyperlightExit::Unknown(format!("Unknown MSHV VCPU error: {}", e)),
712585
},
713586
};
714587
Ok(result)
@@ -747,11 +620,6 @@ impl Hypervisor for HypervLinuxDriver {
747620
Ok(())
748621
}
749622

750-
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
751-
fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
752-
self as &mut dyn Hypervisor
753-
}
754-
755623
fn interrupt_handle(&self) -> Arc<dyn InterruptHandle> {
756624
self.interrupt_handle.clone()
757625
}
@@ -970,13 +838,6 @@ impl Drop for HypervLinuxDriver {
970838
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
971839
fn drop(&mut self) {
972840
self.interrupt_handle.set_dropped();
973-
for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
974-
let mshv_region: mshv_user_mem_region = region.to_owned().into();
975-
match self.vm_fd.unmap_user_memory(mshv_region) {
976-
Ok(_) => (),
977-
Err(e) => error!("Failed to unmap user memory in HyperVOnLinux ({:?})", e),
978-
}
979-
}
980841
}
981842
}
982843

@@ -1036,7 +897,6 @@ mod tests {
1036897
let config: SandboxConfiguration = Default::default();
1037898

1038899
super::HypervLinuxDriver::new(
1039-
regions.build(),
1040900
entrypoint_ptr,
1041901
rsp_ptr,
1042902
pml4_ptr,

0 commit comments

Comments
 (0)