summaryrefslogtreecommitdiffstats
path: root/arch/arm64/mm/fixmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/mm/fixmap.c')
-rw-r--r--arch/arm64/mm/fixmap.c148
1 files changed, 68 insertions, 80 deletions
diff --git a/arch/arm64/mm/fixmap.c b/arch/arm64/mm/fixmap.c
index 7fbdd6faf500..c0a3301203bd 100644
--- a/arch/arm64/mm/fixmap.c
+++ b/arch/arm64/mm/fixmap.c
@@ -16,34 +16,77 @@
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
-static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
+#define NR_BM_PTE_TABLES \
+ SPAN_NR_ENTRIES(FIXADDR_TOT_START, FIXADDR_TOP, PMD_SHIFT)
+#define NR_BM_PMD_TABLES \
+ SPAN_NR_ENTRIES(FIXADDR_TOT_START, FIXADDR_TOP, PUD_SHIFT)
+
+static_assert(NR_BM_PMD_TABLES == 1);
+
+#define __BM_TABLE_IDX(addr, shift) \
+ (((addr) >> (shift)) - (FIXADDR_TOT_START >> (shift)))
+
+#define BM_PTE_TABLE_IDX(addr) __BM_TABLE_IDX(addr, PMD_SHIFT)
+
+static pte_t bm_pte[NR_BM_PTE_TABLES][PTRS_PER_PTE] __page_aligned_bss;
static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused;
static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;
-static inline pud_t *fixmap_pud(unsigned long addr)
+static inline pte_t *fixmap_pte(unsigned long addr)
{
- pgd_t *pgdp = pgd_offset_k(addr);
- p4d_t *p4dp = p4d_offset(pgdp, addr);
- p4d_t p4d = READ_ONCE(*p4dp);
+ return &bm_pte[BM_PTE_TABLE_IDX(addr)][pte_index(addr)];
+}
- BUG_ON(p4d_none(p4d) || p4d_bad(p4d));
+static void __init early_fixmap_init_pte(pmd_t *pmdp, unsigned long addr)
+{
+ pmd_t pmd = READ_ONCE(*pmdp);
+ pte_t *ptep;
- return pud_offset_kimg(p4dp, addr);
+ if (pmd_none(pmd)) {
+ ptep = bm_pte[BM_PTE_TABLE_IDX(addr)];
+ __pmd_populate(pmdp, __pa_symbol(ptep), PMD_TYPE_TABLE);
+ }
}
-static inline pmd_t *fixmap_pmd(unsigned long addr)
+static void __init early_fixmap_init_pmd(pud_t *pudp, unsigned long addr,
+ unsigned long end)
{
- pud_t *pudp = fixmap_pud(addr);
+ unsigned long next;
pud_t pud = READ_ONCE(*pudp);
+ pmd_t *pmdp;
- BUG_ON(pud_none(pud) || pud_bad(pud));
+ if (pud_none(pud))
+ __pud_populate(pudp, __pa_symbol(bm_pmd), PUD_TYPE_TABLE);
- return pmd_offset_kimg(pudp, addr);
+ pmdp = pmd_offset_kimg(pudp, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ early_fixmap_init_pte(pmdp, addr);
+ } while (pmdp++, addr = next, addr != end);
}
-static inline pte_t *fixmap_pte(unsigned long addr)
+
+static void __init early_fixmap_init_pud(p4d_t *p4dp, unsigned long addr,
+ unsigned long end)
{
- return &bm_pte[pte_index(addr)];
+ p4d_t p4d = READ_ONCE(*p4dp);
+ pud_t *pudp;
+
+ if (CONFIG_PGTABLE_LEVELS > 3 && !p4d_none(p4d) &&
+ p4d_page_paddr(p4d) != __pa_symbol(bm_pud)) {
+ /*
+ * We only end up here if the kernel mapping and the fixmap
+ * share the top level pgd entry, which should only happen on
+ * 16k/4 levels configurations.
+ */
+ BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
+ }
+
+ if (p4d_none(p4d))
+ __p4d_populate(p4dp, __pa_symbol(bm_pud), P4D_TYPE_TABLE);
+
+ pudp = pud_offset_kimg(p4dp, addr);
+ early_fixmap_init_pmd(pudp, addr, end);
}
/*
@@ -54,55 +97,13 @@ static inline pte_t *fixmap_pte(unsigned long addr)
*/
void __init early_fixmap_init(void)
{
- pgd_t *pgdp;
- p4d_t *p4dp, p4d;
- pud_t *pudp;
- pmd_t *pmdp;
unsigned long addr = FIXADDR_TOT_START;
+ unsigned long end = FIXADDR_TOP;
- pgdp = pgd_offset_k(addr);
- p4dp = p4d_offset(pgdp, addr);
- p4d = READ_ONCE(*p4dp);
- if (CONFIG_PGTABLE_LEVELS > 3 &&
- !(p4d_none(p4d) || p4d_page_paddr(p4d) == __pa_symbol(bm_pud))) {
- /*
- * We only end up here if the kernel mapping and the fixmap
- * share the top level pgd entry, which should only happen on
- * 16k/4 levels configurations.
- */
- BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));
- pudp = pud_offset_kimg(p4dp, addr);
- } else {
- if (p4d_none(p4d))
- __p4d_populate(p4dp, __pa_symbol(bm_pud), P4D_TYPE_TABLE);
- pudp = fixmap_pud(addr);
- }
- if (pud_none(READ_ONCE(*pudp)))
- __pud_populate(pudp, __pa_symbol(bm_pmd), PUD_TYPE_TABLE);
- pmdp = fixmap_pmd(addr);
- __pmd_populate(pmdp, __pa_symbol(bm_pte), PMD_TYPE_TABLE);
+ pgd_t *pgdp = pgd_offset_k(addr);
+ p4d_t *p4dp = p4d_offset(pgdp, addr);
- /*
- * The boot-ioremap range spans multiple pmds, for which
- * we are not prepared:
- */
- BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
- != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
-
- if ((pmdp != fixmap_pmd(__fix_to_virt(FIX_BTMAP_BEGIN)))
- || pmdp != fixmap_pmd(__fix_to_virt(FIX_BTMAP_END))) {
- WARN_ON(1);
- pr_warn("pmdp %p != %p, %p\n",
- pmdp, fixmap_pmd(__fix_to_virt(FIX_BTMAP_BEGIN)),
- fixmap_pmd(__fix_to_virt(FIX_BTMAP_END)));
- pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
- __fix_to_virt(FIX_BTMAP_BEGIN));
- pr_warn("fix_to_virt(FIX_BTMAP_END): %08lx\n",
- __fix_to_virt(FIX_BTMAP_END));
-
- pr_warn("FIX_BTMAP_END: %d\n", FIX_BTMAP_END);
- pr_warn("FIX_BTMAP_BEGIN: %d\n", FIX_BTMAP_BEGIN);
- }
+ early_fixmap_init_pud(p4dp, addr, end);
}
/*
@@ -130,6 +131,7 @@ void __set_fixmap(enum fixed_addresses idx,
void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
{
const u64 dt_virt_base = __fix_to_virt(FIX_FDT);
+ phys_addr_t dt_phys_base;
int offset;
void *dt_virt;
@@ -144,27 +146,12 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
if (!dt_phys || dt_phys % MIN_FDT_ALIGN)
return NULL;
- /*
- * Make sure that the FDT region can be mapped without the need to
- * allocate additional translation table pages, so that it is safe
- * to call create_mapping_noalloc() this early.
- *
- * On 64k pages, the FDT will be mapped using PTEs, so we need to
- * be in the same PMD as the rest of the fixmap.
- * On 4k pages, we'll use section mappings for the FDT so we only
- * have to be in the same PUD.
- */
- BUILD_BUG_ON(dt_virt_base % SZ_2M);
-
- BUILD_BUG_ON(__fix_to_virt(FIX_FDT_END) >> SWAPPER_TABLE_SHIFT !=
- __fix_to_virt(FIX_BTMAP_BEGIN) >> SWAPPER_TABLE_SHIFT);
-
- offset = dt_phys % SWAPPER_BLOCK_SIZE;
+ dt_phys_base = round_down(dt_phys, PAGE_SIZE);
+ offset = dt_phys % PAGE_SIZE;
dt_virt = (void *)dt_virt_base + offset;
/* map the first chunk so we can read the size from the header */
- create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE),
- dt_virt_base, SWAPPER_BLOCK_SIZE, prot);
+ create_mapping_noalloc(dt_phys_base, dt_virt_base, PAGE_SIZE, prot);
if (fdt_magic(dt_virt) != FDT_MAGIC)
return NULL;
@@ -173,9 +160,10 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)
if (*size > MAX_FDT_SIZE)
return NULL;
- if (offset + *size > SWAPPER_BLOCK_SIZE)
- create_mapping_noalloc(round_down(dt_phys, SWAPPER_BLOCK_SIZE), dt_virt_base,
- round_up(offset + *size, SWAPPER_BLOCK_SIZE), prot);
+ if (offset + *size > PAGE_SIZE) {
+ create_mapping_noalloc(dt_phys_base, dt_virt_base,
+ offset + *size, prot);
+ }
return dt_virt;
}