18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Page table handling routines for radix page table. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "radix-mmu: " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/memblock.h> 148c2ecf20Sopenharmony_ci#include <linux/of_fdt.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 178c2ecf20Sopenharmony_ci#include <linux/string_helpers.h> 188c2ecf20Sopenharmony_ci#include <linux/memory.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 218c2ecf20Sopenharmony_ci#include <asm/mmu_context.h> 228c2ecf20Sopenharmony_ci#include <asm/dma.h> 238c2ecf20Sopenharmony_ci#include <asm/machdep.h> 248c2ecf20Sopenharmony_ci#include <asm/mmu.h> 258c2ecf20Sopenharmony_ci#include <asm/firmware.h> 268c2ecf20Sopenharmony_ci#include <asm/powernv.h> 278c2ecf20Sopenharmony_ci#include <asm/sections.h> 288c2ecf20Sopenharmony_ci#include <asm/smp.h> 298c2ecf20Sopenharmony_ci#include <asm/trace.h> 308c2ecf20Sopenharmony_ci#include <asm/uaccess.h> 318c2ecf20Sopenharmony_ci#include <asm/ultravisor.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <trace/events/thp.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciunsigned int mmu_pid_bits; 368c2ecf20Sopenharmony_ciunsigned int mmu_base_pid; 378c2ecf20Sopenharmony_ciunsigned long radix_mem_block_size __ro_after_init; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic __ref void *early_alloc_pgtable(unsigned long size, int nid, 408c2ecf20Sopenharmony_ci unsigned long region_start, unsigned long region_end) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci phys_addr_t min_addr = MEMBLOCK_LOW_LIMIT; 438c2ecf20Sopenharmony_ci phys_addr_t max_addr = MEMBLOCK_ALLOC_ANYWHERE; 448c2ecf20Sopenharmony_ci void *ptr; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (region_start) 478c2ecf20Sopenharmony_ci min_addr = region_start; 488c2ecf20Sopenharmony_ci if (region_end) 498c2ecf20Sopenharmony_ci max_addr = region_end; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ptr = memblock_alloc_try_nid(size, size, min_addr, max_addr, nid); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (!ptr) 548c2ecf20Sopenharmony_ci panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%pa max_addr=%pa\n", 558c2ecf20Sopenharmony_ci __func__, size, size, nid, &min_addr, &max_addr); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return ptr; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * When allocating pud or pmd pointers, we allocate a complete page 628c2ecf20Sopenharmony_ci * of PAGE_SIZE rather than PUD_TABLE_SIZE or PMD_TABLE_SIZE. This 638c2ecf20Sopenharmony_ci * is to ensure that the page obtained from the memblock allocator 648c2ecf20Sopenharmony_ci * can be completely used as page table page and can be freed 658c2ecf20Sopenharmony_ci * correctly when the page table entries are removed. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_cistatic int early_map_kernel_page(unsigned long ea, unsigned long pa, 688c2ecf20Sopenharmony_ci pgprot_t flags, 698c2ecf20Sopenharmony_ci unsigned int map_page_size, 708c2ecf20Sopenharmony_ci int nid, 718c2ecf20Sopenharmony_ci unsigned long region_start, unsigned long region_end) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned long pfn = pa >> PAGE_SHIFT; 748c2ecf20Sopenharmony_ci pgd_t *pgdp; 758c2ecf20Sopenharmony_ci p4d_t *p4dp; 768c2ecf20Sopenharmony_ci pud_t *pudp; 778c2ecf20Sopenharmony_ci pmd_t *pmdp; 788c2ecf20Sopenharmony_ci pte_t *ptep; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci pgdp = pgd_offset_k(ea); 818c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, ea); 828c2ecf20Sopenharmony_ci if (p4d_none(*p4dp)) { 838c2ecf20Sopenharmony_ci pudp = early_alloc_pgtable(PAGE_SIZE, nid, 848c2ecf20Sopenharmony_ci region_start, region_end); 858c2ecf20Sopenharmony_ci p4d_populate(&init_mm, p4dp, pudp); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci pudp = pud_offset(p4dp, ea); 888c2ecf20Sopenharmony_ci if (map_page_size == PUD_SIZE) { 898c2ecf20Sopenharmony_ci ptep = (pte_t *)pudp; 908c2ecf20Sopenharmony_ci goto set_the_pte; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci if (pud_none(*pudp)) { 938c2ecf20Sopenharmony_ci pmdp = early_alloc_pgtable(PAGE_SIZE, nid, region_start, 948c2ecf20Sopenharmony_ci region_end); 958c2ecf20Sopenharmony_ci pud_populate(&init_mm, pudp, pmdp); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci pmdp = pmd_offset(pudp, ea); 988c2ecf20Sopenharmony_ci if (map_page_size == PMD_SIZE) { 998c2ecf20Sopenharmony_ci ptep = pmdp_ptep(pmdp); 1008c2ecf20Sopenharmony_ci goto set_the_pte; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci if (!pmd_present(*pmdp)) { 1038c2ecf20Sopenharmony_ci ptep = early_alloc_pgtable(PAGE_SIZE, nid, 1048c2ecf20Sopenharmony_ci region_start, region_end); 1058c2ecf20Sopenharmony_ci pmd_populate_kernel(&init_mm, pmdp, ptep); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci ptep = pte_offset_kernel(pmdp, ea); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciset_the_pte: 1108c2ecf20Sopenharmony_ci set_pte_at(&init_mm, ea, ptep, pfn_pte(pfn, flags)); 1118c2ecf20Sopenharmony_ci asm volatile("ptesync": : :"memory"); 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * nid, region_start, and region_end are hints to try to place the page 1178c2ecf20Sopenharmony_ci * table memory in the same node or region. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic int __map_kernel_page(unsigned long ea, unsigned long pa, 1208c2ecf20Sopenharmony_ci pgprot_t flags, 1218c2ecf20Sopenharmony_ci unsigned int map_page_size, 1228c2ecf20Sopenharmony_ci int nid, 1238c2ecf20Sopenharmony_ci unsigned long region_start, unsigned long region_end) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned long pfn = pa >> PAGE_SHIFT; 1268c2ecf20Sopenharmony_ci pgd_t *pgdp; 1278c2ecf20Sopenharmony_ci p4d_t *p4dp; 1288c2ecf20Sopenharmony_ci pud_t *pudp; 1298c2ecf20Sopenharmony_ci pmd_t *pmdp; 1308c2ecf20Sopenharmony_ci pte_t *ptep; 1318c2ecf20Sopenharmony_ci /* 1328c2ecf20Sopenharmony_ci * Make sure task size is correct as per the max adddr 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci BUILD_BUG_ON(TASK_SIZE_USER64 > RADIX_PGTABLE_RANGE); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_64K_PAGES 1378c2ecf20Sopenharmony_ci BUILD_BUG_ON(RADIX_KERN_MAP_SIZE != (1UL << MAX_EA_BITS_PER_CONTEXT)); 1388c2ecf20Sopenharmony_ci#endif 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (unlikely(!slab_is_available())) 1418c2ecf20Sopenharmony_ci return early_map_kernel_page(ea, pa, flags, map_page_size, 1428c2ecf20Sopenharmony_ci nid, region_start, region_end); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Should make page table allocation functions be able to take a 1468c2ecf20Sopenharmony_ci * node, so we can place kernel page tables on the right nodes after 1478c2ecf20Sopenharmony_ci * boot. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci pgdp = pgd_offset_k(ea); 1508c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, ea); 1518c2ecf20Sopenharmony_ci pudp = pud_alloc(&init_mm, p4dp, ea); 1528c2ecf20Sopenharmony_ci if (!pudp) 1538c2ecf20Sopenharmony_ci return -ENOMEM; 1548c2ecf20Sopenharmony_ci if (map_page_size == PUD_SIZE) { 1558c2ecf20Sopenharmony_ci ptep = (pte_t *)pudp; 1568c2ecf20Sopenharmony_ci goto set_the_pte; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci pmdp = pmd_alloc(&init_mm, pudp, ea); 1598c2ecf20Sopenharmony_ci if (!pmdp) 1608c2ecf20Sopenharmony_ci return -ENOMEM; 1618c2ecf20Sopenharmony_ci if (map_page_size == PMD_SIZE) { 1628c2ecf20Sopenharmony_ci ptep = pmdp_ptep(pmdp); 1638c2ecf20Sopenharmony_ci goto set_the_pte; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci ptep = pte_alloc_kernel(pmdp, ea); 1668c2ecf20Sopenharmony_ci if (!ptep) 1678c2ecf20Sopenharmony_ci return -ENOMEM; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciset_the_pte: 1708c2ecf20Sopenharmony_ci set_pte_at(&init_mm, ea, ptep, pfn_pte(pfn, flags)); 1718c2ecf20Sopenharmony_ci asm volatile("ptesync": : :"memory"); 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciint radix__map_kernel_page(unsigned long ea, unsigned long pa, 1768c2ecf20Sopenharmony_ci pgprot_t flags, 1778c2ecf20Sopenharmony_ci unsigned int map_page_size) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return __map_kernel_page(ea, pa, flags, map_page_size, -1, 0, 0); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 1838c2ecf20Sopenharmony_civoid radix__change_memory_range(unsigned long start, unsigned long end, 1848c2ecf20Sopenharmony_ci unsigned long clear) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci unsigned long idx; 1878c2ecf20Sopenharmony_ci pgd_t *pgdp; 1888c2ecf20Sopenharmony_ci p4d_t *p4dp; 1898c2ecf20Sopenharmony_ci pud_t *pudp; 1908c2ecf20Sopenharmony_ci pmd_t *pmdp; 1918c2ecf20Sopenharmony_ci pte_t *ptep; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci start = ALIGN_DOWN(start, PAGE_SIZE); 1948c2ecf20Sopenharmony_ci end = PAGE_ALIGN(end); // aligns up 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci pr_debug("Changing flags on range %lx-%lx removing 0x%lx\n", 1978c2ecf20Sopenharmony_ci start, end, clear); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci for (idx = start; idx < end; idx += PAGE_SIZE) { 2008c2ecf20Sopenharmony_ci pgdp = pgd_offset_k(idx); 2018c2ecf20Sopenharmony_ci p4dp = p4d_offset(pgdp, idx); 2028c2ecf20Sopenharmony_ci pudp = pud_alloc(&init_mm, p4dp, idx); 2038c2ecf20Sopenharmony_ci if (!pudp) 2048c2ecf20Sopenharmony_ci continue; 2058c2ecf20Sopenharmony_ci if (pud_is_leaf(*pudp)) { 2068c2ecf20Sopenharmony_ci ptep = (pte_t *)pudp; 2078c2ecf20Sopenharmony_ci goto update_the_pte; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci pmdp = pmd_alloc(&init_mm, pudp, idx); 2108c2ecf20Sopenharmony_ci if (!pmdp) 2118c2ecf20Sopenharmony_ci continue; 2128c2ecf20Sopenharmony_ci if (pmd_is_leaf(*pmdp)) { 2138c2ecf20Sopenharmony_ci ptep = pmdp_ptep(pmdp); 2148c2ecf20Sopenharmony_ci goto update_the_pte; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci ptep = pte_alloc_kernel(pmdp, idx); 2178c2ecf20Sopenharmony_ci if (!ptep) 2188c2ecf20Sopenharmony_ci continue; 2198c2ecf20Sopenharmony_ciupdate_the_pte: 2208c2ecf20Sopenharmony_ci radix__pte_update(&init_mm, idx, ptep, clear, 0, 0); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci radix__flush_tlb_kernel_range(start, end); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_civoid radix__mark_rodata_ro(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci unsigned long start, end; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci start = (unsigned long)_stext; 2318c2ecf20Sopenharmony_ci end = (unsigned long)__init_begin; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci radix__change_memory_range(start, end, _PAGE_WRITE); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_civoid radix__mark_initmem_nx(void) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned long start = (unsigned long)__init_begin; 2398c2ecf20Sopenharmony_ci unsigned long end = (unsigned long)__init_end; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci radix__change_memory_range(start, end, _PAGE_EXEC); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci#endif /* CONFIG_STRICT_KERNEL_RWX */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic inline void __meminit 2468c2ecf20Sopenharmony_ciprint_mapping(unsigned long start, unsigned long end, unsigned long size, bool exec) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci char buf[10]; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (end <= start) 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci string_get_size(size, 1, STRING_UNITS_2, buf, sizeof(buf)); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci pr_info("Mapped 0x%016lx-0x%016lx with %s pages%s\n", start, end, buf, 2568c2ecf20Sopenharmony_ci exec ? " (exec)" : ""); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic unsigned long next_boundary(unsigned long addr, unsigned long end) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 2628c2ecf20Sopenharmony_ci if (addr < __pa_symbol(__init_begin)) 2638c2ecf20Sopenharmony_ci return __pa_symbol(__init_begin); 2648c2ecf20Sopenharmony_ci#endif 2658c2ecf20Sopenharmony_ci return end; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __meminit create_physical_mapping(unsigned long start, 2698c2ecf20Sopenharmony_ci unsigned long end, 2708c2ecf20Sopenharmony_ci unsigned long max_mapping_size, 2718c2ecf20Sopenharmony_ci int nid, pgprot_t _prot) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci unsigned long vaddr, addr, mapping_size = 0; 2748c2ecf20Sopenharmony_ci bool prev_exec, exec = false; 2758c2ecf20Sopenharmony_ci pgprot_t prot; 2768c2ecf20Sopenharmony_ci int psize; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci start = ALIGN(start, PAGE_SIZE); 2798c2ecf20Sopenharmony_ci end = ALIGN_DOWN(end, PAGE_SIZE); 2808c2ecf20Sopenharmony_ci for (addr = start; addr < end; addr += mapping_size) { 2818c2ecf20Sopenharmony_ci unsigned long gap, previous_size; 2828c2ecf20Sopenharmony_ci int rc; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci gap = next_boundary(addr, end) - addr; 2858c2ecf20Sopenharmony_ci if (gap > max_mapping_size) 2868c2ecf20Sopenharmony_ci gap = max_mapping_size; 2878c2ecf20Sopenharmony_ci previous_size = mapping_size; 2888c2ecf20Sopenharmony_ci prev_exec = exec; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (IS_ALIGNED(addr, PUD_SIZE) && gap >= PUD_SIZE && 2918c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_1G].shift) { 2928c2ecf20Sopenharmony_ci mapping_size = PUD_SIZE; 2938c2ecf20Sopenharmony_ci psize = MMU_PAGE_1G; 2948c2ecf20Sopenharmony_ci } else if (IS_ALIGNED(addr, PMD_SIZE) && gap >= PMD_SIZE && 2958c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_2M].shift) { 2968c2ecf20Sopenharmony_ci mapping_size = PMD_SIZE; 2978c2ecf20Sopenharmony_ci psize = MMU_PAGE_2M; 2988c2ecf20Sopenharmony_ci } else { 2998c2ecf20Sopenharmony_ci mapping_size = PAGE_SIZE; 3008c2ecf20Sopenharmony_ci psize = mmu_virtual_psize; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci vaddr = (unsigned long)__va(addr); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (overlaps_kernel_text(vaddr, vaddr + mapping_size) || 3068c2ecf20Sopenharmony_ci overlaps_interrupt_vector_text(vaddr, vaddr + mapping_size)) { 3078c2ecf20Sopenharmony_ci prot = PAGE_KERNEL_X; 3088c2ecf20Sopenharmony_ci exec = true; 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci prot = _prot; 3118c2ecf20Sopenharmony_ci exec = false; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (mapping_size != previous_size || exec != prev_exec) { 3158c2ecf20Sopenharmony_ci print_mapping(start, addr, previous_size, prev_exec); 3168c2ecf20Sopenharmony_ci start = addr; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci rc = __map_kernel_page(vaddr, addr, prot, mapping_size, nid, start, end); 3208c2ecf20Sopenharmony_ci if (rc) 3218c2ecf20Sopenharmony_ci return rc; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci update_page_count(psize, 1); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci print_mapping(start, addr, mapping_size, exec); 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void __init radix_init_pgtable(void) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci unsigned long rts_field; 3338c2ecf20Sopenharmony_ci phys_addr_t start, end; 3348c2ecf20Sopenharmony_ci u64 i; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* We don't support slb for radix */ 3378c2ecf20Sopenharmony_ci mmu_slb_size = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * Create the linear mapping 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci for_each_mem_range(i, &start, &end) { 3438c2ecf20Sopenharmony_ci /* 3448c2ecf20Sopenharmony_ci * The memblock allocator is up at this point, so the 3458c2ecf20Sopenharmony_ci * page tables will be allocated within the range. No 3468c2ecf20Sopenharmony_ci * need or a node (which we don't have yet). 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (end >= RADIX_VMALLOC_START) { 3508c2ecf20Sopenharmony_ci pr_warn("Outside the supported range\n"); 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci WARN_ON(create_physical_mapping(start, end, 3558c2ecf20Sopenharmony_ci radix_mem_block_size, 3568c2ecf20Sopenharmony_ci -1, PAGE_KERNEL)); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* Find out how many PID bits are supported */ 3608c2ecf20Sopenharmony_ci if (!cpu_has_feature(CPU_FTR_P9_RADIX_PREFETCH_BUG)) { 3618c2ecf20Sopenharmony_ci if (!mmu_pid_bits) 3628c2ecf20Sopenharmony_ci mmu_pid_bits = 20; 3638c2ecf20Sopenharmony_ci mmu_base_pid = 1; 3648c2ecf20Sopenharmony_ci } else if (cpu_has_feature(CPU_FTR_HVMODE)) { 3658c2ecf20Sopenharmony_ci if (!mmu_pid_bits) 3668c2ecf20Sopenharmony_ci mmu_pid_bits = 20; 3678c2ecf20Sopenharmony_ci#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * When KVM is possible, we only use the top half of the 3708c2ecf20Sopenharmony_ci * PID space to avoid collisions between host and guest PIDs 3718c2ecf20Sopenharmony_ci * which can cause problems due to prefetch when exiting the 3728c2ecf20Sopenharmony_ci * guest with AIL=3 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci mmu_base_pid = 1 << (mmu_pid_bits - 1); 3758c2ecf20Sopenharmony_ci#else 3768c2ecf20Sopenharmony_ci mmu_base_pid = 1; 3778c2ecf20Sopenharmony_ci#endif 3788c2ecf20Sopenharmony_ci } else { 3798c2ecf20Sopenharmony_ci /* The guest uses the bottom half of the PID space */ 3808c2ecf20Sopenharmony_ci if (!mmu_pid_bits) 3818c2ecf20Sopenharmony_ci mmu_pid_bits = 19; 3828c2ecf20Sopenharmony_ci mmu_base_pid = 1; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Allocate Partition table and process table for the 3878c2ecf20Sopenharmony_ci * host. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci BUG_ON(PRTB_SIZE_SHIFT > 36); 3908c2ecf20Sopenharmony_ci process_tb = early_alloc_pgtable(1UL << PRTB_SIZE_SHIFT, -1, 0, 0); 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * Fill in the process table. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci rts_field = radix__get_tree_size(); 3958c2ecf20Sopenharmony_ci process_tb->prtb0 = cpu_to_be64(rts_field | __pa(init_mm.pgd) | RADIX_PGD_INDEX_SIZE); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* 3988c2ecf20Sopenharmony_ci * The init_mm context is given the first available (non-zero) PID, 3998c2ecf20Sopenharmony_ci * which is the "guard PID" and contains no page table. PIDR should 4008c2ecf20Sopenharmony_ci * never be set to zero because that duplicates the kernel address 4018c2ecf20Sopenharmony_ci * space at the 0x0... offset (quadrant 0)! 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * An arbitrary PID that may later be allocated by the PID allocator 4048c2ecf20Sopenharmony_ci * for userspace processes must not be used either, because that 4058c2ecf20Sopenharmony_ci * would cause stale user mappings for that PID on CPUs outside of 4068c2ecf20Sopenharmony_ci * the TLB invalidation scheme (because it won't be in mm_cpumask). 4078c2ecf20Sopenharmony_ci * 4088c2ecf20Sopenharmony_ci * So permanently carve out one PID for the purpose of a guard PID. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci init_mm.context.id = mmu_base_pid; 4118c2ecf20Sopenharmony_ci mmu_base_pid++; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void __init radix_init_partition_table(void) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci unsigned long rts_field, dw0, dw1; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci mmu_partition_table_init(); 4198c2ecf20Sopenharmony_ci rts_field = radix__get_tree_size(); 4208c2ecf20Sopenharmony_ci dw0 = rts_field | __pa(init_mm.pgd) | RADIX_PGD_INDEX_SIZE | PATB_HR; 4218c2ecf20Sopenharmony_ci dw1 = __pa(process_tb) | (PRTB_SIZE_SHIFT - 12) | PATB_GR; 4228c2ecf20Sopenharmony_ci mmu_partition_table_set_entry(0, dw0, dw1, false); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pr_info("Initializing Radix MMU\n"); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int __init get_idx_from_shift(unsigned int shift) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci int idx = -1; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci switch (shift) { 4328c2ecf20Sopenharmony_ci case 0xc: 4338c2ecf20Sopenharmony_ci idx = MMU_PAGE_4K; 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci case 0x10: 4368c2ecf20Sopenharmony_ci idx = MMU_PAGE_64K; 4378c2ecf20Sopenharmony_ci break; 4388c2ecf20Sopenharmony_ci case 0x15: 4398c2ecf20Sopenharmony_ci idx = MMU_PAGE_2M; 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case 0x1e: 4428c2ecf20Sopenharmony_ci idx = MMU_PAGE_1G; 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci return idx; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic int __init radix_dt_scan_page_sizes(unsigned long node, 4498c2ecf20Sopenharmony_ci const char *uname, int depth, 4508c2ecf20Sopenharmony_ci void *data) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci int size = 0; 4538c2ecf20Sopenharmony_ci int shift, idx; 4548c2ecf20Sopenharmony_ci unsigned int ap; 4558c2ecf20Sopenharmony_ci const __be32 *prop; 4568c2ecf20Sopenharmony_ci const char *type = of_get_flat_dt_prop(node, "device_type", NULL); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* We are scanning "cpu" nodes only */ 4598c2ecf20Sopenharmony_ci if (type == NULL || strcmp(type, "cpu") != 0) 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* Find MMU PID size */ 4638c2ecf20Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,mmu-pid-bits", &size); 4648c2ecf20Sopenharmony_ci if (prop && size == 4) 4658c2ecf20Sopenharmony_ci mmu_pid_bits = be32_to_cpup(prop); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Grab page size encodings */ 4688c2ecf20Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,processor-radix-AP-encodings", &size); 4698c2ecf20Sopenharmony_ci if (!prop) 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci pr_info("Page sizes from device-tree:\n"); 4738c2ecf20Sopenharmony_ci for (; size >= 4; size -= 4, ++prop) { 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci struct mmu_psize_def *def; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* top 3 bit is AP encoding */ 4788c2ecf20Sopenharmony_ci shift = be32_to_cpu(prop[0]) & ~(0xe << 28); 4798c2ecf20Sopenharmony_ci ap = be32_to_cpu(prop[0]) >> 29; 4808c2ecf20Sopenharmony_ci pr_info("Page size shift = %d AP=0x%x\n", shift, ap); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci idx = get_idx_from_shift(shift); 4838c2ecf20Sopenharmony_ci if (idx < 0) 4848c2ecf20Sopenharmony_ci continue; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci def = &mmu_psize_defs[idx]; 4878c2ecf20Sopenharmony_ci def->shift = shift; 4888c2ecf20Sopenharmony_ci def->ap = ap; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* needed ? */ 4928c2ecf20Sopenharmony_ci cur_cpu_spec->mmu_features &= ~MMU_FTR_NO_SLBIE_B; 4938c2ecf20Sopenharmony_ci return 1; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 4978c2ecf20Sopenharmony_cistatic int __init probe_memory_block_size(unsigned long node, const char *uname, int 4988c2ecf20Sopenharmony_ci depth, void *data) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci unsigned long *mem_block_size = (unsigned long *)data; 5018c2ecf20Sopenharmony_ci const __be32 *prop; 5028c2ecf20Sopenharmony_ci int len; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (depth != 1) 5058c2ecf20Sopenharmony_ci return 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (strcmp(uname, "ibm,dynamic-reconfiguration-memory")) 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,lmb-size", &len); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!prop || len < dt_root_size_cells * sizeof(__be32)) 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * Nothing in the device tree 5158c2ecf20Sopenharmony_ci */ 5168c2ecf20Sopenharmony_ci *mem_block_size = MIN_MEMORY_BLOCK_SIZE; 5178c2ecf20Sopenharmony_ci else 5188c2ecf20Sopenharmony_ci *mem_block_size = of_read_number(prop, dt_root_size_cells); 5198c2ecf20Sopenharmony_ci return 1; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic unsigned long radix_memory_block_size(void) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci unsigned long mem_block_size = MIN_MEMORY_BLOCK_SIZE; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * OPAL firmware feature is set by now. Hence we are ok 5288c2ecf20Sopenharmony_ci * to test OPAL feature. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_OPAL)) 5318c2ecf20Sopenharmony_ci mem_block_size = 1UL * 1024 * 1024 * 1024; 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci of_scan_flat_dt(probe_memory_block_size, &mem_block_size); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return mem_block_size; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci#else /* CONFIG_MEMORY_HOTPLUG */ 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic unsigned long radix_memory_block_size(void) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci return 1UL * 1024 * 1024 * 1024; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci#endif /* CONFIG_MEMORY_HOTPLUG */ 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_civoid __init radix__early_init_devtree(void) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci int rc; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * Try to find the available page sizes in the device-tree 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_ci rc = of_scan_flat_dt(radix_dt_scan_page_sizes, NULL); 5568c2ecf20Sopenharmony_ci if (!rc) { 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * No page size details found in device tree. 5598c2ecf20Sopenharmony_ci * Let's assume we have page 4k and 64k support 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_4K].shift = 12; 5628c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_4K].ap = 0x0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_64K].shift = 16; 5658c2ecf20Sopenharmony_ci mmu_psize_defs[MMU_PAGE_64K].ap = 0x5; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * Max mapping size used when mapping pages. We don't use 5708c2ecf20Sopenharmony_ci * ppc_md.memory_block_size() here because this get called 5718c2ecf20Sopenharmony_ci * early and we don't have machine probe called yet. Also 5728c2ecf20Sopenharmony_ci * the pseries implementation only check for ibm,lmb-size. 5738c2ecf20Sopenharmony_ci * All hypervisor supporting radix do expose that device 5748c2ecf20Sopenharmony_ci * tree node. 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci radix_mem_block_size = radix_memory_block_size(); 5778c2ecf20Sopenharmony_ci return; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic void radix_init_amor(void) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci /* 5838c2ecf20Sopenharmony_ci * In HV mode, we init AMOR (Authority Mask Override Register) so that 5848c2ecf20Sopenharmony_ci * the hypervisor and guest can setup IAMR (Instruction Authority Mask 5858c2ecf20Sopenharmony_ci * Register), enable key 0 and set it to 1. 5868c2ecf20Sopenharmony_ci * 5878c2ecf20Sopenharmony_ci * AMOR = 0b1100 .... 0000 (Mask for key 0 is 11) 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci mtspr(SPRN_AMOR, (3ul << 62)); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_KUEP 5938c2ecf20Sopenharmony_civoid setup_kuep(bool disabled) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci if (disabled || !early_radix_enabled()) 5968c2ecf20Sopenharmony_ci return; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (smp_processor_id() == boot_cpuid) { 5998c2ecf20Sopenharmony_ci pr_info("Activating Kernel Userspace Execution Prevention\n"); 6008c2ecf20Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_KUEP; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* 6048c2ecf20Sopenharmony_ci * Radix always uses key0 of the IAMR to determine if an access is 6058c2ecf20Sopenharmony_ci * allowed. We set bit 0 (IBM bit 1) of key0, to prevent instruction 6068c2ecf20Sopenharmony_ci * fetch. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci mtspr(SPRN_IAMR, (1ul << 62)); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci#endif 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_KUAP 6138c2ecf20Sopenharmony_civoid setup_kuap(bool disabled) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci if (disabled || !early_radix_enabled()) 6168c2ecf20Sopenharmony_ci return; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (smp_processor_id() == boot_cpuid) { 6198c2ecf20Sopenharmony_ci pr_info("Activating Kernel Userspace Access Prevention\n"); 6208c2ecf20Sopenharmony_ci cur_cpu_spec->mmu_features |= MMU_FTR_RADIX_KUAP; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* Make sure userspace can't change the AMR */ 6248c2ecf20Sopenharmony_ci mtspr(SPRN_UAMOR, 0); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * Set the default kernel AMR values on all cpus. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci mtspr(SPRN_AMR, AMR_KUAP_BLOCKED); 6308c2ecf20Sopenharmony_ci isync(); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci#endif 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_civoid __init radix__early_init_mmu(void) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci unsigned long lpcr; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_64K_PAGES 6398c2ecf20Sopenharmony_ci /* PAGE_SIZE mappings */ 6408c2ecf20Sopenharmony_ci mmu_virtual_psize = MMU_PAGE_64K; 6418c2ecf20Sopenharmony_ci#else 6428c2ecf20Sopenharmony_ci mmu_virtual_psize = MMU_PAGE_4K; 6438c2ecf20Sopenharmony_ci#endif 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 6468c2ecf20Sopenharmony_ci /* vmemmap mapping */ 6478c2ecf20Sopenharmony_ci if (mmu_psize_defs[MMU_PAGE_2M].shift) { 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * map vmemmap using 2M if available 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci mmu_vmemmap_psize = MMU_PAGE_2M; 6528c2ecf20Sopenharmony_ci } else 6538c2ecf20Sopenharmony_ci mmu_vmemmap_psize = mmu_virtual_psize; 6548c2ecf20Sopenharmony_ci#endif 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * initialize page table size 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci __pte_index_size = RADIX_PTE_INDEX_SIZE; 6598c2ecf20Sopenharmony_ci __pmd_index_size = RADIX_PMD_INDEX_SIZE; 6608c2ecf20Sopenharmony_ci __pud_index_size = RADIX_PUD_INDEX_SIZE; 6618c2ecf20Sopenharmony_ci __pgd_index_size = RADIX_PGD_INDEX_SIZE; 6628c2ecf20Sopenharmony_ci __pud_cache_index = RADIX_PUD_INDEX_SIZE; 6638c2ecf20Sopenharmony_ci __pte_table_size = RADIX_PTE_TABLE_SIZE; 6648c2ecf20Sopenharmony_ci __pmd_table_size = RADIX_PMD_TABLE_SIZE; 6658c2ecf20Sopenharmony_ci __pud_table_size = RADIX_PUD_TABLE_SIZE; 6668c2ecf20Sopenharmony_ci __pgd_table_size = RADIX_PGD_TABLE_SIZE; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci __pmd_val_bits = RADIX_PMD_VAL_BITS; 6698c2ecf20Sopenharmony_ci __pud_val_bits = RADIX_PUD_VAL_BITS; 6708c2ecf20Sopenharmony_ci __pgd_val_bits = RADIX_PGD_VAL_BITS; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci __kernel_virt_start = RADIX_KERN_VIRT_START; 6738c2ecf20Sopenharmony_ci __vmalloc_start = RADIX_VMALLOC_START; 6748c2ecf20Sopenharmony_ci __vmalloc_end = RADIX_VMALLOC_END; 6758c2ecf20Sopenharmony_ci __kernel_io_start = RADIX_KERN_IO_START; 6768c2ecf20Sopenharmony_ci __kernel_io_end = RADIX_KERN_IO_END; 6778c2ecf20Sopenharmony_ci vmemmap = (struct page *)RADIX_VMEMMAP_START; 6788c2ecf20Sopenharmony_ci ioremap_bot = IOREMAP_BASE; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 6818c2ecf20Sopenharmony_ci pci_io_base = ISA_IO_BASE; 6828c2ecf20Sopenharmony_ci#endif 6838c2ecf20Sopenharmony_ci __pte_frag_nr = RADIX_PTE_FRAG_NR; 6848c2ecf20Sopenharmony_ci __pte_frag_size_shift = RADIX_PTE_FRAG_SIZE_SHIFT; 6858c2ecf20Sopenharmony_ci __pmd_frag_nr = RADIX_PMD_FRAG_NR; 6868c2ecf20Sopenharmony_ci __pmd_frag_size_shift = RADIX_PMD_FRAG_SIZE_SHIFT; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci radix_init_pgtable(); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 6918c2ecf20Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 6928c2ecf20Sopenharmony_ci mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR); 6938c2ecf20Sopenharmony_ci radix_init_partition_table(); 6948c2ecf20Sopenharmony_ci radix_init_amor(); 6958c2ecf20Sopenharmony_ci } else { 6968c2ecf20Sopenharmony_ci radix_init_pseries(); 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci memblock_set_current_limit(MEMBLOCK_ALLOC_ANYWHERE); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Switch to the guard PID before turning on MMU */ 7028c2ecf20Sopenharmony_ci radix__switch_mmu_context(NULL, &init_mm); 7038c2ecf20Sopenharmony_ci tlbiel_all(); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_civoid radix__early_init_mmu_secondary(void) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci unsigned long lpcr; 7098c2ecf20Sopenharmony_ci /* 7108c2ecf20Sopenharmony_ci * update partition table control register and UPRT 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 7138c2ecf20Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 7148c2ecf20Sopenharmony_ci mtspr(SPRN_LPCR, lpcr | LPCR_UPRT | LPCR_HR); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci set_ptcr_when_no_uv(__pa(partition_tb) | 7178c2ecf20Sopenharmony_ci (PATB_SIZE_SHIFT - 12)); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci radix_init_amor(); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci radix__switch_mmu_context(NULL, &init_mm); 7238c2ecf20Sopenharmony_ci tlbiel_all(); 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_civoid radix__mmu_cleanup_all(void) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci unsigned long lpcr; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) { 7318c2ecf20Sopenharmony_ci lpcr = mfspr(SPRN_LPCR); 7328c2ecf20Sopenharmony_ci mtspr(SPRN_LPCR, lpcr & ~LPCR_UPRT); 7338c2ecf20Sopenharmony_ci set_ptcr_when_no_uv(0); 7348c2ecf20Sopenharmony_ci powernv_set_nmmu_ptcr(0); 7358c2ecf20Sopenharmony_ci radix__flush_tlb_all(); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 7408c2ecf20Sopenharmony_cistatic void free_pte_table(pte_t *pte_start, pmd_t *pmd) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci pte_t *pte; 7438c2ecf20Sopenharmony_ci int i; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++) { 7468c2ecf20Sopenharmony_ci pte = pte_start + i; 7478c2ecf20Sopenharmony_ci if (!pte_none(*pte)) 7488c2ecf20Sopenharmony_ci return; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci pte_free_kernel(&init_mm, pte_start); 7528c2ecf20Sopenharmony_ci pmd_clear(pmd); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic void free_pmd_table(pmd_t *pmd_start, pud_t *pud) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci pmd_t *pmd; 7588c2ecf20Sopenharmony_ci int i; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++) { 7618c2ecf20Sopenharmony_ci pmd = pmd_start + i; 7628c2ecf20Sopenharmony_ci if (!pmd_none(*pmd)) 7638c2ecf20Sopenharmony_ci return; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci pmd_free(&init_mm, pmd_start); 7678c2ecf20Sopenharmony_ci pud_clear(pud); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic void free_pud_table(pud_t *pud_start, p4d_t *p4d) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci pud_t *pud; 7738c2ecf20Sopenharmony_ci int i; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PUD; i++) { 7768c2ecf20Sopenharmony_ci pud = pud_start + i; 7778c2ecf20Sopenharmony_ci if (!pud_none(*pud)) 7788c2ecf20Sopenharmony_ci return; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci pud_free(&init_mm, pud_start); 7828c2ecf20Sopenharmony_ci p4d_clear(p4d); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void remove_pte_table(pte_t *pte_start, unsigned long addr, 7868c2ecf20Sopenharmony_ci unsigned long end, bool direct) 7878c2ecf20Sopenharmony_ci{ 7888c2ecf20Sopenharmony_ci unsigned long next, pages = 0; 7898c2ecf20Sopenharmony_ci pte_t *pte; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci pte = pte_start + pte_index(addr); 7928c2ecf20Sopenharmony_ci for (; addr < end; addr = next, pte++) { 7938c2ecf20Sopenharmony_ci next = (addr + PAGE_SIZE) & PAGE_MASK; 7948c2ecf20Sopenharmony_ci if (next > end) 7958c2ecf20Sopenharmony_ci next = end; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (!pte_present(*pte)) 7988c2ecf20Sopenharmony_ci continue; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(next)) { 8018c2ecf20Sopenharmony_ci /* 8028c2ecf20Sopenharmony_ci * The vmemmap_free() and remove_section_mapping() 8038c2ecf20Sopenharmony_ci * codepaths call us with aligned addresses. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 8068c2ecf20Sopenharmony_ci continue; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci pte_clear(&init_mm, addr, pte); 8108c2ecf20Sopenharmony_ci pages++; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci if (direct) 8138c2ecf20Sopenharmony_ci update_page_count(mmu_virtual_psize, -pages); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic void __meminit remove_pmd_table(pmd_t *pmd_start, unsigned long addr, 8178c2ecf20Sopenharmony_ci unsigned long end, bool direct) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci unsigned long next, pages = 0; 8208c2ecf20Sopenharmony_ci pte_t *pte_base; 8218c2ecf20Sopenharmony_ci pmd_t *pmd; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci pmd = pmd_start + pmd_index(addr); 8248c2ecf20Sopenharmony_ci for (; addr < end; addr = next, pmd++) { 8258c2ecf20Sopenharmony_ci next = pmd_addr_end(addr, end); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (!pmd_present(*pmd)) 8288c2ecf20Sopenharmony_ci continue; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (pmd_is_leaf(*pmd)) { 8318c2ecf20Sopenharmony_ci if (!IS_ALIGNED(addr, PMD_SIZE) || 8328c2ecf20Sopenharmony_ci !IS_ALIGNED(next, PMD_SIZE)) { 8338c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 8348c2ecf20Sopenharmony_ci continue; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pmd); 8378c2ecf20Sopenharmony_ci pages++; 8388c2ecf20Sopenharmony_ci continue; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci pte_base = (pte_t *)pmd_page_vaddr(*pmd); 8428c2ecf20Sopenharmony_ci remove_pte_table(pte_base, addr, next, direct); 8438c2ecf20Sopenharmony_ci free_pte_table(pte_base, pmd); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci if (direct) 8468c2ecf20Sopenharmony_ci update_page_count(MMU_PAGE_2M, -pages); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void __meminit remove_pud_table(pud_t *pud_start, unsigned long addr, 8508c2ecf20Sopenharmony_ci unsigned long end, bool direct) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci unsigned long next, pages = 0; 8538c2ecf20Sopenharmony_ci pmd_t *pmd_base; 8548c2ecf20Sopenharmony_ci pud_t *pud; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci pud = pud_start + pud_index(addr); 8578c2ecf20Sopenharmony_ci for (; addr < end; addr = next, pud++) { 8588c2ecf20Sopenharmony_ci next = pud_addr_end(addr, end); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (!pud_present(*pud)) 8618c2ecf20Sopenharmony_ci continue; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (pud_is_leaf(*pud)) { 8648c2ecf20Sopenharmony_ci if (!IS_ALIGNED(addr, PUD_SIZE) || 8658c2ecf20Sopenharmony_ci !IS_ALIGNED(next, PUD_SIZE)) { 8668c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 8678c2ecf20Sopenharmony_ci continue; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pud); 8708c2ecf20Sopenharmony_ci pages++; 8718c2ecf20Sopenharmony_ci continue; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci pmd_base = pud_pgtable(*pud); 8758c2ecf20Sopenharmony_ci remove_pmd_table(pmd_base, addr, next, direct); 8768c2ecf20Sopenharmony_ci free_pmd_table(pmd_base, pud); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci if (direct) 8798c2ecf20Sopenharmony_ci update_page_count(MMU_PAGE_1G, -pages); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic void __meminit remove_pagetable(unsigned long start, unsigned long end, 8838c2ecf20Sopenharmony_ci bool direct) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci unsigned long addr, next; 8868c2ecf20Sopenharmony_ci pud_t *pud_base; 8878c2ecf20Sopenharmony_ci pgd_t *pgd; 8888c2ecf20Sopenharmony_ci p4d_t *p4d; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci spin_lock(&init_mm.page_table_lock); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci for (addr = start; addr < end; addr = next) { 8938c2ecf20Sopenharmony_ci next = pgd_addr_end(addr, end); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci pgd = pgd_offset_k(addr); 8968c2ecf20Sopenharmony_ci p4d = p4d_offset(pgd, addr); 8978c2ecf20Sopenharmony_ci if (!p4d_present(*p4d)) 8988c2ecf20Sopenharmony_ci continue; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (p4d_is_leaf(*p4d)) { 9018c2ecf20Sopenharmony_ci if (!IS_ALIGNED(addr, P4D_SIZE) || 9028c2ecf20Sopenharmony_ci !IS_ALIGNED(next, P4D_SIZE)) { 9038c2ecf20Sopenharmony_ci WARN_ONCE(1, "%s: unaligned range\n", __func__); 9048c2ecf20Sopenharmony_ci continue; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci pte_clear(&init_mm, addr, (pte_t *)pgd); 9088c2ecf20Sopenharmony_ci continue; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci pud_base = p4d_pgtable(*p4d); 9128c2ecf20Sopenharmony_ci remove_pud_table(pud_base, addr, next, direct); 9138c2ecf20Sopenharmony_ci free_pud_table(pud_base, p4d); 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci spin_unlock(&init_mm.page_table_lock); 9178c2ecf20Sopenharmony_ci radix__flush_tlb_kernel_range(start, end); 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ciint __meminit radix__create_section_mapping(unsigned long start, 9218c2ecf20Sopenharmony_ci unsigned long end, int nid, 9228c2ecf20Sopenharmony_ci pgprot_t prot) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci if (end >= RADIX_VMALLOC_START) { 9258c2ecf20Sopenharmony_ci pr_warn("Outside the supported range\n"); 9268c2ecf20Sopenharmony_ci return -1; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return create_physical_mapping(__pa(start), __pa(end), 9308c2ecf20Sopenharmony_ci radix_mem_block_size, nid, prot); 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ciint __meminit radix__remove_section_mapping(unsigned long start, unsigned long end) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci remove_pagetable(start, end, true); 9368c2ecf20Sopenharmony_ci return 0; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci#endif /* CONFIG_MEMORY_HOTPLUG */ 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci#ifdef CONFIG_SPARSEMEM_VMEMMAP 9418c2ecf20Sopenharmony_cistatic int __map_kernel_page_nid(unsigned long ea, unsigned long pa, 9428c2ecf20Sopenharmony_ci pgprot_t flags, unsigned int map_page_size, 9438c2ecf20Sopenharmony_ci int nid) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci return __map_kernel_page(ea, pa, flags, map_page_size, nid, 0, 0); 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ciint __meminit radix__vmemmap_create_mapping(unsigned long start, 9498c2ecf20Sopenharmony_ci unsigned long page_size, 9508c2ecf20Sopenharmony_ci unsigned long phys) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci /* Create a PTE encoding */ 9538c2ecf20Sopenharmony_ci unsigned long flags = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_KERNEL_RW; 9548c2ecf20Sopenharmony_ci int nid = early_pfn_to_nid(phys >> PAGE_SHIFT); 9558c2ecf20Sopenharmony_ci int ret; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if ((start + page_size) >= RADIX_VMEMMAP_END) { 9588c2ecf20Sopenharmony_ci pr_warn("Outside the supported range\n"); 9598c2ecf20Sopenharmony_ci return -1; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci ret = __map_kernel_page_nid(start, phys, __pgprot(flags), page_size, nid); 9638c2ecf20Sopenharmony_ci BUG_ON(ret); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci return 0; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG 9698c2ecf20Sopenharmony_civoid __meminit radix__vmemmap_remove_mapping(unsigned long start, unsigned long page_size) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci remove_pagetable(start, start + page_size, false); 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci#endif 9748c2ecf20Sopenharmony_ci#endif 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci#ifdef CONFIG_TRANSPARENT_HUGEPAGE 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ciunsigned long radix__pmd_hugepage_update(struct mm_struct *mm, unsigned long addr, 9798c2ecf20Sopenharmony_ci pmd_t *pmdp, unsigned long clr, 9808c2ecf20Sopenharmony_ci unsigned long set) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci unsigned long old; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_VM 9858c2ecf20Sopenharmony_ci WARN_ON(!radix__pmd_trans_huge(*pmdp) && !pmd_devmap(*pmdp)); 9868c2ecf20Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 9878c2ecf20Sopenharmony_ci#endif 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci old = radix__pte_update(mm, addr, (pte_t *)pmdp, clr, set, 1); 9908c2ecf20Sopenharmony_ci trace_hugepage_update(addr, old, clr, set); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return old; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cipmd_t radix__pmdp_collapse_flush(struct vm_area_struct *vma, unsigned long address, 9968c2ecf20Sopenharmony_ci pmd_t *pmdp) 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci pmd_t pmd; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci VM_BUG_ON(address & ~HPAGE_PMD_MASK); 10028c2ecf20Sopenharmony_ci VM_BUG_ON(radix__pmd_trans_huge(*pmdp)); 10038c2ecf20Sopenharmony_ci VM_BUG_ON(pmd_devmap(*pmdp)); 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * khugepaged calls this for normal pmd 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci pmd = *pmdp; 10088c2ecf20Sopenharmony_ci pmd_clear(pmdp); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci radix__flush_tlb_collapsed_pmd(vma->vm_mm, address); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci return pmd; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci/* 10168c2ecf20Sopenharmony_ci * For us pgtable_t is pte_t *. Inorder to save the deposisted 10178c2ecf20Sopenharmony_ci * page table, we consider the allocated page table as a list 10188c2ecf20Sopenharmony_ci * head. On withdraw we need to make sure we zero out the used 10198c2ecf20Sopenharmony_ci * list_head memory area. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_civoid radix__pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, 10228c2ecf20Sopenharmony_ci pgtable_t pgtable) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci struct list_head *lh = (struct list_head *) pgtable; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci /* FIFO */ 10298c2ecf20Sopenharmony_ci if (!pmd_huge_pte(mm, pmdp)) 10308c2ecf20Sopenharmony_ci INIT_LIST_HEAD(lh); 10318c2ecf20Sopenharmony_ci else 10328c2ecf20Sopenharmony_ci list_add(lh, (struct list_head *) pmd_huge_pte(mm, pmdp)); 10338c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = pgtable; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cipgtable_t radix__pgtable_trans_huge_withdraw(struct mm_struct *mm, pmd_t *pmdp) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci pte_t *ptep; 10398c2ecf20Sopenharmony_ci pgtable_t pgtable; 10408c2ecf20Sopenharmony_ci struct list_head *lh; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci assert_spin_locked(pmd_lockptr(mm, pmdp)); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* FIFO */ 10458c2ecf20Sopenharmony_ci pgtable = pmd_huge_pte(mm, pmdp); 10468c2ecf20Sopenharmony_ci lh = (struct list_head *) pgtable; 10478c2ecf20Sopenharmony_ci if (list_empty(lh)) 10488c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = NULL; 10498c2ecf20Sopenharmony_ci else { 10508c2ecf20Sopenharmony_ci pmd_huge_pte(mm, pmdp) = (pgtable_t) lh->next; 10518c2ecf20Sopenharmony_ci list_del(lh); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci ptep = (pte_t *) pgtable; 10548c2ecf20Sopenharmony_ci *ptep = __pte(0); 10558c2ecf20Sopenharmony_ci ptep++; 10568c2ecf20Sopenharmony_ci *ptep = __pte(0); 10578c2ecf20Sopenharmony_ci return pgtable; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cipmd_t radix__pmdp_huge_get_and_clear(struct mm_struct *mm, 10618c2ecf20Sopenharmony_ci unsigned long addr, pmd_t *pmdp) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci pmd_t old_pmd; 10648c2ecf20Sopenharmony_ci unsigned long old; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci old = radix__pmd_hugepage_update(mm, addr, pmdp, ~0UL, 0); 10678c2ecf20Sopenharmony_ci old_pmd = __pmd(old); 10688c2ecf20Sopenharmony_ci return old_pmd; 10698c2ecf20Sopenharmony_ci} 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_civoid radix__ptep_set_access_flags(struct vm_area_struct *vma, pte_t *ptep, 10748c2ecf20Sopenharmony_ci pte_t entry, unsigned long address, int psize) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 10778c2ecf20Sopenharmony_ci unsigned long set = pte_val(entry) & (_PAGE_DIRTY | _PAGE_SOFT_DIRTY | 10788c2ecf20Sopenharmony_ci _PAGE_ACCESSED | _PAGE_RW | _PAGE_EXEC); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci unsigned long change = pte_val(entry) ^ pte_val(*ptep); 10818c2ecf20Sopenharmony_ci /* 10828c2ecf20Sopenharmony_ci * To avoid NMMU hang while relaxing access, we need mark 10838c2ecf20Sopenharmony_ci * the pte invalid in between. 10848c2ecf20Sopenharmony_ci */ 10858c2ecf20Sopenharmony_ci if ((change & _PAGE_RW) && atomic_read(&mm->context.copros) > 0) { 10868c2ecf20Sopenharmony_ci unsigned long old_pte, new_pte; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci old_pte = __radix_pte_update(ptep, _PAGE_PRESENT, _PAGE_INVALID); 10898c2ecf20Sopenharmony_ci /* 10908c2ecf20Sopenharmony_ci * new value of pte 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci new_pte = old_pte | set; 10938c2ecf20Sopenharmony_ci radix__flush_tlb_page_psize(mm, address, psize); 10948c2ecf20Sopenharmony_ci __radix_pte_update(ptep, _PAGE_INVALID, new_pte); 10958c2ecf20Sopenharmony_ci } else { 10968c2ecf20Sopenharmony_ci __radix_pte_update(ptep, 0, set); 10978c2ecf20Sopenharmony_ci /* 10988c2ecf20Sopenharmony_ci * Book3S does not require a TLB flush when relaxing access 10998c2ecf20Sopenharmony_ci * restrictions when the address space is not attached to a 11008c2ecf20Sopenharmony_ci * NMMU, because the core MMU will reload the pte after taking 11018c2ecf20Sopenharmony_ci * an access fault, which is defined by the architectue. 11028c2ecf20Sopenharmony_ci */ 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci /* See ptesync comment in radix__set_pte_at */ 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_civoid radix__ptep_modify_prot_commit(struct vm_area_struct *vma, 11088c2ecf20Sopenharmony_ci unsigned long addr, pte_t *ptep, 11098c2ecf20Sopenharmony_ci pte_t old_pte, pte_t pte) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci struct mm_struct *mm = vma->vm_mm; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* 11148c2ecf20Sopenharmony_ci * To avoid NMMU hang while relaxing access we need to flush the tlb before 11158c2ecf20Sopenharmony_ci * we set the new value. We need to do this only for radix, because hash 11168c2ecf20Sopenharmony_ci * translation does flush when updating the linux pte. 11178c2ecf20Sopenharmony_ci */ 11188c2ecf20Sopenharmony_ci if (is_pte_rw_upgrade(pte_val(old_pte), pte_val(pte)) && 11198c2ecf20Sopenharmony_ci (atomic_read(&mm->context.copros) > 0)) 11208c2ecf20Sopenharmony_ci radix__flush_tlb_page(vma, addr); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci set_pte_at(mm, addr, ptep, pte); 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ciint __init arch_ioremap_pud_supported(void) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci /* HPT does not cope with large pages in the vmalloc area */ 11288c2ecf20Sopenharmony_ci return radix_enabled(); 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ciint __init arch_ioremap_pmd_supported(void) 11328c2ecf20Sopenharmony_ci{ 11338c2ecf20Sopenharmony_ci return radix_enabled(); 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ciint p4d_free_pud_page(p4d_t *p4d, unsigned long addr) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci return 0; 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ciint pud_set_huge(pud_t *pud, phys_addr_t addr, pgprot_t prot) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci pte_t *ptep = (pte_t *)pud; 11448c2ecf20Sopenharmony_ci pte_t new_pud = pfn_pte(__phys_to_pfn(addr), prot); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (!radix_enabled()) 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci set_pte_at(&init_mm, 0 /* radix unused */, ptep, new_pud); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci return 1; 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ciint pud_clear_huge(pud_t *pud) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci if (pud_is_leaf(*pud)) { 11578c2ecf20Sopenharmony_ci pud_clear(pud); 11588c2ecf20Sopenharmony_ci return 1; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ciint pud_free_pmd_page(pud_t *pud, unsigned long addr) 11658c2ecf20Sopenharmony_ci{ 11668c2ecf20Sopenharmony_ci pmd_t *pmd; 11678c2ecf20Sopenharmony_ci int i; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci pmd = pud_pgtable(*pud); 11708c2ecf20Sopenharmony_ci pud_clear(pud); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci flush_tlb_kernel_range(addr, addr + PUD_SIZE); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++) { 11758c2ecf20Sopenharmony_ci if (!pmd_none(pmd[i])) { 11768c2ecf20Sopenharmony_ci pte_t *pte; 11778c2ecf20Sopenharmony_ci pte = (pte_t *)pmd_page_vaddr(pmd[i]); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci pte_free_kernel(&init_mm, pte); 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci pmd_free(&init_mm, pmd); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 1; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ciint pmd_set_huge(pmd_t *pmd, phys_addr_t addr, pgprot_t prot) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci pte_t *ptep = (pte_t *)pmd; 11918c2ecf20Sopenharmony_ci pte_t new_pmd = pfn_pte(__phys_to_pfn(addr), prot); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (!radix_enabled()) 11948c2ecf20Sopenharmony_ci return 0; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci set_pte_at(&init_mm, 0 /* radix unused */, ptep, new_pmd); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 1; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ciint pmd_clear_huge(pmd_t *pmd) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci if (pmd_is_leaf(*pmd)) { 12048c2ecf20Sopenharmony_ci pmd_clear(pmd); 12058c2ecf20Sopenharmony_ci return 1; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ciint pmd_free_pte_page(pmd_t *pmd, unsigned long addr) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci pte_t *pte; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci pte = (pte_t *)pmd_page_vaddr(*pmd); 12168c2ecf20Sopenharmony_ci pmd_clear(pmd); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci flush_tlb_kernel_range(addr, addr + PMD_SIZE); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci pte_free_kernel(&init_mm, pte); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci return 1; 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ciint __init arch_ioremap_p4d_supported(void) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci} 1229