Regions marked with MEMBLOCK_PTEMAP are mapped at page granularity so their direct mapping can be modified without splitting blocks at runtime. Introduce direct_map_is_ptemap(), which walks the kernel page tables during init to verify that every page in a range is backed by a present non-contiguous PTE.
Signed-off-by: Vincent Donnefort vdonnefort@google.com --- arch/arm64/include/asm/set_memory.h | 4 ++ arch/arm64/mm/pageattr.c | 65 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+)
diff --git a/arch/arm64/include/asm/set_memory.h b/arch/arm64/include/asm/set_memory.h index 0091ba12200e..b6ec938703d6 100644 --- a/arch/arm64/include/asm/set_memory.h +++ b/arch/arm64/include/asm/set_memory.h @@ -3,12 +3,16 @@ #ifndef _ASM_ARM64_SET_MEMORY_H #define _ASM_ARM64_SET_MEMORY_H
+#include <linux/init.h> + #include <asm/mem_encrypt.h> #include <asm-generic/set_memory.h>
bool can_set_direct_map(void); #define can_set_direct_map can_set_direct_map
+bool __init direct_map_is_ptemap(struct page *page, unsigned int numpages); + int set_memory_valid(unsigned long addr, int numpages, int enable);
int set_direct_map_invalid_noflush(struct page *page, unsigned int numpages); diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c index de7bf95003ea..c7662fd2fcb2 100644 --- a/arch/arm64/mm/pageattr.c +++ b/arch/arm64/mm/pageattr.c @@ -102,6 +102,71 @@ bool can_set_direct_map(void) arm64_kfence_can_set_direct_map() || is_realm_world(); }
+static int __init direct_map_is_ptemap_pud_entry(pud_t *pud, unsigned long addr, + unsigned long next, + struct mm_walk *walk) +{ + if (pud_leaf(pudp_get(pud))) + return -EINVAL; + + return 0; +} + +static int __init direct_map_is_ptemap_pmd_entry(pmd_t *pmd, unsigned long addr, + unsigned long next, + struct mm_walk *walk) +{ + if (pmd_leaf(pmdp_get(pmd))) + return -EINVAL; + + return 0; +} + +static int __init direct_map_is_ptemap_pte_entry(pte_t *pte, unsigned long addr, + unsigned long next, + struct mm_walk *walk) +{ + pte_t val = __ptep_get(pte); + + if (!pte_present(val) || pte_cont(val)) + return -EINVAL; + + return 0; +} + +static int __init direct_map_is_ptemap_pte_hole(unsigned long addr, + unsigned long next, int depth, + struct mm_walk *walk) +{ + return -EINVAL; +} + +static const struct mm_walk_ops direct_map_is_ptemap_ops __initconst = { + .pud_entry = direct_map_is_ptemap_pud_entry, + .pmd_entry = direct_map_is_ptemap_pmd_entry, + .pte_entry = direct_map_is_ptemap_pte_entry, + .pte_hole = direct_map_is_ptemap_pte_hole, +}; + +bool __init direct_map_is_ptemap(struct page *page, unsigned int numpages) +{ + unsigned long addr, end; + + if (!pfn_valid(page_to_pfn(page)) || !numpages) + return false; + + addr = (unsigned long)page_address(page); + end = addr + PAGE_SIZE * numpages; + + /* + * Called during boot before runtime linear-map splitting, so it is + * safe to walk the page-table lockless. + */ + return !walk_kernel_page_table_range_lockless(addr, end, + &direct_map_is_ptemap_ops, + NULL, NULL); +} + static int update_range_prot(unsigned long start, unsigned long size, pgprot_t set_mask, pgprot_t clear_mask) {