|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | +/* |
| 3 | + * Fixmap manipulation code |
| 4 | + */ |
| 5 | + |
| 6 | +#include <linux/bug.h> |
| 7 | +#include <linux/init.h> |
| 8 | +#include <linux/kernel.h> |
| 9 | +#include <linux/libfdt.h> |
| 10 | +#include <linux/memory.h> |
| 11 | +#include <linux/mm.h> |
| 12 | +#include <linux/sizes.h> |
| 13 | + |
| 14 | +#include <asm/fixmap.h> |
| 15 | +#include <asm/kernel-pgtable.h> |
| 16 | +#include <asm/pgalloc.h> |
| 17 | +#include <asm/tlbflush.h> |
| 18 | + |
| 19 | +static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss; |
| 20 | +static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused; |
| 21 | +static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused; |
| 22 | + |
| 23 | +static inline pud_t *fixmap_pud(unsigned long addr) |
| 24 | +{ |
| 25 | + pgd_t *pgdp = pgd_offset_k(addr); |
| 26 | + p4d_t *p4dp = p4d_offset(pgdp, addr); |
| 27 | + p4d_t p4d = READ_ONCE(*p4dp); |
| 28 | + |
| 29 | + BUG_ON(p4d_none(p4d) || p4d_bad(p4d)); |
| 30 | + |
| 31 | + return pud_offset_kimg(p4dp, addr); |
| 32 | +} |
| 33 | + |
| 34 | +static inline pmd_t *fixmap_pmd(unsigned long addr) |
| 35 | +{ |
| 36 | + pud_t *pudp = fixmap_pud(addr); |
| 37 | + pud_t pud = READ_ONCE(*pudp); |
| 38 | + |
| 39 | + BUG_ON(pud_none(pud) || pud_bad(pud)); |
| 40 | + |
| 41 | + return pmd_offset_kimg(pudp, addr); |
| 42 | +} |
| 43 | + |
| 44 | +static inline pte_t *fixmap_pte(unsigned long addr) |
| 45 | +{ |
| 46 | + return &bm_pte[pte_index(addr)]; |
| 47 | +} |
| 48 | + |
| 49 | +/* |
| 50 | + * The p*d_populate functions call virt_to_phys implicitly so they can't be used |
| 51 | + * directly on kernel symbols (bm_p*d). This function is called too early to use |
| 52 | + * lm_alias so __p*d_populate functions must be used to populate with the |
| 53 | + * physical address from __pa_symbol. |
| 54 | + */ |
| 55 | +void __init early_fixmap_init(void) |
| 56 | +{ |
| 57 | + pgd_t *pgdp; |
| 58 | + p4d_t *p4dp, p4d; |
| 59 | + pud_t *pudp; |
| 60 | + pmd_t *pmdp; |
| 61 | + unsigned long addr = FIXADDR_TOT_START; |
| 62 | + |
| 63 | + pgdp = pgd_offset_k(addr); |
| 64 | + p4dp = p4d_offset(pgdp, addr); |
| 65 | + p4d = READ_ONCE(*p4dp); |
| 66 | + if (CONFIG_PGTABLE_LEVELS > 3 && |
| 67 | + !(p4d_none(p4d) || p4d_page_paddr(p4d) == __pa_symbol(bm_pud))) { |
| 68 | + /* |
| 69 | + * We only end up here if the kernel mapping and the fixmap |
| 70 | + * share the top level pgd entry, which should only happen on |
| 71 | + * 16k/4 levels configurations. |
| 72 | + */ |
| 73 | + BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES)); |
| 74 | + pudp = pud_offset_kimg(p4dp, addr); |
| 75 | + } else { |
| 76 | + if (p4d_none(p4d)) |
| 77 | + __p4d_populate(p4dp, __pa_symbol(bm_pud), P4D_TYPE_TABLE); |
| 78 | + pudp = fixmap_pud(addr); |
| 79 | + } |
| 80 | + if (pud_none(READ_ONCE(*pudp))) |
| 81 | + __pud_populate(pudp, __pa_symbol(bm_pmd), PUD_TYPE_TABLE); |
| 82 | + pmdp = fixmap_pmd(addr); |
| 83 | + __pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE); |
| 84 | + |
| 85 | + /* |
| 86 | + * The boot-ioremap range spans multiple pmds, for which |
| 87 | + * we are not prepared: |
| 88 | + */ |
| 89 | + BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT) |
| 90 | + != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT)); |
| 91 | + |
| 92 | + if ((pmdp != fixmap_pmd(__fix_to_virt(FIX_BTMAP_BEGIN))) |
| 93 | + || pmdp != fixmap_pmd(__fix_to_virt(FIX_BTMAP_END))) { |
| 94 | + WARN_ON(1); |
| 95 | + pr_warn("pmdp %p != %p, %p\n", |
| 96 | + pmdp, fixmap_pmd(__fix_to_virt(FIX_BTMAP_BEGIN)), |
| 97 | + fixmap_pmd(__fix_to_virt(FIX_BTMAP_END))); |
| 98 | + pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n", |
| 99 | + __fix_to_virt(FIX_BTMAP_BEGIN)); |
| 100 | + pr_warn("fix_to_virt(FIX_BTMAP_END): %08lx\n", |
| 101 | + __fix_to_virt(FIX_BTMAP_END)); |
| 102 | + |
| 103 | + pr_warn("FIX_BTMAP_END: %d\n", FIX_BTMAP_END); |
| 104 | + pr_warn("FIX_BTMAP_BEGIN: %d\n", FIX_BTMAP_BEGIN); |
| 105 | + } |
| 106 | +} |
| 107 | + |
| 108 | +/* |
| 109 | + * Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we |
| 110 | + * ever need to use IPIs for TLB broadcasting, then we're in trouble here. |
| 111 | + */ |
| 112 | +void __set_fixmap(enum fixed_addresses idx, |
| 113 | + phys_addr_t phys, pgprot_t flags) |
| 114 | +{ |
| 115 | + unsigned long addr = __fix_to_virt(idx); |
| 116 | + pte_t *ptep; |
| 117 | + |
| 118 | + BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); |
| 119 | + |
| 120 | + ptep = fixmap_pte(addr); |
| 121 | + |
| 122 | + if (pgprot_val(flags)) { |
| 123 | + set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags)); |
| 124 | + } else { |
| 125 | + pte_clear(&init_mm, addr, ptep); |
| 126 | + flush_tlb_kernel_range(addr, addr+PAGE_SIZE); |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | +void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) |
| 131 | +{ |
| 132 | + const u64 dt_virt_base = __fix_to_virt(FIX_FDT); |
| 133 | + int offset; |
| 134 | + void *dt_virt; |
| 135 | + |
| 136 | + /* |
| 137 | + * Check whether the physical FDT address is set and meets the minimum |
| 138 | + * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be |
| 139 | + * at least 8 bytes so that we can always access the magic and size |
| 140 | + * fields of the FDT header after mapping the first chunk, double check |
| 141 | + * here if that is indeed the case. |
| 142 | + */ |
| 143 | + BUILD_BUG_ON(MIN_FDT_ALIGN < 8); |
| 144 | + if (!dt_phys || dt_phys % MIN_FDT_ALIGN) |
| 145 | + return NULL; |
| 146 | + |
| 147 | + /* |
| 148 | + * Make sure that the FDT region can be mapped without the need to |
| 149 | + * allocate additional translation table pages, so that it is safe |
| 150 | + * to call create_mapping_noalloc() this early. |
| 151 | + * |
| 152 | + * On 64k pages, the FDT will be mapped using PTEs, so we need to |
| 153 | + * be in the same PMD as the rest of the fixmap. |
| 154 | + * On 4k pages, we'll use section mappings for the FDT so we only |
| 155 | + * have to be in the same PUD. |
| 156 | + */ |
| 157 | + BUILD_BUG_ON(dt_virt_base % SZ_2M); |
| 158 | + |
| 159 | + BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> SWAPPER_TABLE_SHIFT != |
| 160 | + __fix_to_virt(FIX_BTMAP_BEGIN) >> SWAPPER_TABLE_SHIFT); |
| 161 | + |
| 162 | + offset = dt_phys % SWAPPER_BLOCK_SIZE; |
| 163 | + dt_virt = (void *)dt_virt_base + offset; |
| 164 | + |
| 165 | + /* map the first chunk so we can read the size from the header */ |
| 166 | + create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), |
| 167 | + dt_virt_base, SWAPPER_BLOCK_SIZE, prot); |
| 168 | + |
| 169 | + if (fdt_magic(dt_virt) != FDT_MAGIC) |
| 170 | + return NULL; |
| 171 | + |
| 172 | + *size = fdt_totalsize(dt_virt); |
| 173 | + if (*size > MAX_FDT_SIZE) |
| 174 | + return NULL; |
| 175 | + |
| 176 | + if (offset + *size > SWAPPER_BLOCK_SIZE) |
| 177 | + create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base, |
| 178 | + round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot); |
| 179 | + |
| 180 | + return dt_virt; |
| 181 | +} |
| 182 | + |
| 183 | +/* |
| 184 | + * Copy the fixmap region into a new pgdir. |
| 185 | + */ |
| 186 | +void __init fixmap_copy(pgd_t *pgdir) |
| 187 | +{ |
| 188 | + if (!READ_ONCE(pgd_val(*pgd_offset_pgd(pgdir, FIXADDR_TOT_START)))) { |
| 189 | + /* |
| 190 | + * The fixmap falls in a separate pgd to the kernel, and doesn't |
| 191 | + * live in the carveout for the swapper_pg_dir. We can simply |
| 192 | + * re-use the existing dir for the fixmap. |
| 193 | + */ |
| 194 | + set_pgd(pgd_offset_pgd(pgdir, FIXADDR_TOT_START), |
| 195 | + READ_ONCE(*pgd_offset_k(FIXADDR_TOT_START))); |
| 196 | + } else if (CONFIG_PGTABLE_LEVELS > 3) { |
| 197 | + pgd_t *bm_pgdp; |
| 198 | + p4d_t *bm_p4dp; |
| 199 | + pud_t *bm_pudp; |
| 200 | + /* |
| 201 | + * The fixmap shares its top level pgd entry with the kernel |
| 202 | + * mapping. This can really only occur when we are running |
| 203 | + * with 16k/4 levels, so we can simply reuse the pud level |
| 204 | + * entry instead. |
| 205 | + */ |
| 206 | + BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES)); |
| 207 | + bm_pgdp = pgd_offset_pgd(pgdir, FIXADDR_TOT_START); |
| 208 | + bm_p4dp = p4d_offset(bm_pgdp, FIXADDR_TOT_START); |
| 209 | + bm_pudp = pud_set_fixmap_offset(bm_p4dp, FIXADDR_TOT_START); |
| 210 | + pud_populate(&init_mm, bm_pudp, lm_alias(bm_pmd)); |
| 211 | + pud_clear_fixmap(); |
| 212 | + } else { |
| 213 | + BUG(); |
| 214 | + } |
| 215 | +} |
0 commit comments