Skip to content

Commit 07ff84d

Browse files
committed
[hyperlight_host] Allow mapping a host memory region into a guest
Signed-off-by: Lucy Menon <[email protected]>
1 parent 30b24de commit 07ff84d

File tree

8 files changed

+205
-12
lines changed

8 files changed

+205
-12
lines changed

src/hyperlight_host/src/func/call_ctx.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
use tracing::{Span, instrument};
1818

1919
use super::{ParameterTuple, SupportedReturnType};
20+
use crate::mem::memory_region::MemoryRegion;
2021
use crate::sandbox::Callable;
2122
use crate::{MultiUseSandbox, Result};
2223
/// A context for calling guest functions.
@@ -70,6 +71,30 @@ impl MultiUseGuestCallContext {
7071
pub(crate) fn finish_no_reset(self) -> MultiUseSandbox {
7172
self.sbox
7273
}
74+
75+
/// Map a region of host memory into the sandbox.
76+
///
77+
/// Depending on the host platform, there are likely alignment
78+
/// requirements of at least one page for base and len.
79+
///
80+
/// `rgn.region_type` is ignored, since guest PTEs are not created
81+
/// for the new memory.
82+
///
83+
/// # Safety
84+
/// It is the caller's responsibility to ensure that the host side
85+
/// of the region remains intact and is not written to until this
86+
/// mapping is removed, either due to the destruction of the
87+
/// sandbox or due to a state rollback
88+
pub unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
89+
unsafe { self.sbox.map_region(rgn) }
90+
}
91+
92+
/// Map the contents of a file into the guest at a particular address
93+
///
94+
/// Returns the length of the mapping
95+
pub fn map_file_cow(&mut self, fp: &std::path::Path, guest_base: u64) -> Result<u64> {
96+
self.sbox.map_file_cow(fp, guest_base)
97+
}
7398
}
7499

