Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : #include <linux/module.h> 3 : : #include <linux/kernel.h> 4 : : #include <linux/slab.h> 5 : : #include <linux/mm_types.h> 6 : : 7 : : #include <asm/cputype.h> 8 : : #include <asm/idmap.h> 9 : : #include <asm/hwcap.h> 10 : : #include <asm/pgalloc.h> 11 : : #include <asm/pgtable.h> 12 : : #include <asm/sections.h> 13 : : #include <asm/system_info.h> 14 : : 15 : : /* 16 : : * Note: accesses outside of the kernel image and the identity map area 17 : : * are not supported on any CPU using the idmap tables as its current 18 : : * page tables. 19 : : */ 20 : : pgd_t *idmap_pgd __ro_after_init; 21 : : long long arch_phys_to_idmap_offset __ro_after_init; 22 : : 23 : : #ifdef CONFIG_ARM_LPAE 24 : : static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 25 : : unsigned long prot) 26 : : { 27 : : pmd_t *pmd; 28 : : unsigned long next; 29 : : 30 : : if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) { 31 : : pmd = pmd_alloc_one(&init_mm, addr); 32 : : if (!pmd) { 33 : : pr_warn("Failed to allocate identity pmd.\n"); 34 : : return; 35 : : } 36 : : /* 37 : : * Copy the original PMD to ensure that the PMD entries for 38 : : * the kernel image are preserved. 39 : : */ 40 : : if (!pud_none(*pud)) 41 : : memcpy(pmd, pmd_offset(pud, 0), 42 : : PTRS_PER_PMD * sizeof(pmd_t)); 43 : : pud_populate(&init_mm, pud, pmd); 44 : : pmd += pmd_index(addr); 45 : : } else 46 : : pmd = pmd_offset(pud, addr); 47 : : 48 : : do { 49 : : next = pmd_addr_end(addr, end); 50 : : *pmd = __pmd((addr & PMD_MASK) | prot); 51 : : flush_pmd_entry(pmd); 52 : : } while (pmd++, addr = next, addr != end); 53 : : } 54 : : #else /* !CONFIG_ARM_LPAE */ 55 : : static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end, 56 : : unsigned long prot) 57 : : { 58 : : pmd_t *pmd = pmd_offset(pud, addr); 59 : : 60 : 3 : addr = (addr & PMD_MASK) | prot; 61 : 3 : pmd[0] = __pmd(addr); 62 : 3 : addr += SECTION_SIZE; 63 : 3 : pmd[1] = __pmd(addr); 64 : : flush_pmd_entry(pmd); 65 : : } 66 : : #endif /* CONFIG_ARM_LPAE */ 67 : : 68 : : static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end, 69 : : unsigned long prot) 70 : : { 71 : : pud_t *pud = pud_offset(pgd, addr); 72 : : unsigned long next; 73 : : 74 : : do { 75 : : next = pud_addr_end(addr, end); 76 : : idmap_add_pmd(pud, addr, next, prot); 77 : : } while (pud++, addr = next, addr != end); 78 : : } 79 : : 80 : 3 : static void identity_mapping_add(pgd_t *pgd, const char *text_start, 81 : : const char *text_end, unsigned long prot) 82 : : { 83 : : unsigned long addr, end; 84 : : unsigned long next; 85 : : 86 : 3 : addr = virt_to_idmap(text_start); 87 : 3 : end = virt_to_idmap(text_end); 88 : 3 : pr_info("Setting up static identity map for 0x%lx - 0x%lx\n", addr, end); 89 : : 90 : 3 : prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF; 91 : : 92 : 3 : if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale_family()) 93 : 0 : prot |= PMD_BIT4; 94 : : 95 : 3 : pgd += pgd_index(addr); 96 : : do { 97 : 3 : next = pgd_addr_end(addr, end); 98 : : idmap_add_pud(pgd, addr, next, prot); 99 : 3 : } while (pgd++, addr = next, addr != end); 100 : 3 : } 101 : : 102 : : extern char __idmap_text_start[], __idmap_text_end[]; 103 : : 104 : 3 : static int __init init_static_idmap(void) 105 : : { 106 : 3 : idmap_pgd = pgd_alloc(&init_mm); 107 : 3 : if (!idmap_pgd) 108 : : return -ENOMEM; 109 : : 110 : 3 : identity_mapping_add(idmap_pgd, __idmap_text_start, 111 : : __idmap_text_end, 0); 112 : : 113 : : /* Flush L1 for the hardware to see this page table content */ 114 : 3 : if (!(elf_hwcap & HWCAP_LPAE)) 115 : 0 : flush_cache_louis(); 116 : : 117 : : return 0; 118 : : } 119 : : early_initcall(init_static_idmap); 120 : : 121 : : /* 122 : : * In order to soft-boot, we need to switch to a 1:1 mapping for the 123 : : * cpu_reset functions. This will then ensure that we have predictable 124 : : * results when turning off the mmu. 125 : : */ 126 : 0 : void setup_mm_for_reboot(void) 127 : : { 128 : : /* Switch to the identity mapping. */ 129 : 0 : cpu_switch_mm(idmap_pgd, &init_mm); 130 : : local_flush_bp_all(); 131 : : 132 : : #ifdef CONFIG_CPU_HAS_ASID 133 : : /* 134 : : * We don't have a clean ASID for the identity mapping, which 135 : : * may clash with virtual addresses of the previous page tables 136 : : * and therefore potentially in the TLB. 137 : : */ 138 : : local_flush_tlb_all(); 139 : : #endif 140 : 0 : }