162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Page table handling routines for radix page table. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "radix-mmu: " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/sched/mm.h> 1362306a36Sopenharmony_ci#include <linux/memblock.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_fdt.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/hugetlb.h> 1862306a36Sopenharmony_ci#include <linux/string_helpers.h> 1962306a36Sopenharmony_ci#include <linux/memory.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/pgalloc.h> 2262306a36Sopenharmony_ci#include <asm/mmu_context.h> 2362306a36Sopenharmony_ci#include <asm/dma.h> 2462306a36Sopenharmony_ci#include <asm/machdep.h> 2562306a36Sopenharmony_ci#include <asm/mmu.h> 2662306a36Sopenharmony_ci#include <asm/firmware.h> 2762306a36Sopenharmony_ci#include <asm/powernv.h> 2862306a36Sopenharmony_ci#include <asm/sections.h> 2962306a36Sopenharmony_ci#include <asm/smp.h> 3062306a36Sopenharmony_ci#include <asm/trace.h> 3162306a36Sopenharmony_ci#include <asm/uaccess.h> 3262306a36Sopenharmony_ci#include <asm/ultravisor.h> 3362306a36Sopenharmony_ci#include <asm/set_memory.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <trace/events/thp.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <mm/mmu_decl.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciunsigned int mmu_base_pid; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic __ref void *early_alloc_pgtable(unsigned long size, int nid, 4262306a36Sopenharmony_ci unsigned long region_start, unsigned long region_end) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci phys_addr_t min_addr = MEMBLOCK_LOW_LIMIT; 4562306a36Sopenharmony_ci phys_addr_t max_addr = MEMBLOCK_ALLOC_ANYWHERE; 4662306a36Sopenharmony_ci void *ptr; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (region_start) 4962306a36Sopenharmony_ci min_addr = region_start; 5062306a36Sopenharmony_ci if (region_end) 5162306a36Sopenharmony_ci max_addr = region_end; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ptr = memblock_alloc_try_nid(size, size, min_addr, max_addr, nid); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (!ptr) 5662306a36Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%pa max_addr=%pa\n", 5762306a36Sopenharmony_ci __func__, size, size, nid, &min_addr, &max_addr); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return ptr; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * When allocating pud or pmd pointers, we allocate a complete page 6462306a36Sopenharmony_ci * of PAGE_SIZE rather than PUD_TABLE_SIZE or PMD_TABLE_SIZE. This 6562306a36Sopenharmony_ci * is to ensure that the page obtained from the memblock allocator 6662306a36Sopenharmony_ci * can be completely used as page table page and can be freed 6762306a36Sopenharmony_ci * correctly when the page table entries are removed. 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic int early_map_kernel_page(unsigned long ea, unsigned long pa, 7062306a36Sopenharmony_ci pgprot_t flags, 7162306a36Sopenharmony_ci unsigned int map_page_size, 7262306a36Sopenharmony_ci int nid, 7362306a36Sopenharmony_ci unsigned long region_start, unsigned long region_end) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned long pfn = pa >> PAGE_SHIFT; 7662306a36Sopenharmony_ci pgd_t *pgdp; 7762306a36Sopenharmony_ci p4d_t *p4dp; 7862306a36Sopenharmony_ci pud_t *pudp; 7962306a36Sopenharmony_ci pmd_t *pmdp; 8062306a36Sopenharmony_ci pte_t *ptep; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci pgdp = pgd_offset_k(ea); 8362306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, ea); 8462306a36Sopenharmony_ci if (p4d_none(*p4dp)) { 8562306a36Sopenharmony_ci pudp = early_alloc_pgtable(PAGE_SIZE, nid, 8662306a36Sopenharmony_ci region_start, region_end); 8762306a36Sopenharmony_ci p4d_populate(&init_mm, p4dp, pudp); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci pudp = pud_offset(p4dp, ea); 9062306a36Sopenharmony_ci if (map_page_size == PUD_SIZE) { 9162306a36Sopenharmony_ci ptep = (pte_t *)pudp; 9262306a36Sopenharmony_ci goto set_the_pte; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci if (pud_none(*pudp)) { 9562306a36Sopenharmony_ci pmdp = early_alloc_pgtable(PAGE_SIZE, nid, region_start, 9662306a36Sopenharmony_ci region_end); 9762306a36Sopenharmony_ci pud_populate(&init_mm, pudp, pmdp); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci pmdp = pmd_offset(pudp, ea); 10062306a36Sopenharmony_ci if (map_page_size == PMD_SIZE) { 10162306a36Sopenharmony_ci ptep = pmdp_ptep(pmdp); 10262306a36Sopenharmony_ci goto set_the_pte; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci if (!pmd_present(*pmdp)) { 10562306a36Sopenharmony_ci ptep = early_alloc_pgtable(PAGE_SIZE, nid, 10662306a36Sopenharmony_ci region_start, region_end); 10762306a36Sopenharmony_ci pmd_populate_kernel(&init_mm, pmdp, ptep); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci ptep = pte_offset_kernel(pmdp, ea); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciset_the_pte: 11262306a36Sopenharmony_ci set_pte_at(&init_mm, ea, ptep, pfn_pte(pfn, flags)); 11362306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * nid, region_start, and region_end are hints to try to place the page 11962306a36Sopenharmony_ci * table memory in the same node or region. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic int __map_kernel_page(unsigned long ea, unsigned long pa, 12262306a36Sopenharmony_ci pgprot_t flags, 12362306a36Sopenharmony_ci unsigned int map_page_size, 12462306a36Sopenharmony_ci int nid, 12562306a36Sopenharmony_ci unsigned long region_start, unsigned long region_end) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci unsigned long pfn = pa >> PAGE_SHIFT; 12862306a36Sopenharmony_ci pgd_t *pgdp; 12962306a36Sopenharmony_ci p4d_t *p4dp; 13062306a36Sopenharmony_ci pud_t *pudp; 13162306a36Sopenharmony_ci pmd_t *pmdp; 13262306a36Sopenharmony_ci pte_t *ptep; 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * Make sure task size is correct as per the max adddr 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci BUILD_BUG_ON(TASK_SIZE_USER64 > RADIX_PGTABLE_RANGE); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#ifdef CONFIG_PPC_64K_PAGES 13962306a36Sopenharmony_ci BUILD_BUG_ON(RADIX_KERN_MAP_SIZE != (1UL << MAX_EA_BITS_PER_CONTEXT)); 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (unlikely(!slab_is_available())) 14362306a36Sopenharmony_ci return early_map_kernel_page(ea, pa, flags, map_page_size, 14462306a36Sopenharmony_ci nid, region_start, region_end); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * Should make page table allocation functions be able to take a 14862306a36Sopenharmony_ci * node, so we can place kernel page tables on the right nodes after 14962306a36Sopenharmony_ci * boot. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci pgdp = pgd_offset_k(ea); 15262306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, ea); 15362306a36Sopenharmony_ci pudp = pud_alloc(&init_mm, p4dp, ea); 15462306a36Sopenharmony_ci if (!pudp) 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci if (map_page_size == PUD_SIZE) { 15762306a36Sopenharmony_ci ptep = (pte_t *)pudp; 15862306a36Sopenharmony_ci goto set_the_pte; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci pmdp = pmd_alloc(&init_mm, pudp, ea); 16162306a36Sopenharmony_ci if (!pmdp) 16262306a36Sopenharmony_ci return -ENOMEM; 16362306a36Sopenharmony_ci if (map_page_size == PMD_SIZE) { 16462306a36Sopenharmony_ci ptep = pmdp_ptep(pmdp); 16562306a36Sopenharmony_ci goto set_the_pte; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci ptep = pte_alloc_kernel(pmdp, ea); 16862306a36Sopenharmony_ci if (!ptep) 16962306a36Sopenharmony_ci return -ENOMEM; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciset_the_pte: 17262306a36Sopenharmony_ci set_pte_at(&init_mm, ea, ptep, pfn_pte(pfn, flags)); 17362306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint radix__map_kernel_page(unsigned long ea, unsigned long pa, 17862306a36Sopenharmony_ci pgprot_t flags, 17962306a36Sopenharmony_ci unsigned int map_page_size) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci return __map_kernel_page(ea, pa, flags, map_page_size, -1, 0, 0); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 18562306a36Sopenharmony_cistatic void radix__change_memory_range(unsigned long start, unsigned long end, 18662306a36Sopenharmony_ci unsigned long clear) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci unsigned long idx; 18962306a36Sopenharmony_ci pgd_t *pgdp; 19062306a36Sopenharmony_ci p4d_t *p4dp; 19162306a36Sopenharmony_ci pud_t *pudp; 19262306a36Sopenharmony_ci pmd_t *pmdp; 19362306a36Sopenharmony_ci pte_t *ptep; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci start = ALIGN_DOWN(start, PAGE_SIZE); 19662306a36Sopenharmony_ci end = PAGE_ALIGN(end); // aligns up 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci pr_debug("Changing flags on range %lx-%lx removing 0x%lx\n", 19962306a36Sopenharmony_ci start, end, clear); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci for (idx = start; idx < end; idx += PAGE_SIZE) { 20262306a36Sopenharmony_ci pgdp = pgd_offset_k(idx); 20362306a36Sopenharmony_ci p4dp = p4d_offset(pgdp, idx); 20462306a36Sopenharmony_ci pudp = pud_alloc(&init_mm, p4dp, idx); 20562306a36Sopenharmony_ci if (!pudp) 20662306a36Sopenharmony_ci continue; 20762306a36Sopenharmony_ci if (pud_is_leaf(*pudp)) { 20862306a36Sopenharmony_ci ptep = (pte_t *)pudp; 20962306a36Sopenharmony_ci goto update_the_pte; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci pmdp = pmd_alloc(&init_mm, pudp, idx); 21262306a36Sopenharmony_ci if (!pmdp) 21362306a36Sopenharmony_ci continue; 21462306a36Sopenharmony_ci if (pmd_is_leaf(*pmdp)) { 21562306a36Sopenharmony_ci ptep = pmdp_ptep(pmdp); 21662306a36Sopenharmony_ci goto update_the_pte; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci ptep = pte_alloc_kernel(pmdp, idx); 21962306a36Sopenharmony_ci if (!ptep) 22062306a36Sopenharmony_ci continue; 22162306a36Sopenharmony_ciupdate_the_pte: 22262306a36Sopenharmony_ci radix__pte_update(&init_mm, idx, ptep, clear, 0, 0); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci radix__flush_tlb_kernel_range(start, end); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_civoid radix__mark_rodata_ro(void) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci unsigned long start, end; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci start = (unsigned long)_stext; 23362306a36Sopenharmony_ci end = (unsigned long)__end_rodata; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci radix__change_memory_range(start, end, _PAGE_WRITE); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (start = PAGE_OFFSET; start < (unsigned long)_stext; start += PAGE_SIZE) { 23862306a36Sopenharmony_ci end = start + PAGE_SIZE; 23962306a36Sopenharmony_ci if (overlaps_interrupt_vector_text(start, end)) 24062306a36Sopenharmony_ci radix__change_memory_range(start, end, _PAGE_WRITE); 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_civoid radix__mark_initmem_nx(void) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci unsigned long start = (unsigned long)__init_begin; 24962306a36Sopenharmony_ci unsigned long end = (unsigned long)__init_end; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci radix__change_memory_range(start, end, _PAGE_EXEC); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci#endif /* CONFIG_STRICT_KERNEL_RWX */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic inline void __meminit 25662306a36Sopenharmony_ciprint_mapping(unsigned long start, unsigned long end, unsigned long size, bool exec) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci char buf[10]; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (end <= start) 26162306a36Sopenharmony_ci return; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci string_get_size(size, 1, STRING_UNITS_2, buf, sizeof(buf)); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci pr_info("Mapped 0x%016lx-0x%016lx with %s pages%s\n", start, end, buf, 26662306a36Sopenharmony_ci exec ? " (exec)" : ""); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic unsigned long next_boundary(unsigned long addr, unsigned long end) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 27262306a36Sopenharmony_ci unsigned long stext_phys; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci stext_phys = __pa_symbol(_stext); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci // Relocatable kernel running at non-zero real address 27762306a36Sopenharmony_ci if (stext_phys != 0) { 27862306a36Sopenharmony_ci // The end of interrupts code at zero is a rodata boundary 27962306a36Sopenharmony_ci unsigned long end_intr = __pa_symbol(__end_interrupts) - stext_phys; 28062306a36Sopenharmony_ci if (addr < end_intr) 28162306a36Sopenharmony_ci return end_intr; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci // Start of relocated kernel text is a rodata boundary 28462306a36Sopenharmony_ci if (addr < stext_phys) 28562306a36Sopenharmony_ci return stext_phys; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (addr < __pa_symbol(__srwx_boundary)) 28962306a36Sopenharmony_ci return __pa_symbol(__srwx_boundary); 29062306a36Sopenharmony_ci#endif 29162306a36Sopenharmony_ci return end; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int __meminit create_physical_mapping(unsigned long start, 29562306a36Sopenharmony_ci unsigned long end, 29662306a36Sopenharmony_ci int nid, pgprot_t _prot) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci unsigned long vaddr, addr, mapping_size = 0; 29962306a36Sopenharmony_ci bool prev_exec, exec = false; 30062306a36Sopenharmony_ci pgprot_t prot; 30162306a36Sopenharmony_ci int psize; 30262306a36Sopenharmony_ci unsigned long max_mapping_size = memory_block_size; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (debug_pagealloc_enabled_or_kfence()) 30562306a36Sopenharmony_ci max_mapping_size = PAGE_SIZE; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci start = ALIGN(start, PAGE_SIZE); 30862306a36Sopenharmony_ci end = ALIGN_DOWN(end, PAGE_SIZE); 30962306a36Sopenharmony_ci for (addr = start; addr < end; addr += mapping_size) { 31062306a36Sopenharmony_ci unsigned long gap, previous_size; 31162306a36Sopenharmony_ci int rc; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci gap = next_boundary(addr, end) - addr; 31462306a36Sopenharmony_ci if (gap > max_mapping_size) 31562306a36Sopenharmony_ci gap = max_mapping_size; 31662306a36Sopenharmony_ci previous_size = mapping_size; 31762306a36Sopenharmony_ci prev_exec = exec; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (IS_ALIGNED(addr, PUD_SIZE) && gap >= PUD_SIZE && 32062306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_1G].shift) { 32162306a36Sopenharmony_ci mapping_size = PUD_SIZE; 32262306a36Sopenharmony_ci psize = MMU_PAGE_1G; 32362306a36Sopenharmony_ci } else if (IS_ALIGNED(addr, PMD_SIZE) && gap >= PMD_SIZE && 32462306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_2M].shift) { 32562306a36Sopenharmony_ci mapping_size = PMD_SIZE; 32662306a36Sopenharmony_ci psize = MMU_PAGE_2M; 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci mapping_size = PAGE_SIZE; 32962306a36Sopenharmony_ci psize = mmu_virtual_psize; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci vaddr = (unsigned long)__va(addr); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (overlaps_kernel_text(vaddr, vaddr + mapping_size) || 33562306a36Sopenharmony_ci overlaps_interrupt_vector_text(vaddr, vaddr + mapping_size)) { 33662306a36Sopenharmony_ci prot = PAGE_KERNEL_X; 33762306a36Sopenharmony_ci exec = true; 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci prot = _prot; 34062306a36Sopenharmony_ci exec = false; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (mapping_size != previous_size || exec != prev_exec) { 34462306a36Sopenharmony_ci print_mapping(start, addr, previous_size, prev_exec); 34562306a36Sopenharmony_ci start = addr; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rc = __map_kernel_page(vaddr, addr, prot, mapping_size, nid, start, end); 34962306a36Sopenharmony_ci if (rc) 35062306a36Sopenharmony_ci return rc; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci update_page_count(psize, 1); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci print_mapping(start, addr, mapping_size, exec); 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void __init radix_init_pgtable(void) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned long rts_field; 36262306a36Sopenharmony_ci phys_addr_t start, end; 36362306a36Sopenharmony_ci u64 i; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* We don't support slb for radix */ 36662306a36Sopenharmony_ci slb_set_size(0); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Create the linear mapping 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci for_each_mem_range(i, &start, &end) { 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * The memblock allocator is up at this point, so the 37462306a36Sopenharmony_ci * page tables will be allocated within the range. No 37562306a36Sopenharmony_ci * need or a node (which we don't have yet). 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (end >= RADIX_VMALLOC_START) { 37962306a36Sopenharmony_ci pr_warn("Outside the supported range\n"); 38062306a36Sopenharmony_ci continue; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci WARN_ON(create_physical_mapping(start, end, 38462306a36Sopenharmony_ci -1, PAGE_KERNEL)); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_HVMODE) && 38862306a36Sopenharmony_ci cpu_has_feature(CPU_FTR_P9_RADIX_PREFETCH_BUG)) { 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Older versions of KVM on these machines prefer if the 39162306a36Sopenharmony_ci * guest only uses the low 19 PID bits. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci mmu_pid_bits = 19; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci mmu_base_pid = 1; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Allocate Partition table and process table for the 39962306a36Sopenharmony_ci * host. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci BUG_ON(PRTB_SIZE_SHIFT > 36); 40262306a36Sopenharmony_ci process_tb = early_alloc_pgtable(1UL << PRTB_SIZE_SHIFT, -1, 0, 0); 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * Fill in the process table. 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci rts_field = radix__get_tree_size(); 40762306a36Sopenharmony_ci process_tb->prtb0 = cpu_to_be64(rts_field | __pa(init_mm.pgd) | RADIX_PGD_INDEX_SIZE); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * The init_mm context is given the first available (non-zero) PID, 41162306a36Sopenharmony_ci * which is the "guard PID" and contains no page table. PIDR should 41262306a36Sopenharmony_ci * never be set to zero because that duplicates the kernel address 41362306a36Sopenharmony_ci * space at the 0x0... offset (quadrant 0)! 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * An arbitrary PID that may later be allocated by the PID allocator 41662306a36Sopenharmony_ci * for userspace processes must not be used either, because that 41762306a36Sopenharmony_ci * would cause stale user mappings for that PID on CPUs outside of 41862306a36Sopenharmony_ci * the TLB invalidation scheme (because it won't be in mm_cpumask). 41962306a36Sopenharmony_ci * 42062306a36Sopenharmony_ci * So permanently carve out one PID for the purpose of a guard PID. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci init_mm.context.id = mmu_base_pid; 42362306a36Sopenharmony_ci mmu_base_pid++; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void __init radix_init_partition_table(void) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci unsigned long rts_field, dw0, dw1; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci mmu_partition_table_init(); 43162306a36Sopenharmony_ci rts_field = radix__get_tree_size(); 43262306a36Sopenharmony_ci dw0 = rts_field | __pa(init_mm.pgd) | RADIX_PGD_INDEX_SIZE | PATB_HR; 43362306a36Sopenharmony_ci dw1 = __pa(process_tb) | (PRTB_SIZE_SHIFT - 12) | PATB_GR; 43462306a36Sopenharmony_ci mmu_partition_table_set_entry(0, dw0, dw1, false); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci pr_info("Initializing Radix MMU\n"); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int __init get_idx_from_shift(unsigned int shift) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int idx = -1; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci switch (shift) { 44462306a36Sopenharmony_ci case 0xc: 44562306a36Sopenharmony_ci idx = MMU_PAGE_4K; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case 0x10: 44862306a36Sopenharmony_ci idx = MMU_PAGE_64K; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case 0x15: 45162306a36Sopenharmony_ci idx = MMU_PAGE_2M; 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci case 0x1e: 45462306a36Sopenharmony_ci idx = MMU_PAGE_1G; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci return idx; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int __init radix_dt_scan_page_sizes(unsigned long node, 46162306a36Sopenharmony_ci const char *uname, int depth, 46262306a36Sopenharmony_ci void *data) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci int size = 0; 46562306a36Sopenharmony_ci int shift, idx; 46662306a36Sopenharmony_ci unsigned int ap; 46762306a36Sopenharmony_ci const __be32 *prop; 46862306a36Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* We are scanning "cpu" nodes only */ 47162306a36Sopenharmony_ci if (type == NULL || strcmp(type, "cpu") != 0) 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Grab page size encodings */ 47562306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,processor-radix-AP-encodings", &size); 47662306a36Sopenharmony_ci if (!prop) 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci pr_info("Page sizes from device-tree:\n"); 48062306a36Sopenharmony_ci for (; size >= 4; size -= 4, ++prop) { 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci struct mmu_psize_def *def; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* top 3 bit is AP encoding */ 48562306a36Sopenharmony_ci shift = be32_to_cpu(prop[0]) & ~(0xe << 28); 48662306a36Sopenharmony_ci ap = be32_to_cpu(prop[0]) >> 29; 48762306a36Sopenharmony_ci pr_info("Page size shift = %d AP=0x%x\n", shift, ap); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci idx = get_idx_from_shift(shift); 49062306a36Sopenharmony_ci if (idx < 0) 49162306a36Sopenharmony_ci continue; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci def = &mmu_psize_defs[idx]; 49462306a36Sopenharmony_ci def->shift = shift; 49562306a36Sopenharmony_ci def->ap = ap; 49662306a36Sopenharmony_ci def->h_rpt_pgsize = psize_to_rpti_pgsize(idx); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* needed ? */ 50062306a36Sopenharmony_ci cur_cpu_spec->mmu_features &= ~MMU_FTR_NO_SLBIE_B; 50162306a36Sopenharmony_ci return 1; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid __init radix__early_init_devtree(void) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int rc; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* 50962306a36Sopenharmony_ci * Try to find the available page sizes in the device-tree 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci rc = of_scan_flat_dt(radix_dt_scan_page_sizes, NULL); 51262306a36Sopenharmony_ci if (!rc) { 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * No page size details found in device tree. 51562306a36Sopenharmony_ci * Let's assume we have page 4k and 64k support 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_4K].shift = 12; 51862306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_4K].ap = 0x0; 51962306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_4K].h_rpt_pgsize = 52062306a36Sopenharmony_ci psize_to_rpti_pgsize(MMU_PAGE_4K); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_64K].shift = 16; 52362306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_64K].ap = 0x5; 52462306a36Sopenharmony_ci mmu_psize_defs[MMU_PAGE_64K].h_rpt_pgsize = 52562306a36Sopenharmony_ci psize_to_rpti_pgsize(MMU_PAGE_64K); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci return; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_civoid __init radix__early_init_mmu(void) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci unsigned long lpcr; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci#ifdef CONFIG_PPC_64S_HASH_MMU 53562306a36Sopenharmony_ci#ifdef CONFIG_PPC_64K_PAGES 53662306a36Sopenharmony_ci /* PAGE_SIZE mappings */ 53762306a36Sopenharmony_ci mmu_virtual_psize = MMU_PAGE_64K; 53862306a36Sopenharmony_ci#else 53962306a36Sopenharmony_ci mmu_virtual_psize = MMU_PAGE_4K; 54062306a36Sopenharmony_ci#endif 54162306a36Sopenharmony_ci#endif 54262306a36Sopenharmony_ci /* 54362306a36Sopenharmony_ci * initialize page table size 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci __pte_index_size = RADIX_PTE_INDEX_SIZE; 54662306a36Sopenharmony_ci __pmd_index_size = RADIX_PMD_INDEX_SIZE; 54762306a36Sopenharmony_ci __pud_index_size = RADIX_PUD_INDEX_SIZE; 54862306a36Sopenharmony_ci __pgd_index_size = RADIX_PGD_INDEX_SIZE; 54962306a36Sopenharmony_ci __pud_cache_index = RADIX_PUD_INDEX_SIZE; 55062306a36Sopenharmony_ci __pte_table_size = RADIX_PTE_TABLE_SIZE; 55162306a36Sopenharmony_ci __pmd_table_size = RADIX_PMD_TABLE_SIZE; 55262306a36Sopenharmony_ci __pud_table_size = RADIX_PUD_TABLE_SIZE; 55362306a36Sopenharmony_ci __pgd_table_size = RADIX_PGD_TABLE_SIZE; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci __pmd_val_bits = RADIX_PMD_VAL_BITS; 55662306a36Sopenharmony_ci __pud_val_bits = RADIX_PUD_VAL_BITS; 55762306a36Sopenharmony_ci __pgd_val_bits = RADIX_PGD_VAL_BITS; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci __kernel_virt_start = RADIX_KERN_VIRT_START; 56062306a36Sopenharmony_ci __vmalloc_start = RADIX_VMALLOC_START; 56162306a36Sopenharmony_ci __vmalloc_end = RADIX_VMALLOC_END; 56262306a36Sopenharmony_ci __kernel_io_start = RADIX_KERN_IO_START; 56362306a36Sopenharmony_ci __kernel_io_end = RADIX_KERN_IO_END; 56462306a36Sopenharmony_ci vmemmap = (struct page *)RADIX_VMEMMAP_START; 56562306a36Sopenharmony_ci ioremap_bot = IOREMAP_BASE; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci#ifdef CONFIG_PCI 56862306a36Sopenharmony_ci pci_io_base = ISA_IO_BASE; 56962306a36Sopenharmony_ci#endif 57062306a36Sopenharmony_ci __pte_frag_nr = RADIX_PTE_FRAG_NR; 57162306a36Sopenharmony_ci __pte_frag_size_shift = RADIX_PTE_FRAG_SIZE_SHIFT; 57262306a36Sopenharmony_ci __pmd_frag_nr = RADIX_PMD_FRAG_NR; 57362306a36Sopenharmony_ci __pmd_frag_size_shift = RADIX_PMD_FRAG_SIZE_SHIFT; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci radix_init_pgtable(); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 57862306a36Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 57962306a36Sopenharmony_ci mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR); 58062306a36Sopenharmony_ci radix_init_partition_table(); 58162306a36Sopenharmony_ci } else { 58262306a36Sopenharmony_ci radix_init_pseries(); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci memblock_set_current_limit(MEMBLOCK_ALLOC_ANYWHERE); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Switch to the guard PID before turning on MMU */ 58862306a36Sopenharmony_ci radix__switch_mmu_context(NULL, &init_mm); 58962306a36Sopenharmony_ci tlbiel_all(); 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_civoid radix__early_init_mmu_secondary(void) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci unsigned long lpcr; 59562306a36Sopenharmony_ci /* 59662306a36Sopenharmony_ci * update partition table control register and UPRT 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 59962306a36Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 60062306a36Sopenharmony_ci mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci set_ptcr_when_no_uv(__pa(partition_tb) | 60362306a36Sopenharmony_ci (PATB_SIZE_SHIFT - 12)); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci radix__switch_mmu_context(NULL, &init_mm); 60762306a36Sopenharmony_ci tlbiel_all(); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Make sure userspace can't change the AMR */ 61062306a36Sopenharmony_ci mtspr(SPRN_UAMOR, 0); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci/* Called during kexec sequence with MMU off */ 61462306a36Sopenharmony_cinotrace void radix__mmu_cleanup_all(void) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci unsigned long lpcr; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 61962306a36Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 62062306a36Sopenharmony_ci mtspr(SPRN_LPCR, lpcr & ~LPCR_UPRT); 62162306a36Sopenharmony_ci set_ptcr_when_no_uv(0); 62262306a36Sopenharmony_ci powernv_set_nmmu_ptcr(0); 62362306a36Sopenharmony_ci radix__flush_tlb_all(); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 62862306a36Sopenharmony_cistatic void free_pte_table(pte_t *pte_start, pmd_t *pmd) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci pte_t *pte; 63162306a36Sopenharmony_ci int i; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++) { 63462306a36Sopenharmony_ci pte = pte_start + i; 63562306a36Sopenharmony_ci if (!pte_none(*pte)) 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci pte_free_kernel(&init_mm, pte_start); 64062306a36Sopenharmony_ci pmd_clear(pmd); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void free_pmd_table(pmd_t *pmd_start, pud_t *pud) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci pmd_t *pmd; 64662306a36Sopenharmony_ci int i; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++) { 64962306a36Sopenharmony_ci pmd = pmd_start + i; 65062306a36Sopenharmony_ci if (!pmd_none(*pmd)) 65162306a36Sopenharmony_ci return; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci pmd_free(&init_mm, pmd_start); 65562306a36Sopenharmony_ci pud_clear(pud); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void free_pud_table(pud_t *pud_start, p4d_t *p4d) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci pud_t *pud; 66162306a36Sopenharmony_ci int i; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PUD; i++) { 66462306a36Sopenharmony_ci pud = pud_start + i; 66562306a36Sopenharmony_ci if (!pud_none(*pud)) 66662306a36Sopenharmony_ci return; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci pud_free(&init_mm, pud_start); 67062306a36Sopenharmony_ci p4d_clear(p4d); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 67462306a36Sopenharmony_cistatic bool __meminit vmemmap_pmd_is_unused(unsigned long addr, unsigned long end) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci unsigned long start = ALIGN_DOWN(addr, PMD_SIZE); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return !vmemmap_populated(start, PMD_SIZE); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic bool __meminit vmemmap_page_is_unused(unsigned long addr, unsigned long end) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci unsigned long start = ALIGN_DOWN(addr, PAGE_SIZE); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return !vmemmap_populated(start, PAGE_SIZE); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci#endif 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void __meminit free_vmemmap_pages(struct page *page, 69162306a36Sopenharmony_ci struct vmem_altmap *altmap, 69262306a36Sopenharmony_ci int order) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci unsigned int nr_pages = 1 << order; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (altmap) { 69762306a36Sopenharmony_ci unsigned long alt_start, alt_end; 69862306a36Sopenharmony_ci unsigned long base_pfn = page_to_pfn(page); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * with 2M vmemmap mmaping we can have things setup 70262306a36Sopenharmony_ci * such that even though atlmap is specified we never 70362306a36Sopenharmony_ci * used altmap. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci alt_start = altmap->base_pfn; 70662306a36Sopenharmony_ci alt_end = altmap->base_pfn + altmap->reserve + altmap->free; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (base_pfn >= alt_start && base_pfn < alt_end) { 70962306a36Sopenharmony_ci vmem_altmap_free(altmap, nr_pages); 71062306a36Sopenharmony_ci return; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (PageReserved(page)) { 71562306a36Sopenharmony_ci /* allocated from memblock */ 71662306a36Sopenharmony_ci while (nr_pages--) 71762306a36Sopenharmony_ci free_reserved_page(page++); 71862306a36Sopenharmony_ci } else 71962306a36Sopenharmony_ci free_pages((unsigned long)page_address(page), order); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic void __meminit remove_pte_table(pte_t *pte_start, unsigned long addr, 72362306a36Sopenharmony_ci unsigned long end, bool direct, 72462306a36Sopenharmony_ci struct vmem_altmap *altmap) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci unsigned long next, pages = 0; 72762306a36Sopenharmony_ci pte_t *pte; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci pte = pte_start + pte_index(addr); 73062306a36Sopenharmony_ci for (; addr < end; addr = next, pte++) { 73162306a36Sopenharmony_ci next = (addr + PAGE_SIZE) & PAGE_MASK; 73262306a36Sopenharmony_ci if (next > end) 73362306a36Sopenharmony_ci next = end; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (!pte_present(*pte)) 73662306a36Sopenharmony_ci continue; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (PAGE_ALIGNED(addr) && PAGE_ALIGNED(next)) { 73962306a36Sopenharmony_ci if (!direct) 74062306a36Sopenharmony_ci free_vmemmap_pages(pte_page(*pte), altmap, 0); 74162306a36Sopenharmony_ci pte_clear(&init_mm, addr, pte); 74262306a36Sopenharmony_ci pages++; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 74562306a36Sopenharmony_ci else if (!direct && vmemmap_page_is_unused(addr, next)) { 74662306a36Sopenharmony_ci free_vmemmap_pages(pte_page(*pte), altmap, 0); 74762306a36Sopenharmony_ci pte_clear(&init_mm, addr, pte); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci#endif 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci if (direct) 75262306a36Sopenharmony_ci update_page_count(mmu_virtual_psize, -pages); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr, 75662306a36Sopenharmony_ci unsigned long end, bool direct, 75762306a36Sopenharmony_ci struct vmem_altmap *altmap) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci unsigned long next, pages = 0; 76062306a36Sopenharmony_ci pte_t *pte_base; 76162306a36Sopenharmony_ci pmd_t *pmd; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci pmd = pmd_start + pmd_index(addr); 76462306a36Sopenharmony_ci for (; addr < end; addr = next, pmd++) { 76562306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!pmd_present(*pmd)) 76862306a36Sopenharmony_ci continue; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (pmd_is_leaf(*pmd)) { 77162306a36Sopenharmony_ci if (IS_ALIGNED(addr, PMD_SIZE) && 77262306a36Sopenharmony_ci IS_ALIGNED(next, PMD_SIZE)) { 77362306a36Sopenharmony_ci if (!direct) 77462306a36Sopenharmony_ci free_vmemmap_pages(pmd_page(*pmd), altmap, get_order(PMD_SIZE)); 77562306a36Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pmd); 77662306a36Sopenharmony_ci pages++; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 77962306a36Sopenharmony_ci else if (!direct && vmemmap_pmd_is_unused(addr, next)) { 78062306a36Sopenharmony_ci free_vmemmap_pages(pmd_page(*pmd), altmap, get_order(PMD_SIZE)); 78162306a36Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pmd); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci#endif 78462306a36Sopenharmony_ci continue; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci pte_base = (pte_t *)pmd_page_vaddr(*pmd); 78862306a36Sopenharmony_ci remove_pte_table(pte_base, addr, next, direct, altmap); 78962306a36Sopenharmony_ci free_pte_table(pte_base, pmd); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci if (direct) 79262306a36Sopenharmony_ci update_page_count(MMU_PAGE_2M, -pages); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr, 79662306a36Sopenharmony_ci unsigned long end, bool direct, 79762306a36Sopenharmony_ci struct vmem_altmap *altmap) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci unsigned long next, pages = 0; 80062306a36Sopenharmony_ci pmd_t *pmd_base; 80162306a36Sopenharmony_ci pud_t *pud; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci pud = pud_start + pud_index(addr); 80462306a36Sopenharmony_ci for (; addr < end; addr = next, pud++) { 80562306a36Sopenharmony_ci next = pud_addr_end(addr, end); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!pud_present(*pud)) 80862306a36Sopenharmony_ci continue; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (pud_is_leaf(*pud)) { 81162306a36Sopenharmony_ci if (!IS_ALIGNED(addr, PUD_SIZE) || 81262306a36Sopenharmony_ci !IS_ALIGNED(next, PUD_SIZE)) { 81362306a36Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 81462306a36Sopenharmony_ci continue; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pud); 81762306a36Sopenharmony_ci pages++; 81862306a36Sopenharmony_ci continue; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci pmd_base = pud_pgtable(*pud); 82262306a36Sopenharmony_ci remove_pmd_table(pmd_base, addr, next, direct, altmap); 82362306a36Sopenharmony_ci free_pmd_table(pmd_base, pud); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci if (direct) 82662306a36Sopenharmony_ci update_page_count(MMU_PAGE_1G, -pages); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic void __meminit 83062306a36Sopenharmony_ciremove_pagetable(unsigned long start, unsigned long end, bool direct, 83162306a36Sopenharmony_ci struct vmem_altmap *altmap) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci unsigned long addr, next; 83462306a36Sopenharmony_ci pud_t *pud_base; 83562306a36Sopenharmony_ci pgd_t *pgd; 83662306a36Sopenharmony_ci p4d_t *p4d; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci spin_lock(&init_mm.page_table_lock); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (addr = start; addr < end; addr = next) { 84162306a36Sopenharmony_ci next = pgd_addr_end(addr, end); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci pgd = pgd_offset_k(addr); 84462306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr); 84562306a36Sopenharmony_ci if (!p4d_present(*p4d)) 84662306a36Sopenharmony_ci continue; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (p4d_is_leaf(*p4d)) { 84962306a36Sopenharmony_ci if (!IS_ALIGNED(addr, P4D_SIZE) || 85062306a36Sopenharmony_ci !IS_ALIGNED(next, P4D_SIZE)) { 85162306a36Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 85262306a36Sopenharmony_ci continue; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pgd); 85662306a36Sopenharmony_ci continue; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci pud_base = p4d_pgtable(*p4d); 86062306a36Sopenharmony_ci remove_pud_table(pud_base, addr, next, direct, altmap); 86162306a36Sopenharmony_ci free_pud_table(pud_base, p4d); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci spin_unlock(&init_mm.page_table_lock); 86562306a36Sopenharmony_ci radix__flush_tlb_kernel_range(start, end); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ciint __meminit radix__create_section_mapping(unsigned long start, 86962306a36Sopenharmony_ci unsigned long end, int nid, 87062306a36Sopenharmony_ci pgprot_t prot) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci if (end >= RADIX_VMALLOC_START) { 87362306a36Sopenharmony_ci pr_warn("Outside the supported range\n"); 87462306a36Sopenharmony_ci return -1; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return create_physical_mapping(__pa(start), __pa(end), 87862306a36Sopenharmony_ci nid, prot); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciint __meminit radix__remove_section_mapping(unsigned long start, unsigned long end) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci remove_pagetable(start, end, true, NULL); 88462306a36Sopenharmony_ci return 0; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci#endif /* CONFIG_MEMORY_HOTPLUG */ 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 88962306a36Sopenharmony_cistatic int __map_kernel_page_nid(unsigned long ea, unsigned long pa, 89062306a36Sopenharmony_ci pgprot_t flags, unsigned int map_page_size, 89162306a36Sopenharmony_ci int nid) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci return __map_kernel_page(ea, pa, flags, map_page_size, nid, 0, 0); 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ciint __meminit radix__vmemmap_create_mapping(unsigned long start, 89762306a36Sopenharmony_ci unsigned long page_size, 89862306a36Sopenharmony_ci unsigned long phys) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci /* Create a PTE encoding */ 90162306a36Sopenharmony_ci int nid = early_pfn_to_nid(phys >> PAGE_SHIFT); 90262306a36Sopenharmony_ci int ret; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if ((start + page_size) >= RADIX_VMEMMAP_END) { 90562306a36Sopenharmony_ci pr_warn("Outside the supported range\n"); 90662306a36Sopenharmony_ci return -1; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci ret = __map_kernel_page_nid(start, phys, PAGE_KERNEL, page_size, nid); 91062306a36Sopenharmony_ci BUG_ON(ret); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cibool vmemmap_can_optimize(struct vmem_altmap *altmap, struct dev_pagemap *pgmap) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci if (radix_enabled()) 91962306a36Sopenharmony_ci return __vmemmap_can_optimize(altmap, pgmap); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return false; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ciint __meminit vmemmap_check_pmd(pmd_t *pmdp, int node, 92562306a36Sopenharmony_ci unsigned long addr, unsigned long next) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci int large = pmd_large(*pmdp); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (large) 93062306a36Sopenharmony_ci vmemmap_verify(pmdp_ptep(pmdp), node, addr, next); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci return large; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_civoid __meminit vmemmap_set_pmd(pmd_t *pmdp, void *p, int node, 93662306a36Sopenharmony_ci unsigned long addr, unsigned long next) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci pte_t entry; 93962306a36Sopenharmony_ci pte_t *ptep = pmdp_ptep(pmdp); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci VM_BUG_ON(!IS_ALIGNED(addr, PMD_SIZE)); 94262306a36Sopenharmony_ci entry = pfn_pte(__pa(p) >> PAGE_SHIFT, PAGE_KERNEL); 94362306a36Sopenharmony_ci set_pte_at(&init_mm, addr, ptep, entry); 94462306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci vmemmap_verify(ptep, node, addr, next); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic pte_t * __meminit radix__vmemmap_pte_populate(pmd_t *pmdp, unsigned long addr, 95062306a36Sopenharmony_ci int node, 95162306a36Sopenharmony_ci struct vmem_altmap *altmap, 95262306a36Sopenharmony_ci struct page *reuse) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci pte_t *pte = pte_offset_kernel(pmdp, addr); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (pte_none(*pte)) { 95762306a36Sopenharmony_ci pte_t entry; 95862306a36Sopenharmony_ci void *p; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (!reuse) { 96162306a36Sopenharmony_ci /* 96262306a36Sopenharmony_ci * make sure we don't create altmap mappings 96362306a36Sopenharmony_ci * covering things outside the device. 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_ci if (altmap && altmap_cross_boundary(altmap, addr, PAGE_SIZE)) 96662306a36Sopenharmony_ci altmap = NULL; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci p = vmemmap_alloc_block_buf(PAGE_SIZE, node, altmap); 96962306a36Sopenharmony_ci if (!p && altmap) 97062306a36Sopenharmony_ci p = vmemmap_alloc_block_buf(PAGE_SIZE, node, NULL); 97162306a36Sopenharmony_ci if (!p) 97262306a36Sopenharmony_ci return NULL; 97362306a36Sopenharmony_ci pr_debug("PAGE_SIZE vmemmap mapping\n"); 97462306a36Sopenharmony_ci } else { 97562306a36Sopenharmony_ci /* 97662306a36Sopenharmony_ci * When a PTE/PMD entry is freed from the init_mm 97762306a36Sopenharmony_ci * there's a free_pages() call to this page allocated 97862306a36Sopenharmony_ci * above. Thus this get_page() is paired with the 97962306a36Sopenharmony_ci * put_page_testzero() on the freeing path. 98062306a36Sopenharmony_ci * This can only called by certain ZONE_DEVICE path, 98162306a36Sopenharmony_ci * and through vmemmap_populate_compound_pages() when 98262306a36Sopenharmony_ci * slab is available. 98362306a36Sopenharmony_ci */ 98462306a36Sopenharmony_ci get_page(reuse); 98562306a36Sopenharmony_ci p = page_to_virt(reuse); 98662306a36Sopenharmony_ci pr_debug("Tail page reuse vmemmap mapping\n"); 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci VM_BUG_ON(!PAGE_ALIGNED(addr)); 99062306a36Sopenharmony_ci entry = pfn_pte(__pa(p) >> PAGE_SHIFT, PAGE_KERNEL); 99162306a36Sopenharmony_ci set_pte_at(&init_mm, addr, pte, entry); 99262306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci return pte; 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic inline pud_t *vmemmap_pud_alloc(p4d_t *p4dp, int node, 99862306a36Sopenharmony_ci unsigned long address) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci pud_t *pud; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* All early vmemmap mapping to keep simple do it at PAGE_SIZE */ 100362306a36Sopenharmony_ci if (unlikely(p4d_none(*p4dp))) { 100462306a36Sopenharmony_ci if (unlikely(!slab_is_available())) { 100562306a36Sopenharmony_ci pud = early_alloc_pgtable(PAGE_SIZE, node, 0, 0); 100662306a36Sopenharmony_ci p4d_populate(&init_mm, p4dp, pud); 100762306a36Sopenharmony_ci /* go to the pud_offset */ 100862306a36Sopenharmony_ci } else 100962306a36Sopenharmony_ci return pud_alloc(&init_mm, p4dp, address); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci return pud_offset(p4dp, address); 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic inline pmd_t *vmemmap_pmd_alloc(pud_t *pudp, int node, 101562306a36Sopenharmony_ci unsigned long address) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci pmd_t *pmd; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* All early vmemmap mapping to keep simple do it at PAGE_SIZE */ 102062306a36Sopenharmony_ci if (unlikely(pud_none(*pudp))) { 102162306a36Sopenharmony_ci if (unlikely(!slab_is_available())) { 102262306a36Sopenharmony_ci pmd = early_alloc_pgtable(PAGE_SIZE, node, 0, 0); 102362306a36Sopenharmony_ci pud_populate(&init_mm, pudp, pmd); 102462306a36Sopenharmony_ci } else 102562306a36Sopenharmony_ci return pmd_alloc(&init_mm, pudp, address); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci return pmd_offset(pudp, address); 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic inline pte_t *vmemmap_pte_alloc(pmd_t *pmdp, int node, 103162306a36Sopenharmony_ci unsigned long address) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci pte_t *pte; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* All early vmemmap mapping to keep simple do it at PAGE_SIZE */ 103662306a36Sopenharmony_ci if (unlikely(pmd_none(*pmdp))) { 103762306a36Sopenharmony_ci if (unlikely(!slab_is_available())) { 103862306a36Sopenharmony_ci pte = early_alloc_pgtable(PAGE_SIZE, node, 0, 0); 103962306a36Sopenharmony_ci pmd_populate(&init_mm, pmdp, pte); 104062306a36Sopenharmony_ci } else 104162306a36Sopenharmony_ci return pte_alloc_kernel(pmdp, address); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci return pte_offset_kernel(pmdp, address); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ciint __meminit radix__vmemmap_populate(unsigned long start, unsigned long end, int node, 104962306a36Sopenharmony_ci struct vmem_altmap *altmap) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci unsigned long addr; 105262306a36Sopenharmony_ci unsigned long next; 105362306a36Sopenharmony_ci pgd_t *pgd; 105462306a36Sopenharmony_ci p4d_t *p4d; 105562306a36Sopenharmony_ci pud_t *pud; 105662306a36Sopenharmony_ci pmd_t *pmd; 105762306a36Sopenharmony_ci pte_t *pte; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci for (addr = start; addr < end; addr = next) { 106062306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci pgd = pgd_offset_k(addr); 106362306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr); 106462306a36Sopenharmony_ci pud = vmemmap_pud_alloc(p4d, node, addr); 106562306a36Sopenharmony_ci if (!pud) 106662306a36Sopenharmony_ci return -ENOMEM; 106762306a36Sopenharmony_ci pmd = vmemmap_pmd_alloc(pud, node, addr); 106862306a36Sopenharmony_ci if (!pmd) 106962306a36Sopenharmony_ci return -ENOMEM; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (pmd_none(READ_ONCE(*pmd))) { 107262306a36Sopenharmony_ci void *p; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* 107562306a36Sopenharmony_ci * keep it simple by checking addr PMD_SIZE alignment 107662306a36Sopenharmony_ci * and verifying the device boundary condition. 107762306a36Sopenharmony_ci * For us to use a pmd mapping, both addr and pfn should 107862306a36Sopenharmony_ci * be aligned. We skip if addr is not aligned and for 107962306a36Sopenharmony_ci * pfn we hope we have extra area in the altmap that 108062306a36Sopenharmony_ci * can help to find an aligned block. This can result 108162306a36Sopenharmony_ci * in altmap block allocation failures, in which case 108262306a36Sopenharmony_ci * we fallback to RAM for vmemmap allocation. 108362306a36Sopenharmony_ci */ 108462306a36Sopenharmony_ci if (altmap && (!IS_ALIGNED(addr, PMD_SIZE) || 108562306a36Sopenharmony_ci altmap_cross_boundary(altmap, addr, PMD_SIZE))) { 108662306a36Sopenharmony_ci /* 108762306a36Sopenharmony_ci * make sure we don't create altmap mappings 108862306a36Sopenharmony_ci * covering things outside the device. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_ci goto base_mapping; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci p = vmemmap_alloc_block_buf(PMD_SIZE, node, altmap); 109462306a36Sopenharmony_ci if (p) { 109562306a36Sopenharmony_ci vmemmap_set_pmd(pmd, p, node, addr, next); 109662306a36Sopenharmony_ci pr_debug("PMD_SIZE vmemmap mapping\n"); 109762306a36Sopenharmony_ci continue; 109862306a36Sopenharmony_ci } else if (altmap) { 109962306a36Sopenharmony_ci /* 110062306a36Sopenharmony_ci * A vmemmap block allocation can fail due to 110162306a36Sopenharmony_ci * alignment requirements and we trying to align 110262306a36Sopenharmony_ci * things aggressively there by running out of 110362306a36Sopenharmony_ci * space. Try base mapping on failure. 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_ci goto base_mapping; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci } else if (vmemmap_check_pmd(pmd, node, addr, next)) { 110862306a36Sopenharmony_ci /* 110962306a36Sopenharmony_ci * If a huge mapping exist due to early call to 111062306a36Sopenharmony_ci * vmemmap_populate, let's try to use that. 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci continue; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_cibase_mapping: 111562306a36Sopenharmony_ci /* 111662306a36Sopenharmony_ci * Not able allocate higher order memory to back memmap 111762306a36Sopenharmony_ci * or we found a pointer to pte page. Allocate base page 111862306a36Sopenharmony_ci * size vmemmap 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci pte = vmemmap_pte_alloc(pmd, node, addr); 112162306a36Sopenharmony_ci if (!pte) 112262306a36Sopenharmony_ci return -ENOMEM; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci pte = radix__vmemmap_pte_populate(pmd, addr, node, altmap, NULL); 112562306a36Sopenharmony_ci if (!pte) 112662306a36Sopenharmony_ci return -ENOMEM; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); 112962306a36Sopenharmony_ci next = addr + PAGE_SIZE; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci return 0; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic pte_t * __meminit radix__vmemmap_populate_address(unsigned long addr, int node, 113562306a36Sopenharmony_ci struct vmem_altmap *altmap, 113662306a36Sopenharmony_ci struct page *reuse) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci pgd_t *pgd; 113962306a36Sopenharmony_ci p4d_t *p4d; 114062306a36Sopenharmony_ci pud_t *pud; 114162306a36Sopenharmony_ci pmd_t *pmd; 114262306a36Sopenharmony_ci pte_t *pte; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci pgd = pgd_offset_k(addr); 114562306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr); 114662306a36Sopenharmony_ci pud = vmemmap_pud_alloc(p4d, node, addr); 114762306a36Sopenharmony_ci if (!pud) 114862306a36Sopenharmony_ci return NULL; 114962306a36Sopenharmony_ci pmd = vmemmap_pmd_alloc(pud, node, addr); 115062306a36Sopenharmony_ci if (!pmd) 115162306a36Sopenharmony_ci return NULL; 115262306a36Sopenharmony_ci if (pmd_leaf(*pmd)) 115362306a36Sopenharmony_ci /* 115462306a36Sopenharmony_ci * The second page is mapped as a hugepage due to a nearby request. 115562306a36Sopenharmony_ci * Force our mapping to page size without deduplication 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ci return NULL; 115862306a36Sopenharmony_ci pte = vmemmap_pte_alloc(pmd, node, addr); 115962306a36Sopenharmony_ci if (!pte) 116062306a36Sopenharmony_ci return NULL; 116162306a36Sopenharmony_ci radix__vmemmap_pte_populate(pmd, addr, node, NULL, NULL); 116262306a36Sopenharmony_ci vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci return pte; 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic pte_t * __meminit vmemmap_compound_tail_page(unsigned long addr, 116862306a36Sopenharmony_ci unsigned long pfn_offset, int node) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci pgd_t *pgd; 117162306a36Sopenharmony_ci p4d_t *p4d; 117262306a36Sopenharmony_ci pud_t *pud; 117362306a36Sopenharmony_ci pmd_t *pmd; 117462306a36Sopenharmony_ci pte_t *pte; 117562306a36Sopenharmony_ci unsigned long map_addr; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* the second vmemmap page which we use for duplication */ 117862306a36Sopenharmony_ci map_addr = addr - pfn_offset * sizeof(struct page) + PAGE_SIZE; 117962306a36Sopenharmony_ci pgd = pgd_offset_k(map_addr); 118062306a36Sopenharmony_ci p4d = p4d_offset(pgd, map_addr); 118162306a36Sopenharmony_ci pud = vmemmap_pud_alloc(p4d, node, map_addr); 118262306a36Sopenharmony_ci if (!pud) 118362306a36Sopenharmony_ci return NULL; 118462306a36Sopenharmony_ci pmd = vmemmap_pmd_alloc(pud, node, map_addr); 118562306a36Sopenharmony_ci if (!pmd) 118662306a36Sopenharmony_ci return NULL; 118762306a36Sopenharmony_ci if (pmd_leaf(*pmd)) 118862306a36Sopenharmony_ci /* 118962306a36Sopenharmony_ci * The second page is mapped as a hugepage due to a nearby request. 119062306a36Sopenharmony_ci * Force our mapping to page size without deduplication 119162306a36Sopenharmony_ci */ 119262306a36Sopenharmony_ci return NULL; 119362306a36Sopenharmony_ci pte = vmemmap_pte_alloc(pmd, node, map_addr); 119462306a36Sopenharmony_ci if (!pte) 119562306a36Sopenharmony_ci return NULL; 119662306a36Sopenharmony_ci /* 119762306a36Sopenharmony_ci * Check if there exist a mapping to the left 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci if (pte_none(*pte)) { 120062306a36Sopenharmony_ci /* 120162306a36Sopenharmony_ci * Populate the head page vmemmap page. 120262306a36Sopenharmony_ci * It can fall in different pmd, hence 120362306a36Sopenharmony_ci * vmemmap_populate_address() 120462306a36Sopenharmony_ci */ 120562306a36Sopenharmony_ci pte = radix__vmemmap_populate_address(map_addr - PAGE_SIZE, node, NULL, NULL); 120662306a36Sopenharmony_ci if (!pte) 120762306a36Sopenharmony_ci return NULL; 120862306a36Sopenharmony_ci /* 120962306a36Sopenharmony_ci * Populate the tail pages vmemmap page 121062306a36Sopenharmony_ci */ 121162306a36Sopenharmony_ci pte = radix__vmemmap_pte_populate(pmd, map_addr, node, NULL, NULL); 121262306a36Sopenharmony_ci if (!pte) 121362306a36Sopenharmony_ci return NULL; 121462306a36Sopenharmony_ci vmemmap_verify(pte, node, map_addr, map_addr + PAGE_SIZE); 121562306a36Sopenharmony_ci return pte; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci return pte; 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ciint __meminit vmemmap_populate_compound_pages(unsigned long start_pfn, 122162306a36Sopenharmony_ci unsigned long start, 122262306a36Sopenharmony_ci unsigned long end, int node, 122362306a36Sopenharmony_ci struct dev_pagemap *pgmap) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci /* 122662306a36Sopenharmony_ci * we want to map things as base page size mapping so that 122762306a36Sopenharmony_ci * we can save space in vmemmap. We could have huge mapping 122862306a36Sopenharmony_ci * covering out both edges. 122962306a36Sopenharmony_ci */ 123062306a36Sopenharmony_ci unsigned long addr; 123162306a36Sopenharmony_ci unsigned long addr_pfn = start_pfn; 123262306a36Sopenharmony_ci unsigned long next; 123362306a36Sopenharmony_ci pgd_t *pgd; 123462306a36Sopenharmony_ci p4d_t *p4d; 123562306a36Sopenharmony_ci pud_t *pud; 123662306a36Sopenharmony_ci pmd_t *pmd; 123762306a36Sopenharmony_ci pte_t *pte; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci for (addr = start; addr < end; addr = next) { 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci pgd = pgd_offset_k(addr); 124262306a36Sopenharmony_ci p4d = p4d_offset(pgd, addr); 124362306a36Sopenharmony_ci pud = vmemmap_pud_alloc(p4d, node, addr); 124462306a36Sopenharmony_ci if (!pud) 124562306a36Sopenharmony_ci return -ENOMEM; 124662306a36Sopenharmony_ci pmd = vmemmap_pmd_alloc(pud, node, addr); 124762306a36Sopenharmony_ci if (!pmd) 124862306a36Sopenharmony_ci return -ENOMEM; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (pmd_leaf(READ_ONCE(*pmd))) { 125162306a36Sopenharmony_ci /* existing huge mapping. Skip the range */ 125262306a36Sopenharmony_ci addr_pfn += (PMD_SIZE >> PAGE_SHIFT); 125362306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 125462306a36Sopenharmony_ci continue; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci pte = vmemmap_pte_alloc(pmd, node, addr); 125762306a36Sopenharmony_ci if (!pte) 125862306a36Sopenharmony_ci return -ENOMEM; 125962306a36Sopenharmony_ci if (!pte_none(*pte)) { 126062306a36Sopenharmony_ci /* 126162306a36Sopenharmony_ci * This could be because we already have a compound 126262306a36Sopenharmony_ci * page whose VMEMMAP_RESERVE_NR pages were mapped and 126362306a36Sopenharmony_ci * this request fall in those pages. 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci addr_pfn += 1; 126662306a36Sopenharmony_ci next = addr + PAGE_SIZE; 126762306a36Sopenharmony_ci continue; 126862306a36Sopenharmony_ci } else { 126962306a36Sopenharmony_ci unsigned long nr_pages = pgmap_vmemmap_nr(pgmap); 127062306a36Sopenharmony_ci unsigned long pfn_offset = addr_pfn - ALIGN_DOWN(addr_pfn, nr_pages); 127162306a36Sopenharmony_ci pte_t *tail_page_pte; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci /* 127462306a36Sopenharmony_ci * if the address is aligned to huge page size it is the 127562306a36Sopenharmony_ci * head mapping. 127662306a36Sopenharmony_ci */ 127762306a36Sopenharmony_ci if (pfn_offset == 0) { 127862306a36Sopenharmony_ci /* Populate the head page vmemmap page */ 127962306a36Sopenharmony_ci pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, NULL); 128062306a36Sopenharmony_ci if (!pte) 128162306a36Sopenharmony_ci return -ENOMEM; 128262306a36Sopenharmony_ci vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* 128562306a36Sopenharmony_ci * Populate the tail pages vmemmap page 128662306a36Sopenharmony_ci * It can fall in different pmd, hence 128762306a36Sopenharmony_ci * vmemmap_populate_address() 128862306a36Sopenharmony_ci */ 128962306a36Sopenharmony_ci pte = radix__vmemmap_populate_address(addr + PAGE_SIZE, node, NULL, NULL); 129062306a36Sopenharmony_ci if (!pte) 129162306a36Sopenharmony_ci return -ENOMEM; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci addr_pfn += 2; 129462306a36Sopenharmony_ci next = addr + 2 * PAGE_SIZE; 129562306a36Sopenharmony_ci continue; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci /* 129862306a36Sopenharmony_ci * get the 2nd mapping details 129962306a36Sopenharmony_ci * Also create it if that doesn't exist 130062306a36Sopenharmony_ci */ 130162306a36Sopenharmony_ci tail_page_pte = vmemmap_compound_tail_page(addr, pfn_offset, node); 130262306a36Sopenharmony_ci if (!tail_page_pte) { 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, NULL); 130562306a36Sopenharmony_ci if (!pte) 130662306a36Sopenharmony_ci return -ENOMEM; 130762306a36Sopenharmony_ci vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci addr_pfn += 1; 131062306a36Sopenharmony_ci next = addr + PAGE_SIZE; 131162306a36Sopenharmony_ci continue; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci pte = radix__vmemmap_pte_populate(pmd, addr, node, NULL, pte_page(*tail_page_pte)); 131562306a36Sopenharmony_ci if (!pte) 131662306a36Sopenharmony_ci return -ENOMEM; 131762306a36Sopenharmony_ci vmemmap_verify(pte, node, addr, addr + PAGE_SIZE); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci addr_pfn += 1; 132062306a36Sopenharmony_ci next = addr + PAGE_SIZE; 132162306a36Sopenharmony_ci continue; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci return 0; 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 132962306a36Sopenharmony_civoid __meminit radix__vmemmap_remove_mapping(unsigned long start, unsigned long page_size) 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci remove_pagetable(start, start + page_size, true, NULL); 133262306a36Sopenharmony_ci} 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_civoid __ref radix__vmemmap_free(unsigned long start, unsigned long end, 133562306a36Sopenharmony_ci struct vmem_altmap *altmap) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci remove_pagetable(start, end, false, altmap); 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci#endif 134062306a36Sopenharmony_ci#endif 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KFENCE) 134362306a36Sopenharmony_civoid radix__kernel_map_pages(struct page *page, int numpages, int enable) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci unsigned long addr; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci addr = (unsigned long)page_address(page); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (enable) 135062306a36Sopenharmony_ci set_memory_p(addr, numpages); 135162306a36Sopenharmony_ci else 135262306a36Sopenharmony_ci set_memory_np(addr, numpages); 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci#endif 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ciunsigned long radix__pmd_hugepage_update(struct mm_struct *mm, unsigned long addr, 135962306a36Sopenharmony_ci pmd_t *pmdp, unsigned long clr, 136062306a36Sopenharmony_ci unsigned long set) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci unsigned long old; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_VM 136562306a36Sopenharmony_ci WARN_ON(!radix__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp)); 136662306a36Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 136762306a36Sopenharmony_ci#endif 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci old = radix__pte_update(mm, addr, pmdp_ptep(pmdp), clr, set, 1); 137062306a36Sopenharmony_ci trace_hugepage_update_pmd(addr, old, clr, set); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return old; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ciunsigned long radix__pud_hugepage_update(struct mm_struct *mm, unsigned long addr, 137662306a36Sopenharmony_ci pud_t *pudp, unsigned long clr, 137762306a36Sopenharmony_ci unsigned long set) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci unsigned long old; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_VM 138262306a36Sopenharmony_ci WARN_ON(!pud_devmap(*pudp)); 138362306a36Sopenharmony_ci assert_spin_locked(pud_lockptr(mm, pudp)); 138462306a36Sopenharmony_ci#endif 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci old = radix__pte_update(mm, addr, pudp_ptep(pudp), clr, set, 1); 138762306a36Sopenharmony_ci trace_hugepage_update_pud(addr, old, clr, set); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci return old; 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cipmd_t radix__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address, 139362306a36Sopenharmony_ci pmd_t *pmdp) 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci pmd_t pmd; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci VM_BUG_ON(address & ~HPAGE_PMD_MASK); 139962306a36Sopenharmony_ci VM_BUG_ON(radix__pmd_trans_huge(*pmdp)); 140062306a36Sopenharmony_ci VM_BUG_ON(pmd_devmap(*pmdp)); 140162306a36Sopenharmony_ci /* 140262306a36Sopenharmony_ci * khugepaged calls this for normal pmd 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_ci pmd = *pmdp; 140562306a36Sopenharmony_ci pmd_clear(pmdp); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci radix__flush_tlb_collapsed_pmd(vma->vm_mm, address); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci return pmd; 141062306a36Sopenharmony_ci} 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci/* 141362306a36Sopenharmony_ci * For us pgtable_t is pte_t *. Inorder to save the deposisted 141462306a36Sopenharmony_ci * page table, we consider the allocated page table as a list 141562306a36Sopenharmony_ci * head. On withdraw we need to make sure we zero out the used 141662306a36Sopenharmony_ci * list_head memory area. 141762306a36Sopenharmony_ci */ 141862306a36Sopenharmony_civoid radix__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, 141962306a36Sopenharmony_ci pgtable_t pgtable) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct list_head *lh = (struct list_head *) pgtable; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* FIFO */ 142662306a36Sopenharmony_ci if (!pmd_huge_pte(mm, pmdp)) 142762306a36Sopenharmony_ci INIT_LIST_HEAD(lh); 142862306a36Sopenharmony_ci else 142962306a36Sopenharmony_ci list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp)); 143062306a36Sopenharmony_ci pmd_huge_pte(mm, pmdp) = pgtable; 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cipgtable_t radix__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci pte_t *ptep; 143662306a36Sopenharmony_ci pgtable_t pgtable; 143762306a36Sopenharmony_ci struct list_head *lh; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* FIFO */ 144262306a36Sopenharmony_ci pgtable = pmd_huge_pte(mm, pmdp); 144362306a36Sopenharmony_ci lh = (struct list_head *) pgtable; 144462306a36Sopenharmony_ci if (list_empty(lh)) 144562306a36Sopenharmony_ci pmd_huge_pte(mm, pmdp) = NULL; 144662306a36Sopenharmony_ci else { 144762306a36Sopenharmony_ci pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next; 144862306a36Sopenharmony_ci list_del(lh); 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci ptep = (pte_t *) pgtable; 145162306a36Sopenharmony_ci *ptep = __pte(0); 145262306a36Sopenharmony_ci ptep++; 145362306a36Sopenharmony_ci *ptep = __pte(0); 145462306a36Sopenharmony_ci return pgtable; 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cipmd_t radix__pmdp_huge_get_and_clear(struct mm_struct *mm, 145862306a36Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci pmd_t old_pmd; 146162306a36Sopenharmony_ci unsigned long old; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci old = radix__pmd_hugepage_update(mm, addr, pmdp, ~0UL, 0); 146462306a36Sopenharmony_ci old_pmd = __pmd(old); 146562306a36Sopenharmony_ci return old_pmd; 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cipud_t radix__pudp_huge_get_and_clear(struct mm_struct *mm, 146962306a36Sopenharmony_ci unsigned long addr, pud_t *pudp) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci pud_t old_pud; 147262306a36Sopenharmony_ci unsigned long old; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci old = radix__pud_hugepage_update(mm, addr, pudp, ~0UL, 0); 147562306a36Sopenharmony_ci old_pud = __pud(old); 147662306a36Sopenharmony_ci return old_pud; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_civoid radix__ptep_set_access_flags(struct vm_area_struct *vma, pte_t *ptep, 148262306a36Sopenharmony_ci pte_t entry, unsigned long address, int psize) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 148562306a36Sopenharmony_ci unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_SOFT_DIRTY | 148662306a36Sopenharmony_ci _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci unsigned long change = pte_val(entry) ^ pte_val(*ptep); 148962306a36Sopenharmony_ci /* 149062306a36Sopenharmony_ci * On POWER9, the NMMU is not able to relax PTE access permissions 149162306a36Sopenharmony_ci * for a translation with a TLB. The PTE must be invalidated, TLB 149262306a36Sopenharmony_ci * flushed before the new PTE is installed. 149362306a36Sopenharmony_ci * 149462306a36Sopenharmony_ci * This only needs to be done for radix, because hash translation does 149562306a36Sopenharmony_ci * flush when updating the linux pte (and we don't support NMMU 149662306a36Sopenharmony_ci * accelerators on HPT on POWER9 anyway XXX: do we?). 149762306a36Sopenharmony_ci * 149862306a36Sopenharmony_ci * POWER10 (and P9P) NMMU does behave as per ISA. 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31) && (change & _PAGE_RW) && 150162306a36Sopenharmony_ci atomic_read(&mm->context.copros) > 0) { 150262306a36Sopenharmony_ci unsigned long old_pte, new_pte; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci old_pte = __radix_pte_update(ptep, _PAGE_PRESENT, _PAGE_INVALID); 150562306a36Sopenharmony_ci new_pte = old_pte | set; 150662306a36Sopenharmony_ci radix__flush_tlb_page_psize(mm, address, psize); 150762306a36Sopenharmony_ci __radix_pte_update(ptep, _PAGE_INVALID, new_pte); 150862306a36Sopenharmony_ci } else { 150962306a36Sopenharmony_ci __radix_pte_update(ptep, 0, set); 151062306a36Sopenharmony_ci /* 151162306a36Sopenharmony_ci * Book3S does not require a TLB flush when relaxing access 151262306a36Sopenharmony_ci * restrictions when the address space (modulo the POWER9 nest 151362306a36Sopenharmony_ci * MMU issue above) because the MMU will reload the PTE after 151462306a36Sopenharmony_ci * taking an access fault, as defined by the architecture. See 151562306a36Sopenharmony_ci * "Setting a Reference or Change Bit or Upgrading Access 151662306a36Sopenharmony_ci * Authority (PTE Subject to Atomic Hardware Updates)" in 151762306a36Sopenharmony_ci * Power ISA Version 3.1B. 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci /* See ptesync comment in radix__set_pte_at */ 152162306a36Sopenharmony_ci} 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_civoid radix__ptep_modify_prot_commit(struct vm_area_struct *vma, 152462306a36Sopenharmony_ci unsigned long addr, pte_t *ptep, 152562306a36Sopenharmony_ci pte_t old_pte, pte_t pte) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci /* 153062306a36Sopenharmony_ci * POWER9 NMMU must flush the TLB after clearing the PTE before 153162306a36Sopenharmony_ci * installing a PTE with more relaxed access permissions, see 153262306a36Sopenharmony_ci * radix__ptep_set_access_flags. 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_ARCH_31) && 153562306a36Sopenharmony_ci is_pte_rw_upgrade(pte_val(old_pte), pte_val(pte)) && 153662306a36Sopenharmony_ci (atomic_read(&mm->context.copros) > 0)) 153762306a36Sopenharmony_ci radix__flush_tlb_page(vma, addr); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci set_pte_at(mm, addr, ptep, pte); 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ciint pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci pte_t *ptep = (pte_t *)pud; 154562306a36Sopenharmony_ci pte_t new_pud = pfn_pte(__phys_to_pfn(addr), prot); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (!radix_enabled()) 154862306a36Sopenharmony_ci return 0; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci set_pte_at(&init_mm, 0 /* radix unused */, ptep, new_pud); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci return 1; 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ciint pud_clear_huge(pud_t *pud) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci if (pud_is_leaf(*pud)) { 155862306a36Sopenharmony_ci pud_clear(pud); 155962306a36Sopenharmony_ci return 1; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ciint pud_free_pmd_page(pud_t *pud, unsigned long addr) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci pmd_t *pmd; 156862306a36Sopenharmony_ci int i; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci pmd = pud_pgtable(*pud); 157162306a36Sopenharmony_ci pud_clear(pud); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci flush_tlb_kernel_range(addr, addr + PUD_SIZE); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++) { 157662306a36Sopenharmony_ci if (!pmd_none(pmd[i])) { 157762306a36Sopenharmony_ci pte_t *pte; 157862306a36Sopenharmony_ci pte = (pte_t *)pmd_page_vaddr(pmd[i]); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci pte_free_kernel(&init_mm, pte); 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci pmd_free(&init_mm, pmd); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci return 1; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ciint pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci pte_t *ptep = (pte_t *)pmd; 159262306a36Sopenharmony_ci pte_t new_pmd = pfn_pte(__phys_to_pfn(addr), prot); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!radix_enabled()) 159562306a36Sopenharmony_ci return 0; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci set_pte_at(&init_mm, 0 /* radix unused */, ptep, new_pmd); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci return 1; 160062306a36Sopenharmony_ci} 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ciint pmd_clear_huge(pmd_t *pmd) 160362306a36Sopenharmony_ci{ 160462306a36Sopenharmony_ci if (pmd_is_leaf(*pmd)) { 160562306a36Sopenharmony_ci pmd_clear(pmd); 160662306a36Sopenharmony_ci return 1; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return 0; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ciint pmd_free_pte_page(pmd_t *pmd, unsigned long addr) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci pte_t *pte; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci pte = (pte_t *)pmd_page_vaddr(*pmd); 161762306a36Sopenharmony_ci pmd_clear(pmd); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci flush_tlb_kernel_range(addr, addr + PMD_SIZE); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci pte_free_kernel(&init_mm, pte); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return 1; 162462306a36Sopenharmony_ci} 1625