static void build_page_tables(pgentry_t *top_table, unsigned long *pt_pfn, void *start_va, void *end_va) { // // *pt_pfn - unused pages already mapped by domain builder (512K+) // carve new page tables/directories from *pt_pfn as needed // start_va - end_va - range of frames to map // struct mmu_update mmu_updates[L1_PAGETABLE_ENTRIES]; int count = 0; while (start_va + PAGE_SIZE <= end_va) { pgentry_t *tab = top_table; unsigned int offset; pgentry_t pte; #if defined(__x86_64__) offset = l4_table_offset((unsigned long)start_va); pte = tab[offset]; if ((pte & _PAGE_PRESENT) == 0) pte = new_pt_page(pt_pfn, tab, offset, L3_FRAME); tab = pte_to_virt(pte); #endif offset = l3_table_offset((unsigned long)start_va); pte = tab[offset]; if ((pte & _PAGE_PRESENT) == 0) pte = new_pt_page(pt_pfn, tab, offset, L2_FRAME); tab = pte_to_virt(pte); offset = l2_table_offset((unsigned long)start_va); pte = tab[offset]; if ((pte & _PAGE_PRESENT) == 0) pte = new_pt_page(pt_pfn, tab, offset, L1_FRAME); tab = pte_to_virt(pte); offset = l1_table_offset((unsigned long)start_va); pte = tab[offset]; if ((pte & _PAGE_PRESENT) == 0) { unsigned long pt_mfn = virt_to_mfn(tab); mmu_updates[count].ptr = ((pgentry_t)pt_mfn << PAGE_SHIFT) + sizeof(pgentry_t)*offset; mmu_updates[count].val = virt_to_mfn(start_va) << PAGE_SHIFT | L1_PROT; count++; } start_va += PAGE_SIZE; if (count == L1_PAGETABLE_ENTRIES || start_va + PAGE_SIZE > end_va) { int rc = HYPERVISOR_mmu_update(mmu_updates, count, 0, DOMID_SELF); if (rc < 0) fatal_error("build_page_tables: mmu_update failed: %d", rc); count = 0; } } }
void page_walk(unsigned long virt_address) { pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; unsigned long addr = virt_address; printk("Pagetable walk from virt %lx, base %lx:\n", virt_address, start_info.pt_base); page = tab[l4_table_offset(addr)]; tab = pte_to_virt(page); printk(" L4 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l4_table_offset(addr)); page = tab[l3_table_offset(addr)]; tab = pte_to_virt(page); printk(" L3 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l3_table_offset(addr)); page = tab[l2_table_offset(addr)]; tab = pte_to_virt(page); printk(" L2 = %"PRIpte" (%p) [offset = %lx]\n", page, tab, l2_table_offset(addr)); page = tab[l1_table_offset(addr)]; printk(" L1 = %"PRIpte" [offset = %lx]\n", page, l1_table_offset(addr)); }
static int handle_cow(unsigned long addr) { pgentry_t *tab = (pgentry_t *)start_info.pt_base, page; unsigned long new_page; int rc; page = tab[l4_table_offset(addr)]; if (!(page & _PAGE_PRESENT)) return 0; tab = pte_to_virt(page); page = tab[l3_table_offset(addr)]; if (!(page & _PAGE_PRESENT)) return 0; tab = pte_to_virt(page); page = tab[l2_table_offset(addr)]; if (!(page & _PAGE_PRESENT)) return 0; tab = pte_to_virt(page); page = tab[l1_table_offset(addr)]; if (!(page & _PAGE_PRESENT)) return 0; /* Only support CoW for the zero page. */ if (PHYS_PFN(page) != mfn_zero) return 0; new_page = alloc_pages(0); memset((void*) new_page, 0, PAGE_SIZE); rc = HYPERVISOR_update_va_mapping(addr & PAGE_MASK, __pte(virt_to_mach(new_page) | L1_PROT), UVMF_INVLPG); if (!rc) return 1; printk("Map zero page to %lx failed: %d.\n", addr, rc); return 0; }
/* * Make pt_pfn a new 'level' page table frame and hook it into the page * table at offset in previous level MFN (pref_l_mfn). pt_pfn is a guest * PFN. */ static void new_pt_frame(unsigned long *pt_pfn, unsigned long prev_l_mfn, unsigned long offset, unsigned long level) { pgentry_t *tab = (pgentry_t *)start_info.pt_base; unsigned long pt_page = (unsigned long)pfn_to_virt(*pt_pfn); pgentry_t prot_e, prot_t; mmu_update_t mmu_updates[1]; int rc; prot_e = prot_t = 0; DEBUG("Allocating new L%d pt frame for pfn=%lx, " "prev_l_mfn=%lx, offset=%lx", level, *pt_pfn, prev_l_mfn, offset); /* We need to clear the page, otherwise we might fail to map it as a page table page */ memset((void*) pt_page, 0, PAGE_SIZE); switch ( level ) { case L1_FRAME: prot_e = L1_PROT; prot_t = L2_PROT; break; case L2_FRAME: prot_e = L2_PROT; prot_t = L3_PROT; break; #if defined(__x86_64__) case L3_FRAME: prot_e = L3_PROT; prot_t = L4_PROT; break; #endif default: printk("new_pt_frame() called with invalid level number %d\n", level); do_exit(); break; } /* Make PFN a page table page */ #if defined(__x86_64__) tab = pte_to_virt(tab[l4_table_offset(pt_page)]); #endif tab = pte_to_virt(tab[l3_table_offset(pt_page)]); mmu_updates[0].ptr = (tab[l2_table_offset(pt_page)] & PAGE_MASK) + sizeof(pgentry_t) * l1_table_offset(pt_page); mmu_updates[0].val = (pgentry_t)pfn_to_mfn(*pt_pfn) << PAGE_SHIFT | (prot_e & ~_PAGE_RW); if ( (rc = HYPERVISOR_mmu_update(mmu_updates, 1, NULL, DOMID_SELF)) < 0 ) { printk("ERROR: PTE for new page table page could not be updated\n"); printk(" mmu_update failed with rc=%d\n", rc); do_exit(); } /* Hook the new page table page into the hierarchy */ mmu_updates[0].ptr = ((pgentry_t)prev_l_mfn << PAGE_SHIFT) + sizeof(pgentry_t) * offset; mmu_updates[0].val = (pgentry_t)pfn_to_mfn(*pt_pfn) << PAGE_SHIFT | prot_t; if ( (rc = HYPERVISOR_mmu_update(mmu_updates, 1, NULL, DOMID_SELF)) < 0 ) { printk("ERROR: mmu_update failed with rc=%d\n", rc); do_exit(); } *pt_pfn += 1; }