* [PATCH v3 0/2] arm64: ptdump flush fixes
@ 2026-08-14 22:24 Wei-Lin Chang
2026-08-14 22:24 ` [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware Wei-Lin Chang
2026-08-14 22:24 ` [PATCH v3 2/2] KVM: arm64: ptdump: Flush the last region Wei-Lin Chang
0 siblings, 2 replies; 5+ messages in thread
From: Wei-Lin Chang @ 2026-08-14 22:24 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, kvmarm
Cc: Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Mike Rapoport (Microsoft), Ryan Roberts,
David Hildenbrand (Arm), Anshuman Khandual, Dev Jain,
Ard Biesheuvel, Mark Rutland, Matt Fleming, Vincent Donnefort,
Sebastian Ene, Wei-Lin Chang
Hi,
This series fixes two problems around ptdumps:
1. note_page_flush(), which flushes out the last row of ptdumps, does
not account for address spaces that have IA < 64. Other than making
the last region extremely huge, the attributes of the last region
within the address spaces appear to extend all the way to 1 << 64.
2. KVM/arm64's stage-2 ptdump missed calling note_page_flush().
To address Will's comment [1], I have created an end_address field for
struct ptdump_pg_state, and initialized it with the end address of the
ptdumps. It follows the same convention as the last range->end:
exclusive end, except for the case where the address space ends at
1 << 64. In that case it is set as ULONG_MAX.
Caching the end address avoids duplicating the range iteration in
note_page_flush(), at the cost of duplicating state in ptdump_pg_state.
Series is based on v7.2-rc5.
* Changes from v2:
- Instead of scanning ptdump_state.range[] to find the end address,
cache the end address in a new field end_address when we initialize
struct ptdump_pg_state.
- Adjust KVM's struct ptdump_pg_state initialization so it uses
end_address instead of ptdump_state.range[].
- Collected Reviewed-by and Tested-by from Dev, thanks!
- v2: https://lore.kernel.org/r/20260724185431.2990395-1-weilin.chang@arm.com/
* Changes from v1:
- Instead of manually calling note_page() for flushing, fix
note_page_flush() so that it ends the ptdump at the end of the
address space.
- Changed the start address of the second marker to ULONG_MAX for KVM
ptdump, so we don't output extra marker names, and advance past the
end of the marker array.
- v1: https://lore.kernel.org/r/20260717231233.2299068-1-weilin.chang@arm.com/
Thanks!
[1]: https://lore.kernel.org/r/anXFo-igVdqrCogQ@willie-the-truck/
Wei-Lin Chang (2):
arm64: ptdump: Make note_page_flush() range aware
KVM: arm64: ptdump: Flush the last region
arch/arm64/include/asm/ptdump.h | 2 ++
arch/arm64/kvm/ptdump.c | 11 +++++++----
arch/arm64/mm/ptdump.c | 14 +++++++++++++-
3 files changed, 22 insertions(+), 5 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware
2026-08-14 22:24 [PATCH v3 0/2] arm64: ptdump flush fixes Wei-Lin Chang
@ 2026-08-14 22:24 ` Wei-Lin Chang
2026-08-15 11:28 ` Marc Zyngier
2026-08-14 22:24 ` [PATCH v3 2/2] KVM: arm64: ptdump: Flush the last region Wei-Lin Chang
1 sibling, 1 reply; 5+ messages in thread
From: Wei-Lin Chang @ 2026-08-14 22:24 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, kvmarm
Cc: Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Mike Rapoport (Microsoft), Ryan Roberts,
David Hildenbrand (Arm), Anshuman Khandual, Dev Jain,
Ard Biesheuvel, Mark Rutland, Matt Fleming, Vincent Donnefort,
Sebastian Ene, Wei-Lin Chang
note_page_flush() calls note_page() with addr == 0 and level == -1 to
dump the last row of a ptdump. addr == 0 (1 << 64 wrapped around)
renders a huge region with enormous size for address spaces with
IA bits < 64. For example the stage-2 page tables and the EFI runtime
page table.
More importantly, the last region of the address space and everything
after the address space up to 1 << 64 are merged into one row of
output. If the last region within the address space is valid, it will
appear to remain valid up to 1 << 64 with the same attributes.
Currently only the EFI runtime ptdump is affected by this, but KVM will
soon fix its stage-2 ptdump by using note_page_flush(). Here is an
example of an EFI runtime ptdump (last row):
0x0000008000000000-0x0000000000000000 17179868672G PGD
With this patch:
0x0000008000000000-0x0001000000000000 261632G PGD
To fix this, cache the end address of a ptdump in ptdump_pg_state so
note_page_flush() can call the final note_page() with the correct end
address.
Fixes: 9d80448ac92b ("efi/arm64: Add debugfs node to dump UEFI runtime page tables")
Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
---
arch/arm64/include/asm/ptdump.h | 2 ++
arch/arm64/mm/ptdump.c | 14 +++++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
index 5b374a6ab34a..1b743de7d89e 100644
--- a/arch/arm64/include/asm/ptdump.h
+++ b/arch/arm64/include/asm/ptdump.h
@@ -52,6 +52,8 @@ struct ptdump_pg_state {
const struct addr_marker *marker;
const struct mm_struct *mm;
unsigned long start_address;
+ /* exclusive end, ULONG_MAX represents an end at 1 << 64 */
+ unsigned long end_address;
int level;
ptval_t current_prot;
bool check_wx;
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index 1c20144700d7..eab400e744d9 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -278,9 +278,19 @@ void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
void note_page_flush(struct ptdump_state *pt_st)
{
+ struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
+ unsigned long end = st->end_address;
pte_t pte_zero = {0};
- note_page(pt_st, 0, -1, pte_val(pte_zero));
+ /*
+ * Address spaces that end at 1 << 64 have end_address == ULONG_MAX,
+ * but note_page() expects the exclusive end. In this case adjust end
+ * to the wraparound value 0.
+ */
+ if (end == ULONG_MAX)
+ end = 0;
+
+ note_page(pt_st, end, -1, pte_val(pte_zero));
}
static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
@@ -303,6 +313,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
.marker = info->markers,
.mm = info->mm,
.pg_level = &kernel_pg_levels[0],
+ .end_address = end,
.level = -1,
.ptdump = {
.note_page_pte = note_page_pte,
@@ -344,6 +355,7 @@ bool ptdump_check_wx(void)
{ -1, NULL},
},
.pg_level = &kernel_pg_levels[0],
+ .end_address = ~0UL,
.level = -1,
.check_wx = true,
.ptdump = {
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/2] KVM: arm64: ptdump: Flush the last region
2026-08-14 22:24 [PATCH v3 0/2] arm64: ptdump flush fixes Wei-Lin Chang
2026-08-14 22:24 ` [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware Wei-Lin Chang
@ 2026-08-14 22:24 ` Wei-Lin Chang
1 sibling, 0 replies; 5+ messages in thread
From: Wei-Lin Chang @ 2026-08-14 22:24 UTC (permalink / raw)
To: linux-arm-kernel, linux-kernel, kvmarm
Cc: Catalin Marinas, Will Deacon, Marc Zyngier, Oliver Upton,
Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Mike Rapoport (Microsoft), Ryan Roberts,
David Hildenbrand (Arm), Anshuman Khandual, Dev Jain,
Ard Biesheuvel, Mark Rutland, Matt Fleming, Vincent Donnefort,
Sebastian Ene, Wei-Lin Chang, Sashiko AI
Currently the stage-2 ptdump calls note_page() at each leaf entry visit.
This simply misses the output of the last region, because note_page()
only dumps output when it detects a change in level/prot, or when the
walk enters a next marker section. The last region in the guest IPA
space with the same level/prot is not dumped since there is no change
after it.
Call note_page_flush() to dump the final region. note_page_flush()
uses ptdump_pg_state.end_address to call the final note_page(), so also
provide the end address.
Also change the second marker's start address to ULONG_MAX so we never
cross it. This avoids dumping redundant marker names (which are NULL),
and advancing beyond the end of the marker array.
Fixes: 7c4f73548ed1 ("KVM: arm64: Register ptdump with debugfs on guest creation")
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/kvmarm/20260630122758.891011F00A3A@smtp.kernel.org/
Reviewed-by: Dev Jain <dev.jain@arm.com>
Tested-by: Dev Jain <dev.jain@arm.com>
Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
---
arch/arm64/kvm/ptdump.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
index c9140e22abcf..69899797dbad 100644
--- a/arch/arm64/kvm/ptdump.c
+++ b/arch/arm64/kvm/ptdump.c
@@ -130,7 +130,7 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu
}
st->ipa_marker[0].name = "Guest IPA";
- st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);
+ st->ipa_marker[1].start_address = ULONG_MAX;
st->mmu = mmu;
return st;
@@ -148,18 +148,21 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
.flags = KVM_PGTABLE_WALK_LEAF,
};
+ guard(write_lock)(&kvm->mmu_lock);
st->parser_state = (struct ptdump_pg_state) {
.marker = &st->ipa_marker[0],
+ .end_address = BIT(mmu->pgt->ia_bits),
.level = -1,
.pg_level = &st->level[0],
.seq = m,
};
- write_lock(&kvm->mmu_lock);
ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
- write_unlock(&kvm->mmu_lock);
+ if (ret)
+ return ret;
+ note_page_flush(&st->parser_state.ptdump);
- return ret;
+ return 0;
}
static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware
2026-08-14 22:24 ` [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware Wei-Lin Chang
@ 2026-08-15 11:28 ` Marc Zyngier
2026-08-15 23:16 ` Wei-Lin Chang
0 siblings, 1 reply; 5+ messages in thread
From: Marc Zyngier @ 2026-08-15 11:28 UTC (permalink / raw)
To: Wei-Lin Chang
Cc: linux-arm-kernel, linux-kernel, kvmarm, Catalin Marinas,
Will Deacon, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Mike Rapoport (Microsoft),
Ryan Roberts, David Hildenbrand (Arm), Anshuman Khandual,
Dev Jain, Ard Biesheuvel, Mark Rutland, Matt Fleming,
Vincent Donnefort, Sebastian Ene
On Fri, 14 Aug 2026 23:24:57 +0100,
Wei-Lin Chang <weilin.chang@arm.com> wrote:
>
> note_page_flush() calls note_page() with addr == 0 and level == -1 to
> dump the last row of a ptdump. addr == 0 (1 << 64 wrapped around)
> renders a huge region with enormous size for address spaces with
> IA bits < 64. For example the stage-2 page tables and the EFI runtime
> page table.
>
> More importantly, the last region of the address space and everything
> after the address space up to 1 << 64 are merged into one row of
> output. If the last region within the address space is valid, it will
> appear to remain valid up to 1 << 64 with the same attributes.
>
> Currently only the EFI runtime ptdump is affected by this, but KVM will
> soon fix its stage-2 ptdump by using note_page_flush(). Here is an
> example of an EFI runtime ptdump (last row):
>
> 0x0000008000000000-0x0000000000000000 17179868672G PGD
>
> With this patch:
>
> 0x0000008000000000-0x0001000000000000 261632G PGD
>
> To fix this, cache the end address of a ptdump in ptdump_pg_state so
> note_page_flush() can call the final note_page() with the correct end
> address.
>
> Fixes: 9d80448ac92b ("efi/arm64: Add debugfs node to dump UEFI runtime page tables")
> Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
> ---
> arch/arm64/include/asm/ptdump.h | 2 ++
> arch/arm64/mm/ptdump.c | 14 +++++++++++++-
> 2 files changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
> index 5b374a6ab34a..1b743de7d89e 100644
> --- a/arch/arm64/include/asm/ptdump.h
> +++ b/arch/arm64/include/asm/ptdump.h
> @@ -52,6 +52,8 @@ struct ptdump_pg_state {
> const struct addr_marker *marker;
> const struct mm_struct *mm;
> unsigned long start_address;
> + /* exclusive end, ULONG_MAX represents an end at 1 << 64 */
> + unsigned long end_address;
> int level;
> ptval_t current_prot;
> bool check_wx;
> diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
> index 1c20144700d7..eab400e744d9 100644
> --- a/arch/arm64/mm/ptdump.c
> +++ b/arch/arm64/mm/ptdump.c
> @@ -278,9 +278,19 @@ void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
>
> void note_page_flush(struct ptdump_state *pt_st)
> {
> + struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
> + unsigned long end = st->end_address;
> pte_t pte_zero = {0};
>
> - note_page(pt_st, 0, -1, pte_val(pte_zero));
> + /*
> + * Address spaces that end at 1 << 64 have end_address == ULONG_MAX,
> + * but note_page() expects the exclusive end. In this case adjust end
> + * to the wraparound value 0.
> + */
> + if (end == ULONG_MAX)
> + end = 0;
> +
> + note_page(pt_st, end, -1, pte_val(pte_zero));
> }
>
> static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
> @@ -303,6 +313,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
> .marker = info->markers,
> .mm = info->mm,
> .pg_level = &kernel_pg_levels[0],
> + .end_address = end,
> .level = -1,
> .ptdump = {
> .note_page_pte = note_page_pte,
> @@ -344,6 +355,7 @@ bool ptdump_check_wx(void)
> { -1, NULL},
> },
> .pg_level = &kernel_pg_levels[0],
> + .end_address = ~0UL,
nit: shouldn't this be ULONG_MAX instead? Yes, this is the same thing,
but it doesn't hurt to match the documentation.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware
2026-08-15 11:28 ` Marc Zyngier
@ 2026-08-15 23:16 ` Wei-Lin Chang
0 siblings, 0 replies; 5+ messages in thread
From: Wei-Lin Chang @ 2026-08-15 23:16 UTC (permalink / raw)
To: Marc Zyngier
Cc: linux-arm-kernel, linux-kernel, kvmarm, Catalin Marinas,
Will Deacon, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Mike Rapoport (Microsoft),
Ryan Roberts, David Hildenbrand (Arm), Anshuman Khandual,
Dev Jain, Ard Biesheuvel, Mark Rutland, Matt Fleming,
Vincent Donnefort, Sebastian Ene
On Sat, Aug 15, 2026 at 12:28:23PM +0100, Marc Zyngier wrote:
> On Fri, 14 Aug 2026 23:24:57 +0100,
> Wei-Lin Chang <weilin.chang@arm.com> wrote:
> >
> > note_page_flush() calls note_page() with addr == 0 and level == -1 to
> > dump the last row of a ptdump. addr == 0 (1 << 64 wrapped around)
> > renders a huge region with enormous size for address spaces with
> > IA bits < 64. For example the stage-2 page tables and the EFI runtime
> > page table.
> >
> > More importantly, the last region of the address space and everything
> > after the address space up to 1 << 64 are merged into one row of
> > output. If the last region within the address space is valid, it will
> > appear to remain valid up to 1 << 64 with the same attributes.
> >
> > Currently only the EFI runtime ptdump is affected by this, but KVM will
> > soon fix its stage-2 ptdump by using note_page_flush(). Here is an
> > example of an EFI runtime ptdump (last row):
> >
> > 0x0000008000000000-0x0000000000000000 17179868672G PGD
> >
> > With this patch:
> >
> > 0x0000008000000000-0x0001000000000000 261632G PGD
> >
> > To fix this, cache the end address of a ptdump in ptdump_pg_state so
> > note_page_flush() can call the final note_page() with the correct end
> > address.
> >
> > Fixes: 9d80448ac92b ("efi/arm64: Add debugfs node to dump UEFI runtime page tables")
> > Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
> > ---
> > arch/arm64/include/asm/ptdump.h | 2 ++
> > arch/arm64/mm/ptdump.c | 14 +++++++++++++-
> > 2 files changed, 15 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
> > index 5b374a6ab34a..1b743de7d89e 100644
> > --- a/arch/arm64/include/asm/ptdump.h
> > +++ b/arch/arm64/include/asm/ptdump.h
> > @@ -52,6 +52,8 @@ struct ptdump_pg_state {
> > const struct addr_marker *marker;
> > const struct mm_struct *mm;
> > unsigned long start_address;
> > + /* exclusive end, ULONG_MAX represents an end at 1 << 64 */
> > + unsigned long end_address;
> > int level;
> > ptval_t current_prot;
> > bool check_wx;
> > diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
> > index 1c20144700d7..eab400e744d9 100644
> > --- a/arch/arm64/mm/ptdump.c
> > +++ b/arch/arm64/mm/ptdump.c
> > @@ -278,9 +278,19 @@ void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
> >
> > void note_page_flush(struct ptdump_state *pt_st)
> > {
> > + struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
> > + unsigned long end = st->end_address;
> > pte_t pte_zero = {0};
> >
> > - note_page(pt_st, 0, -1, pte_val(pte_zero));
> > + /*
> > + * Address spaces that end at 1 << 64 have end_address == ULONG_MAX,
> > + * but note_page() expects the exclusive end. In this case adjust end
> > + * to the wraparound value 0.
> > + */
> > + if (end == ULONG_MAX)
> > + end = 0;
> > +
> > + note_page(pt_st, end, -1, pte_val(pte_zero));
> > }
> >
> > static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
> > @@ -303,6 +313,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
> > .marker = info->markers,
> > .mm = info->mm,
> > .pg_level = &kernel_pg_levels[0],
> > + .end_address = end,
> > .level = -1,
> > .ptdump = {
> > .note_page_pte = note_page_pte,
> > @@ -344,6 +355,7 @@ bool ptdump_check_wx(void)
> > { -1, NULL},
> > },
> > .pg_level = &kernel_pg_levels[0],
> > + .end_address = ~0UL,
>
> nit: shouldn't this be ULONG_MAX instead? Yes, this is the same thing,
> but it doesn't hurt to match the documentation.
I wrote ~0UL because the range[] initialization was using ~0UL so I did
the same for consistency. I think changing them both to ULONG_MAX is the
better approach. I'll do that if a v4 is required.
Thanks,
Wei-Lin Chang
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-15 23:17 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 22:24 [PATCH v3 0/2] arm64: ptdump flush fixes Wei-Lin Chang
2026-08-14 22:24 ` [PATCH v3 1/2] arm64: ptdump: Make note_page_flush() range aware Wei-Lin Chang
2026-08-15 11:28 ` Marc Zyngier
2026-08-15 23:16 ` Wei-Lin Chang
2026-08-14 22:24 ` [PATCH v3 2/2] KVM: arm64: ptdump: Flush the last region Wei-Lin Chang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox