Skip to content

Commit 2dee742

Browse files
authored
Make snapshots region aware (#742)
* refactor: move get_memory_access_violation out from trait - Accept iterator instead of slice - Move function from trait to module level. Necessary to maintain object safety of Hypervisor trait Signed-off-by: Ludvig Liljenberg <[email protected]> * implement memory region tracking in drivers - Separate Vecs for initial sandbox regions and mmap regions - Replace unmap_regions(n) with unmap_region(region) for precise control - Add Hash derive for MemoryRegion and related types - Enable use of MemoryRegino in HashSet for efficient set operations Signed-off-by: Ludvig Liljenberg <[email protected]> * Make snapshot region aware - Snapshot now contains the memory region that were mapped at the time of snapshot - On restore, unmap regions that were not mapped at time of snapshot. Map regions that were - Add simple test Signed-off-by: Ludvig Liljenberg <[email protected]> --------- Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent dfe7772 commit 2dee742

File tree

8 files changed

+245
-108
lines changed

8 files changed

+245
-108
lines changed

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ use super::{
7575
use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
7676
#[cfg(gdb)]
7777
use crate::HyperlightError;
78+
use crate::hypervisor::get_memory_access_violation;
7879
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
7980
use crate::mem::ptr::{GuestPtr, RawPtr};
8081
use crate::mem::shared_mem::HostSharedMemory;
@@ -312,12 +313,15 @@ pub(crate) struct HypervLinuxDriver {
312313
page_size: usize,
313314
vm_fd: VmFd,
314315
vcpu_fd: VcpuFd,
315-
entrypoint: u64,
316-
mem_regions: Vec<MemoryRegion>,
317316
orig_rsp: GuestPtr,
317+
entrypoint: u64,
318318
interrupt_handle: Arc<LinuxInterruptHandle>,
319319
mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
320320
host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
321+
322+
sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
323+
mmap_regions: Vec<MemoryRegion>, // Later mapped regions
324+
321325
#[cfg(gdb)]
322326
debug: Option<MshvDebug>,
323327
#[cfg(gdb)]
@@ -447,7 +451,8 @@ impl HypervLinuxDriver {
447451
page_size: 0,
448452
vm_fd,
449453
vcpu_fd,
450-
mem_regions,
454+
sandbox_regions: mem_regions,
455+
mmap_regions: Vec::new(),
451456
entrypoint: entrypoint_ptr.absolute()?,
452457
orig_rsp: rsp_ptr,
453458
interrupt_handle: interrupt_handle.clone(),
@@ -540,8 +545,11 @@ impl Debug for HypervLinuxDriver {
540545
f.field("Entrypoint", &self.entrypoint)
541546
.field("Original RSP", &self.orig_rsp);
542547

543-
for region in &self.mem_regions {
544-
f.field("Memory Region", &region);
548+
for region in &self.sandbox_regions {
549+
f.field("Sandbox Memory Region", &region);
550+
}
551+
for region in &self.mmap_regions {
552+
f.field("Mapped Memory Region", &region);
545553
}
546554

547555
let regs = self.vcpu_fd.get_regs();
@@ -631,20 +639,24 @@ impl Hypervisor for HypervLinuxDriver {
631639
}
632640
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
633641
self.vm_fd.map_user_memory(mshv_region)?;
634-
self.mem_regions.push(rgn.to_owned());
642+
self.mmap_regions.push(rgn.to_owned());
635643
Ok(())
636644
}
637645

638646
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
639-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
640-
for rgn in self
641-
.mem_regions
642-
.split_off(self.mem_regions.len() - n as usize)
643-
{
644-
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
647+
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
648+
if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
649+
let removed_region = self.mmap_regions.remove(pos);
650+
let mshv_region: mshv_user_mem_region = removed_region.into();
645651
self.vm_fd.unmap_user_memory(mshv_region)?;
652+
Ok(())
653+
} else {
654+
Err(new_error!("Tried to unmap region that is not mapped"))
646655
}
647-
Ok(())
656+
}
657+
658+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
659+
Box::new(self.mmap_regions.iter())
648660
}
649661

650662
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -867,9 +879,9 @@ impl Hypervisor for HypervLinuxDriver {
867879
gpa,
868880
&self
869881
);
870-
match self.get_memory_access_violation(
882+
match get_memory_access_violation(
871883
gpa as usize,
872-
&self.mem_regions,
884+
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
873885
access_info,
874886
) {
875887
Some(access_info_violation) => access_info_violation,
@@ -999,7 +1011,7 @@ impl Hypervisor for HypervLinuxDriver {
9991011
});
10001012

10011013
Ok(Some(crashdump::CrashDumpContext::new(
1002-
&self.mem_regions,
1014+
&self.sandbox_regions,
10031015
regs,
10041016
xsave.buffer.to_vec(),
10051017
self.entrypoint,
@@ -1180,7 +1192,7 @@ impl Drop for HypervLinuxDriver {
11801192
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
11811193
fn drop(&mut self) {
11821194
self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
1183-
for region in &self.mem_regions {
1195+
for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
11841196
let mshv_region: mshv_user_mem_region = region.to_owned().into();
11851197
match self.vm_fd.unmap_user_memory(mshv_region) {
11861198
Ok(_) => (),

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ use super::{
5757
};
5858
use super::{HyperlightExit, Hypervisor, InterruptHandle, VirtualCPU};
5959
use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
60+
use crate::hypervisor::get_memory_access_violation;
6061
use crate::hypervisor::wrappers::WHvGeneralRegisters;
6162
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
6263
use crate::mem::ptr::{GuestPtr, RawPtr};
@@ -281,10 +282,13 @@ pub(crate) struct HypervWindowsDriver {
281282
_surrogate_process: SurrogateProcess, // we need to keep a reference to the SurrogateProcess for the duration of the driver since otherwise it will dropped and the memory mapping will be unmapped and the surrogate process will be returned to the pool
282283
entrypoint: u64,
283284
orig_rsp: GuestPtr,
284-
mem_regions: Vec<MemoryRegion>,
285285
interrupt_handle: Arc<WindowsInterruptHandle>,
286286
mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
287287
host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
288+
289+
sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
290+
mmap_regions: Vec<MemoryRegion>, // Later mapped regions
291+
288292
#[cfg(gdb)]
289293
debug: Option<HypervDebug>,
290294
#[cfg(gdb)]
@@ -358,7 +362,8 @@ impl HypervWindowsDriver {
358362
_surrogate_process: surrogate_process,
359363
entrypoint,
360364
orig_rsp: GuestPtr::try_from(RawPtr::from(rsp))?,
361-
mem_regions,
365+
sandbox_regions: mem_regions,
366+
mmap_regions: Vec::new(),
362367
interrupt_handle: interrupt_handle.clone(),
363368
mem_mgr: None,
364369
host_funcs: None,
@@ -457,8 +462,11 @@ impl Debug for HypervWindowsDriver {
457462
fs.field("Entrypoint", &self.entrypoint)
458463
.field("Original RSP", &self.orig_rsp);
459464

460-
for region in &self.mem_regions {
461-
fs.field("Memory Region", &region);
465+
for region in &self.sandbox_regions {
466+
fs.field("Sandbox Memory Region", &region);
467+
}
468+
for region in &self.mmap_regions {
469+
fs.field("Mapped Memory Region", &region);
462470
}
463471

464472
// Get the registers
@@ -631,18 +639,17 @@ impl Hypervisor for HypervWindowsDriver {
631639
}
632640

633641
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
634-
unsafe fn map_region(&mut self, _rgn: &MemoryRegion) -> Result<()> {
642+
unsafe fn map_region(&mut self, _region: &MemoryRegion) -> Result<()> {
635643
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
636644
}
637645

638646
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
639-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
640-
if n > 0 {
641-
log_then_return!(
642-
"Mapping host memory into the guest not yet supported on this platform"
643-
);
644-
}
645-
Ok(())
647+
unsafe fn unmap_region(&mut self, _region: &MemoryRegion) -> Result<()> {
648+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
649+
}
650+
651+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
652+
Box::new(self.mmap_regions.iter())
646653
}
647654

648655
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -824,8 +831,11 @@ impl Hypervisor for HypervWindowsDriver {
824831
gpa, access_info, &self
825832
);
826833

827-
match self.get_memory_access_violation(gpa as usize, &self.mem_regions, access_info)
828-
{
834+
match get_memory_access_violation(
835+
gpa as usize,
836+
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
837+
access_info,
838+
) {
829839
Some(access_info) => access_info,
830840
None => HyperlightExit::Mmio(gpa),
831841
}
@@ -934,7 +944,7 @@ impl Hypervisor for HypervWindowsDriver {
934944
});
935945

936946
Ok(Some(crashdump::CrashDumpContext::new(
937-
&self.mem_regions,
947+
&self.sandbox_regions,
938948
regs,
939949
xsave,
940950
self.entrypoint,

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use super::{
4242
use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
4343
#[cfg(gdb)]
4444
use crate::HyperlightError;
45+
use crate::hypervisor::get_memory_access_violation;
4546
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4647
use crate::mem::ptr::{GuestPtr, RawPtr};
4748
use crate::mem::shared_mem::HostSharedMemory;
@@ -294,10 +295,15 @@ pub(crate) struct KVMDriver {
294295
vcpu_fd: VcpuFd,
295296
entrypoint: u64,
296297
orig_rsp: GuestPtr,
297-
mem_regions: Vec<MemoryRegion>,
298298
interrupt_handle: Arc<LinuxInterruptHandle>,
299299
mem_mgr: Option<MemMgrWrapper<HostSharedMemory>>,
300300
host_funcs: Option<Arc<Mutex<FunctionRegistry>>>,
301+
302+
sandbox_regions: Vec<MemoryRegion>, // Initially mapped regions when sandbox is created
303+
mmap_regions: Vec<(MemoryRegion, u32)>, // Later mapped regions (region, slot number)
304+
next_slot: u32, // Monotonically increasing slot number
305+
freed_slots: Vec<u32>, // Reusable slots from unmapped regions
306+
301307
#[cfg(gdb)]
302308
debug: Option<KvmDebug>,
303309
#[cfg(gdb)]
@@ -384,7 +390,10 @@ impl KVMDriver {
384390
vcpu_fd,
385391
entrypoint,
386392
orig_rsp: rsp_gp,
387-
mem_regions,
393+
next_slot: mem_regions.len() as u32,
394+
sandbox_regions: mem_regions,
395+
mmap_regions: Vec::new(),
396+
freed_slots: Vec::new(),
388397
interrupt_handle: interrupt_handle.clone(),
389398
mem_mgr: None,
390399
host_funcs: None,
@@ -434,8 +443,11 @@ impl Debug for KVMDriver {
434443
let mut f = f.debug_struct("KVM Driver");
435444
// Output each memory region
436445

437-
for region in &self.mem_regions {
438-
f.field("Memory Region", &region);
446+
for region in &self.sandbox_regions {
447+
f.field("Sandbox Memory Region", &region);
448+
}
449+
for region in &self.mmap_regions {
450+
f.field("Mapped Memory Region", &region);
439451
}
440452
let regs = self.vcpu_fd.get_regs();
441453
// check that regs is OK and then set field in debug struct
@@ -517,25 +529,45 @@ impl Hypervisor for KVMDriver {
517529
}
518530

519531
let mut kvm_region: kvm_userspace_memory_region = region.clone().into();
520-
kvm_region.slot = self.mem_regions.len() as u32;
532+
533+
// Try to reuse a freed slot first, otherwise use next_slot
534+
let slot = if let Some(freed_slot) = self.freed_slots.pop() {
535+
freed_slot
536+
} else {
537+
let slot = self.next_slot;
538+
self.next_slot += 1;
539+
slot
540+
};
541+
542+
kvm_region.slot = slot;
521543
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
522-
self.mem_regions.push(region.to_owned());
544+
self.mmap_regions.push((region.to_owned(), slot));
523545
Ok(())
524546
}
525547

526548
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
527-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
528-
let n_keep = self.mem_regions.len() - n as usize;
529-
for (k, region) in self.mem_regions.split_off(n_keep).iter().enumerate() {
530-
let mut kvm_region: kvm_userspace_memory_region = region.clone().into();
531-
kvm_region.slot = (n_keep + k) as u32;
549+
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
550+
if let Some(idx) = self.mmap_regions.iter().position(|(r, _)| r == region) {
551+
let (region, slot) = self.mmap_regions.remove(idx);
552+
let mut kvm_region: kvm_userspace_memory_region = region.into();
553+
kvm_region.slot = slot;
532554
// Setting memory_size to 0 unmaps the slot's region
533555
// From https://docs.kernel.org/virt/kvm/api.html
534556
// > Deleting a slot is done by passing zero for memory_size.
535557
kvm_region.memory_size = 0;
536558
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
559+
560+
// Add the freed slot to the reuse list
561+
self.freed_slots.push(slot);
562+
563+
Ok(())
564+
} else {
565+
Err(new_error!("Tried to unmap region that is not mapped"))
537566
}
538-
Ok(())
567+
}
568+
569+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
570+
Box::new(self.mmap_regions.iter().map(|(region, _)| region))
539571
}
540572

541573
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -717,9 +749,11 @@ impl Hypervisor for KVMDriver {
717749
Ok(VcpuExit::MmioRead(addr, _)) => {
718750
crate::debug!("KVM MMIO Read -Details: Address: {} \n {:#?}", addr, &self);
719751

720-
match self.get_memory_access_violation(
752+
match get_memory_access_violation(
721753
addr as usize,
722-
&self.mem_regions,
754+
self.sandbox_regions
755+
.iter()
756+
.chain(self.mmap_regions.iter().map(|(r, _)| r)),
723757
MemoryRegionFlags::READ,
724758
) {
725759
Some(access_violation_exit) => access_violation_exit,
@@ -729,9 +763,11 @@ impl Hypervisor for KVMDriver {
729763
Ok(VcpuExit::MmioWrite(addr, _)) => {
730764
crate::debug!("KVM MMIO Write -Details: Address: {} \n {:#?}", addr, &self);
731765

732-
match self.get_memory_access_violation(
766+
match get_memory_access_violation(
733767
addr as usize,
734-
&self.mem_regions,
768+
self.sandbox_regions
769+
.iter()
770+
.chain(self.mmap_regions.iter().map(|(r, _)| r)),
735771
MemoryRegionFlags::WRITE,
736772
) {
737773
Some(access_violation_exit) => access_violation_exit,
@@ -847,7 +883,7 @@ impl Hypervisor for KVMDriver {
847883
// The [`CrashDumpContext`] accepts xsave as a vector of u8, so we need to convert the
848884
// xsave region to a vector of u8
849885
Ok(Some(crashdump::CrashDumpContext::new(
850-
&self.mem_regions,
886+
&self.sandbox_regions,
851887
regs,
852888
xsave
853889
.region

0 commit comments

Comments
 (0)