18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/module.h> 38c2ecf20Sopenharmony_ci#include <linux/kernel.h> 48c2ecf20Sopenharmony_ci#include <linux/slab.h> 58c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 68c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/cputype.h> 98c2ecf20Sopenharmony_ci#include <asm/idmap.h> 108c2ecf20Sopenharmony_ci#include <asm/hwcap.h> 118c2ecf20Sopenharmony_ci#include <asm/pgalloc.h> 128c2ecf20Sopenharmony_ci#include <asm/sections.h> 138c2ecf20Sopenharmony_ci#include <asm/system_info.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * Note: accesses outside of the kernel image and the identity map area 178c2ecf20Sopenharmony_ci * are not supported on any CPU using the idmap tables as its current 188c2ecf20Sopenharmony_ci * page tables. 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_cipgd_t *idmap_pgd __ro_after_init; 218c2ecf20Sopenharmony_cilong long arch_phys_to_idmap_offset __ro_after_init; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_LPAE 248c2ecf20Sopenharmony_cistatic void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 258c2ecf20Sopenharmony_ci unsigned long prot) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci pmd_t *pmd; 288c2ecf20Sopenharmony_ci unsigned long next; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) { 318c2ecf20Sopenharmony_ci pmd = pmd_alloc_one(&init_mm, addr); 328c2ecf20Sopenharmony_ci if (!pmd) { 338c2ecf20Sopenharmony_ci pr_warn("Failed to allocate identity pmd.\n"); 348c2ecf20Sopenharmony_ci return; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci /* 378c2ecf20Sopenharmony_ci * Copy the original PMD to ensure that the PMD entries for 388c2ecf20Sopenharmony_ci * the kernel image are preserved. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci if (!pud_none(*pud)) 418c2ecf20Sopenharmony_ci memcpy(pmd, pmd_offset(pud, 0), 428c2ecf20Sopenharmony_ci PTRS_PER_PMD * sizeof(pmd_t)); 438c2ecf20Sopenharmony_ci pud_populate(&init_mm, pud, pmd); 448c2ecf20Sopenharmony_ci pmd += pmd_index(addr); 458c2ecf20Sopenharmony_ci } else 468c2ecf20Sopenharmony_ci pmd = pmd_offset(pud, addr); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci do { 498c2ecf20Sopenharmony_ci next = pmd_addr_end(addr, end); 508c2ecf20Sopenharmony_ci *pmd = __pmd((addr & PMD_MASK) | prot); 518c2ecf20Sopenharmony_ci flush_pmd_entry(pmd); 528c2ecf20Sopenharmony_ci } while (pmd++, addr = next, addr != end); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci#else /* !CONFIG_ARM_LPAE */ 558c2ecf20Sopenharmony_cistatic void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 568c2ecf20Sopenharmony_ci unsigned long prot) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci pmd_t *pmd = pmd_offset(pud, addr); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci addr = (addr & PMD_MASK) | prot; 618c2ecf20Sopenharmony_ci pmd[0] = __pmd(addr); 628c2ecf20Sopenharmony_ci addr += SECTION_SIZE; 638c2ecf20Sopenharmony_ci pmd[1] = __pmd(addr); 648c2ecf20Sopenharmony_ci flush_pmd_entry(pmd); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci#endif /* CONFIG_ARM_LPAE */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end, 698c2ecf20Sopenharmony_ci unsigned long prot) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci p4d_t *p4d = p4d_offset(pgd, addr); 728c2ecf20Sopenharmony_ci pud_t *pud = pud_offset(p4d, addr); 738c2ecf20Sopenharmony_ci unsigned long next; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci do { 768c2ecf20Sopenharmony_ci next = pud_addr_end(addr, end); 778c2ecf20Sopenharmony_ci idmap_add_pmd(pud, addr, next, prot); 788c2ecf20Sopenharmony_ci } while (pud++, addr = next, addr != end); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void identity_mapping_add(pgd_t *pgd, const char *text_start, 828c2ecf20Sopenharmony_ci const char *text_end, unsigned long prot) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci unsigned long addr, end; 858c2ecf20Sopenharmony_ci unsigned long next; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci addr = virt_to_idmap(text_start); 888c2ecf20Sopenharmony_ci end = virt_to_idmap(text_end); 898c2ecf20Sopenharmony_ci pr_info("Setting up static identity map for 0x%lx - 0x%lx\n", addr, end); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale_family()) 948c2ecf20Sopenharmony_ci prot |= PMD_BIT4; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pgd += pgd_index(addr); 978c2ecf20Sopenharmony_ci do { 988c2ecf20Sopenharmony_ci next = pgd_addr_end(addr, end); 998c2ecf20Sopenharmony_ci idmap_add_pud(pgd, addr, next, prot); 1008c2ecf20Sopenharmony_ci } while (pgd++, addr = next, addr != end); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ciextern char __idmap_text_start[], __idmap_text_end[]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int __init init_static_idmap(void) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci idmap_pgd = pgd_alloc(&init_mm); 1088c2ecf20Sopenharmony_ci if (!idmap_pgd) 1098c2ecf20Sopenharmony_ci return -ENOMEM; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci identity_mapping_add(idmap_pgd, __idmap_text_start, 1128c2ecf20Sopenharmony_ci __idmap_text_end, 0); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Flush L1 for the hardware to see this page table content */ 1158c2ecf20Sopenharmony_ci if (!(elf_hwcap & HWCAP_LPAE)) 1168c2ecf20Sopenharmony_ci flush_cache_louis(); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ciearly_initcall(init_static_idmap); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * In order to soft-boot, we need to switch to a 1:1 mapping for the 1248c2ecf20Sopenharmony_ci * cpu_reset functions. This will then ensure that we have predictable 1258c2ecf20Sopenharmony_ci * results when turning off the mmu. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_civoid setup_mm_for_reboot(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci /* Switch to the identity mapping. */ 1308c2ecf20Sopenharmony_ci cpu_switch_mm(idmap_pgd, &init_mm); 1318c2ecf20Sopenharmony_ci local_flush_bp_all(); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_ASID 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * We don't have a clean ASID for the identity mapping, which 1368c2ecf20Sopenharmony_ci * may clash with virtual addresses of the previous page tables 1378c2ecf20Sopenharmony_ci * and therefore potentially in the TLB. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci local_flush_tlb_all(); 1408c2ecf20Sopenharmony_ci#endif 1418c2ecf20Sopenharmony_ci} 142