Skip to content

Commit 0994d3e

Browse files
committed
add memory mapping support with kvm
Signed-off-by: Jorge Prendes <[email protected]>
1 parent ea6fa8f commit 0994d3e

File tree

3 files changed

+144
-22
lines changed

3 files changed

+144
-22
lines changed

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ mod debug {
284284
/// A Hypervisor driver for KVM on Linux
285285
pub(crate) struct KVMDriver {
286286
_kvm: Kvm,
287-
_vm_fd: VmFd,
287+
vm_fd: VmFd,
288+
page_size: usize,
288289
vcpu_fd: VcpuFd,
289290
entrypoint: u64,
290291
orig_rsp: GuestPtr,
@@ -317,21 +318,8 @@ impl KVMDriver {
317318

318319
let vm_fd = kvm.create_vm_with_type(0)?;
319320

320-
let perm_flags =
321-
MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
322-
323321
mem_regions.iter().enumerate().try_for_each(|(i, region)| {
324-
let perm_flags = perm_flags.intersection(region.flags);
325-
let kvm_region = kvm_userspace_memory_region {
326-
slot: i as u32,
327-
guest_phys_addr: region.guest_region.start as u64,
328-
memory_size: (region.guest_region.end - region.guest_region.start) as u64,
329-
userspace_addr: region.host_region.start as u64,
330-
flags: match perm_flags {
331-
MemoryRegionFlags::READ => KVM_MEM_READONLY,
332-
_ => 0, // normal, RWX
333-
},
334-
};
322+
let kvm_region = mem_region_to_kvm_region(region, i);
335323
unsafe { vm_fd.set_user_memory_region(kvm_region) }
336324
})?;
337325

@@ -378,7 +366,8 @@ impl KVMDriver {
378366
#[allow(unused_mut)]
379367
let mut hv = Self {
380368
_kvm: kvm,
381-
_vm_fd: vm_fd,
369+
vm_fd,
370+
page_size: 0,
382371
vcpu_fd,
383372
entrypoint,
384373
orig_rsp: rsp_gp,
@@ -463,6 +452,8 @@ impl Hypervisor for KVMDriver {
463452
max_guest_log_level: Option<LevelFilter>,
464453
#[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
465454
) -> Result<()> {
455+
self.page_size = page_size as usize;
456+
466457
let max_guest_log_level: u64 = match max_guest_log_level {
467458
Some(level) => level as u64,
468459
None => self.get_max_log_level().into(),
@@ -494,16 +485,39 @@ impl Hypervisor for KVMDriver {
494485
}
495486

496487
#[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");
488+
unsafe fn map_region(&mut self, region: &MemoryRegion) -> Result<()> {
489+
if [
490+
region.guest_region.start,
491+
region.guest_region.end,
492+
region.host_region.start,
493+
region.host_region.end,
494+
]
495+
.iter()
496+
.any(|x| x % self.page_size != 0)
497+
{
498+
log_then_return!(
499+
"region is not page-aligned {:x}, {region:?}",
500+
self.page_size
501+
);
502+
}
503+
504+
let slot = self.mem_regions.len();
505+
let kvm_region = mem_region_to_kvm_region(region, slot);
506+
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
507+
self.mem_regions.push(region.to_owned());
508+
Ok(())
499509
}
500510

501511
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
502512
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
503-
if n > 0 {
504-
log_then_return!(
505-
"Mapping host memory into the guest not yet supported on this platform"
506-
);
513+
let n_keep = self.mem_regions.len() - n as usize;
514+
for (k, region) in self.mem_regions.split_off(n_keep).iter().enumerate() {
515+
let mut kvm_region = mem_region_to_kvm_region(region, n_keep + k);
516+
// Setting memory_size to 0 unmaps the slot's region
517+
// From https://docs.kernel.org/virt/kvm/api.html
518+
// > Deleting a slot is done by passing zero for memory_size.
519+
kvm_region.memory_size = 0;
520+
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
507521
}
508522
Ok(())
509523
}
@@ -938,3 +952,21 @@ impl Drop for KVMDriver {
938952
self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
939953
}
940954
}
955+
956+
fn mem_region_to_kvm_region(region: &MemoryRegion, slot: usize) -> kvm_userspace_memory_region {
957+
let perm_flags =
958+
MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
959+
960+
let perm_flags = perm_flags.intersection(region.flags);
961+
962+
kvm_userspace_memory_region {
963+
slot: slot as u32,
964+
guest_phys_addr: region.guest_region.start as u64,
965+
memory_size: (region.guest_region.end - region.guest_region.start) as u64,
966+
userspace_addr: region.host_region.start as u64,
967+
flags: match perm_flags {
968+
MemoryRegionFlags::READ => KVM_MEM_READONLY,
969+
_ => 0, // normal, RWX
970+
},
971+
}
972+
}

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ mod tests {
402402
use hyperlight_testing::simple_guest_as_string;
403403

404404
use crate::func::call_ctx::MultiUseGuestCallContext;
405+
#[cfg(target_os = "linux")]
406+
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionType};
405407
use crate::sandbox::{Callable, SandboxConfiguration};
406408
use crate::sandbox_state::sandbox::{DevolvableSandbox, EvolvableSandbox};
407409
use crate::sandbox_state::transition::{MultiUseContextCallback, Noop};
@@ -693,4 +695,60 @@ mod tests {
693695
handle.join().unwrap();
694696
}
695697
}
698+
699+
#[cfg(target_os = "linux")]
700+
#[test]
701+
fn test_mmap() {
702+
let mut sbox = UninitializedSandbox::new(
703+
GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
704+
None,
705+
)
706+
.unwrap()
707+
.evolve(Noop::default())
708+
.unwrap();
709+
710+
let expected = b"hello world";
711+
let data = page_aligned_buffer(expected);
712+
let guest_base = 0x1_0000_0000; // Arbitrary guest base address
713+
714+
unsafe {
715+
sbox.map_region(&region_for_buffer(&data, guest_base))
716+
.unwrap();
717+
}
718+
719+
let data: Vec<u8> = sbox
720+
.call_guest_function_by_name(
721+
"ReadMappedBuffer",
722+
(guest_base as u64, expected.len() as u64),
723+
)
724+
.unwrap();
725+
726+
assert_eq!(data, expected);
727+
}
728+
729+
#[cfg(target_os = "linux")]
730+
fn page_aligned_buffer(src: &[u8]) -> Vec<u8> {
731+
let page_size = page_size::get();
732+
let len = src.len().div_ceil(page_size) * page_size;
733+
734+
let layout = std::alloc::Layout::from_size_align(len, page_size).unwrap();
735+
let ptr = unsafe { std::alloc::alloc(layout) };
736+
737+
let mut vec = unsafe { std::vec::Vec::from_raw_parts(ptr, len, len) };
738+
vec[0..src.len()].copy_from_slice(src);
739+
740+
vec
741+
}
742+
743+
#[cfg(target_os = "linux")]
744+
fn region_for_buffer(buff: &[u8], guest_base: usize) -> MemoryRegion {
745+
let ptr = buff.as_ptr() as usize;
746+
let len = buff.len();
747+
MemoryRegion {
748+
host_region: ptr..(ptr + len),
749+
guest_region: guest_base..(guest_base + len),
750+
flags: MemoryRegionFlags::READ,
751+
region_type: MemoryRegionType::Heap,
752+
}
753+
}
696754
}

src/tests/rust_guests/simpleguest/src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,29 @@ fn read_from_user_memory(function_call: &FunctionCall) -> Result<Vec<u8>> {
775775
}
776776
}
777777

778+
fn read_mapped_buffer(function_call: &FunctionCall) -> Result<Vec<u8>> {
779+
if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = (
780+
function_call.parameters.clone().unwrap()[0].clone(),
781+
function_call.parameters.clone().unwrap()[1].clone(),
782+
) {
783+
let base = base as usize as *const u8;
784+
let len = len as usize;
785+
786+
unsafe {
787+
hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096)
788+
};
789+
790+
let data = unsafe { core::slice::from_raw_parts(base, len) };
791+
792+
Ok(get_flatbuffer_result(data))
793+
} else {
794+
Err(HyperlightGuestError::new(
795+
ErrorCode::GuestFunctionParameterTypeMismatch,
796+
"Invalid parameters passed to read_mapped_buffer".to_string(),
797+
))
798+
}
799+
}
800+
778801
#[no_mangle]
779802
pub extern "C" fn hyperlight_main() {
780803
let read_from_user_memory_def = GuestFunctionDefinition::new(
@@ -786,6 +809,15 @@ pub extern "C" fn hyperlight_main() {
786809

787810
register_function(read_from_user_memory_def);
788811

812+
let read_mapped_buffer_def = GuestFunctionDefinition::new(
813+
"ReadMappedBuffer".to_string(),
814+
Vec::from(&[ParameterType::ULong, ParameterType::ULong]),
815+
ReturnType::VecBytes,
816+
read_mapped_buffer as usize,
817+
);
818+
819+
register_function(read_mapped_buffer_def);
820+
789821
let set_static_def = GuestFunctionDefinition::new(
790822
"SetStatic".to_string(),
791823
Vec::new(),

0 commit comments

Comments
 (0)