162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Fixmap manipulation code 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bug.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/libfdt.h> 1062306a36Sopenharmony_ci#include <linux/memory.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/sizes.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/fixmap.h> 1562306a36Sopenharmony_ci#include <asm/kernel-pgtable.h> 1662306a36Sopenharmony_ci#include <asm/pgalloc.h> 1762306a36Sopenharmony_ci#include <asm/tlbflush.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define NR_BM_PTE_TABLES \ 2062306a36Sopenharmony_ci SPAN_NR_ENTRIES(FIXADDR_TOT_START, FIXADDR_TOP, PMD_SHIFT) 2162306a36Sopenharmony_ci#define NR_BM_PMD_TABLES \ 2262306a36Sopenharmony_ci SPAN_NR_ENTRIES(FIXADDR_TOT_START, FIXADDR_TOP, PUD_SHIFT) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic_assert(NR_BM_PMD_TABLES == 1); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define __BM_TABLE_IDX(addr, shift) \ 2762306a36Sopenharmony_ci (((addr) >> (shift)) - (FIXADDR_TOT_START >> (shift))) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __page_aligned_bss; 3262306a36Sopenharmony_cistatic pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused; 3362306a36Sopenharmony_cistatic pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline pte_t *fixmap_pte(unsigned long addr) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci return &bm_pte[BM_PTE_TABLE_IDX(addr)][pte_index(addr)]; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void __init early_fixmap_init_pte(pmd_t *pmdp, unsigned long addr) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci pmd_t pmd = READ_ONCE(*pmdp); 4362306a36Sopenharmony_ci pte_t *ptep; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (pmd_none(pmd)) { 4662306a36Sopenharmony_ci ptep = bm_pte[BM_PTE_TABLE_IDX(addr)]; 4762306a36Sopenharmony_ci __pmd_populate(pmdp, __pa_symbol(ptep), PMD_TYPE_TABLE); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void __init early_fixmap_init_pmd(pud_t *pudp, unsigned long addr, 5262306a36Sopenharmony_ci unsigned long end) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned long next; 5562306a36Sopenharmony_ci pud_t pud = READ_ONCE(*pudp); 5662306a36Sopenharmony_ci pmd_t *pmdp; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (pud_none(pud)) 5962306a36Sopenharmony_ci __pud_populate(pudp, __pa_symbol(bm_pmd), PUD_TYPE_TABLE); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci pmdp = pmd_offset_kimg(pudp, addr); 6262306a36Sopenharmony_ci do { 6362306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 6462306a36Sopenharmony_ci early_fixmap_init_pte(pmdp, addr); 6562306a36Sopenharmony_ci } while (pmdp++, addr = next, addr != end); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void __init early_fixmap_init_pud(p4d_t *p4dp, unsigned long addr, 7062306a36Sopenharmony_ci unsigned long end) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci p4d_t p4d = READ_ONCE(*p4dp); 7362306a36Sopenharmony_ci pud_t *pudp; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (CONFIG_PGTABLE_LEVELS > 3 && !p4d_none(p4d) && 7662306a36Sopenharmony_ci p4d_page_paddr(p4d) != __pa_symbol(bm_pud)) { 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * We only end up here if the kernel mapping and the fixmap 7962306a36Sopenharmony_ci * share the top level pgd entry, which should only happen on 8062306a36Sopenharmony_ci * 16k/4 levels configurations. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES)); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (p4d_none(p4d)) 8662306a36Sopenharmony_ci __p4d_populate(p4dp, __pa_symbol(bm_pud), P4D_TYPE_TABLE); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci pudp = pud_offset_kimg(p4dp, addr); 8962306a36Sopenharmony_ci early_fixmap_init_pmd(pudp, addr, end); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * The p*d_populate functions call virt_to_phys implicitly so they can't be used 9462306a36Sopenharmony_ci * directly on kernel symbols (bm_p*d). This function is called too early to use 9562306a36Sopenharmony_ci * lm_alias so __p*d_populate functions must be used to populate with the 9662306a36Sopenharmony_ci * physical address from __pa_symbol. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_civoid __init early_fixmap_init(void) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci unsigned long addr = FIXADDR_TOT_START; 10162306a36Sopenharmony_ci unsigned long end = FIXADDR_TOP; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pgd_t *pgdp = pgd_offset_k(addr); 10462306a36Sopenharmony_ci p4d_t *p4dp = p4d_offset(pgdp, addr); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci early_fixmap_init_pud(p4dp, addr, end); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Unusually, this is also called in IRQ context (ghes_iounmap_irq) so if we 11162306a36Sopenharmony_ci * ever need to use IPIs for TLB broadcasting, then we're in trouble here. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_civoid __set_fixmap(enum fixed_addresses idx, 11462306a36Sopenharmony_ci phys_addr_t phys, pgprot_t flags) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned long addr = __fix_to_virt(idx); 11762306a36Sopenharmony_ci pte_t *ptep; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ptep = fixmap_pte(addr); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (pgprot_val(flags)) { 12462306a36Sopenharmony_ci set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags)); 12562306a36Sopenharmony_ci } else { 12662306a36Sopenharmony_ci pte_clear(&init_mm, addr, ptep); 12762306a36Sopenharmony_ci flush_tlb_kernel_range(addr, addr+PAGE_SIZE); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci const u64 dt_virt_base = __fix_to_virt(FIX_FDT); 13462306a36Sopenharmony_ci phys_addr_t dt_phys_base; 13562306a36Sopenharmony_ci int offset; 13662306a36Sopenharmony_ci void *dt_virt; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* 13962306a36Sopenharmony_ci * Check whether the physical FDT address is set and meets the minimum 14062306a36Sopenharmony_ci * alignment requirement. Since we are relying on MIN_FDT_ALIGN to be 14162306a36Sopenharmony_ci * at least 8 bytes so that we can always access the magic and size 14262306a36Sopenharmony_ci * fields of the FDT header after mapping the first chunk, double check 14362306a36Sopenharmony_ci * here if that is indeed the case. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci BUILD_BUG_ON(MIN_FDT_ALIGN < 8); 14662306a36Sopenharmony_ci if (!dt_phys || dt_phys % MIN_FDT_ALIGN) 14762306a36Sopenharmony_ci return NULL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci dt_phys_base = round_down(dt_phys, PAGE_SIZE); 15062306a36Sopenharmony_ci offset = dt_phys % PAGE_SIZE; 15162306a36Sopenharmony_ci dt_virt = (void *)dt_virt_base + offset; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* map the first chunk so we can read the size from the header */ 15462306a36Sopenharmony_ci create_mapping_noalloc(dt_phys_base, dt_virt_base, PAGE_SIZE, prot); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (fdt_magic(dt_virt) != FDT_MAGIC) 15762306a36Sopenharmony_ci return NULL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci *size = fdt_totalsize(dt_virt); 16062306a36Sopenharmony_ci if (*size > MAX_FDT_SIZE) 16162306a36Sopenharmony_ci return NULL; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (offset + *size > PAGE_SIZE) { 16462306a36Sopenharmony_ci create_mapping_noalloc(dt_phys_base, dt_virt_base, 16562306a36Sopenharmony_ci offset + *size, prot); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return dt_virt; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* 17262306a36Sopenharmony_ci * Copy the fixmap region into a new pgdir. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_civoid __init fixmap_copy(pgd_t *pgdir) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (!READ_ONCE(pgd_val(*pgd_offset_pgd(pgdir, FIXADDR_TOT_START)))) { 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * The fixmap falls in a separate pgd to the kernel, and doesn't 17962306a36Sopenharmony_ci * live in the carveout for the swapper_pg_dir. We can simply 18062306a36Sopenharmony_ci * re-use the existing dir for the fixmap. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci set_pgd(pgd_offset_pgd(pgdir, FIXADDR_TOT_START), 18362306a36Sopenharmony_ci READ_ONCE(*pgd_offset_k(FIXADDR_TOT_START))); 18462306a36Sopenharmony_ci } else if (CONFIG_PGTABLE_LEVELS > 3) { 18562306a36Sopenharmony_ci pgd_t *bm_pgdp; 18662306a36Sopenharmony_ci p4d_t *bm_p4dp; 18762306a36Sopenharmony_ci pud_t *bm_pudp; 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * The fixmap shares its top level pgd entry with the kernel 19062306a36Sopenharmony_ci * mapping. This can really only occur when we are running 19162306a36Sopenharmony_ci * with 16k/4 levels, so we can simply reuse the pud level 19262306a36Sopenharmony_ci * entry instead. 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES)); 19562306a36Sopenharmony_ci bm_pgdp = pgd_offset_pgd(pgdir, FIXADDR_TOT_START); 19662306a36Sopenharmony_ci bm_p4dp = p4d_offset(bm_pgdp, FIXADDR_TOT_START); 19762306a36Sopenharmony_ci bm_pudp = pud_set_fixmap_offset(bm_p4dp, FIXADDR_TOT_START); 19862306a36Sopenharmony_ci pud_populate(&init_mm, bm_pudp, lm_alias(bm_pmd)); 19962306a36Sopenharmony_ci pud_clear_fixmap(); 20062306a36Sopenharmony_ci } else { 20162306a36Sopenharmony_ci BUG(); 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 204