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


Reply via email to