On 9/1/26 10:15 AM, Rui Qi wrote:
RISC-V page table entries at any level may be leaf entries when V is
set and any of R/W/X is set. The riscv64 vtop walkers currently treat
non-zero intermediate entries as pointers to the next page table, so a
PMD leaf can be used as a page table address and make vtop fail with a
physical seek error.
Detect leaf entries at PGD/P4D/PUD/PMD levels and translate them using
the page size implied by that level. Continue walking only for non-leaf
table entries.
Signed-off-by: Rui Qi <qirui.001(a)bytedance.com>
---
riscv64.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/riscv64.c b/riscv64.c
index 4dcecc2f6b2c..ca26965421d7 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -602,6 +602,33 @@ riscv64_translate_pte(ulong pte, void *physaddr, ulonglong unused)
return page_present;
}
+static int
+riscv64_pte_is_leaf(ulong pte)
+{
+ return ((pte & _PAGE_PRESENT) &&
+ (pte & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)));
+}
+
+static int
+riscv64_handle_leaf_pte(ulong pte, ulong vaddr, int shift,
+ physaddr_t *paddr, int verbose)
+{
+ physaddr_t paddr_base, page_mask;
+
+ pte &= PTE_PFN_PROT_MASK;
+ paddr_base = PTOB(pte >> _PAGE_PFN_SHIFT);
+ page_mask = ~(((physaddr_t)1 << shift) - 1);
+ *paddr = (paddr_base & page_mask) + (vaddr & ~page_mask);
+
+ if (verbose) {
+ fprintf(fp, " PAGE: %016llx\n\n",
+ (ulonglong)(*paddr & page_mask));
+ riscv64_translate_pte(pte, 0, 0);
+ }
+
+ return TRUE;
+}
+
static void
riscv64_page_type_init(void)
{
@@ -655,6 +682,9 @@ riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
if (!pgd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pgd_val))
+ return riscv64_handle_leaf_pte(pgd_val, vaddr,
+ PGD_SHIFT_L3, paddr, verbose);
pgd_val &= PTE_PFN_PROT_MASK;
pmd_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -666,6 +696,9 @@ riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
if (!pmd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pmd_val))
+ return riscv64_handle_leaf_pte(pmd_val, vaddr,
+ PMD_SHIFT, paddr, verbose);
pmd_val &= PTE_PFN_PROT_MASK;
pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1237,6 +1270,9 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
if (!pgd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pgd_val))
+ return riscv64_handle_leaf_pte(pgd_val, vaddr,
+ PGD_SHIFT_L4, paddr, verbose);
pgd_val &= PTE_PFN_PROT_MASK;
pud_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1248,6 +1284,9 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PUD: %016lx => %016lx\n", pud_addr, pud_val);
if (!pud_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pud_val))
+ return riscv64_handle_leaf_pte(pud_val, vaddr,
+ PUD_SHIFT, paddr, verbose);
pud_val &= PTE_PFN_PROT_MASK;
pmd_base = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1259,6 +1298,9 @@ riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
if (!pmd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pmd_val))
+ return riscv64_handle_leaf_pte(pmd_val, vaddr,
+ PMD_SHIFT, paddr, verbose);
pmd_val &= PTE_PFN_PROT_MASK;
pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1316,6 +1358,9 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
if (!pgd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pgd_val))
+ return riscv64_handle_leaf_pte(pgd_val, vaddr,
+ PGD_SHIFT_L5, paddr, verbose);
pgd_val &= PTE_PFN_PROT_MASK;
p4d_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1327,6 +1372,9 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " P4D: %016lx => %016lx\n", p4d_addr, p4d_val);
if (!p4d_val)
goto no_page;
+ if (riscv64_pte_is_leaf(p4d_val))
+ return riscv64_handle_leaf_pte(p4d_val, vaddr,
+ P4D_SHIFT, paddr, verbose);
p4d_val &= PTE_PFN_PROT_MASK;
pud_base = (p4d_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1338,6 +1386,9 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PUD: %016lx => %016lx\n", pud_addr, pud_val);
if (!pud_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pud_val))
+ return riscv64_handle_leaf_pte(pud_val, vaddr,
+ PUD_SHIFT, paddr, verbose);
pud_val &= PTE_PFN_PROT_MASK;
pmd_base = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
@@ -1349,6 +1400,9 @@ riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
int verbose)
fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
if (!pmd_val)
goto no_page;
+ if (riscv64_pte_is_leaf(pmd_val))
+ return riscv64_handle_leaf_pte(pmd_val, vaddr,
+ PMD_SHIFT, paddr, verbose);
pmd_val &= PTE_PFN_PROT_MASK;
pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
Hi,
This patch fixes a crash utility issue on RISC-V where `vtop -k` may
fail to translate valid vmalloc/vmap addresses.
The issue was observed while analyzing a RISC-V system running the
6.12.95 kernel. For some vmalloc addresses, `vtop` failed with:
crash> vtop -k ffff8f8044680000
VIRTUAL
PHYSICAL
vtop: seek error: physical address
However, `kmem -v` showed that the same address belonged to a valid vmap
area.
Further investigation showed that the address was mapped by a leaf PTE at
an intermediate page table level, such as PMD. On RISC-V, a PTE at any
level may be a leaf entry when the V bit is set and at least one of the
R/W/X bits is set. Leaf PTEs are therefore not limited to the last page
table level.
The existing crash RISC-V page table walkers only handled leaf entries at
the final PTE level. For PGD/P4D/PUD/PMD entries, any non-zero entry was
treated as a pointer to the next-level page table. When such an entry was
actually a leaf PTE, crash incorrectly used its PFN as a page-table-page
address, which led to a physical seek error.
This patch fixes the issue by detecting leaf PTEs during the Sv39, Sv48,
and Sv57 page table walks. When a leaf entry is found at PGD/P4D/PUD/PMD
level, crash now translates the virtual address using the page size
implied by that level. Non-leaf entries continue to be handled by walking
to the next page table level.
After the fix, the previously failing address can be translated correctly,
for example:
crash> vtop -k ffff8f803c000000
VIRTUAL PHYSICAL
ffff8f803c000000 b003c000000
Addresses with an offset inside the same large mapping are also handled
correctly:
crash> vtop -k ffff8f803c123456
VIRTUAL PHYSICAL
ffff8f803c123456 b003c123456
The normal 4KB PTE mapping path was also verified to continue working.
This is a crash utility bug fix only. No kernel change is required.