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