[v4,10/10] arm64: ptdump: Add guest stage-2 pagetables dumping
Commit Message
Register a debugfs file on guest creation to be able to view their
second translation tables with ptdump. This assumes that the host is in
control of the guest stage-2 and has direct access to the pagetables.
Signed-off-by: Sebastian Ene <sebastianene@google.com>
---
arch/arm64/kvm/debug.c | 6 ++++++
arch/arm64/kvm/kvm_ptdump.h | 3 +++
arch/arm64/kvm/ptdump.c | 35 ++++++++++++++++++++++++++++++++---
3 files changed, 41 insertions(+), 3 deletions(-)
Comments
On Mon, Dec 18, 2023 at 01:59:00PM +0000, Sebastian Ene wrote:
> Register a debugfs file on guest creation to be able to view their
> second translation tables with ptdump. This assumes that the host is in
> control of the guest stage-2 and has direct access to the pagetables.
>
> Signed-off-by: Sebastian Ene <sebastianene@google.com>
> ---
> arch/arm64/kvm/debug.c | 6 ++++++
> arch/arm64/kvm/kvm_ptdump.h | 3 +++
> arch/arm64/kvm/ptdump.c | 35 ++++++++++++++++++++++++++++++++---
> 3 files changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c
> index 8725291cb..7c4c2902d 100644
> --- a/arch/arm64/kvm/debug.c
> +++ b/arch/arm64/kvm/debug.c
> @@ -13,6 +13,7 @@
> #include <asm/kvm_asm.h>
> #include <asm/kvm_arm.h>
> #include <asm/kvm_emulate.h>
> +#include <kvm_ptdump.h>
>
> #include "trace.h"
>
> @@ -342,3 +343,8 @@ void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
> vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
> }
> +
> +int kvm_arch_create_vm_debugfs(struct kvm *kvm)
> +{
> + return kvm_ptdump_register_guest(kvm);
> +}
> diff --git a/arch/arm64/kvm/kvm_ptdump.h b/arch/arm64/kvm/kvm_ptdump.h
> index 98b595ce8..5f5a455d0 100644
> --- a/arch/arm64/kvm/kvm_ptdump.h
> +++ b/arch/arm64/kvm/kvm_ptdump.h
> @@ -6,13 +6,16 @@
> #ifndef __KVM_PTDUMP_H
> #define __KVM_PTDUMP_H
>
> +#include <linux/kvm_host.h>
> #include <asm/ptdump.h>
>
>
> #ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
> void kvm_ptdump_register_host(void);
> +int kvm_ptdump_register_guest(struct kvm *kvm);
> #else
> static inline void kvm_ptdump_register_host(void) { }
> +static inline int kvm_ptdump_register_guest(struct kvm *kvm) { return -1; }
> #endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */
>
> #endif /* __KVM_PTDUMP_H */
> diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
> index 4296e739f..62a753d6b 100644
> --- a/arch/arm64/kvm/ptdump.c
> +++ b/arch/arm64/kvm/ptdump.c
> @@ -181,6 +181,8 @@ static int kvm_ptdump_open(struct inode *inode, struct file *file)
> info = reg->get_ptdump_info(reg);
> if (!info)
> return -ENOMEM;
> + } else {
> + info = inode->i_private;
FIXME: Don't call kvm_ptdump_release with this ^ argument.
> }
>
> if (!reg->show_ptdump_info)
> @@ -239,15 +241,14 @@ static int kvm_ptdump_visitor(const struct kvm_pgtable_visit_ctx *ctx,
> return 0;
> }
>
> -static int kvm_ptdump_show(struct seq_file *m, void *)
> +static int kvm_ptdump_show_common(struct seq_file *m,
> + struct kvm_pgtable *pgtable)
> {
> u64 ipa_size;
> char ipa_description[32];
> struct pg_state st;
> struct addr_marker ipa_addr_markers[3] = {0};
> struct pg_level pg_level_descr[KVM_PGTABLE_MAX_LEVELS] = {0};
> - struct kvm_pgtable_snapshot *snapshot = m->private;
> - struct kvm_pgtable *pgtable = &snapshot->pgtable;
> struct kvm_pgtable_walker walker = (struct kvm_pgtable_walker) {
> .cb = kvm_ptdump_visitor,
> .arg = &st,
> @@ -282,6 +283,26 @@ static int kvm_ptdump_show(struct seq_file *m, void *)
> return kvm_pgtable_walk(pgtable, 0, ipa_size, &walker);
> }
>
> +static int kvm_host_ptdump_show(struct seq_file *m, void *)
> +{
> + struct kvm_pgtable_snapshot *snapshot = m->private;
> +
> + return kvm_ptdump_show_common(m, &snapshot->pgtable);
> +}
> +
> +static int kvm_ptdump_show(struct seq_file *m, void *)
> +{
> + struct kvm *guest_kvm = m->private;
> + struct kvm_s2_mmu *mmu = &guest_kvm->arch.mmu;
> + int ret;
> +
> + write_lock(&guest_kvm->mmu_lock);
> + ret = kvm_ptdump_show_common(m, mmu->pgt);
> + write_unlock(&guest_kvm->mmu_lock);
> +
> + return ret;
> +}
> +
> static void kvm_ptdump_debugfs_register(struct kvm_ptdump_register *reg,
> const char *name, struct dentry *parent)
> {
> @@ -393,11 +414,19 @@ void kvm_ptdump_register_host(void)
>
> host_reg.get_ptdump_info = kvm_host_get_ptdump_info;
> host_reg.put_ptdump_info = kvm_host_put_ptdump_info;
> + host_reg.show_ptdump_info = kvm_host_ptdump_show;
>
> kvm_ptdump_debugfs_register(&host_reg, "host_page_tables",
> kvm_debugfs_dir);
> }
>
> +int kvm_ptdump_register_guest(struct kvm *kvm)
> +{
> + debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry,
> + kvm, &kvm_ptdump_fops);
> + return 0;
> +}
> +
> static int __init kvm_host_ptdump_init(void)
> {
> host_reg.priv = (void *)host_s2_pgtable_pages();
> --
> 2.43.0.472.g3155946c3a-goog
>
On Mon, Dec 18, 2023 at 01:59:00PM +0000, Sebastian Ene wrote:
> Register a debugfs file on guest creation to be able to view their
> second translation tables with ptdump. This assumes that the host is in
> control of the guest stage-2 and has direct access to the pagetables.
>
> Signed-off-by: Sebastian Ene <sebastianene@google.com>
I couldn't see how this patched worked at all until I went back to patch
1 and found this:
> +static int kvm_ptdump_open(struct inode *inode, struct file *file)
> +{
[...]
> + if (!reg->show_ptdump_info)
> + reg->show_ptdump_info = kvm_ptdump_show;
[...]
> +}
> +static int kvm_ptdump_show(struct seq_file *m, void *)
> +{
> + struct kvm *guest_kvm = m->private;
> + struct kvm_s2_mmu *mmu = &guest_kvm->arch.mmu;
> + int ret;
> +
> + write_lock(&guest_kvm->mmu_lock);
> + ret = kvm_ptdump_show_common(m, mmu->pgt);
> + write_unlock(&guest_kvm->mmu_lock);
> +
> + return ret;
> +}
Where are you getting a reference on the kvm struct? You need to do this
to ensure the VM doesn't get destroyed behind your back.
@@ -13,6 +13,7 @@
#include <asm/kvm_asm.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_emulate.h>
+#include <kvm_ptdump.h>
#include "trace.h"
@@ -342,3 +343,8 @@ void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu)
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE);
vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE);
}
+
+int kvm_arch_create_vm_debugfs(struct kvm *kvm)
+{
+ return kvm_ptdump_register_guest(kvm);
+}
@@ -6,13 +6,16 @@
#ifndef __KVM_PTDUMP_H
#define __KVM_PTDUMP_H
+#include <linux/kvm_host.h>
#include <asm/ptdump.h>
#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
void kvm_ptdump_register_host(void);
+int kvm_ptdump_register_guest(struct kvm *kvm);
#else
static inline void kvm_ptdump_register_host(void) { }
+static inline int kvm_ptdump_register_guest(struct kvm *kvm) { return -1; }
#endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */
#endif /* __KVM_PTDUMP_H */
@@ -181,6 +181,8 @@ static int kvm_ptdump_open(struct inode *inode, struct file *file)
info = reg->get_ptdump_info(reg);
if (!info)
return -ENOMEM;
+ } else {
+ info = inode->i_private;
}
if (!reg->show_ptdump_info)
@@ -239,15 +241,14 @@ static int kvm_ptdump_visitor(const struct kvm_pgtable_visit_ctx *ctx,
return 0;
}
-static int kvm_ptdump_show(struct seq_file *m, void *)
+static int kvm_ptdump_show_common(struct seq_file *m,
+ struct kvm_pgtable *pgtable)
{
u64 ipa_size;
char ipa_description[32];
struct pg_state st;
struct addr_marker ipa_addr_markers[3] = {0};
struct pg_level pg_level_descr[KVM_PGTABLE_MAX_LEVELS] = {0};
- struct kvm_pgtable_snapshot *snapshot = m->private;
- struct kvm_pgtable *pgtable = &snapshot->pgtable;
struct kvm_pgtable_walker walker = (struct kvm_pgtable_walker) {
.cb = kvm_ptdump_visitor,
.arg = &st,
@@ -282,6 +283,26 @@ static int kvm_ptdump_show(struct seq_file *m, void *)
return kvm_pgtable_walk(pgtable, 0, ipa_size, &walker);
}
+static int kvm_host_ptdump_show(struct seq_file *m, void *)
+{
+ struct kvm_pgtable_snapshot *snapshot = m->private;
+
+ return kvm_ptdump_show_common(m, &snapshot->pgtable);
+}
+
+static int kvm_ptdump_show(struct seq_file *m, void *)
+{
+ struct kvm *guest_kvm = m->private;
+ struct kvm_s2_mmu *mmu = &guest_kvm->arch.mmu;
+ int ret;
+
+ write_lock(&guest_kvm->mmu_lock);
+ ret = kvm_ptdump_show_common(m, mmu->pgt);
+ write_unlock(&guest_kvm->mmu_lock);
+
+ return ret;
+}
+
static void kvm_ptdump_debugfs_register(struct kvm_ptdump_register *reg,
const char *name, struct dentry *parent)
{
@@ -393,11 +414,19 @@ void kvm_ptdump_register_host(void)
host_reg.get_ptdump_info = kvm_host_get_ptdump_info;
host_reg.put_ptdump_info = kvm_host_put_ptdump_info;
+ host_reg.show_ptdump_info = kvm_host_ptdump_show;
kvm_ptdump_debugfs_register(&host_reg, "host_page_tables",
kvm_debugfs_dir);
}
+int kvm_ptdump_register_guest(struct kvm *kvm)
+{
+ debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry,
+ kvm, &kvm_ptdump_fops);
+ return 0;
+}
+
static int __init kvm_host_ptdump_init(void)
{
host_reg.priv = (void *)host_s2_pgtable_pages();