static int relocate_restore_code(void) { pgd_t *pgd; pud_t *pud; relocated_restore_code = get_safe_page(GFP_ATOMIC); if (!relocated_restore_code) return -ENOMEM; memcpy((void *)relocated_restore_code, &core_restore_code, PAGE_SIZE); /* Make the page containing the relocated code executable */ pgd = (pgd_t *)__va(read_cr3()) + pgd_index(relocated_restore_code); pud = pud_offset(pgd, relocated_restore_code); if (pud_large(*pud)) { set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX)); } else { pmd_t *pmd = pmd_offset(pud, relocated_restore_code); if (pmd_large(*pmd)) { set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX)); } else { pte_t *pte = pte_offset_kernel(pmd, relocated_restore_code); set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX)); } } __flush_tlb_all(); return 0; }
static void * __init init_pmd(unsigned long vaddr, unsigned long n_pages) { pgd_t *pgd = pgd_offset_k(vaddr); pmd_t *pmd = pmd_offset(pgd, vaddr); pte_t *pte; unsigned long i; n_pages = ALIGN(n_pages, PTRS_PER_PTE); pr_debug("%s: vaddr: 0x%08lx, n_pages: %ld\n", __func__, vaddr, n_pages); pte = memblock_alloc_low(n_pages * sizeof(pte_t), PAGE_SIZE); for (i = 0; i < n_pages; ++i) pte_clear(NULL, 0, pte + i); for (i = 0; i < n_pages; i += PTRS_PER_PTE, ++pmd) { pte_t *cur_pte = pte + i; BUG_ON(!pmd_none(*pmd)); set_pmd(pmd, __pmd(((unsigned long)cur_pte) & PAGE_MASK)); BUG_ON(cur_pte != pte_offset_kernel(pmd, 0)); pr_debug("%s: pmd: 0x%p, pte: 0x%p\n", __func__, pmd, cur_pte); } return pte; }
pmd_t pfn_pmd(unsigned long pfn, pgprot_t pgprot) { unsigned long pmdv; pmdv = (pfn << PAGE_SHIFT) & PTE_RPN_MASK; return pmd_set_protbits(__pmd(pmdv), pgprot); }
static int set_up_temporary_text_mapping(pgd_t *pgd) { pmd_t *pmd; pud_t *pud; /* * The new mapping only has to cover the page containing the image * kernel's entry point (jump_address_phys), because the switch over to * it is carried out by relocated code running from a page allocated * specifically for this purpose and covered by the identity mapping, so * the temporary kernel text mapping is only needed for the final jump. * Moreover, in that mapping the virtual address of the image kernel's * entry point must be the same as its virtual address in the image * kernel (restore_jump_address), so the image kernel's * restore_registers() code doesn't find itself in a different area of * the virtual address space after switching over to the original page * tables used by the image kernel. */ pud = (pud_t *)get_safe_page(GFP_ATOMIC); if (!pud) return -ENOMEM; pmd = (pmd_t *)get_safe_page(GFP_ATOMIC); if (!pmd) return -ENOMEM; set_pmd(pmd + pmd_index(restore_jump_address), __pmd((jump_address_phys & PMD_MASK) | __PAGE_KERNEL_LARGE_EXEC)); set_pud(pud + pud_index(restore_jump_address), __pud(__pa(pmd) | _KERNPG_TABLE)); set_pgd(pgd + pgd_index(restore_jump_address), __pgd(__pa(pud) | _KERNPG_TABLE)); return 0; }
static int copy_pmd(pud_t *dst_pudp, pud_t *src_pudp, unsigned long start, unsigned long end) { pmd_t *src_pmdp; pmd_t *dst_pmdp; unsigned long next; unsigned long addr = start; if (pud_none(READ_ONCE(*dst_pudp))) { dst_pmdp = (pmd_t *)get_safe_page(GFP_ATOMIC); if (!dst_pmdp) return -ENOMEM; pud_populate(&init_mm, dst_pudp, dst_pmdp); } dst_pmdp = pmd_offset(dst_pudp, start); src_pmdp = pmd_offset(src_pudp, start); do { pmd_t pmd = READ_ONCE(*src_pmdp); next = pmd_addr_end(addr, end); if (pmd_none(pmd)) continue; if (pmd_table(pmd)) { if (copy_pte(dst_pmdp, src_pmdp, addr, next)) return -ENOMEM; } else { set_pmd(dst_pmdp, __pmd(pmd_val(pmd) & ~PMD_SECT_RDONLY)); } } while (dst_pmdp++, src_pmdp++, addr = next, addr != end); return 0; }
static int res_phys_pud_init(pud_t *pud, unsigned long address, unsigned long end) { long i, j; i = pud_index(address); pud = pud + i; for (; i < PTRS_PER_PUD; pud++, i++) { unsigned long paddr; pmd_t *pmd; paddr = address + i*PUD_SIZE; if (paddr >= end) break; pmd = (pmd_t *)get_safe_page(GFP_ATOMIC); if (!pmd) return -ENOMEM; set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE)); for (j = 0; j < PTRS_PER_PMD; pmd++, j++, paddr += PMD_SIZE) { unsigned long pe; if (paddr >= end) break; pe = __PAGE_KERNEL_LARGE_EXEC | paddr; pe &= __supported_pte_mask; set_pmd(pmd, __pmd(pe)); } } return 0; }
static void shmedia_mapioaddr(unsigned long pa, unsigned long va) { pgd_t *pgdp; pmd_t *pmdp; pte_t *ptep; unsigned long flags = 1; /* 1 = CB0-1 device */ DEBUG_IOREMAP(("shmedia_mapiopage pa %08x va %08x\n", pa, va)); pgdp = pgd_offset_k(va); if (pgd_none(*pgdp)) { pmdp = alloc_bootmem_low_pages(PTRS_PER_PMD * sizeof(pmd_t)); if (pmdp == NULL) panic("No memory for pmd\n"); memset(pmdp, 0, PTRS_PER_PGD * sizeof(pmd_t)); set_pgd(pgdp, __pgd((unsigned long)pmdp | _KERNPG_TABLE)); } pmdp = pmd_offset(pgdp, va); if (pmd_none(*pmdp)) { ptep = alloc_bootmem_low_pages(PTRS_PER_PTE * sizeof(pte_t)); if (ptep == NULL) panic("No memory for pte\n"); clear_page((void *)ptep); set_pmd(pmdp, __pmd((unsigned long)ptep + _PAGE_TABLE)); } ptep = pte_offset(pmdp, va); set_pte(ptep, mk_pte_phys(pa, __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | _PAGE_DIRTY | _PAGE_ACCESSED |_PAGE_SHARED | flags))); }
static int remap_area_sections(unsigned long virt, unsigned long pfn, size_t size, const struct mem_type *type) { unsigned long addr = virt, end = virt + size; pgd_t *pgd; /* * Remove and free any PTE-based mapping, and * sync the current kernel mapping. */ unmap_area_sections(virt, size); pgd = pgd_offset_k(addr); do { pmd_t *pmd = pmd_offset((pud_t *)pgd, addr); set_pmd(pmd, __pmd(__pfn_to_phys(pfn) | type->prot_sect)); pfn += SZ_4M >> PAGE_SHIFT; flush_pmd_entry(pmd); addr += PGDIR_SIZE; pgd++; } while (addr < end); return 0; }
static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, unsigned long prot) { pmd_t *pmd; unsigned long next; if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) { pmd = pmd_alloc_one(&init_mm, addr); if (!pmd) { pr_warning("Failed to allocate identity pmd.\n"); return; } /* * Copy the original PMD to ensure that the PMD entries for * the kernel image are preserved. */ if (!pud_none(*pud)) memcpy(pmd, pmd_offset(pud, 0), PTRS_PER_PMD * sizeof(pmd_t)); pud_populate(&init_mm, pud, pmd); pmd += pmd_index(addr); } else pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); *pmd = __pmd((addr & PMD_MASK) | prot); flush_pmd_entry(pmd); } while (pmd++, addr = next, addr != end); }
static int set_up_temporary_text_mapping(pgd_t *pgd_base) { pgd_t *pgd; pmd_t *pmd; pte_t *pte; pgd = pgd_base + pgd_index(restore_jump_address); pmd = resume_one_md_table_init(pgd); if (!pmd) return -ENOMEM; if (boot_cpu_has(X86_FEATURE_PSE)) { set_pmd(pmd + pmd_index(restore_jump_address), __pmd((jump_address_phys & PMD_MASK) | pgprot_val(PAGE_KERNEL_LARGE_EXEC))); } else { pte = resume_one_page_table_init(pmd); if (!pte) return -ENOMEM; set_pte(pte + pte_index(restore_jump_address), __pte((jump_address_phys & PAGE_MASK) | pgprot_val(PAGE_KERNEL_EXEC))); } return 0; }
void __init kasan_early_init(void) { int i; pteval_t pte_val = __pa_nodebug(kasan_early_shadow_page) | __PAGE_KERNEL | _PAGE_ENC; pmdval_t pmd_val = __pa_nodebug(kasan_early_shadow_pte) | _KERNPG_TABLE; pudval_t pud_val = __pa_nodebug(kasan_early_shadow_pmd) | _KERNPG_TABLE; p4dval_t p4d_val = __pa_nodebug(kasan_early_shadow_pud) | _KERNPG_TABLE; /* Mask out unsupported __PAGE_KERNEL bits: */ pte_val &= __default_kernel_pte_mask; pmd_val &= __default_kernel_pte_mask; pud_val &= __default_kernel_pte_mask; p4d_val &= __default_kernel_pte_mask; for (i = 0; i < PTRS_PER_PTE; i++) kasan_early_shadow_pte[i] = __pte(pte_val); for (i = 0; i < PTRS_PER_PMD; i++) kasan_early_shadow_pmd[i] = __pmd(pmd_val); for (i = 0; i < PTRS_PER_PUD; i++) kasan_early_shadow_pud[i] = __pud(pud_val); for (i = 0; pgtable_l5_enabled() && i < PTRS_PER_P4D; i++) kasan_early_shadow_p4d[i] = __p4d(p4d_val); kasan_map_early_shadow(early_top_pgt); kasan_map_early_shadow(init_top_pgt); }
static int remap_area_supersections(unsigned long virt, unsigned long pfn, unsigned long size, unsigned long flags) { unsigned long prot, addr = virt, end = virt + size; pgd_t *pgd; /* * Remove and free any PTE-based mapping, and * sync the current kernel mapping. */ unmap_area_sections(virt, size); prot = PMD_TYPE_SECT | PMD_SECT_SUPER | PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO) | (flags & (L_PTE_CACHEABLE | L_PTE_BUFFERABLE)); /* * ARMv6 and above need XN set to prevent speculative prefetches * hitting IO. */ if (cpu_architecture() >= CPU_ARCH_ARMv6) prot |= PMD_SECT_XN; pgd = pgd_offset_k(virt); do { unsigned long super_pmd_val, i; super_pmd_val = __pfn_to_phys(pfn) | prot; super_pmd_val |= ((pfn >> (32 - PAGE_SHIFT)) & 0xf) << 20; for (i = 0; i < 8; i++) { pmd_t *pmd = pmd_offset(pgd, addr); pmd[0] = __pmd(super_pmd_val); pmd[1] = __pmd(super_pmd_val); flush_pmd_entry(pmd); addr += PGDIR_SIZE; pgd++; } pfn += SUPERSECTION_SIZE >> PAGE_SHIFT; } while (addr < end); return 0; }
pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) { unsigned long pmdv; pmdv = pmd_val(pmd); pmdv &= _HPAGE_CHG_MASK; return pmd_set_protbits(__pmd(pmdv), newprot); }
static int set_up_temporary_text_mapping(pgd_t *pgd) { pmd_t *pmd; pud_t *pud; p4d_t *p4d = NULL; pgprot_t pgtable_prot = __pgprot(_KERNPG_TABLE); pgprot_t pmd_text_prot = __pgprot(__PAGE_KERNEL_LARGE_EXEC); /* Filter out unsupported __PAGE_KERNEL* bits: */ pgprot_val(pmd_text_prot) &= __default_kernel_pte_mask; pgprot_val(pgtable_prot) &= __default_kernel_pte_mask; /* * The new mapping only has to cover the page containing the image * kernel's entry point (jump_address_phys), because the switch over to * it is carried out by relocated code running from a page allocated * specifically for this purpose and covered by the identity mapping, so * the temporary kernel text mapping is only needed for the final jump. * Moreover, in that mapping the virtual address of the image kernel's * entry point must be the same as its virtual address in the image * kernel (restore_jump_address), so the image kernel's * restore_registers() code doesn't find itself in a different area of * the virtual address space after switching over to the original page * tables used by the image kernel. */ if (pgtable_l5_enabled()) { p4d = (p4d_t *)get_safe_page(GFP_ATOMIC); if (!p4d) return -ENOMEM; } pud = (pud_t *)get_safe_page(GFP_ATOMIC); if (!pud) return -ENOMEM; pmd = (pmd_t *)get_safe_page(GFP_ATOMIC); if (!pmd) return -ENOMEM; set_pmd(pmd + pmd_index(restore_jump_address), __pmd((jump_address_phys & PMD_MASK) | pgprot_val(pmd_text_prot))); set_pud(pud + pud_index(restore_jump_address), __pud(__pa(pmd) | pgprot_val(pgtable_prot))); if (p4d) { p4d_t new_p4d = __p4d(__pa(pud) | pgprot_val(pgtable_prot)); pgd_t new_pgd = __pgd(__pa(p4d) | pgprot_val(pgtable_prot)); set_p4d(p4d + p4d_index(restore_jump_address), new_p4d); set_pgd(pgd + pgd_index(restore_jump_address), new_pgd); } else { /* No p4d for 4-level paging: point the pgd to the pud page table */ pgd_t new_pgd = __pgd(__pa(pud) | pgprot_val(pgtable_prot)); set_pgd(pgd + pgd_index(restore_jump_address), new_pgd); } return 0; }
static pte_t *fill_pte(pmd_t *pmd, unsigned long vaddr) { if (pmd_none(*pmd)) { pte_t *pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); set_pmd(pmd, __pmd(_PAGE_TABLE | __pa(pte))); if (pte != pte_offset_kernel(pmd, 0)) printk(KERN_ERR "EFI PAGETABLE BUG #02!\n"); } return pte_offset_kernel(pmd, vaddr); }
/* * Allocate new pmd(s). It is checked whether the old pmd is still in place. * If not, nothing is changed. This is okay as the only reason for allocating * a new pmd is to replace p2m_missing_pte or p2m_identity_pte by a individual * pmd. In case of PAE/x86-32 there are multiple pmds to allocate! */ static pte_t *alloc_p2m_pmd(unsigned long addr, pte_t *pte_pg) { pte_t *ptechk; pte_t *pte_newpg[PMDS_PER_MID_PAGE]; pmd_t *pmdp; unsigned int level; unsigned long flags; unsigned long vaddr; int i; /* Do all allocations first to bail out in error case. */ for (i = 0; i < PMDS_PER_MID_PAGE; i++) { pte_newpg[i] = alloc_p2m_page(); if (!pte_newpg[i]) { for (i--; i >= 0; i--) free_p2m_page(pte_newpg[i]); return NULL; } } vaddr = addr & ~(PMD_SIZE * PMDS_PER_MID_PAGE - 1); for (i = 0; i < PMDS_PER_MID_PAGE; i++) { copy_page(pte_newpg[i], pte_pg); paravirt_alloc_pte(&init_mm, __pa(pte_newpg[i]) >> PAGE_SHIFT); pmdp = lookup_pmd_address(vaddr); BUG_ON(!pmdp); spin_lock_irqsave(&p2m_update_lock, flags); ptechk = lookup_address(vaddr, &level); if (ptechk == pte_pg) { HYPERVISOR_shared_info->arch.p2m_generation++; wmb(); /* Tools are synchronizing via p2m_generation. */ set_pmd(pmdp, __pmd(__pa(pte_newpg[i]) | _KERNPG_TABLE)); wmb(); /* Tools are synchronizing via p2m_generation. */ HYPERVISOR_shared_info->arch.p2m_generation++; pte_newpg[i] = NULL; } spin_unlock_irqrestore(&p2m_update_lock, flags); if (pte_newpg[i]) { paravirt_release_pte(__pa(pte_newpg[i]) >> PAGE_SHIFT); free_p2m_page(pte_newpg[i]); } vaddr += PMD_SIZE; } return lookup_address(addr, &level); }
/* * Map all physical memory into kernel's address space. * * This is explicitly coded for two-level page tables, so if you need * something else then this needs to change. */ static void __init map_ram(void) { unsigned long v, p, e; pgprot_t prot; pgd_t *pge; pud_t *pue; pmd_t *pme; pte_t *pte; /* These mark extents of read-only kernel pages... * ...from vmlinux.lds.S */ struct memblock_region *region; v = PAGE_OFFSET; for_each_memblock(memory, region) { p = (u32) region->base & PAGE_MASK; e = p + (u32) region->size; v = (u32) __va(p); pge = pgd_offset_k(v); while (p < e) { int j; pue = pud_offset(pge, v); pme = pmd_offset(pue, v); if ((u32) pue != (u32) pge || (u32) pme != (u32) pge) { panic("%s: OR1K kernel hardcoded for " "two-level page tables", __func__); } /* Alloc one page for holding PTE's... */ pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pmd(pme, __pmd(_KERNPG_TABLE + __pa(pte))); /* Fill the newly allocated page with PTE'S */ for (j = 0; p < e && j < PTRS_PER_PGD; v += PAGE_SIZE, p += PAGE_SIZE, j++, pte++) { if (v >= (u32) _e_kernel_ro || v < (u32) _s_kernel_ro) prot = PAGE_KERNEL; else prot = PAGE_KERNEL_RO; set_pte(pte, mk_pte_phys(p, prot)); } pge++; } printk(KERN_INFO "%s: Memory: 0x%x-0x%x\n", __func__, region->base, region->base + region->size); }
void split_pud(pud_t *old_pud, pmd_t *pmd) { unsigned long addr = pud_pfn(*old_pud) << PAGE_SHIFT; pgprot_t prot = __pgprot(pud_val(*old_pud) ^ addr); int i = 0; do { set_pmd(pmd, __pmd(addr | prot)); addr += PMD_SIZE; } while (pmd++, i++, i < PTRS_PER_PMD); }
/* * On Assabet, we must probe for the Neponset board _before_ * paging_init() has occurred to actually determine the amount * of RAM available. To do so, we map the appropriate IO section * in the page table here in order to access GPIO registers. */ static void __init map_sa1100_gpio_regs( void ) { unsigned long phys = __PREG(GPLR) & PMD_MASK; unsigned long virt = io_p2v(phys); int prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO); pmd_t *pmd; pmd = pmd_offset(pgd_offset_k(virt), virt); *pmd = __pmd(phys | prot); flush_pmd_entry(pmd); }
#include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <asm/cputype.h> #include <asm/idmap.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> #include <asm/sections.h> #include <asm/system_info.h> pgd_t *idmap_pgd; #ifdef CONFIG_ARM_LPAE static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, unsigned long prot) { pmd_t *pmd; unsigned long next; if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) { pmd = pmd_alloc_one(&init_mm, addr); if (!pmd) { pr_warning("Failed to allocate identity pmd.\n"); return; } pud_populate(&init_mm, pud, pmd); pmd += pmd_index(addr); } else pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); *pmd = __pmd((addr & PMD_MASK) | prot); flush_pmd_entry(pmd); } while (pmd++, addr = next, addr != end); } #else /* !CONFIG_ARM_LPAE */ static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, unsigned long prot) { #ifdef CONFIG_TIMA_RKP_L1_TABLES unsigned long cmd_id = 0x3f809221; #endif pmd_t *pmd = pmd_offset(pud, addr); addr = (addr & PMD_MASK) | prot; #ifdef CONFIG_TIMA_RKP_L1_TABLES if (tima_is_pg_protected((unsigned long) pmd) != 0) { #ifndef CONFIG_TIMA_RKP_COHERENT_TT clean_dcache_area(pmd, 8); tima_cache_flush((unsigned long)pmd); #endif tima_send_cmd5((unsigned long)__pa(pmd), (unsigned long)__pmd(addr), (unsigned long)__pmd(addr+SECTION_SIZE), 0, 0, cmd_id); #ifndef CONFIG_TIMA_RKP_COHERENT_TT tima_cache_inval((unsigned long)pmd); #endif tima_tlb_inval_is(0); } else { pmd[0] = __pmd(addr); addr += SECTION_SIZE; pmd[1] = __pmd(addr); } #else /* CONFIG_TIMA_RKP_L1_TABLES */ pmd[0] = __pmd(addr); addr += SECTION_SIZE; pmd[1] = __pmd(addr); #endif /* CONFIG_TIMA_RKP_L1_TABLES */ flush_pmd_entry(pmd); }
static void alloc_init_pmd(struct mm_struct *mm, pud_t *pud, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, void *(*alloc)(unsigned long size)) { pmd_t *pmd; unsigned long next; /* * Check for initial section mappings in the pgd/pud and remove them. */ if (pud_none(*pud) || pud_sect(*pud)) { pmd = alloc(PTRS_PER_PMD * sizeof(pmd_t)); if (pud_sect(*pud)) { /* * need to have the 1G of mappings continue to be * present */ split_pud(pud, pmd); } pud_populate(mm, pud, pmd); flush_tlb_all(); } BUG_ON(pud_bad(*pud)); pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); /* try section mapping first */ if (((addr | next | phys) & ~SECTION_MASK) == 0) { pmd_t old_pmd =*pmd; set_pmd(pmd, __pmd(phys | pgprot_val(mk_sect_prot(prot)))); /* * Check for previous table entries created during * boot (__create_page_tables) and flush them. */ if (!pmd_none(old_pmd)) { flush_tlb_all(); if (pmd_table(old_pmd)) { phys_addr_t table = __pa(pte_offset_map(&old_pmd, 0)); if (!WARN_ON_ONCE(slab_is_available())) memblock_free(table, PAGE_SIZE); } } } else { alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), prot, alloc); } phys += next - addr; } while (pmd++, addr = next, addr != end); }
/* * 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_none(*pmd)) { pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); if (page_table != pte_offset_kernel(pmd, 0)) BUG(); return page_table; } return pte_offset_kernel(pmd, 0); }
void setup_mm_for_kdump(char mode) { unsigned long base_pmdval; pgd_t *pgd; int i; pgd = init_mm.pgd; cpu_switch_mm(pgd, &init_mm); base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++, pgd++) { unsigned long pmdval = (i << PGDIR_SHIFT) | base_pmdval; pmd_t *pmd; pmd = pmd_offset(pgd, i << PGDIR_SHIFT); pmd[0] = __pmd(pmdval); pmd[1] = __pmd(pmdval + (1 << (PGDIR_SHIFT - 1))); flush_pmd_entry(pmd); } }
static int remap_area_supersections(unsigned long virt, unsigned long pfn, size_t size, const struct mem_type *type) { unsigned long addr = virt, end = virt + size; pgd_t *pgd; pud_t *pud; pmd_t *pmd; /* * Remove and free any PTE-based mapping, and * sync the current kernel mapping. */ unmap_area_sections(virt, size); pgd = pgd_offset_k(virt); pud = pud_offset(pgd, addr); pmd = pmd_offset(pud, addr); do { unsigned long super_pmd_val, i; super_pmd_val = __pfn_to_phys(pfn) | type->prot_sect | PMD_SECT_SUPER; super_pmd_val |= ((pfn >> (32 - PAGE_SHIFT)) & 0xf) << 20; for (i = 0; i < 8; i++) { pmd[0] = __pmd(super_pmd_val); pmd[1] = __pmd(super_pmd_val); flush_pmd_entry(pmd); addr += PMD_SIZE; pmd += 2; } pfn += SUPERSECTION_SIZE >> PAGE_SHIFT; } while (addr < end); return 0; }
static void ident_pmd_init(struct x86_mapping_info *info, pmd_t *pmd_page, unsigned long addr, unsigned long end) { addr &= PMD_MASK; for (; addr < end; addr += PMD_SIZE) { pmd_t *pmd = pmd_page + pmd_index(addr); if (pmd_present(*pmd)) continue; set_pmd(pmd, __pmd((addr - info->offset) | info->pmd_flag)); } }
/* * We use this to invalidate a pmdp entry before switching from a * hugepte to regular pmd entry. */ pmd_t pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp) { unsigned long old_pmd; old_pmd = pmd_hugepage_update(vma->vm_mm, address, pmdp, _PAGE_PRESENT, _PAGE_INVALID); flush_pmd_tlb_range(vma, address, address + HPAGE_PMD_SIZE); /* * This ensures that generic code that rely on IRQ disabling * to prevent a parallel THP split work as expected. */ serialize_against_pte_lookup(vma->vm_mm); return __pmd(old_pmd); }
static void __meminit phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end) { int i = pmd_index(address); for (; i < PTRS_PER_PMD; i++, address += PMD_SIZE) { unsigned long entry; pmd_t *pmd = pmd_page + pmd_index(address); if (address >= end) { if (!after_bootmem) for (; i < PTRS_PER_PMD; i++, pmd++) set_pmd(pmd, __pmd(0)); break; } if (pmd_val(*pmd)) continue; entry = _PAGE_NX|_PAGE_PSE|_KERNPG_TABLE|_PAGE_GLOBAL|address; entry &= __supported_pte_mask; set_pmd(pmd, __pmd(entry)); } }
static void release_pmd(pmd_t *spmd) { /* If the entry's not present, there's nothing to release. */ if (pmd_flags(*spmd) & _PAGE_PRESENT) { unsigned int i; pte_t *ptepage = __va(pmd_pfn(*spmd) << PAGE_SHIFT); /* For each entry in the page, we might need to release it. */ for (i = 0; i < PTRS_PER_PTE; i++) release_pte(ptepage[i]); /* Now we can free the page of PTEs */ free_page((long)ptepage); /* And zero out the PMD entry so we never release it twice. */ set_pmd(spmd, __pmd(0)); } }
static pte_t *resume_one_page_table_init(pmd_t *pmd) { if (pmd_none(*pmd)) { pte_t *page_table = (pte_t *)get_safe_page(GFP_ATOMIC); if (!page_table) return NULL; set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_KERNEL_TABLE)); BUG_ON(page_table != pte_offset_kernel(pmd, 0)); return page_table; } return pte_offset_kernel(pmd, 0); }
static __init void set_pte_phys(unsigned long vaddr, unsigned long phys, pgprot_t prot) { pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte, new_pte; Dprintk("set_pte_phys %lx to %lx\n", vaddr, phys); pgd = pgd_offset_k(vaddr); if (pgd_none(*pgd)) { printk("PGD FIXMAP MISSING, it should be setup in head.S!\n"); return; } pud = pud_offset(pgd, vaddr); if (pud_none(*pud)) { pmd = (pmd_t *) spp_getpage(); set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE | _PAGE_USER)); if (pmd != pmd_offset(pud, 0)) { printk("PAGETABLE BUG #01! %p <-> %p\n", pmd, pmd_offset(pud,0)); return; } } pmd = pmd_offset(pud, vaddr); if (pmd_none(*pmd)) { pte = (pte_t *) spp_getpage(); set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE | _PAGE_USER)); if (pte != pte_offset_kernel(pmd, 0)) { printk("PAGETABLE BUG #02!\n"); return; } } new_pte = pfn_pte(phys >> PAGE_SHIFT, prot); pte = pte_offset_kernel(pmd, vaddr); if (!pte_none(*pte) && pte_val(*pte) != (pte_val(new_pte) & __supported_pte_mask)) pte_ERROR(*pte); set_pte(pte, new_pte); /* * It's enough to flush this one mapping. * (PGE mappings get flushed as well) */ __flush_tlb_one(vaddr); }