75100
impl Callable for MultiUseGuestCallContext {

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ pub(crate) fn is_hypervisor_present() -> bool {
297297
/// called the Microsoft Hypervisor (MSHV)
298298
pub(crate) struct HypervLinuxDriver {
299299
_mshv: Mshv,
300+
page_size: usize,
300301
vm_fd: VmFd,
301302
vcpu_fd: VcpuFd,
302303
entrypoint: u64,
@@ -424,6 +425,7 @@ impl HypervLinuxDriver {
424425
#[allow(unused_mut)]
425426
let mut hv = Self {
426427
_mshv: mshv,
428+
page_size: 0,
427429
vm_fd,
428430
vcpu_fd,
429431
mem_regions,
@@ -525,6 +527,8 @@ impl Hypervisor for HypervLinuxDriver {
525527
max_guest_log_level: Option<LevelFilter>,
526528
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
527529
) -> Result<()> {
530+
self.page_size = page_size as usize;
531+
528532
let max_guest_log_level: u64 = match max_guest_log_level {
529533
Some(level) => level as u64,
530534
None => self.get_max_log_level().into(),
@@ -556,6 +560,37 @@ impl Hypervisor for HypervLinuxDriver {
556560
Ok(())
557561
}
558562

563+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
564+
unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> {
565+
if [
566+
rgn.guest_region.start,
567+
rgn.guest_region.end,
568+
rgn.host_region.start,
569+
rgn.host_region.end,
570+
]
571+
.iter()
572+
.any(|x| x % self.page_size != 0)
573+
{
574+
log_then_return!("region is not page-aligned");
575+
}
576+
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
577+
self.vm_fd.map_user_memory(mshv_region)?;
578+
self.mem_regions.push(rgn.to_owned());
579+
Ok(())
580+
}
581+
582+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
583+
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
584+
for rgn in self
585+
.mem_regions
586+
.split_off(self.mem_regions.len() - n as usize)
587+
{
588+
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
589+
self.vm_fd.unmap_user_memory(mshv_region)?;
590+
}
591+
Ok(())
592+
}
593+
559594
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
560595
fn dispatch_call_from_host(
561596
&mut self,

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,16 @@ impl Hypervisor for HypervWindowsDriver {
606606
Ok(())
607607
}
608608

609+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
610+
unsafe fn map_region(&mut self, _rgn: &MemoryRegion) -> Result<()> {
611+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
612+
}
613+
614+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
615+
unsafe fn unmap_regions(&mut self, _n: u64) -> Result<()> {
616+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
617+
}
618+
609619
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
610620
fn dispatch_call_from_host(
611621
&mut self,

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,16 @@ impl Hypervisor for KVMDriver {
493493
Ok(())
494494
}
495495

496+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
497+
unsafe fn map_region(&mut self, _rgn: &MemoryRegion) -> Result<()> {
498+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
499+
}
500+
501+
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
502+
unsafe fn unmap_regions(&mut self, _n: u64) -> Result<()> {
503+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
504+
}
505+
496506
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
497507
fn dispatch_call_from_host(
498508
&mut self,

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ pub(crate) trait Hypervisor: Debug + Sync + Send {
132132
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
133133
) -> Result<()>;
134134

135+
/// Map a region of host memory into the sandbox.
136+
///
137+
/// Depending on the host platform, there are likely alignment
138+
/// requirements of at least one page for base and len.
139+
unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()>;
140+
141+
/// Unmap the most recent `n` regions mapped by `map_region`
142+
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()>;
143+
135144
/// Dispatch a call from the host to the guest using the given pointer
136145
/// to the dispatch function _in the guest's address space_.
137146
///

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub(crate) struct SandboxMemoryManager<S> {
7373
pub(crate) load_addr: RawPtr,
7474
/// Offset for the execution entrypoint from `load_addr`
7575
pub(crate) entrypoint_offset: Offset,
76+
/// How many memory regions were mapped after sandbox creation
77+
pub(crate) mapped_rgns: u64,
7678
/// A vector of memory snapshots that can be used to save and restore the state of the memory
7779
/// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API)
7880
snapshots: Arc<Mutex<Vec<SharedMemorySnapshot>>>,
@@ -95,6 +97,7 @@ where
9597
shared_mem,
9698
load_addr,
9799
entrypoint_offset,
100+
mapped_rgns: 0,
98101
snapshots: Arc::new(Mutex::new(Vec::new())),
99102
}
100103
}
@@ -265,7 +268,7 @@ where
265268
/// this function will create a memory snapshot and push it onto the stack of snapshots
266269
/// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
267270
pub(crate) fn push_state(&mut self) -> Result<()> {
268-
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem)?;
271+
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns)?;
269272
self.snapshots
270273
.try_lock()
271274
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
@@ -277,7 +280,11 @@ where
277280
/// off the stack
278281
/// It should be used when you want to restore the state of the memory to a previous state but still want to
279282
/// retain that state, for example after calling a function in the guest
280-
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<()> {
283+
///
284+
/// Returns the number of memory regions mapped into the sandbox
285+
/// that need to be unmapped in order for the restore to be
286+
/// completed.
287+
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<u64> {
281288
let mut snapshots = self
282289
.snapshots
283290
.try_lock()
@@ -288,13 +295,15 @@ where
288295
}
289296
#[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above
290297
let snapshot = last.unwrap();
291-
snapshot.restore_from_snapshot(&mut self.shared_mem)
298+
let old_rgns = self.mapped_rgns;
299+
self.mapped_rgns = snapshot.restore_from_snapshot(&mut self.shared_mem)?;
300+
Ok(old_rgns - self.mapped_rgns)
292301
}
293302

294303
/// this function pops the last snapshot off the stack and restores the memory to the previous state
295304
/// It should be used when you want to restore the state of the memory to a previous state and do not need to retain that state
296305
/// for example when devolving a sandbox to a previous state.
297-
pub(crate) fn pop_and_restore_state_from_snapshot(&mut self) -> Result<()> {
306+
pub(crate) fn pop_and_restore_state_from_snapshot(&mut self) -> Result<u64> {
298307
let last = self
299308
.snapshots
300309
.try_lock()
@@ -430,13 +439,15 @@ impl SandboxMemoryManager<ExclusiveSharedMemory> {
430439
layout: self.layout,
431440
load_addr: self.load_addr.clone(),
432441
entrypoint_offset: self.entrypoint_offset,
442+
mapped_rgns: 0,
433443
snapshots: Arc::new(Mutex::new(Vec::new())),
434444
},
435445
SandboxMemoryManager {
436446
shared_mem: gshm,
437447
layout: self.layout,
438448
load_addr: self.load_addr.clone(),
439449
entrypoint_offset: self.entrypoint_offset,
450+
mapped_rgns: 0,
440451
snapshots: Arc::new(Mutex::new(Vec::new())),
441452
},
442453
)

src/hyperlight_host/src/mem/shared_mem_snapshot.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,21 @@ use crate::Result;
2424
#[derive(Clone)]
2525
pub(super) struct SharedMemorySnapshot {
2626
snapshot: Vec<u8>,
27+
/// How many non-main-RAM regions were mapped when this snapshot was taken?
28+
mapped_rgns: u64,
2729
}
2830

2931
impl SharedMemorySnapshot {
3032
/// Take a snapshot of the memory in `shared_mem`, then create a new
3133
/// instance of `Self` with the snapshot stored therein.
3234
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
33-
pub(super) fn new<S: SharedMemory>(shared_mem: &mut S) -> Result<Self> {
35+
pub(super) fn new<S: SharedMemory>(shared_mem: &mut S, mapped_rgns: u64) -> Result<Self> {
3436
// TODO: Track dirty pages instead of copying entire memory
3537
let snapshot = shared_mem.with_exclusivity(|e| e.copy_all_to_vec())??;
36-
Ok(Self { snapshot })
38+
Ok(Self {
39+
snapshot,
40+
mapped_rgns,
41+
})
3742
}
3843

3944
/// Take another snapshot of the internally-stored `SharedMemory`,
@@ -51,8 +56,9 @@ impl SharedMemorySnapshot {
5156
pub(super) fn restore_from_snapshot<S: SharedMemory>(
5257
&mut self,
5358
shared_mem: &mut S,
54-
) -> Result<()> {
55-
shared_mem.with_exclusivity(|e| e.copy_from_slice(self.snapshot.as_slice(), 0))?
59+
) -> Result<u64> {
60+
shared_mem.with_exclusivity(|e| e.copy_from_slice(self.snapshot.as_slice(), 0))??;
61+
Ok(self.mapped_rgns)
5662
}
5763
}
5864

@@ -69,7 +75,7 @@ mod tests {
6975
let data2 = data1.iter().map(|b| b + 1).collect::<Vec<u8>>();
7076
let mut gm = ExclusiveSharedMemory::new(PAGE_SIZE_USIZE).unwrap();
7177
gm.copy_from_slice(data1.as_slice(), 0).unwrap();
72-
let mut snap = super::SharedMemorySnapshot::new(&mut gm).unwrap();
78+
let mut snap = super::SharedMemorySnapshot::new(&mut gm, 0).unwrap();
7379
{
7480
// after the first snapshot is taken, make sure gm has the equivalent
7581
// of data1

0 commit comments

Comments
 (0)