From: "Dylan.Wu" <fredwudi0305@gmail.com>
To: paul.walmsley@sifive.com, pjw@kernel.org
Cc: palmer@dabbelt.com, aou@eecs.berkeley.edu, alex@ghiti.fr,
anup@brainfault.org, atish.patra@linux.dev,
linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org,
kvm-riscv@lists.infradead.org, kvm@vger.kernel.org,
"Dylan.Wu" <fredwudi0305@gmail.com>,
Quan Zhou <zhouquan@iscas.ac.cn>
Subject: [PATCH v3 3/3] KVM: riscv: Register ptdump with debugfs on guest creation
Date: Tue, 28 Jul 2026 12:14:49 -0400 [thread overview]
Message-ID: <20260728161449.190058-4-fredwudi0305@gmail.com> (raw)
In-Reply-To: <20260728161449.190058-1-fredwudi0305@gmail.com>
Implement the gstage page table dumper for RISC-V KVM, which exposes
the guest's gstage page tables via debugfs. When
CONFIG_PTDUMP_GSTAGE_DEBUGFS is enabled, a "gstage_page_tables" file
will be created under each VM's debugfs directory.
This reuses the ptdump framework from the kernel page table dumper,
with gstage-specific attribute bits and level definitions.
Assisted-by: YuanSheng: deepseek-v4-pro
Co-developed-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Quan Zhou <zhouquan@iscas.ac.cn>
Signed-off-by: Dylan.Wu <fredwudi0305@gmail.com>
---
arch/riscv/kvm/Kconfig | 12 +++
arch/riscv/kvm/Makefile | 1 +
arch/riscv/kvm/ptdump.c | 177 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 190 insertions(+)
create mode 100644 arch/riscv/kvm/ptdump.c
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a3..6d62b967f 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -37,4 +37,16 @@ config KVM
If unsure, say N.
+config PTDUMP_GSTAGE_DEBUGFS
+ bool "Present the gstage pagetables to debugfs"
+ depends on KVM && DEBUG_KERNEL && DEBUG_FS && PTDUMP_DEBUGFS
+ default n
+ help
+ Say Y here if you want to show the RISC-V KVM gstage guest
+ page tables in debugfs under each VM's debugfs directory as
+ a file named "gstage_page_tables". This allows inspection of
+ the second-stage address translation setup for each guest.
+
+ If unsure, say N.
+
endif # VIRTUALIZATION
diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile
index 296c2ba05..905593d6f 100644
--- a/arch/riscv/kvm/Makefile
+++ b/arch/riscv/kvm/Makefile
@@ -19,6 +19,7 @@ kvm-y += isa.o
kvm-y += main.o
kvm-y += mmu.o
kvm-y += nacl.o
+kvm-$(CONFIG_PTDUMP_GSTAGE_DEBUGFS) += ptdump.o
kvm-y += tlb.o
kvm-y += vcpu.o
kvm-y += vcpu_config.o
diff --git a/arch/riscv/kvm/ptdump.c b/arch/riscv/kvm/ptdump.c
new file mode 100644
index 000000000..f436ad48c
--- /dev/null
+++ b/arch/riscv/kvm/ptdump.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024 SiFive
+ */
+
+#include <linux/debugfs.h>
+#include <linux/ptdump.h>
+#include <linux/seq_file.h>
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_gstage.h>
+#include <asm/pgtable.h>
+#include <asm/ptdump.h>
+
+enum gstage_address_markers_idx {
+ GSTAGE_GPA_START_NR,
+ GSTAGE_GPA_END_NR,
+ END_OF_GSTAGE_SPACE_NR
+};
+
+static const struct ptdump_prot_bits gstage_pte_bits[] = {
+ {
+ .mask = _PAGE_DIRTY,
+ .set = "D",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_ACCESSED,
+ .set = "A",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_GLOBAL,
+ .set = "G",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_USER,
+ .set = "U",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_EXEC,
+ .set = "X",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_WRITE,
+ .set = "W",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_READ,
+ .set = "R",
+ .clear = ".",
+ }, {
+ .mask = _PAGE_PRESENT,
+ .set = "V",
+ .clear = ".",
+ }
+};
+
+static struct ptdump_pg_level gstage_pg_levels[] = {
+ { /* pgd */
+ .bits = gstage_pte_bits,
+ .num = ARRAY_SIZE(gstage_pte_bits),
+ .name = "PGD",
+ }, { /* p4d */
+ .bits = gstage_pte_bits,
+ .num = ARRAY_SIZE(gstage_pte_bits),
+ .name = "P4D",
+ }, { /* pud */
+ .bits = gstage_pte_bits,
+ .num = ARRAY_SIZE(gstage_pte_bits),
+ .name = "PUD",
+ }, { /* pmd */
+ .bits = gstage_pte_bits,
+ .num = ARRAY_SIZE(gstage_pte_bits),
+ .name = "PMD",
+ }, { /* pte */
+ .bits = gstage_pte_bits,
+ .num = ARRAY_SIZE(gstage_pte_bits),
+ .name = "PTE",
+ },
+};
+
+static void gstage_note_page(struct ptdump_state *pt_st, unsigned long addr,
+ int level, u64 val)
+{
+ struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
+
+ note_page(pt_st, addr, level, val);
+
+ if (level >= 0)
+ st->current_prot = val & gstage_pg_levels[level].mask;
+}
+
+static void gstage_note_page_pte(struct ptdump_state *pt_st, unsigned long addr, pte_t pte)
+{
+ gstage_note_page(pt_st, addr, 4, pte_val(pte));
+}
+
+static void gstage_note_page_pmd(struct ptdump_state *pt_st, unsigned long addr, pmd_t pmd)
+{
+ gstage_note_page(pt_st, addr, 3, pmd_val(pmd));
+}
+
+static void gstage_note_page_pud(struct ptdump_state *pt_st, unsigned long addr, pud_t pud)
+{
+ gstage_note_page(pt_st, addr, 2, pud_val(pud));
+}
+
+static void gstage_note_page_p4d(struct ptdump_state *pt_st, unsigned long addr, p4d_t p4d)
+{
+ gstage_note_page(pt_st, addr, 1, p4d_val(p4d));
+}
+
+static void gstage_note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
+{
+ gstage_note_page(pt_st, addr, 0, pgd_val(pgd));
+}
+
+static void gstage_note_page_flush(struct ptdump_state *pt_st)
+{
+ pte_t pte_zero = {0};
+
+ gstage_note_page(pt_st, 0, -1, pte_val(pte_zero));
+}
+
+static int gstage_ptdump_show(struct seq_file *m, void *v)
+{
+ struct kvm *kvm = m->private;
+ gpa_t gpa_size = kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels);
+ struct addr_marker gpa_markers[] = {
+ [GSTAGE_GPA_START_NR] = { 0, "Guest Physical Address Start" },
+ [GSTAGE_GPA_END_NR] = { gpa_size - 1, "Guest Physical Address End" },
+ [END_OF_GSTAGE_SPACE_NR] = { -1, NULL },
+ };
+ struct ptdump_pg_state st = {
+ .seq = m,
+ .marker = gpa_markers,
+ .level = -1,
+ .pg_level = gstage_pg_levels,
+ .ptdump = {
+ .note_page_pte = gstage_note_page_pte,
+ .note_page_pmd = gstage_note_page_pmd,
+ .note_page_pud = gstage_note_page_pud,
+ .note_page_p4d = gstage_note_page_p4d,
+ .note_page_pgd = gstage_note_page_pgd,
+ .note_page_flush = gstage_note_page_flush,
+ .range = (struct ptdump_range[]) {
+ {0, gpa_size - 1},
+ {0, 0}
+ }
+ }
+ };
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(gstage_pg_levels); i++) {
+ gstage_pg_levels[i].mask = 0;
+ for (j = 0; j < ARRAY_SIZE(gstage_pte_bits); j++)
+ gstage_pg_levels[i].mask |= gstage_pte_bits[j].mask;
+ }
+
+ if (kvm->arch.pgd_levels < 5)
+ gstage_pg_levels[1].name = "PGD";
+ if (kvm->arch.pgd_levels < 4)
+ gstage_pg_levels[2].name = "PGD";
+ if (kvm->arch.pgd_levels < 3)
+ gstage_pg_levels[3].name = "PGD";
+
+ ptdump_walk_pgd(&st.ptdump, kvm->mm, kvm->arch.pgd);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(gstage_ptdump);
+
+void kvm_arch_create_vm_debugfs(struct kvm *kvm)
+{
+ debugfs_create_file("gstage_page_tables", 0400,
+ kvm->debugfs_dentry, kvm, &gstage_ptdump_fops);
+}
--
2.34.1
next prev parent reply other threads:[~2026-07-28 16:15 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 8:50 [PATCH 0/2] riscv: ptdump: Refactor for KVM gstage ptdump support Dylan.Wu
2026-07-01 8:50 ` [PATCH 1/2] riscv: ptdump: Move pagetable definitions to common header Dylan.Wu
2026-07-01 9:01 ` sashiko-bot
2026-07-21 2:58 ` Paul Walmsley
2026-07-27 12:39 ` Dylan.Wu
2026-07-28 16:14 ` [PATCH v3 0/3] RISC-V KVM gstage page table dumper Dylan.Wu
2026-07-28 16:14 ` [PATCH v3 1/3] riscv: ptdump: Create ptdump.h and move declarations Dylan.Wu
2026-07-28 16:26 ` sashiko-bot
2026-07-28 16:14 ` [PATCH v3 2/3] riscv: ptdump: Use per-level attribute bits for parsing Dylan.Wu
2026-07-28 16:31 ` sashiko-bot
2026-07-28 16:14 ` Dylan.Wu [this message]
2026-07-28 16:50 ` [PATCH v3 3/3] KVM: riscv: Register ptdump with debugfs on guest creation sashiko-bot
2026-07-01 8:50 ` [PATCH 2/2] " Dylan.Wu
2026-07-01 9:06 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260728161449.190058-4-fredwudi0305@gmail.com \
--to=fredwudi0305@gmail.com \
--cc=alex@ghiti.fr \
--cc=anup@brainfault.org \
--cc=aou@eecs.berkeley.edu \
--cc=atish.patra@linux.dev \
--cc=kvm-riscv@lists.infradead.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=pjw@kernel.org \
--cc=zhouquan@iscas.ac.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox