Skip to content

Commit f83ea1c

Browse files
committed
Eagerly copy page tables to the snapshot region
This is necessary for now on amd64, where processor page table walks of the guest page tables are treated as writes w.r.t. to the second-level page tables, meaning that no guest page table can ever be in the (mapped readonly at stage 2) snapshot region. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent c33136e commit f83ea1c

10 files changed

Lines changed: 111 additions & 211 deletions

File tree

src/hyperlight_guest_bin/src/paging.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,19 @@ use hyperlight_guest::prim_alloc::alloc_phys_pages;
2929

3030
#[derive(Copy, Clone)]
3131
struct GuestMappingOperations {
32-
snapshot_pt_base_gpa: u64,
33-
snapshot_pt_base_gva: u64,
3432
scratch_base_gpa: u64,
3533
scratch_base_gva: u64,
3634
}
3735
impl GuestMappingOperations {
3836
fn new() -> Self {
3937
Self {
40-
snapshot_pt_base_gpa: unsafe {
41-
hyperlight_guest::layout::snapshot_pt_gpa_base_gva().read_volatile()
42-
},
43-
snapshot_pt_base_gva: hyperlight_common::layout::SNAPSHOT_PT_GVA_MIN as u64,
4438
scratch_base_gpa: hyperlight_guest::layout::scratch_base_gpa(),
4539
scratch_base_gva: hyperlight_guest::layout::scratch_base_gva(),
4640
}
4741
}
4842
fn try_phys_to_virt(&self, addr: u64) -> Option<*mut u8> {
4943
if addr >= self.scratch_base_gpa {
5044
Some((self.scratch_base_gva + (addr - self.scratch_base_gpa)) as *mut u8)
51-
} else if addr >= self.snapshot_pt_base_gpa {
52-
Some((self.snapshot_pt_base_gva + (addr - self.snapshot_pt_base_gpa)) as *mut u8)
5345
} else {
5446
None
5547
}
@@ -94,6 +86,11 @@ impl vmem::TableReadOps for GuestMappingOperations {
9486
}
9587

9688
impl vmem::TableOps for GuestMappingOperations {
89+
// Currently, we don't actually move tables anywhere on amd64
90+
// because of issues with guest PTs in IPAs that are mapped
91+
// readonly in Stage 2 translation. However, this code all works
92+
// and will re-enabled as soon as there is improved
93+
// architecture/hypervisor support.
9794
type TableMovability = vmem::MayMoveTable;
9895
unsafe fn alloc_table(&self) -> u64 {
9996
let page_addr = unsafe { alloc_phys_pages(1) };
@@ -104,26 +101,11 @@ impl vmem::TableOps for GuestMappingOperations {
104101
page_addr
105102
}
106103
unsafe fn write_entry(&self, addr: u64, entry: u64) -> Option<u64> {
107-
let mut addr = addr;
108-
let mut ret = None;
109-
if addr >= self.snapshot_pt_base_gpa && addr < self.scratch_base_gpa {
110-
// This needs to be CoW'd over to the scratch region
111-
unsafe {
112-
let new_table = alloc_phys_pages(1);
113-
core::ptr::copy(
114-
self.phys_to_virt(addr & !0xfff),
115-
self.phys_to_virt(new_table),
116-
vmem::PAGE_TABLE_SIZE,
117-
);
118-
addr = new_table | (addr & 0xfff);
119-
ret = Some(new_table);
120-
}
121-
}
122104
let addr = self.phys_to_virt(addr);
123105
unsafe {
124106
asm!("mov qword ptr [{}], {}", in(reg) addr, in(reg) entry);
125107
}
126-
ret
108+
None
127109
}
128110
unsafe fn update_root(&self, new_root: u64) {
129111
unsafe {

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,13 @@ impl SandboxSize {
6262
Self::Medium => {
6363
let mut cfg = SandboxConfiguration::default();
6464
cfg.set_heap_size(MEDIUM_HEAP_SIZE);
65+
cfg.set_scratch_size(0x50000);
6566
Some(cfg)
6667
}
6768
Self::Large => {
6869
let mut cfg = SandboxConfiguration::default();
6970
cfg.set_heap_size(LARGE_HEAP_SIZE);
71+
cfg.set_scratch_size(0x100000);
7072
Some(cfg)
7173
}
7274
}

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 27 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ impl HyperlightVm {
928928
/// - Special registers (restored from snapshot, with CR3 updated to new page table location)
929929
// TODO: check if other state needs to be reset
930930
pub(crate) fn reset_vcpu(
931-
&self,
931+
&mut self,
932932
cr3: u64,
933933
sregs: &CommonSpecialRegisters,
934934
) -> std::result::Result<(), RegisterError> {
@@ -1492,7 +1492,6 @@ mod tests {
14921492
use crate::mem::memory_region::{GuestMemoryRegion, MemoryRegionFlags};
14931493
use crate::mem::mgr::{GuestPageTableBuffer, SandboxMemoryManager};
14941494
use crate::mem::ptr::RawPtr;
1495-
use crate::mem::ptr_offset::Offset;
14961495
use crate::mem::shared_mem::ExclusiveSharedMemory;
14971496
use crate::sandbox::SandboxConfiguration;
14981497
use crate::sandbox::host_funcs::FunctionRegistry;
@@ -2041,26 +2040,24 @@ mod tests {
20412040
#[cfg(any(crashdump, gdb))]
20422041
let rt_cfg: SandboxRuntimeConfig = Default::default();
20432042

2044-
let mut layout =
2045-
SandboxMemoryLayout::new(config, code.len(), 4096, 4096, 0x3000, None).unwrap();
2043+
let mut layout = SandboxMemoryLayout::new(config, code.len(), 4096, None).unwrap();
20462044

2047-
let pt_base_gpa = SandboxMemoryLayout::BASE_ADDRESS + layout.get_pt_offset();
2048-
let pt_buf = GuestPageTableBuffer::new(pt_base_gpa);
2045+
let pt_base_gpa = layout.get_pt_base_gpa();
2046+
let pt_buf = GuestPageTableBuffer::new(pt_base_gpa as usize);
20492047

20502048
for rgn in layout
20512049
.get_memory_regions_::<GuestMemoryRegion>(())
20522050
.unwrap()
20532051
.iter()
20542052
{
20552053
let readable = rgn.flags.contains(MemoryRegionFlags::READ);
2056-
let writable = rgn.flags.contains(MemoryRegionFlags::WRITE)
2057-
|| rgn.flags.contains(MemoryRegionFlags::STACK_GUARD);
2054+
let writable = rgn.flags.contains(MemoryRegionFlags::WRITE);
20582055
let executable = rgn.flags.contains(MemoryRegionFlags::EXECUTE);
20592056
let mapping = Mapping {
20602057
phys_base: rgn.guest_region.start as u64,
20612058
virt_base: rgn.guest_region.start as u64,
20622059
len: rgn.guest_region.len() as u64,
2063-
kind: MappingKind::BasicMapping(BasicMapping {
2060+
kind: MappingKind::Basic(BasicMapping {
20642061
readable,
20652062
writable,
20662063
executable,
@@ -2069,22 +2066,6 @@ mod tests {
20692066
unsafe { vmem::map(&pt_buf, mapping) };
20702067
}
20712068

2072-
let mut pt_size_mapped = 0;
2073-
while pt_buf.size() > pt_size_mapped {
2074-
let mapping = Mapping {
2075-
phys_base: (pt_base_gpa + pt_size_mapped) as u64,
2076-
virt_base: (hyperlight_common::layout::SNAPSHOT_PT_GVA_MIN + pt_size_mapped) as u64,
2077-
len: (pt_buf.size() - pt_size_mapped) as u64,
2078-
kind: MappingKind::BasicMapping(BasicMapping {
2079-
readable: true,
2080-
writable: true,
2081-
executable: false,
2082-
}),
2083-
};
2084-
unsafe { vmem::map(&pt_buf, mapping) };
2085-
pt_size_mapped = pt_buf.size();
2086-
}
2087-
20882069
// Map the scratch region at the top of the address space
20892070
let scratch_size = config.get_scratch_size();
20902071
let scratch_gpa = hyperlight_common::layout::scratch_base_gpa(scratch_size);
@@ -2093,48 +2074,30 @@ mod tests {
20932074
phys_base: scratch_gpa,
20942075
virt_base: scratch_gva,
20952076
len: scratch_size as u64,
2096-
kind: MappingKind::BasicMapping(BasicMapping {
2077+
kind: MappingKind::Basic(BasicMapping {
20972078
readable: true,
20982079
writable: true,
20992080
executable: true, // Match regular codepath (map_specials)
21002081
}),
21012082
};
21022083
unsafe { vmem::map(&pt_buf, scratch_mapping) };
21032084

2104-
// Re-map page tables if they grew from scratch mapping
2105-
while pt_buf.size() > pt_size_mapped {
2106-
let mapping = Mapping {
2107-
phys_base: (pt_base_gpa + pt_size_mapped) as u64,
2108-
virt_base: (hyperlight_common::layout::SNAPSHOT_PT_GVA_MIN + pt_size_mapped) as u64,
2109-
len: (pt_buf.size() - pt_size_mapped) as u64,
2110-
kind: MappingKind::BasicMapping(BasicMapping {
2111-
readable: true,
2112-
writable: true,
2113-
executable: false,
2114-
}),
2115-
};
2116-
unsafe { vmem::map(&pt_buf, mapping) };
2117-
pt_size_mapped = pt_buf.size();
2118-
}
2119-
21202085
let pt_bytes = pt_buf.into_bytes();
2121-
layout.set_pt_size(pt_bytes.len());
2086+
layout.set_pt_size(pt_bytes.len()).unwrap();
21222087

21232088
let mem_size = layout.get_memory_size().unwrap();
21242089
let mut eshm = ExclusiveSharedMemory::new(mem_size).unwrap();
2125-
eshm.copy_from_slice(&pt_bytes, layout.get_pt_offset())
2126-
.unwrap();
2090+
let snapshot_pt_start = mem_size - layout.get_pt_size();
2091+
eshm.copy_from_slice(&pt_bytes, snapshot_pt_start).unwrap();
21272092
eshm.copy_from_slice(code, layout.get_guest_code_offset())
21282093
.unwrap();
21292094

2130-
let load_addr = RawPtr::from(layout.get_guest_code_address() as u64);
21312095
let scratch_mem = ExclusiveSharedMemory::new(config.get_scratch_size()).unwrap();
21322096
let mut mem_mgr = SandboxMemoryManager::new(
21332097
layout,
21342098
eshm,
21352099
scratch_mem,
2136-
load_addr,
2137-
Some(Offset::from(0u64)),
2100+
NextAction::Initialise(layout.get_guest_code_address() as u64),
21382101
);
21392102
mem_mgr.write_memory_layout().unwrap();
21402103

@@ -2197,7 +2160,7 @@ mod tests {
21972160
fn reset_vcpu_simple() {
21982161
// push rax; hlt - aligns stack to 16 bytes
21992162
const CODE: [u8; 2] = [0x50, 0xf4];
2200-
let hyperlight_vm = hyperlight_vm(&CODE);
2163+
let mut hyperlight_vm = hyperlight_vm(&CODE);
22012164
let available_hv = *get_available_hypervisor().as_ref().unwrap();
22022165

22032166
// Get the initial CR3 value before dirtying sregs
@@ -2360,7 +2323,7 @@ mod tests {
23602323
a.hlt().unwrap();
23612324
let code = a.assemble(0).unwrap();
23622325

2363-
let hyperlight_vm = hyperlight_vm(&code);
2326+
let mut hyperlight_vm = hyperlight_vm(&code);
23642327

23652328
// After run, check registers match expected dirty state
23662329
let regs = hyperlight_vm.vm.regs().unwrap();
@@ -2462,7 +2425,7 @@ mod tests {
24622425
a.hlt().unwrap();
24632426
let code = a.assemble(0).unwrap();
24642427

2465-
let hyperlight_vm = hyperlight_vm(&code);
2428+
let mut hyperlight_vm = hyperlight_vm(&code);
24662429

24672430
// After run, check FPU state matches expected dirty values
24682431
let fpu = hyperlight_vm.vm.fpu().unwrap();
@@ -2547,7 +2510,7 @@ mod tests {
25472510
a.hlt().unwrap();
25482511
let code = a.assemble(0).unwrap();
25492512

2550-
let hyperlight_vm = hyperlight_vm(&code);
2513+
let mut hyperlight_vm = hyperlight_vm(&code);
25512514

25522515
// Verify debug registers are dirty
25532516
let debug_regs = hyperlight_vm.vm.debug_regs().unwrap();
@@ -2593,7 +2556,7 @@ mod tests {
25932556
a.hlt().unwrap();
25942557
let code = a.assemble(0).unwrap();
25952558

2596-
let hyperlight_vm = hyperlight_vm(&code);
2559+
let mut hyperlight_vm = hyperlight_vm(&code);
25972560

25982561
// Get the initial CR3 value and expected defaults
25992562
let initial_cr3 = hyperlight_vm.vm.sregs().unwrap().cr3;
@@ -2653,8 +2616,11 @@ mod tests {
26532616

26542617
// Re-run from entrypoint (flag=1 means guest skips dirty phase, just does FXSAVE)
26552618
// Use stack_top - 8 to match initialise()'s behavior (simulates call pushing return addr)
2619+
let NextAction::Call(rip) = ctx.ctx.vm.entrypoint else {
2620+
panic!("entrypoint should be call");
2621+
};
26562622
let regs = CommonRegisters {
2657-
rip: ctx.ctx.vm.entrypoint.expect("entrypoint should be set"),
2623+
rip,
26582624
rsp: ctx.stack_top_gva() - 8,
26592625
rflags: 1 << 1,
26602626
..Default::default()
@@ -2736,7 +2702,7 @@ mod tests {
27362702
let mut fxsave = [0u8; 512];
27372703
self.ctx
27382704
.hshm
2739-
.shared_mem
2705+
.scratch_mem
27402706
.copy_to_slice(&mut fxsave, self.fxsave_offset)
27412707
.unwrap();
27422708
fxsave
@@ -2759,9 +2725,9 @@ mod tests {
27592725
// These are in the output_data region which starts at a known offset.
27602726
// We use a default SandboxConfiguration to get the same layout as create_test_vm_context.
27612727
let config: SandboxConfiguration = Default::default();
2762-
let layout = SandboxMemoryLayout::new(config, 512, 4096, 4096, 0x3000, None).unwrap();
2763-
let fxsave_offset = layout.get_output_data_offset();
2764-
let fxsave_gva = (SandboxMemoryLayout::BASE_ADDRESS + fxsave_offset) as u64;
2728+
let layout = SandboxMemoryLayout::new(config, 512, 4096, None).unwrap();
2729+
let fxsave_offset = layout.get_output_data_buffer_scratch_host_offset();
2730+
let fxsave_gva = layout.get_output_data_buffer_gva();
27652731
let flag_gva = fxsave_gva + 512;
27662732

27672733
let mut a = CodeAssembler::new(64).unwrap();
@@ -2812,6 +2778,9 @@ mod tests {
28122778
a.mov(rax, fxsave_gva).unwrap();
28132779
a.fxsave(ptr(rax)).unwrap();
28142780

2781+
// Return dispatch ptr
2782+
a.mov(rax, layout.get_guest_code_address() as u64).unwrap();
2783+
28152784
a.hlt().unwrap();
28162785

28172786
let code = a.assemble(0).unwrap();

0 commit comments

Comments
 (0)