18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file contains the routines for initializing the MMU 48c2ecf20Sopenharmony_ci * on the 8xx series of chips. 58c2ecf20Sopenharmony_ci * -- christophe 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from arch/powerpc/mm/40x_mmu.c: 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/memblock.h> 118c2ecf20Sopenharmony_ci#include <linux/mmu_context.h> 128c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 138c2ecf20Sopenharmony_ci#include <asm/fixmap.h> 148c2ecf20Sopenharmony_ci#include <asm/code-patching.h> 158c2ecf20Sopenharmony_ci#include <asm/inst.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <mm/mmu_decl.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define IMMR_SIZE (FIX_IMMR_SIZE << PAGE_SHIFT) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciextern int __map_without_ltlbs; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic unsigned long block_mapped_ram; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Return PA for this VA if it is in an area mapped with LTLBs or fixmap. 278c2ecf20Sopenharmony_ci * Otherwise, returns 0 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ciphys_addr_t v_block_mapped(unsigned long va) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci unsigned long p = PHYS_IMMR_BASE; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (va >= VIRT_IMMR_BASE && va < VIRT_IMMR_BASE + IMMR_SIZE) 348c2ecf20Sopenharmony_ci return p + va - VIRT_IMMR_BASE; 358c2ecf20Sopenharmony_ci if (__map_without_ltlbs) 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci if (va >= PAGE_OFFSET && va < PAGE_OFFSET + block_mapped_ram) 388c2ecf20Sopenharmony_ci return __pa(va); 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * Return VA for a given PA mapped with LTLBs or fixmap 448c2ecf20Sopenharmony_ci * Return 0 if not mapped 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ciunsigned long p_block_mapped(phys_addr_t pa) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci unsigned long p = PHYS_IMMR_BASE; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (pa >= p && pa < p + IMMR_SIZE) 518c2ecf20Sopenharmony_ci return VIRT_IMMR_BASE + pa - p; 528c2ecf20Sopenharmony_ci if (__map_without_ltlbs) 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci if (pa < block_mapped_ram) 558c2ecf20Sopenharmony_ci return (unsigned long)__va(pa); 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic pte_t __init *early_hugepd_alloc_kernel(hugepd_t *pmdp, unsigned long va) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci if (hpd_val(*pmdp) == 0) { 628c2ecf20Sopenharmony_ci pte_t *ptep = memblock_alloc(sizeof(pte_basic_t), SZ_4K); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (!ptep) 658c2ecf20Sopenharmony_ci return NULL; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci hugepd_populate_kernel((hugepd_t *)pmdp, ptep, PAGE_SHIFT_8M); 688c2ecf20Sopenharmony_ci hugepd_populate_kernel((hugepd_t *)pmdp + 1, ptep, PAGE_SHIFT_8M); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci return hugepte_offset(*(hugepd_t *)pmdp, va, PGDIR_SHIFT); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int __ref __early_map_kernel_hugepage(unsigned long va, phys_addr_t pa, 748c2ecf20Sopenharmony_ci pgprot_t prot, int psize, bool new) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci pmd_t *pmdp = pmd_off_k(va); 778c2ecf20Sopenharmony_ci pte_t *ptep; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (WARN_ON(psize != MMU_PAGE_512K && psize != MMU_PAGE_8M)) 808c2ecf20Sopenharmony_ci return -EINVAL; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (new) { 838c2ecf20Sopenharmony_ci if (WARN_ON(slab_is_available())) 848c2ecf20Sopenharmony_ci return -EINVAL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (psize == MMU_PAGE_512K) 878c2ecf20Sopenharmony_ci ptep = early_pte_alloc_kernel(pmdp, va); 888c2ecf20Sopenharmony_ci else 898c2ecf20Sopenharmony_ci ptep = early_hugepd_alloc_kernel((hugepd_t *)pmdp, va); 908c2ecf20Sopenharmony_ci } else { 918c2ecf20Sopenharmony_ci if (psize == MMU_PAGE_512K) 928c2ecf20Sopenharmony_ci ptep = pte_offset_kernel(pmdp, va); 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci ptep = hugepte_offset(*(hugepd_t *)pmdp, va, PGDIR_SHIFT); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (WARN_ON(!ptep)) 988c2ecf20Sopenharmony_ci return -ENOMEM; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* The PTE should never be already present */ 1018c2ecf20Sopenharmony_ci if (new && WARN_ON(pte_present(*ptep) && pgprot_val(prot))) 1028c2ecf20Sopenharmony_ci return -EINVAL; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci set_huge_pte_at(&init_mm, va, ptep, pte_mkhuge(pfn_pte(pa >> PAGE_SHIFT, prot))); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * MMU_init_hw does the chip-specific initialization of the MMU hardware. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_civoid __init MMU_init_hw(void) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic bool immr_is_mapped __initdata; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_civoid __init mmu_mapin_immr(void) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (immr_is_mapped) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci immr_is_mapped = true; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci __early_map_kernel_hugepage(VIRT_IMMR_BASE, PHYS_IMMR_BASE, 1268c2ecf20Sopenharmony_ci PAGE_KERNEL_NCG, MMU_PAGE_512K, true); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void mmu_mapin_ram_chunk(unsigned long offset, unsigned long top, 1308c2ecf20Sopenharmony_ci pgprot_t prot, bool new) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned long v = PAGE_OFFSET + offset; 1338c2ecf20Sopenharmony_ci unsigned long p = offset; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci WARN_ON(!IS_ALIGNED(offset, SZ_512K) || !IS_ALIGNED(top, SZ_512K)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (; p < ALIGN(p, SZ_8M) && p < top; p += SZ_512K, v += SZ_512K) 1388c2ecf20Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new); 1398c2ecf20Sopenharmony_ci for (; p < ALIGN_DOWN(top, SZ_8M) && p < top; p += SZ_8M, v += SZ_8M) 1408c2ecf20Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_8M, new); 1418c2ecf20Sopenharmony_ci for (; p < ALIGN_DOWN(top, SZ_512K) && p < top; p += SZ_512K, v += SZ_512K) 1428c2ecf20Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!new) 1458c2ecf20Sopenharmony_ci flush_tlb_kernel_range(PAGE_OFFSET + v, PAGE_OFFSET + top); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciunsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M); 1518c2ecf20Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 1528c2ecf20Sopenharmony_ci bool strict_boundary = strict_kernel_rwx_enabled() || debug_pagealloc_enabled(); 1538c2ecf20Sopenharmony_ci unsigned long boundary = strict_boundary ? sinittext : etext8; 1548c2ecf20Sopenharmony_ci unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci WARN_ON(top < einittext8); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci mmu_mapin_immr(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (__map_without_ltlbs) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(0, boundary, PAGE_KERNEL_TEXT, true); 1648c2ecf20Sopenharmony_ci if (debug_pagealloc_enabled()) { 1658c2ecf20Sopenharmony_ci top = boundary; 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL_TEXT, true); 1688c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(einittext8, top, PAGE_KERNEL, true); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (top > SZ_32M) 1728c2ecf20Sopenharmony_ci memblock_set_current_limit(top); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci block_mapped_ram = top; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return top; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_civoid mmu_mark_initmem_nx(void) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M); 1828c2ecf20Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 1838c2ecf20Sopenharmony_ci unsigned long boundary = strict_kernel_rwx_enabled() ? sinittext : etext8; 1848c2ecf20Sopenharmony_ci unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(0, boundary, PAGE_KERNEL_TEXT, false); 1878c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL, false); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PIN_TLB_TEXT)) 1908c2ecf20Sopenharmony_ci mmu_pin_tlb(block_mapped_ram, false); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 1948c2ecf20Sopenharmony_civoid mmu_mark_rodata_ro(void) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci mmu_mapin_ram_chunk(0, sinittext, PAGE_KERNEL_ROX, false); 1998c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PIN_TLB_DATA)) 2008c2ecf20Sopenharmony_ci mmu_pin_tlb(block_mapped_ram, true); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci#endif 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_civoid __init setup_initial_memory_limit(phys_addr_t first_memblock_base, 2058c2ecf20Sopenharmony_ci phys_addr_t first_memblock_size) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci /* We don't currently support the first MEMBLOCK not mapping 0 2088c2ecf20Sopenharmony_ci * physical on those processors 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci BUG_ON(first_memblock_base != 0); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 8xx can only access 32MB at the moment */ 2138c2ecf20Sopenharmony_ci memblock_set_current_limit(min_t(u64, first_memblock_size, SZ_32M)); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * Set up to use a given MMU context. 2188c2ecf20Sopenharmony_ci * id is context number, pgd is PGD pointer. 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * We place the physical address of the new task page directory loaded 2218c2ecf20Sopenharmony_ci * into the MMU base register, and set the ASID compare register with 2228c2ecf20Sopenharmony_ci * the new "context." 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_civoid set_context(unsigned long id, pgd_t *pgd) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci s16 offset = (s16)(__pa(swapper_pg_dir)); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Context switch the PTE pointer for the Abatron BDI2000. 2298c2ecf20Sopenharmony_ci * The PGDIR is passed as second argument. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_BDI_SWITCH)) 2328c2ecf20Sopenharmony_ci abatron_pteptrs[1] = pgd; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Register M_TWB will contain base address of level 1 table minus the 2358c2ecf20Sopenharmony_ci * lower part of the kernel PGDIR base address, so that all accesses to 2368c2ecf20Sopenharmony_ci * level 1 table are done relative to lower part of kernel PGDIR base 2378c2ecf20Sopenharmony_ci * address. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci mtspr(SPRN_M_TWB, __pa(pgd) - offset); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Update context */ 2428c2ecf20Sopenharmony_ci mtspr(SPRN_M_CASID, id - 1); 2438c2ecf20Sopenharmony_ci /* sync */ 2448c2ecf20Sopenharmony_ci mb(); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_KUEP 2488c2ecf20Sopenharmony_civoid __init setup_kuep(bool disabled) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci if (disabled) 2518c2ecf20Sopenharmony_ci return; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci pr_info("Activating Kernel Userspace Execution Prevention\n"); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci mtspr(SPRN_MI_AP, MI_APG_KUEP); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci#endif 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_KUAP 2608c2ecf20Sopenharmony_civoid __init setup_kuap(bool disabled) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci pr_info("Activating Kernel Userspace Access Protection\n"); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (disabled) 2658c2ecf20Sopenharmony_ci pr_warn("KUAP cannot be disabled yet on 8xx when compiled in\n"); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mtspr(SPRN_MD_AP, MD_APG_KUAP); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci#endif 270