@@ -164,9 +164,9 @@ uint64_t setup_amd64_paging(vMemory& memory,
164164 const uint64_t vdso_pdpt_addr = pml4_addr + PD_END + 0x0 ;
165165 const uint64_t vsyscall_pd_addr = pml4_addr + PD_END + 0x1000 ;
166166 const uint64_t vsyscall_pt_addr = pml4_addr + PD_END + 0x2000 ;
167- auto * vdso_pdpt = (uint64_t *)(pagetable + PD_END + 0x0000 );
168- auto * vsyscall_pd = (uint64_t *)(pagetable + PD_END + 0x1000 );
169- auto * vsyscall_pt = (uint64_t *)(pagetable + PD_END + 0x2000 );
167+ auto * vdso_pdpt = (uint64_t *)(pagetable + PD_END + 0x0000 );
168+ auto * vsyscall_pd = (uint64_t *)(pagetable + PD_END + 0x1000 );
169+ auto * vsyscall_pt = (uint64_t *)(pagetable + PD_END + 0x2000 );
170170
171171 // next free page for ELF loader
172172 uint64_t free_page = pml4_addr + PD_END + 0x3000 ;
@@ -390,6 +390,10 @@ uint64_t setup_amd64_paging(vMemory& memory,
390390 | (base_giga_page << 30 ) | (base_2mb_page << 21 ) | (i << 12 );
391391 }
392392
393+ if (free_page >= memory.physbase + (2ULL << 20 )) {
394+ throw MachineException (" Pagetable setup exceeded 2MB limit" );
395+ }
396+
393397 /* Verify a kernel page */
394398 page_at (memory, memory.physbase + 0x1000 ,
395399 [&memory] (uint64_t addr, uint64_t & entry, size_t size) {
@@ -532,8 +536,9 @@ void foreach_page(vMemory& memory, foreach_page_t callback, bool skip_oob_addres
532536 {
533537 if (pd[k] & PDE64_PRESENT ) {
534538 const auto [pt_base, pt_mem, pt_size] = pt_from_index (k, pd_base, pd);
539+ const bool is_2mb_page = (pd[k] & PDE64_PS ) != 0 ;
535540 callback (pt_base, pd[k], pt_size);
536- if (!(pd[k] & PDE64_PS )) { // not 2MB page
541+ if (!is_2mb_page) {
537542 auto * pt = memory.page_at (pt_mem);
538543 for (uint64_t e = 0 ; e < 512 ; e++) {
539544 const auto [pte_base, pte_mem, pte_size] = pte_from_index (e, pt_base, pt);
@@ -554,19 +559,49 @@ void foreach_page(const vMemory& mem, foreach_page_t callback, bool skip_oob_add
554559 foreach_page (const_cast <vMemory&>(mem), std::move (callback), skip_oob_addresses);
555560}
556561
557- void foreach_page_makecow (vMemory& mem, uint64_t kernel_end, uint64_t shared_memory_boundary)
562+ void foreach_page_makecow (vMemory& mem, uint64_t kernel_end,
563+ uint64_t shared_memory_boundary, bool split_accessed_hugepages)
558564{
559565 if (UNLIKELY (shared_memory_boundary < kernel_end)) {
560566 memory_exception (" Shared memory boundary was illegal (zero)" , shared_memory_boundary, 0u );
561567 }
562568 foreach_page (mem,
563- [=] (uint64_t addr, uint64_t & entry, size_t /* size*/ ) {
569+ [=, m = &mem ] (uint64_t addr, uint64_t & entry, size_t size) {
564570 if (addr < shared_memory_boundary) {
565571 const uint64_t flags = (PDE64_PRESENT | PDE64_RW );
566572 if ((entry & flags) == flags) {
567573 entry &= ~PDE64_RW ;
568574 entry |= PDE64_CLONEABLE | PDE64_G ; // Global bit for read-only pages
569575 }
576+ if ((entry & PDE64_ACCESSED ) != 0 )
577+ {
578+ // Since this page has been accessed, check if it's a 2MB leaf page
579+ // and if it is, split it into 4k pages with the ACCESS bit removed.
580+ if (size == (1ULL << 21 ) && (entry & PDE64_PS ) != 0 && split_accessed_hugepages)
581+ {
582+ // Split 2MB page into 4k pages
583+ const uint64_t pd_base = entry & PDE64_ADDR_MASK ;
584+ // Allocate new page for page table
585+ const auto new_page = m->allocate_unmapped_kernelpage ();
586+ if (new_page.addr != 0 ) {
587+ // Set default attributes + new page table
588+ const uint64_t pd_entry_flags = entry & ~(PDE64_ADDR_MASK | PDE64_PS | PDE64_ACCESSED );
589+ entry = pd_entry_flags | new_page.addr ;
590+ // Fill new page with default attributes
591+ uint64_t * pagetable = new_page.pmem ;
592+ for (uint64_t i = 0 ; i < 512 ; i++) {
593+ // Set writable 4k attributes
594+ uint64_t addr4k = pd_base | (i << 12 );
595+ pagetable[i] = pd_entry_flags | addr4k;
596+ }
597+ return ;
598+ } // new_page.addr
599+ // Failing to allocate, so we simply won't
600+ // split the page, but this may lead to extra
601+ // memory usage if forks don't need the whole
602+ // 2MB page.
603+ }
604+ }
570605 }
571606 // Clear accessed bit for *all* pages
572607 // Doing this makes it possible to send a dummy request to estimate which
0 commit comments