pud_t *pud; pmd_t *pmd_table; #ifdef CONFIG_X86_PAE if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { pmd_table = (pmd_t *)alloc_low_page(); paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); p4d = p4d_offset(pgd, 0); pud = pud_offset(p4d, 0); BUG_ON(pmd_table != pmd_offset(pud, 0)); return pmd_table; } #endif p4d = p4d_offset(pgd, 0); pud = pud_offset(p4d, 0); pmd_table = pmd_offset(pud, 0); return pmd_table; } /* * Create a page table and place a pointer to it in a middle page * directory entry: */ static pte_t * __init one_page_table_init(pmd_t *pmd) { if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { pte_t *page_table = (pte_t *)alloc_low_page();
/** * page_vma_mapped_walk - check if @pvmw->page is mapped in @pvmw->vma at * @pvmw->address * @pvmw: pointer to struct page_vma_mapped_walk. page, vma, address and flags * must be set. pmd, pte and ptl must be NULL. * * Returns true if the page is mapped in the vma. @pvmw->pmd and @pvmw->pte point * to relevant page table entries. @pvmw->ptl is locked. @pvmw->address is * adjusted if needed (for PTE-mapped THPs). * * If @pvmw->pmd is set but @pvmw->pte is not, you have found PMD-mapped page * (usually THP). For PTE-mapped THP, you should run page_vma_mapped_walk() in * a loop to find all PTEs that map the THP. * * For HugeTLB pages, @pvmw->pte is set to the relevant page table entry * regardless of which page table level the page is mapped at. @pvmw->pmd is * NULL. * * Retruns false if there are no more page table entries for the page in * the vma. @pvmw->ptl is unlocked and @pvmw->pte is unmapped. * * If you need to stop the walk before page_vma_mapped_walk() returned false, * use page_vma_mapped_walk_done(). It will do the housekeeping. */ bool page_vma_mapped_walk(struct page_vma_mapped_walk *pvmw) { struct mm_struct *mm = pvmw->vma->vm_mm; struct page *page = pvmw->page; pgd_t *pgd; p4d_t *p4d; pud_t *pud; pmd_t pmde; /* The only possible pmd mapping has been handled on last iteration */ if (pvmw->pmd && !pvmw->pte) return not_found(pvmw); if (pvmw->pte) goto next_pte; if (unlikely(PageHuge(pvmw->page))) { /* when pud is not present, pte will be NULL */ pvmw->pte = huge_pte_offset(mm, pvmw->address, PAGE_SIZE << compound_order(page)); if (!pvmw->pte) return false; pvmw->ptl = huge_pte_lockptr(page_hstate(page), mm, pvmw->pte); spin_lock(pvmw->ptl); if (!check_pte(pvmw)) return not_found(pvmw); return true; } restart: pgd = pgd_offset(mm, pvmw->address); if (!pgd_present(*pgd)) return false; p4d = p4d_offset(pgd, pvmw->address); if (!p4d_present(*p4d)) return false; pud = pud_offset(p4d, pvmw->address); if (!pud_present(*pud)) return false; pvmw->pmd = pmd_offset(pud, pvmw->address); /* * Make sure the pmd value isn't cached in a register by the * compiler and used as a stale value after we've observed a * subsequent update. */ pmde = READ_ONCE(*pvmw->pmd); if (pmd_trans_huge(pmde) || is_pmd_migration_entry(pmde)) { pvmw->ptl = pmd_lock(mm, pvmw->pmd); if (likely(pmd_trans_huge(*pvmw->pmd))) { if (pvmw->flags & PVMW_MIGRATION) return not_found(pvmw); if (pmd_page(*pvmw->pmd) != page) return not_found(pvmw); return true; } else if (!pmd_present(*pvmw->pmd)) { if (thp_migration_supported()) { if (!(pvmw->flags & PVMW_MIGRATION)) return not_found(pvmw); if (is_migration_entry(pmd_to_swp_entry(*pvmw->pmd))) { swp_entry_t entry = pmd_to_swp_entry(*pvmw->pmd); if (migration_entry_to_page(entry) != page) return not_found(pvmw); return true; } } return not_found(pvmw); } else { /* THP pmd was split under us: handle on pte level */ spin_unlock(pvmw->ptl); pvmw->ptl = NULL; } } else if (!pmd_present(pmde)) { return false; } if (!map_pte(pvmw)) goto next_pte; while (1) { if (check_pte(pvmw)) return true; next_pte: /* Seek to next pte only makes sense for THP */ if (!PageTransHuge(pvmw->page) || PageHuge(pvmw->page)) return not_found(pvmw); do { pvmw->address += PAGE_SIZE; if (pvmw->address >= pvmw->vma->vm_end || pvmw->address >= __vma_address(pvmw->page, pvmw->vma) + hpage_nr_pages(pvmw->page) * PAGE_SIZE) return not_found(pvmw); /* Did we cross page table boundary? */ if (pvmw->address % PMD_SIZE == 0) { pte_unmap(pvmw->pte); if (pvmw->ptl) { spin_unlock(pvmw->ptl); pvmw->ptl = NULL; } goto restart; } else { pvmw->pte++; } } while (pte_none(*pvmw->pte)); if (!pvmw->ptl) { pvmw->ptl = pte_lockptr(mm, pvmw->pmd); spin_lock(pvmw->ptl); } } }
/* * Add low kernel mappings for passing arguments to EFI functions. */ void efi_sync_low_kernel_mappings(void) { unsigned num_entries; pgd_t *pgd_k, *pgd_efi; p4d_t *p4d_k, *p4d_efi; pud_t *pud_k, *pud_efi; if (efi_enabled(EFI_OLD_MEMMAP)) return; /* * We can share all PGD entries apart from the one entry that * covers the EFI runtime mapping space. * * Make sure the EFI runtime region mappings are guaranteed to * only span a single PGD entry and that the entry also maps * other important kernel regions. */ BUILD_BUG_ON(pgd_index(EFI_VA_END) != pgd_index(MODULES_END)); BUILD_BUG_ON((EFI_VA_START & PGDIR_MASK) != (EFI_VA_END & PGDIR_MASK)); pgd_efi = efi_pgd + pgd_index(PAGE_OFFSET); pgd_k = pgd_offset_k(PAGE_OFFSET); num_entries = pgd_index(EFI_VA_END) - pgd_index(PAGE_OFFSET); memcpy(pgd_efi, pgd_k, sizeof(pgd_t) * num_entries); /* * As with PGDs, we share all P4D entries apart from the one entry * that covers the EFI runtime mapping space. */ BUILD_BUG_ON(p4d_index(EFI_VA_END) != p4d_index(MODULES_END)); BUILD_BUG_ON((EFI_VA_START & P4D_MASK) != (EFI_VA_END & P4D_MASK)); pgd_efi = efi_pgd + pgd_index(EFI_VA_END); pgd_k = pgd_offset_k(EFI_VA_END); p4d_efi = p4d_offset(pgd_efi, 0); p4d_k = p4d_offset(pgd_k, 0); num_entries = p4d_index(EFI_VA_END); memcpy(p4d_efi, p4d_k, sizeof(p4d_t) * num_entries); /* * We share all the PUD entries apart from those that map the * EFI regions. Copy around them. */ BUILD_BUG_ON((EFI_VA_START & ~PUD_MASK) != 0); BUILD_BUG_ON((EFI_VA_END & ~PUD_MASK) != 0); p4d_efi = p4d_offset(pgd_efi, EFI_VA_END); p4d_k = p4d_offset(pgd_k, EFI_VA_END); pud_efi = pud_offset(p4d_efi, 0); pud_k = pud_offset(p4d_k, 0); num_entries = pud_index(EFI_VA_END); memcpy(pud_efi, pud_k, sizeof(pud_t) * num_entries); pud_efi = pud_offset(p4d_efi, EFI_VA_START); pud_k = pud_offset(p4d_k, EFI_VA_START); num_entries = PTRS_PER_PUD - pud_index(EFI_VA_START); memcpy(pud_efi, pud_k, sizeof(pud_t) * num_entries); }
pgd_t * __init efi_call_phys_prolog(void) { unsigned long vaddr, addr_pgd, addr_p4d, addr_pud; pgd_t *save_pgd, *pgd_k, *pgd_efi; p4d_t *p4d, *p4d_k, *p4d_efi; pud_t *pud; int pgd; int n_pgds, i, j; if (!efi_enabled(EFI_OLD_MEMMAP)) { save_pgd = (pgd_t *)__read_cr3(); write_cr3((unsigned long)efi_scratch.efi_pgt); goto out; } early_code_mapping_set_exec(1); n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE); save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL); /* * Build 1:1 identity mapping for efi=old_map usage. Note that * PAGE_OFFSET is PGDIR_SIZE aligned when KASLR is disabled, while * it is PUD_SIZE ALIGNED with KASLR enabled. So for a given physical * address X, the pud_index(X) != pud_index(__va(X)), we can only copy * PUD entry of __va(X) to fill in pud entry of X to build 1:1 mapping. * This means here we can only reuse the PMD tables of the direct mapping. */ for (pgd = 0; pgd < n_pgds; pgd++) { addr_pgd = (unsigned long)(pgd * PGDIR_SIZE); vaddr = (unsigned long)__va(pgd * PGDIR_SIZE); pgd_efi = pgd_offset_k(addr_pgd); save_pgd[pgd] = *pgd_efi; p4d = p4d_alloc(&init_mm, pgd_efi, addr_pgd); if (!p4d) { pr_err("Failed to allocate p4d table!\n"); goto out; } for (i = 0; i < PTRS_PER_P4D; i++) { addr_p4d = addr_pgd + i * P4D_SIZE; p4d_efi = p4d + p4d_index(addr_p4d); pud = pud_alloc(&init_mm, p4d_efi, addr_p4d); if (!pud) { pr_err("Failed to allocate pud table!\n"); goto out; } for (j = 0; j < PTRS_PER_PUD; j++) { addr_pud = addr_p4d + j * PUD_SIZE; if (addr_pud > (max_pfn << PAGE_SHIFT)) break; vaddr = (unsigned long)__va(addr_pud); pgd_k = pgd_offset_k(vaddr); p4d_k = p4d_offset(pgd_k, vaddr); pud[j] = *pud_offset(p4d_k, vaddr); } } } out: __flush_tlb_all(); return save_pgd; }
/* * map any virtual address of the current process to its * physical one. */ static unsigned long long any_v2p(unsigned long long vaddr) { pgd_t *pgd = pgd_offset(current->mm, vaddr); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) p4d_t *p4d; #endif pud_t *pud; pmd_t *pmd; pte_t *pte; /* to lock the page */ struct page *pg; unsigned long long paddr; if (bad_address(pgd)) { printk(KERN_ALERT "[nskk] Alert: bad address of pgd %p\n", pgd); goto bad; } if (!pgd_present(*pgd)) { printk(KERN_ALERT "[nskk] Alert: pgd not present %lu\n", *pgd); goto out; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) p4d = p4d_offset(pgd, vaddr); if (p4d_none(*p4d)) return 0; pud = pud_offset(p4d, vaddr); #else pud = pud_offset(pgd, vaddr); #endif if (bad_address(pud)) { printk(KERN_ALERT "[nskk] Alert: bad address of pud %p\n", pud); goto bad; } if (!pud_present(*pud) || pud_large(*pud)) { printk(KERN_ALERT "[nskk] Alert: pud not present %lu\n", *pud); goto out; } pmd = pmd_offset(pud, vaddr); if (bad_address(pmd)) { printk(KERN_ALERT "[nskk] Alert: bad address of pmd %p\n", pmd); goto bad; } if (!pmd_present(*pmd) || pmd_large(*pmd)) { printk(KERN_ALERT "[nskk] Alert: pmd not present %lu\n", *pmd); goto out; } pte = pte_offset_kernel(pmd, vaddr); if (bad_address(pte)) { printk(KERN_ALERT "[nskk] Alert: bad address of pte %p\n", pte); goto bad; } if (!pte_present(*pte)) { printk(KERN_ALERT "[nskk] Alert: pte not present %lu\n", *pte); goto out; } pg = pte_page(*pte); #if 1 paddr = (pte_val(*pte) & PHYSICAL_PAGE_MASK) | (vaddr&(PAGE_SIZE-1)); #else pte->pte |= _PAGE_RW; // | _PAGE_USER; paddr = pte_val(*pte); #endif out: return paddr; bad: printk(KERN_ALERT "[nskk] Alert: Bad address\n"); return 0; }