On 2026/05/19 12:17, Xiaolei Wang wrote:
> From: Xiaolei wang <[email protected]>
> 
> makedumpfile fails with "PAGE SIZE 0x1000 and VA Bits 52 not supported"
> on ARM64 systems configured with CONFIG_ARM64_VA_BITS_52=y and 4KB page
> size. This combination requires 5-level page tables, which makedumpfile
> does not currently support.
> 
> Add support for 5-level page tables on ARM64 by:
> - Adding the P4D page table level definitions and helpers
> - Updating pud_t to contain p4d_t (reflecting the correct hierarchy)
> - Extending calculate_plat_config() to recognize 4K + 52-bit VA as
>    pgtable_level=5, and 16K + 52-bit VA as pgtable_level=4
> - Adding p4d_offset() to handle the additional translation level
> - Updating vaddr_to_paddr_arm64() to walk through PGD->P4D->PUD->PMD->PTE
> - Using vabits_actual instead of va_bits in calculate_plat_config() and
>    PTRS_PER_PGD to correctly handle the case where the kernel is compiled
>    with VA_BITS=52 but the hardware only supports 48-bit VA (runtime
>    fallback to 4-level)
> 
> The pgtable_level assignments follow arch/arm64/Kconfig:
> - 4K  + 52-bit VA → 5-level (the only 5-level configuration)
> - 16K + 52-bit VA → 4-level
> - 4K  + 48-bit VA → 4-level
> - 16K + 48-bit VA → 4-level
> 
> Fixes: https://github.com/makedumpfile/makedumpfile/issues/18
> Signed-off-by: Xiaolei Wang <[email protected]>

Sorry for the delay.  The patch looks good to me, and applied.
https://github.com/makedumpfile/makedumpfile/commit/b7587f1e8f456af717e12d59ec10e88a3527d144

Thanks,
Kazu

