[v4,10/10] arm64: ptdump: Add guest stage-2 pagetables dumping

Message ID 20231218135859.2513568-12-sebastianene@google.com
State New
Headers
Series arm64: ptdump: View the second stage page-tables |

Commit Message

Sebastian Ene Dec. 18, 2023, 1:59 p.m. UTC
  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

Sebastian Ene Dec. 19, 2023, 11:52 a.m. UTC | #1
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
>
  
Oliver Upton Dec. 21, 2023, 6:27 p.m. UTC | #2
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.
  

Patch

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;
 	}
 
 	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();