1use flatbuffers::FlatBufferBuilder;
5use hyperlight_common::flatbuffer_wrappers::function_call::{
6 FunctionCall, validate_guest_function_call_buffer,
7};
8use hyperlight_common::flatbuffer_wrappers::function_types::FunctionCallResult;
9use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
10use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
11use hyperlight_common::vmem::{self, PAGE_TABLE_SIZE};
12#[cfg(crashdump)]
13use hyperlight_common::vmem::{BasicMapping, MappingKind};
14use tracing::{Span, instrument};
15
16use super::layout::SandboxMemoryLayout;
17use super::shared_mem::{
18 ExclusiveSharedMemory, GuestSharedMemory, HostSharedMemory, ReadonlySharedMemory, SharedMemory,
19};
20use crate::hypervisor::regs::CommonSpecialRegisters;
21use crate::mem::memory_region::MemoryRegion;
22#[cfg(crashdump)]
23use crate::mem::memory_region::{CrashDumpRegion, MemoryRegionFlags, MemoryRegionType};
24use crate::sandbox::snapshot::{NextAction, Snapshot};
25use crate::{Result, new_error};
26
27#[cfg(crashdump)]
28fn mapping_kind_to_flags(kind: &MappingKind) -> (MemoryRegionFlags, MemoryRegionType) {
29 match kind {
30 MappingKind::Basic(BasicMapping {
31 readable,
32 writable,
33 executable,
34 }) => {
35 let mut flags = MemoryRegionFlags::empty();
36 if *readable {
37 flags |= MemoryRegionFlags::READ;
38 }
39 if *writable {
40 flags |= MemoryRegionFlags::WRITE;
41 }
42 if *executable {
43 flags |= MemoryRegionFlags::EXECUTE;
44 }
45 (flags, MemoryRegionType::Snapshot)
46 }
47 MappingKind::Cow(cow) => {
48 let mut flags = MemoryRegionFlags::empty();
49 if cow.readable {
50 flags |= MemoryRegionFlags::READ;
51 }
52 if cow.executable {
53 flags |= MemoryRegionFlags::EXECUTE;
54 }
55 (flags, MemoryRegionType::Scratch)
56 }
57 MappingKind::Unmapped => (MemoryRegionFlags::empty(), MemoryRegionType::Snapshot),
58 }
59}
60
61#[cfg(crashdump)]
66fn try_coalesce_region(
67 regions: &mut [CrashDumpRegion],
68 virt_base: usize,
69 virt_end: usize,
70 host_base: usize,
71 flags: MemoryRegionFlags,
72) -> bool {
73 if let Some(last) = regions.last_mut()
74 && last.guest_region.end == virt_base
75 && last.host_region.end == host_base
76 && last.flags == flags
77 {
78 last.guest_region.end = virt_end;
79 last.host_region.end = host_base + (virt_end - virt_base);
80 return true;
81 }
82 false
83}
84
85mod unused_hack {
93 #[cfg(not(unshared_snapshot_mem))]
94 use crate::mem::shared_mem::ReadonlySharedMemory;
95 use crate::mem::shared_mem::SharedMemory;
96 pub trait SnapshotSharedMemoryT {
97 type T<S: SharedMemory>;
98 }
99 pub struct SnapshotSharedMemory_;
100 impl SnapshotSharedMemoryT for SnapshotSharedMemory_ {
101 #[cfg(not(unshared_snapshot_mem))]
102 type T<S: SharedMemory> = ReadonlySharedMemory;
103 #[cfg(unshared_snapshot_mem)]
104 type T<S: SharedMemory> = S;
105 }
106 pub type SnapshotSharedMemory<S> = <SnapshotSharedMemory_ as SnapshotSharedMemoryT>::T<S>;
107}
108impl ReadonlySharedMemory {
109 pub(crate) fn to_mgr_snapshot_mem(
110 &self,
111 ) -> Result<SnapshotSharedMemory<ExclusiveSharedMemory>> {
112 #[cfg(not(unshared_snapshot_mem))]
113 let ret = self.clone();
114 #[cfg(unshared_snapshot_mem)]
115 let ret = self.copy_to_writable()?;
116 Ok(ret)
117 }
118}
119pub(crate) use unused_hack::SnapshotSharedMemory;
120pub(crate) struct SandboxMemoryManager<S: SharedMemory> {
123 pub(crate) shared_mem: SnapshotSharedMemory<S>,
125 pub(crate) scratch_mem: S,
127 pub(crate) layout: SandboxMemoryLayout,
129 pub(crate) next_action: NextAction,
132 pub(crate) original_entrypoint: u64,
136 pub(crate) abort_buffer: Vec<u8>,
138 pub(crate) snapshot_count: u64,
144}
145
146pub(crate) struct GuestPageTableBuffer {
150 buffer: std::cell::RefCell<Vec<u8>>,
151 phys_base: usize,
152 root: std::cell::Cell<u64>,
157}
158
159impl vmem::TableReadOps for GuestPageTableBuffer {
160 type TableAddr = u64;
161
162 fn entry_addr(addr: u64, offset: u64) -> u64 {
163 addr + offset
164 }
165
166 unsafe fn read_entry(&self, addr: u64) -> vmem::PageTableEntry {
167 let buffer = self.buffer.borrow();
168 let byte_offset = addr as usize - self.phys_base;
169 let pte_size = core::mem::size_of::<vmem::PageTableEntry>();
170 let Some(bytes) = buffer.get(byte_offset..byte_offset + pte_size) else {
171 return 0;
172 };
173 let mut buf = [0u8; 8];
174 buf[..pte_size].copy_from_slice(bytes);
175 vmem::PageTableEntry::from_le_bytes(buf[..pte_size].try_into().unwrap_or_default())
176 }
177
178 fn to_phys(addr: u64) -> vmem::PhysAddr {
179 addr as vmem::PhysAddr
180 }
181
182 fn from_phys(addr: vmem::PhysAddr) -> u64 {
183 #[allow(clippy::unnecessary_cast)]
184 {
185 addr as u64
186 }
187 }
188
189 fn root_table(&self) -> u64 {
190 self.root.get()
191 }
192}
193
194impl vmem::TableOps for GuestPageTableBuffer {
195 type TableMovability = vmem::MayNotMoveTable;
196
197 unsafe fn alloc_table(&self) -> u64 {
198 let mut b = self.buffer.borrow_mut();
199 let offset = b.len();
200 b.resize(offset + PAGE_TABLE_SIZE, 0);
201 (self.phys_base + offset) as u64
202 }
203
204 unsafe fn write_entry(&self, addr: u64, entry: vmem::PageTableEntry) -> Option<vmem::Void> {
205 let mut b = self.buffer.borrow_mut();
206 let byte_offset = addr as usize - self.phys_base;
207 let pte_size = core::mem::size_of::<vmem::PageTableEntry>();
208 if let Some(slice) = b.get_mut(byte_offset..byte_offset + pte_size) {
209 slice.copy_from_slice(&entry.to_le_bytes()[..pte_size]);
210 }
211 None
212 }
213
214 unsafe fn update_root(&self, impossible: vmem::Void) {
215 match impossible {}
216 }
217}
218
219impl core::convert::AsRef<GuestPageTableBuffer> for GuestPageTableBuffer {
220 fn as_ref(&self) -> &Self {
221 self
222 }
223}
224
225impl GuestPageTableBuffer {
226 pub(crate) fn new(phys_base: usize) -> Self {
230 GuestPageTableBuffer {
231 buffer: std::cell::RefCell::new(vec![0u8; PAGE_TABLE_SIZE]),
232 phys_base,
233 root: std::cell::Cell::new(phys_base as u64),
234 }
235 }
236
237 pub(crate) fn set_root(&self, addr: u64) {
240 self.root.set(addr);
241 }
242
243 pub(crate) fn initial_root(&self) -> u64 {
245 self.phys_base as u64
246 }
247
248 #[cfg(test)]
249 #[allow(dead_code)]
250 pub(crate) fn size(&self) -> usize {
251 self.buffer.borrow().len()
252 }
253
254 pub(crate) fn into_bytes(self) -> Box<[u8]> {
255 self.buffer.into_inner().into_boxed_slice()
256 }
257}
258
259impl<S> SandboxMemoryManager<S>
260where
261 S: SharedMemory,
262{
263 #[instrument(skip_all, parent = Span::current(), level= "Trace")]
265 pub(crate) fn new(
266 layout: SandboxMemoryLayout,
267 shared_mem: SnapshotSharedMemory<S>,
268 scratch_mem: S,
269 next_action: NextAction,
270 ) -> Self {
271 Self {
272 layout,
273 shared_mem,
274 scratch_mem,
275 next_action,
276 original_entrypoint: 0,
277 abort_buffer: Vec::new(),
278 snapshot_count: 0,
279 }
280 }
281
282 pub(crate) fn get_abort_buffer_mut(&mut self) -> &mut Vec<u8> {
284 &mut self.abort_buffer
285 }
286
287 #[allow(clippy::too_many_arguments)]
289 pub(crate) fn snapshot(
290 &mut self,
291 mapped_regions: Vec<MemoryRegion>,
292 root_pt_gpas: &[u64],
293 rsp_gva: u64,
294 sregs: CommonSpecialRegisters,
295 #[cfg(target_arch = "x86_64")] msrs: Vec<crate::hypervisor::regs::MsrEntry>,
296 next_action: NextAction,
297 host_functions: HostFunctionDetails,
298 ) -> Result<Snapshot> {
299 self.snapshot_count += 1;
300 Snapshot::new(
301 &mut self.shared_mem,
302 &mut self.scratch_mem,
303 self.layout,
304 crate::mem::exe::LoadInfo::dummy(),
305 mapped_regions,
306 root_pt_gpas,
307 rsp_gva,
308 sregs,
309 #[cfg(target_arch = "x86_64")]
310 msrs,
311 next_action,
312 self.original_entrypoint,
313 self.snapshot_count,
314 host_functions,
315 )
316 }
317}
318
319impl SandboxMemoryManager<ExclusiveSharedMemory> {
320 pub(crate) fn from_snapshot(s: &Snapshot) -> Result<Self> {
321 let layout = *s.layout();
322 let shared_mem = s.memory().to_mgr_snapshot_mem()?;
323 let scratch_mem = ExclusiveSharedMemory::new(s.layout().get_scratch_size())?;
324 let next_action = s.next_action();
325 let mut mgr = Self::new(layout, shared_mem, scratch_mem, next_action);
326 mgr.original_entrypoint = s.original_entrypoint();
327 mgr.snapshot_count = s.snapshot_generation();
332 Ok(mgr)
333 }
334
335 pub fn build(
346 self,
347 ) -> Result<(
348 SandboxMemoryManager<HostSharedMemory>,
349 SandboxMemoryManager<GuestSharedMemory>,
350 )> {
351 let (hshm, gshm) = self.shared_mem.build();
352 let (hscratch, gscratch) = self.scratch_mem.build();
353 let mut host_mgr = SandboxMemoryManager {
354 shared_mem: hshm,
355 scratch_mem: hscratch,
356 layout: self.layout,
357 next_action: self.next_action,
358 original_entrypoint: self.original_entrypoint,
359 abort_buffer: self.abort_buffer,
360 snapshot_count: self.snapshot_count,
361 };
362 let guest_mgr = SandboxMemoryManager {
363 shared_mem: gshm,
364 scratch_mem: gscratch,
365 layout: self.layout,
366 next_action: self.next_action,
367 original_entrypoint: self.original_entrypoint,
368 abort_buffer: Vec::new(), snapshot_count: self.snapshot_count,
370 };
371 host_mgr.update_scratch_bookkeeping()?;
372 Ok((host_mgr, guest_mgr))
373 }
374}
375
376impl SandboxMemoryManager<HostSharedMemory> {
377 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
379 pub(crate) fn get_host_function_call(&mut self) -> Result<FunctionCall> {
380 self.scratch_mem
381 .try_pop_buffer_into::<FunctionCall>(
382 self.layout.get_output_data_buffer_scratch_host_offset(),
383 self.layout.output_data_size(),
384 )
385 .map_err(From::from)
386 }
387
388 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
390 pub(crate) fn write_response_from_host_function_call(
391 &mut self,
392 res: &FunctionCallResult,
393 ) -> Result<()> {
394 let mut builder = FlatBufferBuilder::new();
395 let data = res.encode(&mut builder);
396
397 self.scratch_mem
398 .push_buffer(
399 self.layout.get_input_data_buffer_scratch_host_offset(),
400 self.layout.input_data_size(),
401 data,
402 )
403 .map_err(From::from)
404 }
405
406 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
408 pub(crate) fn write_guest_function_call(&mut self, buffer: &[u8]) -> Result<()> {
409 validate_guest_function_call_buffer(buffer).map_err(|e| {
410 new_error!(
411 "Guest function call buffer validation failed: {}",
412 e.to_string()
413 )
414 })?;
415
416 self.scratch_mem.push_buffer(
417 self.layout.get_input_data_buffer_scratch_host_offset(),
418 self.layout.input_data_size(),
419 buffer,
420 )?;
421 Ok(())
422 }
423
424 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
427 pub(crate) fn get_guest_function_call_result(&mut self) -> Result<FunctionCallResult> {
428 self.scratch_mem
429 .try_pop_buffer_into::<FunctionCallResult>(
430 self.layout.get_output_data_buffer_scratch_host_offset(),
431 self.layout.output_data_size(),
432 )
433 .map_err(From::from)
434 }
435
436 #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
438 pub(crate) fn read_guest_log_data(&mut self) -> Result<GuestLogData> {
439 self.scratch_mem
440 .try_pop_buffer_into::<GuestLogData>(
441 self.layout.get_output_data_buffer_scratch_host_offset(),
442 self.layout.output_data_size(),
443 )
444 .map_err(From::from)
445 }
446
447 pub(crate) fn clear_io_buffers(&mut self) {
448 loop {
450 let Ok(_) = self.scratch_mem.try_pop_buffer_into::<Vec<u8>>(
451 self.layout.get_output_data_buffer_scratch_host_offset(),
452 self.layout.output_data_size(),
453 ) else {
454 break;
455 };
456 }
457 loop {
459 let Ok(_) = self.scratch_mem.try_pop_buffer_into::<Vec<u8>>(
460 self.layout.get_input_data_buffer_scratch_host_offset(),
461 self.layout.input_data_size(),
462 ) else {
463 break;
464 };
465 }
466 }
467
468 pub(crate) fn restore_snapshot(
470 &mut self,
471 snapshot: &Snapshot,
472 ) -> Result<(
473 Option<SnapshotSharedMemory<GuestSharedMemory>>,
474 Option<GuestSharedMemory>,
475 )> {
476 let gsnapshot = if *snapshot.memory() == self.shared_mem {
477 None
485 } else {
486 let new_snapshot_mem = snapshot.memory().to_mgr_snapshot_mem()?;
487 let (hsnapshot, gsnapshot) = new_snapshot_mem.build();
488 self.shared_mem = hsnapshot;
489 Some(gsnapshot)
490 };
491 let new_scratch_size = snapshot.layout().get_scratch_size();
492 let gscratch = if new_scratch_size == self.scratch_mem.mem_size() {
493 self.scratch_mem.zero_or_replace()?
496 } else {
497 let new_scratch_mem = ExclusiveSharedMemory::new(new_scratch_size)?;
498 let (hscratch, gscratch) = new_scratch_mem.build();
499 self.scratch_mem = hscratch;
505 Some(gscratch)
506 };
507 self.layout = *snapshot.layout();
508 self.snapshot_count = snapshot.snapshot_generation();
513 self.original_entrypoint = snapshot.original_entrypoint();
516
517 self.update_scratch_bookkeeping()?;
518 Ok((gsnapshot, gscratch))
519 }
520
521 #[inline]
522 fn update_scratch_bookkeeping_item(&mut self, offset: u64, value: u64) -> Result<()> {
523 let scratch_size = self.scratch_mem.mem_size();
524 let base_offset = scratch_size - offset as usize;
525 self.scratch_mem
526 .write::<u64>(base_offset, value)
527 .map_err(From::from)
528 }
529
530 pub(crate) fn request_libc_rng_reseed(&mut self, seed: u32) -> Result<()> {
531 self.update_scratch_bookkeeping_item(
534 hyperlight_common::layout::SCRATCH_TOP_LIBC_RNG_SEED_OFFSET,
535 (1_u64 << 32) | u64::from(seed),
536 )
537 }
538
539 fn update_scratch_bookkeeping(&mut self) -> Result<()> {
540 use hyperlight_common::layout::*;
541 let scratch_size = self.scratch_mem.mem_size();
542 self.update_scratch_bookkeeping_item(SCRATCH_TOP_SIZE_OFFSET, scratch_size as u64)?;
543 self.update_scratch_bookkeeping_item(
544 SCRATCH_TOP_ALLOCATOR_OFFSET,
545 self.layout.get_first_free_scratch_gpa(),
546 )?;
547 self.update_scratch_bookkeeping_item(
556 SCRATCH_TOP_SNAPSHOT_PT_GPA_BASE_OFFSET,
557 self.layout.get_pt_base_gpa(),
558 )?;
559 self.update_scratch_bookkeeping_item(
560 SCRATCH_TOP_SNAPSHOT_GENERATION_OFFSET,
561 self.snapshot_count,
562 )?;
563
564 self.scratch_mem.write::<u64>(
567 self.layout.get_input_data_buffer_scratch_host_offset(),
568 SandboxMemoryLayout::STACK_POINTER_SIZE_BYTES,
569 )?;
570 self.scratch_mem.write::<u64>(
571 self.layout.get_output_data_buffer_scratch_host_offset(),
572 SandboxMemoryLayout::STACK_POINTER_SIZE_BYTES,
573 )?;
574
575 let snapshot_pt_end = self.shared_mem.mem_size();
582 let snapshot_pt_size = self.layout.get_pt_size();
583 let snapshot_pt_start =
584 snapshot_pt_end - snapshot_pt_size.next_multiple_of(page_size::get());
585 self.scratch_mem.with_exclusivity(|scratch| {
586 #[cfg(not(unshared_snapshot_mem))]
587 let bytes = &self.shared_mem.as_slice()[snapshot_pt_start..snapshot_pt_end];
588 #[cfg(unshared_snapshot_mem)]
589 let bytes = {
590 let mut bytes = vec![0u8; snapshot_pt_size];
591 self.shared_mem
592 .copy_to_slice(&mut bytes, snapshot_pt_start)?;
593 bytes
594 };
595 #[allow(clippy::needless_borrow)]
596 scratch.copy_from_slice(&bytes, self.layout.get_pt_base_scratch_offset())
597 })??;
598
599 Ok(())
600 }
601
602 #[cfg(crashdump)]
607 pub(crate) fn get_guest_memory_regions(
608 &mut self,
609 root_pt: u64,
610 mmap_regions: &[MemoryRegion],
611 ) -> Result<Vec<CrashDumpRegion>> {
612 use crate::sandbox::snapshot::SharedMemoryPageTableBuffer;
613
614 let len = hyperlight_common::layout::SCRATCH_TOP_GVA;
615
616 let regions = self.shared_mem.with_contents(|snapshot| {
617 self.scratch_mem.with_contents(|scratch| {
618 let pt_buf =
619 SharedMemoryPageTableBuffer::new(snapshot, scratch, self.layout, root_pt);
620
621 let mappings: Vec<_> =
622 unsafe { hyperlight_common::vmem::virt_to_phys(&pt_buf, 0, len as u64) }
623 .collect();
624
625 if mappings.is_empty() {
626 return Err(new_error!("No page table mappings found (len {len})",));
627 }
628
629 let mut regions: Vec<CrashDumpRegion> = Vec::new();
630 for mapping in &mappings {
631 let virt_base = mapping.virt_base as usize;
632 let virt_end = (mapping.virt_base + mapping.len) as usize;
633
634 if let Some(resolved) = self.layout.resolve_gpa(mapping.phys_base, mmap_regions)
635 {
636 let (flags, region_type) = mapping_kind_to_flags(&mapping.kind);
637 let resolved = resolved.with_memories(snapshot, scratch);
638 let contents = resolved.as_ref();
639 let host_base = contents.as_ptr() as usize;
640 let host_len = (mapping.len as usize).min(contents.len());
641
642 if try_coalesce_region(&mut regions, virt_base, virt_end, host_base, flags)
643 {
644 continue;
645 }
646
647 regions.push(CrashDumpRegion {
648 guest_region: virt_base..virt_end,
649 host_region: host_base..host_base + host_len,
650 flags,
651 region_type,
652 });
653 }
654 }
655
656 Ok(regions)
657 })
658 })???;
659
660 Ok(regions)
661 }
662
663 #[cfg(feature = "trace_guest")]
676 pub(crate) fn read_guest_memory_by_gva(
677 &mut self,
678 gva: u64,
679 len: usize,
680 root_pt: u64,
681 ) -> Result<Vec<u8>> {
682 use hyperlight_common::vmem::PAGE_SIZE;
683
684 use crate::sandbox::snapshot::{SharedMemoryPageTableBuffer, access_gpa};
685
686 self.shared_mem.with_contents(|snap| {
687 self.scratch_mem.with_contents(|scratch| {
688 let pt_buf = SharedMemoryPageTableBuffer::new(snap, scratch, self.layout, root_pt);
689
690 let mappings: Vec<_> = unsafe {
692 hyperlight_common::vmem::virt_to_phys(&pt_buf, gva, len as u64)
693 }
694 .collect();
695
696 if mappings.is_empty() {
697 return Err(new_error!(
698 "No page table mappings found for GVA {:#x} (len {})",
699 gva,
700 len,
701 ));
702 }
703
704 let mut result = Vec::with_capacity(len);
706 let mut current_gva = gva;
707
708 for mapping in &mappings {
709 if mapping.virt_base > current_gva {
712 return Err(new_error!(
713 "Page table walker returned mapping with virt_base {:#x} > current read position {:#x}",
714 mapping.virt_base,
715 current_gva,
716 ));
717 }
718
719 let page_offset = (current_gva - mapping.virt_base) as usize;
721
722 let bytes_remaining = len - result.len();
723 let available_in_page = PAGE_SIZE - page_offset;
724 let bytes_to_copy = bytes_remaining.min(available_in_page);
725
726 let gpa = mapping.phys_base + page_offset as u64;
728 let (mem, offset) = access_gpa(snap, scratch, self.layout, gpa)
729 .ok_or_else(|| {
730 new_error!(
731 "Failed to resolve GPA {:#x} to host memory (GVA {:#x})",
732 gpa,
733 gva
734 )
735 })?;
736
737 let slice = mem
738 .get(offset..offset + bytes_to_copy)
739 .ok_or_else(|| {
740 new_error!(
741 "GPA {:#x} resolved to out-of-bounds host offset {} (need {} bytes)",
742 gpa,
743 offset,
744 bytes_to_copy
745 )
746 })?;
747
748 result.extend_from_slice(slice);
749 current_gva += bytes_to_copy as u64;
750 }
751
752 if result.len() != len {
753 tracing::error!(
754 "Page table walker returned mappings that don't cover the full requested length: got {}, expected {}",
755 result.len(),
756 len,
757 );
758 return Err(new_error!(
759 "Could not read full GVA range: got {} of {} bytes {:?}",
760 result.len(),
761 len,
762 mappings
763 ));
764 }
765
766 Ok(result)
767 })
768 })??
769 }
770}
771
772#[cfg(test)]
773#[cfg(target_arch = "x86_64")]
774mod tests {
775 use hyperlight_testing::sandbox_sizes::{LARGE_HEAP_SIZE, MEDIUM_HEAP_SIZE, SMALL_HEAP_SIZE};
776 use hyperlight_testing::simple_guest_as_pathbuf;
777
778 use crate::GuestBinary;
779 use crate::sandbox::SandboxConfiguration;
780 use crate::sandbox::snapshot::Snapshot;
781
782 fn verify_page_tables(name: &str, config: SandboxConfiguration) {
785 let path = simple_guest_as_pathbuf();
786 let snapshot = Snapshot::from_env(GuestBinary::FilePath(path), config)
787 .unwrap_or_else(|e| panic!("{}: failed to create snapshot: {}", name, e));
788
789 assert!(
791 unsafe { hyperlight_common::vmem::virt_to_phys(&snapshot, 0, 1) }
792 .next()
793 .is_none(),
794 "{}: NULL page (0x0) should NOT be mapped",
795 name
796 );
797 }
798
799 #[test]
800 fn test_page_tables_for_various_configurations() {
801 let test_cases: [(&str, SandboxConfiguration); 4] = [
802 ("default", { SandboxConfiguration::default() }),
803 ("small (8MB heap)", {
804 let mut cfg = SandboxConfiguration::default();
805 cfg.set_heap_size(SMALL_HEAP_SIZE);
806 cfg
807 }),
808 ("medium (64MB heap)", {
809 let mut cfg = SandboxConfiguration::default();
810 cfg.set_heap_size(MEDIUM_HEAP_SIZE);
811 cfg
812 }),
813 ("large (256MB heap)", {
814 let mut cfg = SandboxConfiguration::default();
815 cfg.set_heap_size(LARGE_HEAP_SIZE);
816 cfg.set_scratch_size(0x100000);
817 cfg
818 }),
819 ];
820
821 for (name, config) in test_cases {
822 verify_page_tables(name, config);
823 }
824 }
825}