* [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA
@ 2026-05-19 3:17 Xiaolei Wang
2026-06-09 5:34 ` Xiaolei Wang
0 siblings, 1 reply; 4+ messages in thread
From: Xiaolei Wang @ 2026-05-19 3:17 UTC (permalink / raw)
To: k-hagio-ab, yamazaki-msmt, bhe, kexec; +Cc: Xiaolei.Wang
From: Xiaolei wang <xiaolei.wang@windriver.com>
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 <xiaolei.wang@windriver.com>
---
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;
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA
2026-05-19 3:17 [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA Xiaolei Wang
@ 2026-06-09 5:34 ` Xiaolei Wang
2026-06-10 0:46 ` Baoquan He
0 siblings, 1 reply; 4+ messages in thread
From: Xiaolei Wang @ 2026-06-09 5:34 UTC (permalink / raw)
To: k-hagio-ab, yamazaki-msmt, bhe, kexec, xiaoleiwangxiaolei
Humble ping...
Can anyone help review this patch?
thanks
xiaolei
On 5/19/26 11:17, Xiaolei Wang wrote:
> From: Xiaolei wang <xiaolei.wang@windriver.com>
>
> 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 <xiaolei.wang@windriver.com>
> ---
> 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;
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA
2026-06-09 5:34 ` Xiaolei Wang
@ 2026-06-10 0:46 ` Baoquan He
2026-06-10 2:13 ` HAGIO KAZUHITO(萩尾 一仁)
0 siblings, 1 reply; 4+ messages in thread
From: Baoquan He @ 2026-06-10 0:46 UTC (permalink / raw)
To: Xiaolei Wang; +Cc: k-hagio-ab, yamazaki-msmt, bhe, kexec, xiaoleiwangxiaolei
On 06/09/26 at 01:34pm, Xiaolei Wang wrote:
> Humble ping...
>
> Can anyone help review this patch?
Are you posting a patch for makedumpfile utility? If yes, you should
indicate that in patch subject, such as
[PATCH][makedumpfile] arm64: Add 5-level page table support for 4K pages with
I thought it's an arm64 kernel patch.
>
> thanks
>
> xiaolei
>
> On 5/19/26 11:17, Xiaolei Wang wrote:
> > From: Xiaolei wang <xiaolei.wang@windriver.com>
> >
> > 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 <xiaolei.wang@windriver.com>
> > ---
> > 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;
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA
2026-06-10 0:46 ` Baoquan He
@ 2026-06-10 2:13 ` HAGIO KAZUHITO(萩尾 一仁)
0 siblings, 0 replies; 4+ messages in thread
From: HAGIO KAZUHITO(萩尾 一仁) @ 2026-06-10 2:13 UTC (permalink / raw)
To: Baoquan He, Xiaolei Wang
Cc: YAMAZAKI MASAMITSU(山崎 真光),
bhe@redhat.com, kexec@lists.infradead.org,
xiaoleiwangxiaolei@gmail.com
On 2026/06/10 9:46, Baoquan He wrote:
> On 06/09/26 at 01:34pm, Xiaolei Wang wrote:
>> Humble ping...
>>
>> Can anyone help review this patch?
>
> Are you posting a patch for makedumpfile utility? If yes, you should
> indicate that in patch subject, such as
>
> [PATCH][makedumpfile] arm64: Add 5-level page table support for 4K pages with
thank you for the comment, Baoquan.
Hi Xiaolei,
sorry for the delay. there is no need to repost this.
I'm working on Tao's patchset, please wait for a while.
Thanks,
Kazu
>
> I thought it's an arm64 kernel patch.
>
>>
>> thanks
>>
>> xiaolei
>>
>> On 5/19/26 11:17, Xiaolei Wang wrote:
>>> From: Xiaolei wang <xiaolei.wang@windriver.com>
>>>
>>> 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 <xiaolei.wang@windriver.com>
>>> ---
>>> 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;
>>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-06-10 2:14 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-19 3:17 [PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA Xiaolei Wang
2026-06-09 5:34 ` Xiaolei Wang
2026-06-10 0:46 ` Baoquan He
2026-06-10 2:13 ` HAGIO KAZUHITO(萩尾 一仁)
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.