162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains the routines for initializing the MMU 462306a36Sopenharmony_ci * on the 8xx series of chips. 562306a36Sopenharmony_ci * -- christophe 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Derived from arch/powerpc/mm/40x_mmu.c: 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <linux/hugetlb.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <mm/mmu_decl.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define IMMR_SIZE (FIX_IMMR_SIZE << PAGE_SHIFT) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic unsigned long block_mapped_ram; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Return PA for this VA if it is in an area mapped with LTLBs or fixmap. 2162306a36Sopenharmony_ci * Otherwise, returns 0 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ciphys_addr_t v_block_mapped(unsigned long va) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long p = PHYS_IMMR_BASE; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (va >= VIRT_IMMR_BASE && va < VIRT_IMMR_BASE + IMMR_SIZE) 2862306a36Sopenharmony_ci return p + va - VIRT_IMMR_BASE; 2962306a36Sopenharmony_ci if (va >= PAGE_OFFSET && va < PAGE_OFFSET + block_mapped_ram) 3062306a36Sopenharmony_ci return __pa(va); 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Return VA for a given PA mapped with LTLBs or fixmap 3662306a36Sopenharmony_ci * Return 0 if not mapped 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ciunsigned long p_block_mapped(phys_addr_t pa) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci unsigned long p = PHYS_IMMR_BASE; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (pa >= p && pa < p + IMMR_SIZE) 4362306a36Sopenharmony_ci return VIRT_IMMR_BASE + pa - p; 4462306a36Sopenharmony_ci if (pa < block_mapped_ram) 4562306a36Sopenharmony_ci return (unsigned long)__va(pa); 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic pte_t __init *early_hugepd_alloc_kernel(hugepd_t *pmdp, unsigned long va) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci if (hpd_val(*pmdp) == 0) { 5262306a36Sopenharmony_ci pte_t *ptep = memblock_alloc(sizeof(pte_basic_t), SZ_4K); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!ptep) 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci hugepd_populate_kernel((hugepd_t *)pmdp, ptep, PAGE_SHIFT_8M); 5862306a36Sopenharmony_ci hugepd_populate_kernel((hugepd_t *)pmdp + 1, ptep, PAGE_SHIFT_8M); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci return hugepte_offset(*(hugepd_t *)pmdp, va, PGDIR_SHIFT); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int __ref __early_map_kernel_hugepage(unsigned long va, phys_addr_t pa, 6462306a36Sopenharmony_ci pgprot_t prot, int psize, bool new) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci pmd_t *pmdp = pmd_off_k(va); 6762306a36Sopenharmony_ci pte_t *ptep; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (WARN_ON(psize != MMU_PAGE_512K && psize != MMU_PAGE_8M)) 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (new) { 7362306a36Sopenharmony_ci if (WARN_ON(slab_is_available())) 7462306a36Sopenharmony_ci return -EINVAL; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (psize == MMU_PAGE_512K) 7762306a36Sopenharmony_ci ptep = early_pte_alloc_kernel(pmdp, va); 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci ptep = early_hugepd_alloc_kernel((hugepd_t *)pmdp, va); 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci if (psize == MMU_PAGE_512K) 8262306a36Sopenharmony_ci ptep = pte_offset_kernel(pmdp, va); 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci ptep = hugepte_offset(*(hugepd_t *)pmdp, va, PGDIR_SHIFT); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (WARN_ON(!ptep)) 8862306a36Sopenharmony_ci return -ENOMEM; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* The PTE should never be already present */ 9162306a36Sopenharmony_ci if (new && WARN_ON(pte_present(*ptep) && pgprot_val(prot))) 9262306a36Sopenharmony_ci return -EINVAL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci set_huge_pte_at(&init_mm, va, ptep, 9562306a36Sopenharmony_ci pte_mkhuge(pfn_pte(pa >> PAGE_SHIFT, prot)), psize); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* 10162306a36Sopenharmony_ci * MMU_init_hw does the chip-specific initialization of the MMU hardware. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_civoid __init MMU_init_hw(void) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic bool immr_is_mapped __initdata; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_civoid __init mmu_mapin_immr(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (immr_is_mapped) 11262306a36Sopenharmony_ci return; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci immr_is_mapped = true; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci __early_map_kernel_hugepage(VIRT_IMMR_BASE, PHYS_IMMR_BASE, 11762306a36Sopenharmony_ci PAGE_KERNEL_NCG, MMU_PAGE_512K, true); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void mmu_mapin_ram_chunk(unsigned long offset, unsigned long top, 12162306a36Sopenharmony_ci pgprot_t prot, bool new) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci unsigned long v = PAGE_OFFSET + offset; 12462306a36Sopenharmony_ci unsigned long p = offset; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci WARN_ON(!IS_ALIGNED(offset, SZ_512K) || !IS_ALIGNED(top, SZ_512K)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for (; p < ALIGN(p, SZ_8M) && p < top; p += SZ_512K, v += SZ_512K) 12962306a36Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new); 13062306a36Sopenharmony_ci for (; p < ALIGN_DOWN(top, SZ_8M) && p < top; p += SZ_8M, v += SZ_8M) 13162306a36Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_8M, new); 13262306a36Sopenharmony_ci for (; p < ALIGN_DOWN(top, SZ_512K) && p < top; p += SZ_512K, v += SZ_512K) 13362306a36Sopenharmony_ci __early_map_kernel_hugepage(v, p, prot, MMU_PAGE_512K, new); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!new) 13662306a36Sopenharmony_ci flush_tlb_kernel_range(PAGE_OFFSET + v, PAGE_OFFSET + top); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciunsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M); 14262306a36Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 14362306a36Sopenharmony_ci bool strict_boundary = strict_kernel_rwx_enabled() || debug_pagealloc_enabled_or_kfence(); 14462306a36Sopenharmony_ci unsigned long boundary = strict_boundary ? sinittext : etext8; 14562306a36Sopenharmony_ci unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci WARN_ON(top < einittext8); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci mmu_mapin_immr(); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci mmu_mapin_ram_chunk(0, boundary, PAGE_KERNEL_TEXT, true); 15262306a36Sopenharmony_ci if (debug_pagealloc_enabled_or_kfence()) { 15362306a36Sopenharmony_ci top = boundary; 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL_TEXT, true); 15662306a36Sopenharmony_ci mmu_mapin_ram_chunk(einittext8, top, PAGE_KERNEL, true); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (top > SZ_32M) 16062306a36Sopenharmony_ci memblock_set_current_limit(top); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci block_mapped_ram = top; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return top; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid mmu_mark_initmem_nx(void) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned long etext8 = ALIGN(__pa(_etext), SZ_8M); 17062306a36Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 17162306a36Sopenharmony_ci unsigned long boundary = strict_kernel_rwx_enabled() ? sinittext : etext8; 17262306a36Sopenharmony_ci unsigned long einittext8 = ALIGN(__pa(_einittext), SZ_8M); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!debug_pagealloc_enabled_or_kfence()) 17562306a36Sopenharmony_ci mmu_mapin_ram_chunk(boundary, einittext8, PAGE_KERNEL, false); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mmu_pin_tlb(block_mapped_ram, false); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#ifdef CONFIG_STRICT_KERNEL_RWX 18162306a36Sopenharmony_civoid mmu_mark_rodata_ro(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci unsigned long sinittext = __pa(_sinittext); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mmu_mapin_ram_chunk(0, sinittext, PAGE_KERNEL_ROX, false); 18662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PIN_TLB_DATA)) 18762306a36Sopenharmony_ci mmu_pin_tlb(block_mapped_ram, true); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci#endif 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid __init setup_initial_memory_limit(phys_addr_t first_memblock_base, 19262306a36Sopenharmony_ci phys_addr_t first_memblock_size) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci /* We don't currently support the first MEMBLOCK not mapping 0 19562306a36Sopenharmony_ci * physical on those processors 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci BUG_ON(first_memblock_base != 0); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* 8xx can only access 32MB at the moment */ 20062306a36Sopenharmony_ci memblock_set_current_limit(min_t(u64, first_memblock_size, SZ_32M)); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciint pud_clear_huge(pud_t *pud) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint pmd_clear_huge(pmd_t *pmd) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return 0; 21162306a36Sopenharmony_ci} 212