When a page table entry has _PAGE_LEAF set at PGD/P4D/PUD/PMD level,
the current code incorrectly falls through to the next level instead
of computing the correct physical address. Fix this by calculating
the paddr with the proper level shift and returning directly.

Co-developed-by: Rui Qi <[email protected]>
Signed-off-by: Shuan He <[email protected]>
---
 arch/riscv64.c | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/arch/riscv64.c b/arch/riscv64.c
index 28365fa0cc5b..67755331de24 100644
--- a/arch/riscv64.c
+++ b/arch/riscv64.c
@@ -107,8 +107,12 @@ vtop_riscv64(pgd_t * pgd, unsigned long vaddr, long 
va_bits)
 
        pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
 
-       if (pt_val & _PAGE_LEAF)
-               goto out;
+       if (pt_val & _PAGE_LEAF) {
+               unsigned long shift = (va_bits == VA_BITS_SV57) ? PGD_SHIFT_L5 :
+                                     (va_bits == VA_BITS_SV48) ? PGD_SHIFT_L4 
: PGD_SHIFT_L3;
+               paddr = (pt_phys & ~((1ULL << shift) - 1)) + (vaddr & ((1ULL << 
shift) - 1));
+               goto invalid;
+       }
 
        if (va_bits == VA_BITS_SV57)
                goto p4d;
@@ -133,8 +137,10 @@ p4d:
 
        pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
 
-       if (pt_val & _PAGE_LEAF)
-               goto out;
+       if (pt_val & _PAGE_LEAF) {
+               paddr = (pt_phys & ~((1ULL << P4D_SHIFT) - 1)) + (vaddr & 
((1ULL << P4D_SHIFT) - 1));
+               goto invalid;
+       }
 pud:
        /* PUD */
        puda = (pud_t *)(pt_phys) + pud_index(vaddr);
@@ -152,8 +158,10 @@ pud:
 
        pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
 
-       if(pt_val & _PAGE_LEAF)
-               goto out;
+       if (pt_val & _PAGE_LEAF) {
+               paddr = (pt_phys & ~((1ULL << PUD_SHIFT) - 1)) + (vaddr & 
((1ULL << PUD_SHIFT) - 1));
+               goto invalid;
+       }
 pmd:
        /* PMD */
        pmda = (pmd_t *)(pt_phys) + pmd_index(vaddr);
@@ -171,8 +179,10 @@ pmd:
 
        pt_phys = (pt_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
 
-       if (pt_val & _PAGE_LEAF)
-               goto out;
+       if (pt_val & _PAGE_LEAF) {
+               paddr = (pt_phys & ~((1ULL << PMD_SHIFT) - 1)) + (vaddr & 
((1ULL << PMD_SHIFT) - 1));
+               goto invalid;
+       }
 
        /* PTE */
        ptea = (pte_t *)(pt_phys) + pte_index(vaddr);
-- 
2.20.1

Reply via email to