[PATCH] arm64: Add 5-level page table support for 4K pages with 52-bit VA

HAGIO KAZUHITO(萩尾 一仁) k-hagio-ab at nec.com
Tue Jun 9 19:13:27 PDT 2026


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 at 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 at 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;
>>


More information about the kexec mailing list