@@ -559,3 +559,202 @@ impl SandboxMemoryManager<HostSharedMemory> {
559559 }
560560 }
561561}
562+
563+ #[ cfg( test) ]
564+ #[ cfg( feature = "init-paging" ) ]
565+ mod tests {
566+ use hyperlight_testing:: sandbox_sizes:: { LARGE_HEAP_SIZE , MEDIUM_HEAP_SIZE , SMALL_HEAP_SIZE } ;
567+ use hyperlight_testing:: simple_guest_as_string;
568+
569+ use super :: * ;
570+ use crate :: GuestBinary ;
571+ use crate :: mem:: shared_mem:: GuestSharedMemory ;
572+ use crate :: sandbox:: SandboxConfiguration ;
573+ use crate :: sandbox:: uninitialized:: UninitializedSandbox ;
574+
575+ /// Helper to create a sandbox with page tables set up and return the manager
576+ fn create_sandbox_with_page_tables (
577+ config : Option < SandboxConfiguration > ,
578+ ) -> Result < SandboxMemoryManager < GuestSharedMemory > > {
579+ let path = simple_guest_as_string ( ) . expect ( "failed to get simple guest path" ) ;
580+ let sandbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , config)
581+ . expect ( "failed to create sandbox" ) ;
582+
583+ let mem_size = sandbox. mgr . layout . get_memory_size ( ) ?;
584+
585+ // Build the shared memory to get GuestSharedMemory
586+ let ( _host_mem, guest_mem) = sandbox. mgr . shared_mem . build ( ) ;
587+ let mut mgr = SandboxMemoryManager {
588+ shared_mem : guest_mem,
589+ layout : sandbox. mgr . layout ,
590+ load_addr : sandbox. mgr . load_addr ,
591+ entrypoint_offset : sandbox. mgr . entrypoint_offset ,
592+ mapped_rgns : sandbox. mgr . mapped_rgns ,
593+ stack_cookie : sandbox. mgr . stack_cookie ,
594+ abort_buffer : sandbox. mgr . abort_buffer ,
595+ } ;
596+
597+ // Get regions and set up page tables
598+ let mut regions = mgr. layout . get_memory_regions ( & mgr. shared_mem ) ?;
599+ let mem_size_u64 = u64:: try_from ( mem_size) ?;
600+ // set_up_shared_memory builds the page tables in shared memory
601+ mgr. set_up_shared_memory ( mem_size_u64, & mut regions) ?;
602+
603+ Ok ( mgr)
604+ }
605+
606+ /// Verify a range of pages all have the same expected flags
607+ fn verify_page_range (
608+ excl_mem : & mut ExclusiveSharedMemory ,
609+ start_addr : usize ,
610+ end_addr : usize ,
611+ expected_flags : u64 ,
612+ region_name : & str ,
613+ ) -> Result < ( ) > {
614+ let mut addr = start_addr;
615+
616+ while addr < end_addr {
617+ let p = addr >> 21 ;
618+ let i = ( addr >> 12 ) & 0x1ff ;
619+ let pte_idx = p * 512 + i;
620+ let offset = SandboxMemoryLayout :: PT_OFFSET + ( pte_idx * 8 ) ;
621+
622+ let pte_val = excl_mem. read_u64 ( offset) ?;
623+ let expected_pte = ( addr as u64 ) | expected_flags;
624+
625+ if pte_val != expected_pte {
626+ return Err ( new_error ! (
627+ "{} region: addr 0x{:x}: expected PTE 0x{:x}, got 0x{:x}" ,
628+ region_name,
629+ addr,
630+ expected_pte,
631+ pte_val
632+ ) ) ;
633+ }
634+
635+ addr += 0x1000 ;
636+ }
637+
638+ Ok ( ( ) )
639+ }
640+
641+ /// Get expected flags for a memory region type
642+ fn get_expected_flags ( region : & MemoryRegion ) -> u64 {
643+ match region. region_type {
644+ MemoryRegionType :: Code => PAGE_PRESENT | PAGE_RW | PAGE_USER ,
645+ MemoryRegionType :: Stack => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX ,
646+ #[ cfg( feature = "executable_heap" ) ]
647+ MemoryRegionType :: Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER ,
648+ #[ cfg( not( feature = "executable_heap" ) ) ]
649+ MemoryRegionType :: Heap => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX ,
650+ MemoryRegionType :: GuardPage => PAGE_PRESENT | PAGE_RW | PAGE_USER | PAGE_NX ,
651+ MemoryRegionType :: InputData => PAGE_PRESENT | PAGE_RW | PAGE_NX ,
652+ MemoryRegionType :: OutputData => PAGE_PRESENT | PAGE_RW | PAGE_NX ,
653+ MemoryRegionType :: Peb => PAGE_PRESENT | PAGE_RW | PAGE_NX ,
654+ MemoryRegionType :: HostFunctionDefinitions => PAGE_PRESENT | PAGE_NX ,
655+ MemoryRegionType :: PageTables => PAGE_PRESENT | PAGE_RW | PAGE_NX ,
656+ MemoryRegionType :: InitData => region. flags . translate_flags ( ) ,
657+ }
658+ }
659+
660+ /// Verify the complete paging structure for a sandbox configuration
661+ fn verify_paging_structure ( name : & str , config : Option < SandboxConfiguration > ) -> Result < ( ) > {
662+ let mut mgr = create_sandbox_with_page_tables ( config) ?;
663+
664+ let regions = mgr. layout . get_memory_regions ( & mgr. shared_mem ) ?;
665+
666+ mgr. shared_mem . with_exclusivity ( |excl_mem| {
667+ // Verify PML4 entry (single entry pointing to PDPT)
668+ let pml4_val = excl_mem. read_u64 ( SandboxMemoryLayout :: PML4_OFFSET ) ?;
669+ let expected_pml4 =
670+ SandboxMemoryLayout :: PDPT_GUEST_ADDRESS as u64 | PAGE_PRESENT | PAGE_RW ;
671+ if pml4_val != expected_pml4 {
672+ return Err ( new_error ! (
673+ "{}: PML4[0] incorrect: expected 0x{:x}, got 0x{:x}" ,
674+ name,
675+ expected_pml4,
676+ pml4_val
677+ ) ) ;
678+ }
679+
680+ // Verify PDPT entry (single entry pointing to PD)
681+ let pdpt_val = excl_mem. read_u64 ( SandboxMemoryLayout :: PDPT_OFFSET ) ?;
682+ let expected_pdpt =
683+ SandboxMemoryLayout :: PD_GUEST_ADDRESS as u64 | PAGE_PRESENT | PAGE_RW ;
684+ if pdpt_val != expected_pdpt {
685+ return Err ( new_error ! (
686+ "{}: PDPT[0] incorrect: expected 0x{:x}, got 0x{:x}" ,
687+ name,
688+ expected_pdpt,
689+ pdpt_val
690+ ) ) ;
691+ }
692+
693+ // Verify all 512 PD entries (each pointing to a PT)
694+ for i in 0 ..512 {
695+ let offset = SandboxMemoryLayout :: PD_OFFSET + ( i * 8 ) ;
696+ let pd_val = excl_mem. read_u64 ( offset) ?;
697+ let expected_pt_addr =
698+ SandboxMemoryLayout :: PT_GUEST_ADDRESS as u64 + ( i as u64 * 4096 ) ;
699+ let expected_pd = expected_pt_addr | PAGE_PRESENT | PAGE_RW ;
700+ if pd_val != expected_pd {
701+ return Err ( new_error ! (
702+ "{}: PD[{}] incorrect: expected 0x{:x}, got 0x{:x}" ,
703+ name,
704+ i,
705+ expected_pd,
706+ pd_val
707+ ) ) ;
708+ }
709+ }
710+
711+ // Verify PTEs for each memory region
712+ for region in & regions {
713+ let start = region. guest_region . start ;
714+ let end = region. guest_region . end ;
715+ let expected_flags = get_expected_flags ( region) ;
716+
717+ verify_page_range (
718+ excl_mem,
719+ start,
720+ end,
721+ expected_flags,
722+ & format ! ( "{} {:?}" , name, region. region_type) ,
723+ ) ?;
724+ }
725+
726+ Ok ( ( ) )
727+ } ) ??;
728+
729+ Ok ( ( ) )
730+ }
731+
732+ /// Test the complete paging structure (PML4, PDPT, PD, and all PTEs) for
733+ /// sandboxes of different sizes: default, small (8MB), medium (64MB), and large (256MB)
734+ #[ test]
735+ fn test_page_table_contents ( ) {
736+ let test_cases: [ ( & str , Option < SandboxConfiguration > ) ; 4 ] = [
737+ ( "default" , None ) ,
738+ ( "small (8MB heap)" , {
739+ let mut cfg = SandboxConfiguration :: default ( ) ;
740+ cfg. set_heap_size ( SMALL_HEAP_SIZE ) ;
741+ Some ( cfg)
742+ } ) ,
743+ ( "medium (64MB heap)" , {
744+ let mut cfg = SandboxConfiguration :: default ( ) ;
745+ cfg. set_heap_size ( MEDIUM_HEAP_SIZE ) ;
746+ Some ( cfg)
747+ } ) ,
748+ ( "large (256MB heap)" , {
749+ let mut cfg = SandboxConfiguration :: default ( ) ;
750+ cfg. set_heap_size ( LARGE_HEAP_SIZE ) ;
751+ Some ( cfg)
752+ } ) ,
753+ ] ;
754+
755+ for ( name, config) in test_cases {
756+ verify_paging_structure ( name, config)
757+ . unwrap_or_else ( |e| panic ! ( "Page table verification failed for {}: {}" , name, e) ) ;
758+ }
759+ }
760+ }
0 commit comments