> ---
>   arch/arm64.c | 89 +++++++++++++++++++++++++++++++++++++++++++---------
>   1 file changed, 74 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/arm64.c b/arch/arm64.c
> index 1072178..f3f33e4 100644
> --- a/arch/arm64.c
> +++ b/arch/arm64.c
> @@ -29,6 +29,10 @@ typedef struct {
>   
>   typedef struct {
>       pgd_t pgd;
> +} p4d_t;
> +
> +typedef struct {
> +     p4d_t p4d;
>   } pud_t;
>   
>   typedef struct {
> @@ -42,6 +46,7 @@ typedef struct {
>   #define __pte(x)    ((pte_t) { (x) } )
>   #define __pmd(x)    ((pmd_t) { (x) } )
>   #define __pud(x)    ((pud_t) { (x) } )
> +#define __p4d(x)     ((p4d_t) { (x) } )
>   #define __pgd(x)    ((pgd_t) { (x) } )
>   
>   static int lpa_52_bit_support_available;
> @@ -62,7 +67,8 @@ static unsigned long kimage_voffset;
>   #define PAGE_OFFSET_48              ((0xffffffffffffffffUL) << 48)
>   
>   #define pgd_val(x)          ((x).pgd)
> -#define pud_val(x)           (pgd_val((x).pgd))
> +#define p4d_val(x)           (pgd_val((x).pgd))
> +#define pud_val(x)           (p4d_val((x).p4d))
>   #define pmd_val(x)          (pud_val((x).pud))
>   #define pte_val(x)          ((x).pte)
>   
> @@ -75,6 +81,7 @@ static unsigned long kimage_voffset;
>   typedef unsigned long pteval_t;
>   typedef unsigned long pmdval_t;
>   typedef unsigned long pudval_t;
> +typedef unsigned long p4dval_t;
>   typedef unsigned long pgdval_t;
>   
>   #define PAGE_SHIFT  PAGESHIFT()
> @@ -101,6 +108,14 @@ typedef unsigned long pgdval_t;
>   #define PUD_MASK            (~(PUD_SIZE-1))
>   #define PTRS_PER_PUD                PTRS_PER_PTE
>   
> +/*
> + * P4D_SHIFT determines the size a level 0 page table entry can map.
> + */
> +#define P4D_SHIFT            ARM64_HW_PGTABLE_LEVEL_SHIFT(0)
> +#define P4D_SIZE             (_AC(1, UL) << P4D_SHIFT)
> +#define P4D_MASK             (~(P4D_SIZE-1))
> +#define PTRS_PER_P4D         PTRS_PER_PTE
> +
>   /*
>    * PGDIR_SHIFT determines the size a top-level page table entry can map
>    * (depending on the configuration, this level can be 0, 1 or 2).
> @@ -108,7 +123,7 @@ typedef unsigned long pgdval_t;
>   #define PGDIR_SHIFT         ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - 
> (pgtable_level))
>   #define PGDIR_SIZE          (_AC(1, UL) << PGDIR_SHIFT)
>   #define PGDIR_MASK          (~(PGDIR_SIZE-1))
> -#define PTRS_PER_PGD         (1 << ((va_bits) - PGDIR_SHIFT))
> +#define PTRS_PER_PGD         (1 << ((vabits_actual) - PGDIR_SHIFT))
>   
>   /*
>    * Section address mask and size definitions.
> @@ -178,6 +193,22 @@ pgd_pte(pgd_t pgd)
>   #define __pgd_to_phys(pgd)          __pte_to_phys(pgd_pte(pgd))
>   #define pgd_offset(pgd, vaddr)              ((pgd_t *)(pgd) + 
> pgd_index(vaddr))
>   
> +/* P4D */
> +#define p4d_index(vaddr)             (((vaddr) >> P4D_SHIFT) & (PTRS_PER_P4D 
> - 1))
> +
> +static inline pte_t p4d_pte(p4d_t p4d)
> +{
> +     return __pte(p4d_val(p4d));
> +}
> +
> +#define __p4d_to_phys(p4d)           __pte_to_phys(p4d_pte(p4d))
> +
> +static inline unsigned long
> +p4d_page_paddr(p4d_t p4d)
> +{
> +     return __p4d_to_phys(p4d);
> +}
> +
>   static inline pte_t pud_pte(pud_t pud)
>   {
>       return __pte(pud_val(pud));
> @@ -237,13 +268,22 @@ __pa(unsigned long vaddr)
>               return (vaddr - kimage_voffset);
>   }
>   
> +static p4d_t *
> +p4d_offset(pgd_t *pgda, pgd_t *pgdv, unsigned long vaddr)
> +{
> +     if (pgtable_level > 4)
> +             return (p4d_t *)(pgd_page_paddr(*pgdv) + p4d_index(vaddr) * 
> sizeof(p4d_t));
> +     else
> +             return (p4d_t *)(pgda);
> +}
> +
>   static pud_t *
> -pud_offset(pgd_t *pgda, pgd_t *pgdv, unsigned long vaddr)
> +pud_offset(p4d_t *p4da, p4d_t *p4dv, unsigned long vaddr)
>   {
>       if (pgtable_level > 3)
> -             return (pud_t *)(pgd_page_paddr(*pgdv) + pud_index(vaddr) * 
> sizeof(pud_t));
> +             return (pud_t *)(p4d_page_paddr(*p4dv) + pud_index(vaddr) * 
> sizeof(pud_t));
>       else
> -             return (pud_t *)(pgda);
> +             return (pud_t *)(p4da);
>   }
>   
>   static pmd_t *
> @@ -257,20 +297,32 @@ pmd_offset(pud_t *puda, pud_t *pudv, unsigned long 
> vaddr)
>   
>   static int calculate_plat_config(void)
>   {
> -     /* derive pgtable_level as per arch/arm64/Kconfig */
> -     if ((PAGESIZE() == SZ_16K && va_bits == 36) ||
> -                     (PAGESIZE() == SZ_64K && va_bits == 42)) {
> +     /*
> +      * Derive pgtable_level as per arch/arm64/Kconfig.
> +      * Use vabits_actual (runtime VA size) rather than va_bits 
> (compile-time)
> +      * because the kernel may reduce the VA space at boot if the hardware
> +      * does not support 52-bit VA (LVA). In that case va_bits=52 but
> +      * vabits_actual=48, and the page tables are 4-level, not 5-level.
> +      */
> +     int va = vabits_actual;
> +
> +     if ((PAGESIZE() == SZ_16K && va == 36) ||
> +                     (PAGESIZE() == SZ_64K && va == 42)) {
>               pgtable_level = 2;
> -     } else if ((PAGESIZE() == SZ_64K && va_bits == 48) ||
> -                     (PAGESIZE() == SZ_64K && va_bits == 52) ||
> -                     (PAGESIZE() == SZ_4K && va_bits == 39) ||
> -                     (PAGESIZE() == SZ_16K && va_bits == 47)) {
> +     } else if ((PAGESIZE() == SZ_64K && va == 48) ||
> +                     (PAGESIZE() == SZ_64K && va == 52) ||
> +                     (PAGESIZE() == SZ_4K && va == 39) ||
> +                     (PAGESIZE() == SZ_16K && va == 47)) {
>               pgtable_level = 3;
> -     } else if ((PAGESIZE() != SZ_64K && va_bits == 48)) {
> +     } else if ((PAGESIZE() == SZ_4K && va == 48) ||
> +                     (PAGESIZE() == SZ_16K && va == 48) ||
> +                     (PAGESIZE() == SZ_16K && va == 52)) {
>               pgtable_level = 4;
> +     } else if (PAGESIZE() == SZ_4K && va == 52) {
> +             pgtable_level = 5;
>       } else {
>               ERRMSG("PAGE SIZE %#lx and VA Bits %d not supported\n",
> -                             PAGESIZE(), va_bits);
> +                             PAGESIZE(), va);
>               return FALSE;
>       }
>       DEBUG_MSG("pgtable_level: %d\n", pgtable_level);
> @@ -548,6 +600,7 @@ vaddr_to_paddr_arm64(unsigned long vaddr)
>       unsigned long long paddr = NOT_PADDR;
>       unsigned long long swapper_phys;
>       pgd_t   *pgda, pgdv;
> +     p4d_t   *p4da, p4dv;
>       pud_t   *puda, pudv;
>       pmd_t   *pmda, pmdv;
>       pte_t   *ptea, ptev;
> @@ -565,7 +618,13 @@ vaddr_to_paddr_arm64(unsigned long vaddr)
>               return NOT_PADDR;
>       }
>   
> -     puda = pud_offset(pgda, &pgdv, vaddr);
> +     p4da = p4d_offset(pgda, &pgdv, vaddr);
> +     if (!readmem(PADDR, (unsigned long long)p4da, &p4dv, sizeof(p4dv))) {
> +             ERRMSG("Can't read p4d\n");
> +             return NOT_PADDR;
> +     }
> +
> +     puda = pud_offset(p4da, &p4dv, vaddr);
>       if (!readmem(PADDR, (unsigned long long)puda, &pudv, sizeof(pudv))) {
>               ERRMSG("Can't read pud\n");
>               return NOT_PADDR;

Reply via email to