[PATCH v9 06/10] arm64: Implement can_set_direct_map_range()
From: Vincent Donnefort
Date: Wed Sep 02 2026 - 07:04:28 EST
For systems where can_set_direct_map() is false, it is still possible
that a subregion of the direct map can be modified if it is mapped at
the last-level. Add an implementation for can_set_direct_map_range(). It
falls back to a page table walk to verify the mapping level if
can_set_direct_map() is false.
Signed-off-by: Vincent Donnefort <vdonnefort@xxxxxxxxxx>
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h
index 90f61b17275e..1a37f4ef130b 100644
--- a/arch/arm64/include/asm/set_memory.h
+++ b/arch/arm64/include/asm/set_memory.h
@@ -9,6 +9,9 @@
bool can_set_direct_map(void);
#define can_set_direct_map can_set_direct_map
+bool can_set_direct_map_range(struct page *page, unsigned long nr_pages);
+#define can_set_direct_map_range can_set_direct_map_range
+
int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page);
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 58898ae53f5d..c59ef17eb0d0 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -103,6 +103,48 @@ bool can_set_direct_map(void)
system_supports_bbml3();
}
+bool can_set_direct_map_range(struct page *page, unsigned long nr_pages)
+{
+ unsigned long addr = (unsigned long)page_address(page);
+ unsigned long end = addr + nr_pages * PAGE_SIZE;
+
+ if (can_set_direct_map())
+ return true;
+
+ /*
+ * If !can_set_direct_map() then no one can split blocks and it is safe
+ * to walk the page-table lockless.
+ */
+ while (addr < end) {
+ pud_t *pudp, pud;
+ pmd_t *pmdp, pmd;
+ pgd_t *pgdp;
+ p4d_t *p4dp;
+
+ pgdp = pgd_offset_k(addr);
+ if (pgd_none(READ_ONCE(*pgdp)))
+ return false;
+
+ p4dp = p4d_offset(pgdp, addr);
+ if (p4d_none(READ_ONCE(*p4dp)))
+ return false;
+
+ pudp = pud_offset(p4dp, addr);
+ pud = READ_ONCE(*pudp);
+ if (pud_none(pud) || pud_leaf(pud))
+ return false;
+
+ pmdp = pmd_offset(pudp, addr);
+ pmd = READ_ONCE(*pmdp);
+ if (pmd_none(pmd) || pmd_leaf(pmd))
+ return false;
+
+ addr = pmd_addr_end(addr, end);
+ }
+
+ return true;
+}
+
static int update_range_prot(unsigned long start, unsigned long size,
pgprot_t set_mask, pgprot_t clear_mask)
{
--
2.55.0.970.g62bdec98f9-goog