162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/module.h> 362306a36Sopenharmony_ci#include <linux/kernel.h> 462306a36Sopenharmony_ci#include <linux/slab.h> 562306a36Sopenharmony_ci#include <linux/mm_types.h> 662306a36Sopenharmony_ci#include <linux/pgtable.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/cputype.h> 962306a36Sopenharmony_ci#include <asm/idmap.h> 1062306a36Sopenharmony_ci#include <asm/hwcap.h> 1162306a36Sopenharmony_ci#include <asm/pgalloc.h> 1262306a36Sopenharmony_ci#include <asm/sections.h> 1362306a36Sopenharmony_ci#include <asm/system_info.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * Note: accesses outside of the kernel image and the identity map area 1762306a36Sopenharmony_ci * are not supported on any CPU using the idmap tables as its current 1862306a36Sopenharmony_ci * page tables. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cipgd_t *idmap_pgd __ro_after_init; 2162306a36Sopenharmony_cilong long arch_phys_to_idmap_offset __ro_after_init; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 2462306a36Sopenharmony_cistatic void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 2562306a36Sopenharmony_ci unsigned long prot) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci pmd_t *pmd; 2862306a36Sopenharmony_ci unsigned long next; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) { 3162306a36Sopenharmony_ci pmd = pmd_alloc_one(&init_mm, addr); 3262306a36Sopenharmony_ci if (!pmd) { 3362306a36Sopenharmony_ci pr_warn("Failed to allocate identity pmd.\n"); 3462306a36Sopenharmony_ci return; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci /* 3762306a36Sopenharmony_ci * Copy the original PMD to ensure that the PMD entries for 3862306a36Sopenharmony_ci * the kernel image are preserved. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci if (!pud_none(*pud)) 4162306a36Sopenharmony_ci memcpy(pmd, pmd_offset(pud, 0), 4262306a36Sopenharmony_ci PTRS_PER_PMD * sizeof(pmd_t)); 4362306a36Sopenharmony_ci pud_populate(&init_mm, pud, pmd); 4462306a36Sopenharmony_ci pmd += pmd_index(addr); 4562306a36Sopenharmony_ci } else 4662306a36Sopenharmony_ci pmd = pmd_offset(pud, addr); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci do { 4962306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 5062306a36Sopenharmony_ci *pmd = __pmd((addr & PMD_MASK) | prot); 5162306a36Sopenharmony_ci flush_pmd_entry(pmd); 5262306a36Sopenharmony_ci } while (pmd++, addr = next, addr != end); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci#else /* !CONFIG_ARM_LPAE */ 5562306a36Sopenharmony_cistatic void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 5662306a36Sopenharmony_ci unsigned long prot) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci pmd_t *pmd = pmd_offset(pud, addr); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci addr = (addr & PMD_MASK) | prot; 6162306a36Sopenharmony_ci pmd[0] = __pmd(addr); 6262306a36Sopenharmony_ci addr += SECTION_SIZE; 6362306a36Sopenharmony_ci pmd[1] = __pmd(addr); 6462306a36Sopenharmony_ci flush_pmd_entry(pmd); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci#endif /* CONFIG_ARM_LPAE */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end, 6962306a36Sopenharmony_ci unsigned long prot) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci p4d_t *p4d = p4d_offset(pgd, addr); 7262306a36Sopenharmony_ci pud_t *pud = pud_offset(p4d, addr); 7362306a36Sopenharmony_ci unsigned long next; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci do { 7662306a36Sopenharmony_ci next = pud_addr_end(addr, end); 7762306a36Sopenharmony_ci idmap_add_pmd(pud, addr, next, prot); 7862306a36Sopenharmony_ci } while (pud++, addr = next, addr != end); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void identity_mapping_add(pgd_t *pgd, const char *text_start, 8262306a36Sopenharmony_ci const char *text_end, unsigned long prot) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci unsigned long addr, end; 8562306a36Sopenharmony_ci unsigned long next; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci addr = virt_to_idmap(text_start); 8862306a36Sopenharmony_ci end = virt_to_idmap(text_end); 8962306a36Sopenharmony_ci pr_info("Setting up static identity map for 0x%lx - 0x%lx\n", addr, end); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale_family()) 9462306a36Sopenharmony_ci prot |= PMD_BIT4; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci pgd += pgd_index(addr); 9762306a36Sopenharmony_ci do { 9862306a36Sopenharmony_ci next = pgd_addr_end(addr, end); 9962306a36Sopenharmony_ci idmap_add_pud(pgd, addr, next, prot); 10062306a36Sopenharmony_ci } while (pgd++, addr = next, addr != end); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciextern char __idmap_text_start[], __idmap_text_end[]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int __init init_static_idmap(void) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci idmap_pgd = pgd_alloc(&init_mm); 10862306a36Sopenharmony_ci if (!idmap_pgd) 10962306a36Sopenharmony_ci return -ENOMEM; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci identity_mapping_add(idmap_pgd, __idmap_text_start, 11262306a36Sopenharmony_ci __idmap_text_end, 0); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Flush L1 for the hardware to see this page table content */ 11562306a36Sopenharmony_ci if (!(elf_hwcap & HWCAP_LPAE)) 11662306a36Sopenharmony_ci flush_cache_louis(); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ciearly_initcall(init_static_idmap); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* 12362306a36Sopenharmony_ci * In order to soft-boot, we need to switch to a 1:1 mapping for the 12462306a36Sopenharmony_ci * cpu_reset functions. This will then ensure that we have predictable 12562306a36Sopenharmony_ci * results when turning off the mmu. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_civoid setup_mm_for_reboot(void) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci /* Switch to the identity mapping. */ 13062306a36Sopenharmony_ci cpu_switch_mm(idmap_pgd, &init_mm); 13162306a36Sopenharmony_ci local_flush_bp_all(); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#ifdef CONFIG_CPU_HAS_ASID 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * We don't have a clean ASID for the identity mapping, which 13662306a36Sopenharmony_ci * may clash with virtual addresses of the previous page tables 13762306a36Sopenharmony_ci * and therefore potentially in the TLB. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci local_flush_tlb_all(); 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci} 142