Introduce trans_pgd_map_range() to allow mapping arbitrary physical memory ranges into transitional page tables.
This helper allocates intermediate translation levels (P4D, PUD, PMD, and PTE) on demand using the trans_pgd allocator, enabling precise virtual-to-physical address mappings in transitional contexts such as isolated execution environments. Add comprehensive kernel documentation detailing the isolated page table mappings: Caretaker text/rodata (ROX), writable data (RW), stacks (RW), and preserved buffers without cloning the direct map. Signed-off-by: Pasha Tatashin <[email protected]> --- arch/arm64/include/asm/trans_pgd.h | 4 ++ arch/arm64/mm/trans_pgd.c | 70 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/arch/arm64/include/asm/trans_pgd.h b/arch/arm64/include/asm/trans_pgd.h index 033d400a4ea4..1e17ae145b0c 100644 --- a/arch/arm64/include/asm/trans_pgd.h +++ b/arch/arm64/include/asm/trans_pgd.h @@ -30,6 +30,10 @@ struct trans_pgd_info { int trans_pgd_create_copy(struct trans_pgd_info *info, pgd_t **trans_pgd, unsigned long start, unsigned long end); +int trans_pgd_map_range(struct trans_pgd_info *info, pgd_t *trans_pgd, + phys_addr_t pa, unsigned long va, size_t size, + pgprot_t prot); + int trans_pgd_idmap_page(struct trans_pgd_info *info, phys_addr_t *trans_ttbr0, unsigned long *t0sz, void *page); diff --git a/arch/arm64/mm/trans_pgd.c b/arch/arm64/mm/trans_pgd.c index cca9706a875c..f0a55d4270cc 100644 --- a/arch/arm64/mm/trans_pgd.c +++ b/arch/arm64/mm/trans_pgd.c @@ -200,6 +200,76 @@ int trans_pgd_create_copy(struct trans_pgd_info *info, pgd_t **dst_pgdp, return rc; } +/** + * trans_pgd_map_range - Map a physical address range into a transition page table + * @info: Transition page table allocation info containing the page allocator + * @trans_pgd: Root transition page table pointer + * @pa: Physical address to map + * @va: Virtual address to map to + * @size: Size of the range to map in bytes (page-aligned) + * @prot: Page protection attributes (e.g. PAGE_KERNEL, PAGE_KERNEL_ROX) + * + * Populates the page table hierarchy (P4D, PUD, PMD, PTE) allocating new + * intermediate tables using info->trans_alloc_page() as needed. Used by + * liveupdate Caretaker to construct isolated page tables. + * + * Return: 0 on success, -ENOMEM on allocation failure. + */ +int trans_pgd_map_range(struct trans_pgd_info *info, pgd_t *trans_pgd, + phys_addr_t pa, unsigned long va, size_t size, + pgprot_t prot) +{ + unsigned long end = va + size; + unsigned long addr = va; + + while (addr < end) { + pgd_t *pgdp = pgd_offset_pgd(trans_pgd, addr); + p4d_t *p4dp; + pud_t *pudp; + pmd_t *pmdp; + pte_t *ptep; + + if (pgd_none(READ_ONCE(*pgdp))) { + p4dp = trans_alloc(info); + if (!p4dp) + return -ENOMEM; + pgd_populate(&init_mm, pgdp, p4dp); + } + p4dp = p4d_offset(pgdp, addr); + + if (p4d_none(READ_ONCE(*p4dp))) { + pudp = trans_alloc(info); + if (!pudp) + return -ENOMEM; + p4d_populate(&init_mm, p4dp, pudp); + } + pudp = pud_offset(p4dp, addr); + + if (pud_none(READ_ONCE(*pudp))) { + pmdp = trans_alloc(info); + if (!pmdp) + return -ENOMEM; + pud_populate(&init_mm, pudp, pmdp); + } + pmdp = pmd_offset(pudp, addr); + + if (pmd_none(READ_ONCE(*pmdp))) { + ptep = trans_alloc(info); + if (!ptep) + return -ENOMEM; + pmd_populate_kernel(&init_mm, pmdp, ptep); + } + ptep = pte_offset_kernel(pmdp, addr); + + set_pte(ptep, pfn_pte(PHYS_PFN(pa), prot)); + + addr += PAGE_SIZE; + pa += PAGE_SIZE; + } + + return 0; +} + /* * The page we want to idmap may be outside the range covered by VA_BITS that * can be built using the kernel's p?d_populate() helpers. As a one off, for a -- 2.55.0.1082.g2b9226bbc0-goog

