summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2026-05-29 17:01:56 +0200
committerWill Deacon <will@kernel.org>2026-06-02 16:29:15 +0100
commitecda73ae92cab57611037cec8d29dd6f2ca68fe2 (patch)
treead53e7510e8d54fd50a90e3f1422ef37978c2f04
parenta64293e993f6ee6b5dcf107d3a7aa8c6ccca029c (diff)
arm64: mm: Preserve non-contiguous descriptors when mapping DRAM
Instead of blindly overwriting existing live entries regardless of the value of their contiguous bit when mapping DRAM regions at contiguous-hint granularity, check whether the contiguous region in question contains any valid descriptors that have the contiguous bit cleared, and in that case, leave the contiguous bit unset on the entire region. This permits the logic of mapping the kernel's linear alias to be simplified in a subsequent patch. Note that this can only result in a misprogrammed contiguous bit (as per ARM ARM RNGLXZ) if the region in question already contains a mix of valid contiguous and valid non-contiguous descriptors, in which case it was already misprogrammed to begin with. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r--arch/arm64/include/asm/pgtable.h4
-rw-r--r--arch/arm64/mm/mmu.c22
2 files changed, 24 insertions, 2 deletions
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index c9e4e00a9af2..491ba0a6492d 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -182,6 +182,10 @@ static inline pteval_t __phys_to_pte_val(phys_addr_t phys)
*/
#define pte_valid_cont(pte) (pte_valid(pte) && pte_cont(pte))
/*
+ * Returns true if the pte is valid and has the contiguous bit cleared.
+ */
+#define pte_valid_noncont(pte) (pte_valid(pte) && !pte_cont(pte))
+/*
* Could the pte be present in the TLB? We must check mm_tlb_flush_pending
* so that we don't erroneously return false for pages that have been
* remapped as PROT_NONE but are yet to be flushed from the TLB.
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 1db44adc8717..1f2ec06a8736 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -187,6 +187,14 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
} while (ptep++, addr += PAGE_SIZE, addr != end);
}
+static bool pte_range_has_valid_noncont(pte_t *ptep)
+{
+ for (int i = 0; i < CONT_PTES; i++)
+ if (pte_valid_noncont(__ptep_get(&ptep[i])))
+ return true;
+ return false;
+}
+
static int alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
unsigned long end, phys_addr_t phys,
pgprot_t prot,
@@ -224,7 +232,8 @@ static int alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
/* use a contiguous mapping if the range is suitably aligned */
if ((((addr | next | phys) & ~CONT_PTE_MASK) == 0) &&
- (flags & NO_CONT_MAPPINGS) == 0)
+ (flags & NO_CONT_MAPPINGS) == 0 &&
+ !pte_range_has_valid_noncont(ptep))
__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
init_pte(ptep, addr, next, phys, __prot);
@@ -283,6 +292,14 @@ static int init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
return 0;
}
+static bool pmd_range_has_valid_noncont(pmd_t *pmdp)
+{
+ for (int i = 0; i < CONT_PMDS; i++)
+ if (pte_valid_noncont(pmd_pte(READ_ONCE(pmdp[i]))))
+ return true;
+ return false;
+}
+
static int alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
unsigned long end, phys_addr_t phys,
pgprot_t prot,
@@ -324,7 +341,8 @@ static int alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
/* use a contiguous mapping if the range is suitably aligned */
if ((((addr | next | phys) & ~CONT_PMD_MASK) == 0) &&
- (flags & NO_CONT_MAPPINGS) == 0)
+ (flags & NO_CONT_MAPPINGS) == 0 &&
+ !pmd_range_has_valid_noncont(pmdp))
__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
ret = init_pmd(pmdp, addr, next, phys, __prot, pgtable_alloc, flags);