@@ -236,13 +236,14 @@ uint64_t setup_amd64_paging(vMemory& memory,
236236
237237 if (split_all_hugepages_during_loading)
238238 {
239+ static constexpr uint64_t MAX_FREE_PAGE = 509ULL * 0x1000 ;
239240 // Stop at 1MB address, to prevent trampling user space
240241 uint64_t max = 512 * PD_PAGES;
241242 if (max > 512U * 1U )
242243 max = 512U * 1U ;
243244
244245 // Split all hugepages into 4k pages for the entire memory area
245- for (uint64_t i = base_2mb_page+2 ; i < max; i++)
246+ for (uint64_t i = base_2mb_page+2 ; i < max && free_page < MAX_FREE_PAGE ; i++)
246247 {
247248 if (pd[i] & PDE64_PS) {
248249 // Set default attributes + free PTE page
@@ -941,7 +942,7 @@ void WritablePage::set_protections(int prot)
941942 }
942943}
943944
944- size_t paging_merge_leaf_pages_into_hugepages (vMemory& memory)
945+ size_t paging_merge_leaf_pages_into_hugepages (vMemory& memory, bool merge_if_dirty )
945946{
946947 unsigned merged_pages = 0 ;
947948 // Try to merge contiguous 4k pages with the same permissions,
@@ -960,26 +961,54 @@ size_t paging_merge_leaf_pages_into_hugepages(vMemory& memory)
960961 for (uint64_t k = 0 ; k < 512 ; k++) { // 2MB entries
961962 if (pd[k] & PDE64_PRESENT) {
962963 // Only consider page tables
963- if (pd[k] & PDE64_PS)
964+ if (( pd[k] & PDE64_PS) != 0 )
964965 continue ;
965966 const auto [pt_base, pt_mem, pt_size] = pt_from_index (k, pd_base, pd);
966967 auto * pt = memory.page_at (pt_mem);
967968 // Check if we can merge 512 entries
968969 bool can_merge = true ;
970+ bool any_dirty = (pt[0 ] & PDE64_DIRTY) != 0 ;
971+ bool all_dirty = (pt[0 ] & PDE64_DIRTY) != 0 ;
969972 static constexpr uint64_t MERGE_MASK =
970973 PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_NX | PDE64_CLONEABLE | PDE64_G;
971- uint64_t first_entry = pt[0 ] & MERGE_MASK;
974+ const uint64_t first_entry = pt[0 ] & MERGE_MASK;
975+ const uint64_t first_addr = pt[0 ] & PDE64_ADDR_MASK;
976+ // 2MB leaf page must be 2MB-aligned
977+ if ((first_addr & 0x1FFFFF ) != 0 ) {
978+ continue ;
979+ }
980+ // All entries must be present and have the same flags
981+ // and be contiguous in physical memory
982+ uint64_t expected_addr = first_addr;
972983 for (size_t e = 1 ; e < 512 ; e++) {
973984 if ((pt[e] & MERGE_MASK) != first_entry) {
974985 can_merge = false ;
975986 break ;
976987 }
988+ expected_addr += 0x1000 ;
989+ if ((pt[e] & PDE64_ADDR_MASK) != expected_addr) {
990+ can_merge = false ;
991+ break ;
992+ }
993+ if (pt[e] & PDE64_DIRTY) {
994+ any_dirty = true ;
995+ }
996+ else {
997+ all_dirty = false ;
998+ }
977999 }
978- if (can_merge) {
1000+ // Perform merge if possible:
1001+ // - either all pages are clean
1002+ // - or merge_if_dirty is set (and zero or more pages are dirty)
1003+ // - or all pages are dirty
1004+ if (can_merge && (merge_if_dirty || !any_dirty || all_dirty)) {
9791005 // Merge into 2MB page with same flags
980- pd[k] = (pt[0 ] & PDE64_ADDR_MASK) | first_entry
981- | PDE64_PS | PDE64_PRESENT;
982- CLPRINT (" Merged 4k pages into 2MB page at PD index %lu\n " , k);
1006+ pd[k] = first_addr | first_entry | PDE64_PS | PDE64_PRESENT;
1007+ if (any_dirty) {
1008+ pd[k] |= PDE64_DIRTY;
1009+ }
1010+ // printf("Entry: PDPT[%lu] PD[%lu] merged 512 pages into 2MB page 0x%lX with flags 0x%lX\n",
1011+ // j, k, pd[k] & PDE64_ADDR_MASK, pd[k] & ~PDE64_ADDR_MASK);
9831012 merged_pages += 512 ;
9841013 }
9851014 } // pd present
0 commit comments