/** * \brief Map a region of physical memory into physical memory address space. * * Maps the region of physical memory, based at base and sized size bytes * to the same-sized virtual memory region. All pages are flagged according to * bitmap. This function automatically fills the needed page directory entries * in the page hierarchy rooted at pml4. base and size will be made * page-aligned by this function. * * \param base Physical base address of memory region * \param size Size in bytes of memory region * \param bitmap Bitmap of flags for page tables/directories * * \return 0 on success, -1 on error (out of range) */ static int paging_map_mem(lpaddr_t base, size_t size, uint64_t bitmap) { lvaddr_t vaddr, vbase = local_phys_to_mem(base); lpaddr_t addr; // Align given physical base address if (base & X86_64_MEM_PAGE_MASK) { base -= base & X86_64_MEM_PAGE_MASK; } paging_align(&vbase, &base, &size, X86_64_MEM_PAGE_SIZE); // Is mapped region out of range? assert(base + size <= (lpaddr_t)K1OM_PADDR_SPACE_LIMIT); if (base + size > (lpaddr_t) K1OM_PADDR_SPACE_LIMIT) { return -1; } // Map pages, tables and directories for (vaddr = vbase, addr = base; vaddr < vbase + size; vaddr += X86_64_MEM_PAGE_SIZE, addr += X86_64_MEM_PAGE_SIZE) { union x86_64_pdir_entry *pml4_base = &pml4[X86_64_PML4_BASE(vaddr)]; union x86_64_pdir_entry *pdpt_base = &mem_pdpt[X86_64_PML4_BASE(addr)][X86_64_PDPT_BASE(vaddr)]; union x86_64_ptable_entry *pdir_base = &mem_pdir[X86_64_PML4_BASE(addr)][X86_64_PDPT_BASE(addr)][X86_64_PDIR_BASE( vaddr)]; debug(SUBSYS_PAGING, "Mapping 2M page: vaddr = 0x%"PRIxLVADDR"x, addr = 0x%lx, " "PML4_BASE = %lu, PDPT_BASE = %lu, PDIR_BASE = %lu -- ", vaddr, addr, X86_64_PML4_BASE(vaddr), X86_64_PDPT_BASE(vaddr), X86_64_PDIR_BASE(vaddr)); mapit(pml4_base, pdpt_base, pdir_base, addr, bitmap); } // XXX FIXME: get rid of this TBL flush code, or move it elsewhere // uint64_t cr3; // __asm__ __volatile__("mov %%cr3,%0" : "=a" (cr3) : ); // __asm__ __volatile__("mov %0,%%cr3" : : "a" (cr3)); return 0; }
void paging_dump_tables(struct dcb *dispatcher) { lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace); // loop over pdpts union x86_64_ptable_entry *pt; size_t kernel_pml4e = X86_64_PML4_BASE(X86_64_MEMORY_OFFSET); for (int pdpt_index = 0; pdpt_index < kernel_pml4e; pdpt_index++) { union x86_64_pdir_entry *pdpt = (union x86_64_pdir_entry *)root_pt + pdpt_index; if (!pdpt->raw) { continue; } genpaddr_t pdpt_gp = pdpt->d.base_addr << BASE_PAGE_BITS; lvaddr_t pdpt_lv = local_phys_to_mem(gen_phys_to_local_phys(pdpt_gp)); for (int pdir_index = 0; pdir_index < X86_64_PTABLE_SIZE; pdir_index++) { // get pdir union x86_64_pdir_entry *pdir = (union x86_64_pdir_entry *)pdpt_lv + pdir_index; pt = (union x86_64_ptable_entry*)pdir; if (!pdir->raw) { continue; } // check if pdir or huge page if (pt->huge.always1) { // is huge page mapping genpaddr_t paddr = (genpaddr_t)pt->huge.base_addr << HUGE_PAGE_BITS; printf("%d.%d: 0x%"PRIxGENPADDR"\n", pdpt_index, pdir_index, paddr); // goto next pdpt entry continue; } genpaddr_t pdir_gp = pdir->d.base_addr << BASE_PAGE_BITS; lvaddr_t pdir_lv = local_phys_to_mem(gen_phys_to_local_phys(pdir_gp)); for (int ptable_index = 0; ptable_index < X86_64_PTABLE_SIZE; ptable_index++) { // get ptable union x86_64_pdir_entry *ptable = (union x86_64_pdir_entry *)pdir_lv + ptable_index; pt = (union x86_64_ptable_entry *)ptable; if (!ptable->raw) { continue; } // check if ptable or large page if (pt->large.always1) { // is large page mapping genpaddr_t paddr = (genpaddr_t)pt->large.base_addr << LARGE_PAGE_BITS; printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdpt_index, pdir_index, ptable_index, paddr); // goto next pdir entry continue; } genpaddr_t ptable_gp = ptable->d.base_addr << BASE_PAGE_BITS; lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp)); for (int entry = 0; entry < X86_64_PTABLE_SIZE; entry++) { union x86_64_ptable_entry *e = (union x86_64_ptable_entry *)ptable_lv + entry; genpaddr_t paddr = (genpaddr_t)e->base.base_addr << BASE_PAGE_BITS; if (!paddr) { continue; } printf("%d.%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdpt_index, pdir_index, ptable_index, entry, paddr); } } } } }
/** * \brief Make a "good" PML4 table out of a page table. * * A "good" PML4 table is one that has all physical address space and * the kernel mapped in. This function modifies the passed PML4, based * at physical address 'base' accordingly. It does this by taking out * the corresponding entries of the kernel's pristine PML4 table. * * \param base Physical base address of PML4 table to make "good". */ void paging_x86_64_make_good_pml4(lpaddr_t base) { union x86_64_pdir_entry *newpml4 = (union x86_64_pdir_entry *)local_phys_to_mem(base); int i; // XXX: Disabled till vaddr_t is figured out debug(SUBSYS_PAGING, "Is now a PML4: table = 0x%"PRIxLPADDR"\n", base); // Map memory for(i = X86_64_PML4_BASE(MEMORY_OFFSET); i < X86_64_PTABLE_SIZE; i++) { newpml4[i] = pml4[i]; } }
static inline lvaddr_t get_leaf_ptable_for_vaddr(genvaddr_t vaddr) { lvaddr_t root_pt = local_phys_to_mem(dcb_current->vspace); // get pdpt union x86_64_pdir_entry *pdpt = (union x86_64_pdir_entry *)root_pt + X86_64_PML4_BASE(vaddr); if (!pdpt->raw) { return 0; } genpaddr_t pdpt_gp = pdpt->d.base_addr << BASE_PAGE_BITS; lvaddr_t pdpt_lv = local_phys_to_mem(gen_phys_to_local_phys(pdpt_gp)); // get pdir union x86_64_pdir_entry *pdir = (union x86_64_pdir_entry *)pdpt_lv + X86_64_PDPT_BASE(vaddr); if (!pdir->raw) { return 0; } genpaddr_t pdir_gp = pdir->d.base_addr << BASE_PAGE_BITS; lvaddr_t pdir_lv = local_phys_to_mem(gen_phys_to_local_phys(pdir_gp)); // get ptable union x86_64_ptable_entry *ptable = (union x86_64_ptable_entry *)pdir_lv + X86_64_PDIR_BASE(vaddr); if (!ptable->raw) { return 0; } genpaddr_t ptable_gp = ptable->base.base_addr << BASE_PAGE_BITS; lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp)); return ptable_lv; }
/// Map within a x86_64 non leaf ptable static errval_t x86_64_non_ptable(struct capability *dest, cslot_t slot, struct capability *src, uintptr_t flags, uintptr_t offset, size_t pte_count) { //printf("page_mappings_arch:x86_64_non_ptable\n"); if (slot >= X86_64_PTABLE_SIZE) { // Within pagetable return SYS_ERR_VNODE_SLOT_INVALID; } if (type_is_vnode(src->type) && pte_count != 1) { // only allow single ptable mappings printf("src type and count mismatch\n"); return SYS_ERR_VM_MAP_SIZE; } if (slot + pte_count > X86_64_PTABLE_SIZE) { // mapping size ok printf("mapping size invalid (%zd)\n", pte_count); return SYS_ERR_VM_MAP_SIZE; } size_t page_size = 0; paging_x86_64_flags_t flags_large = 0; switch (dest->type) { case ObjType_VNode_x86_64_pml4: if (src->type != ObjType_VNode_x86_64_pdpt) { // Right mapping printf("src type invalid\n"); return SYS_ERR_WRONG_MAPPING; } if(slot >= X86_64_PML4_BASE(MEMORY_OFFSET)) { // Kernel mapped here return SYS_ERR_VNODE_SLOT_RESERVED; } break; case ObjType_VNode_x86_64_pdpt: // huge page support if (src->type != ObjType_VNode_x86_64_pdir) { // Right mapping // TODO: check if the system allows 1GB mappings page_size = X86_64_HUGE_PAGE_SIZE; // check offset within frame genpaddr_t off = offset; if (off + pte_count * X86_64_HUGE_PAGE_SIZE > get_size(src)) { return SYS_ERR_FRAME_OFFSET_INVALID; } // Calculate page access protection flags / // Get frame cap rights flags_large = paging_x86_64_cap_to_page_flags(src->rights); // Mask with provided access rights mask flags_large = paging_x86_64_mask_attrs(flags, X86_64_PTABLE_ACCESS(flags)); // Add additional arch-specific flags flags_large |= X86_64_PTABLE_FLAGS(flags); // Unconditionally mark the page present flags_large |= X86_64_PTABLE_PRESENT; } break; case ObjType_VNode_x86_64_pdir: // superpage support if (src->type != ObjType_VNode_x86_64_ptable) { // Right mapping page_size = X86_64_LARGE_PAGE_SIZE; // check offset within frame genpaddr_t off = offset; if (off + pte_count * X86_64_LARGE_PAGE_SIZE > get_size(src)) { return SYS_ERR_FRAME_OFFSET_INVALID; } // Calculate page access protection flags / // Get frame cap rights flags_large = paging_x86_64_cap_to_page_flags(src->rights); // Mask with provided access rights mask flags_large = paging_x86_64_mask_attrs(flags, X86_64_PTABLE_ACCESS(flags)); // Add additional arch-specific flags flags_large |= X86_64_PTABLE_FLAGS(flags); // Unconditionally mark the page present flags_large |= X86_64_PTABLE_PRESENT; } break; default: printf("dest type invalid\n"); return SYS_ERR_DEST_TYPE_INVALID; } // Convert destination base address genpaddr_t dest_gp = get_address(dest); lpaddr_t dest_lp = gen_phys_to_local_phys(dest_gp); lvaddr_t dest_lv = local_phys_to_mem(dest_lp); // Convert source base address genpaddr_t src_gp = get_address(src); lpaddr_t src_lp = gen_phys_to_local_phys(src_gp); // set metadata struct cte *src_cte = cte_for_cap(src); src_cte->mapping_info.pte = dest_lp + slot * sizeof(union x86_64_ptable_entry); src_cte->mapping_info.pte_count = pte_count; src_cte->mapping_info.offset = offset; cslot_t last_slot = slot + pte_count; for (; slot < last_slot; slot++, offset += page_size) { // Destination union x86_64_pdir_entry *entry = (union x86_64_pdir_entry *)dest_lv + slot; if (X86_64_IS_PRESENT(entry)) { // cleanup mapping info // TODO: cleanup already mapped pages memset(&src_cte->mapping_info, 0, sizeof(struct mapping_info)); printf("slot in use\n"); return SYS_ERR_VNODE_SLOT_INUSE; } // determine if we map a large/huge page or a normal entry if (page_size == X86_64_LARGE_PAGE_SIZE) { //a large page is mapped paging_x86_64_map_large((union x86_64_ptable_entry *)entry, src_lp + offset, flags_large); } else if (page_size == X86_64_HUGE_PAGE_SIZE) { // a huge page is mapped paging_x86_64_map_huge((union x86_64_ptable_entry *)entry, src_lp + offset, flags_large); } else { //a normal paging structure entry is mapped paging_x86_64_map_table(entry, src_lp + offset); } } return SYS_ERR_OK; }