Commit e85d1c0c authored by Marc Zyngier's avatar Marc Zyngier
Browse files

Merge branch kvm-arm64/nv-s2-debugfs into kvmarm-master/next



* kvm-arm64/nv-s2-debugfs:
  : .
  : Expand the stage-2 ptdump infrastructure to be able to display
  : the content of the shadow s2 tables generated by nested virt.
  :
  : Patches courtesy of Wei-Lin Chang.
  : .
  KVM: arm64: ptdump: Initialize parser_state before pgtable walk
  KVM: arm64: nv: Expose shadow page tables in debugfs
  KVM: arm64: ptdump: Make KVM ptdump code s2 mmu aware

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
parents f8078d51 57042860
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -217,6 +217,10 @@ struct kvm_s2_mmu {
	 */
	bool	nested_stage2_enabled;

#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
	struct dentry *shadow_pt_debugfs_dentry;
#endif

	/*
	 * true when this MMU needs to be unmapped before being used for a new
	 * purpose.
@@ -408,6 +412,11 @@ struct kvm_arch {
	 * the associated pKVM instance in the hypervisor.
	 */
	struct kvm_protected_vm pkvm;

#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
	/* Nested virtualization info */
	struct dentry *debugfs_nv_dentry;
#endif
};

struct kvm_vcpu_fault_info {
+4 −0
Original line number Diff line number Diff line
@@ -393,8 +393,12 @@ static inline bool kvm_supports_cacheable_pfnmap(void)

#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS
void kvm_s2_ptdump_create_debugfs(struct kvm *kvm);
void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu);
void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu);
#else
static inline void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) {}
static inline void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu) {}
static inline void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu) {}
#endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */

#endif /* __ASSEMBLER__ */
+5 −1
Original line number Diff line number Diff line
@@ -735,8 +735,10 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
	kvm->arch.nested_mmus_next = (i + 1) % kvm->arch.nested_mmus_size;

	/* Make sure we don't forget to do the laundry */
	if (kvm_s2_mmu_valid(s2_mmu))
	if (kvm_s2_mmu_valid(s2_mmu)) {
		kvm_nested_s2_ptdump_remove_debugfs(s2_mmu);
		s2_mmu->pending_unmap = true;
	}

	/*
	 * The virtual VMID (modulo CnP) will be used as a key when matching
@@ -750,6 +752,8 @@ static struct kvm_s2_mmu *get_s2_mmu_nested(struct kvm_vcpu *vcpu)
	s2_mmu->tlb_vtcr = vcpu_read_sys_reg(vcpu, VTCR_EL2);
	s2_mmu->nested_stage2_enabled = vcpu_read_sys_reg(vcpu, HCR_EL2) & HCR_VM;

	kvm_nested_s2_ptdump_create_debugfs(s2_mmu);

out:
	atomic_inc(&s2_mmu->refcnt);

+50 −29
Original line number Diff line number Diff line
@@ -10,19 +10,20 @@
#include <linux/kvm_host.h>
#include <linux/seq_file.h>

#include <asm/cpufeature.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_pgtable.h>
#include <asm/ptdump.h>

#define MARKERS_LEN		2
#define KVM_PGTABLE_MAX_LEVELS	(KVM_PGTABLE_LAST_LEVEL + 1)
#define S2FNAMESZ		sizeof("0x0123456789abcdef-0x0123456789abcdef-s2-disabled")

struct kvm_ptdump_guest_state {
	struct kvm		*kvm;
	struct kvm_s2_mmu	*mmu;
	struct ptdump_pg_state	parser_state;
	struct addr_marker	ipa_marker[MARKERS_LEN];
	struct ptdump_pg_level	level[KVM_PGTABLE_MAX_LEVELS];
	struct ptdump_range	range[MARKERS_LEN];
};

static const struct ptdump_prot_bits stage2_pte_bits[] = {
@@ -112,10 +113,9 @@ static int kvm_ptdump_build_levels(struct ptdump_pg_level *level, u32 start_lvl)
	return 0;
}

static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm)
static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu *mmu)
{
	struct kvm_ptdump_guest_state *st;
	struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
	struct kvm_pgtable *pgtable = mmu->pgt;
	int ret;

@@ -131,17 +131,8 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm *kvm)

	st->ipa_marker[0].name		= "Guest IPA";
	st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);
	st->range[0].end		= BIT(pgtable->ia_bits);

	st->kvm				= kvm;
	st->parser_state = (struct ptdump_pg_state) {
		.marker		= &st->ipa_marker[0],
		.level		= -1,
		.pg_level	= &st->level[0],
		.ptdump.range	= &st->range[0],
		.start_address	= 0,
	};

	st->mmu				= mmu;
	return st;
}

@@ -149,16 +140,20 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
{
	int ret;
	struct kvm_ptdump_guest_state *st = m->private;
	struct kvm *kvm = st->kvm;
	struct kvm_s2_mmu *mmu = &kvm->arch.mmu;
	struct ptdump_pg_state *parser_state = &st->parser_state;
	struct kvm_s2_mmu *mmu = st->mmu;
	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
	struct kvm_pgtable_walker walker = (struct kvm_pgtable_walker) {
		.cb	= kvm_ptdump_visitor,
		.arg	= parser_state,
		.arg	= &st->parser_state,
		.flags	= KVM_PGTABLE_WALK_LEAF,
	};

	parser_state->seq = m;
	st->parser_state = (struct ptdump_pg_state) {
		.marker		= &st->ipa_marker[0],
		.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);
@@ -169,14 +164,15 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)

static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
{
	struct kvm *kvm = m->i_private;
	struct kvm_s2_mmu *mmu = m->i_private;
	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
	struct kvm_ptdump_guest_state *st;
	int ret;

	if (!kvm_get_kvm_safe(kvm))
		return -ENOENT;

	st = kvm_ptdump_parser_create(kvm);
	st = kvm_ptdump_parser_create(mmu);
	if (IS_ERR(st)) {
		ret = PTR_ERR(st);
		goto err_with_kvm_ref;
@@ -194,7 +190,7 @@ static int kvm_ptdump_guest_open(struct inode *m, struct file *file)

static int kvm_ptdump_guest_close(struct inode *m, struct file *file)
{
	struct kvm *kvm = m->i_private;
	struct kvm *kvm = kvm_s2_mmu_to_kvm(m->i_private);
	void *st = ((struct seq_file *)file->private_data)->private;

	kfree(st);
@@ -229,14 +225,15 @@ static int kvm_pgtable_levels_show(struct seq_file *m, void *unused)
static int kvm_pgtable_debugfs_open(struct inode *m, struct file *file,
				    int (*show)(struct seq_file *, void *))
{
	struct kvm *kvm = m->i_private;
	struct kvm_s2_mmu *mmu = m->i_private;
	struct kvm *kvm = kvm_s2_mmu_to_kvm(mmu);
	struct kvm_pgtable *pgtable;
	int ret;

	if (!kvm_get_kvm_safe(kvm))
		return -ENOENT;

	pgtable = kvm->arch.mmu.pgt;
	pgtable = mmu->pgt;

	ret = single_open(file, show, pgtable);
	if (ret < 0)
@@ -256,7 +253,7 @@ static int kvm_pgtable_levels_open(struct inode *m, struct file *file)

static int kvm_pgtable_debugfs_close(struct inode *m, struct file *file)
{
	struct kvm *kvm = m->i_private;
	struct kvm *kvm = kvm_s2_mmu_to_kvm(m->i_private);

	kvm_put_kvm(kvm);
	return single_release(m, file);
@@ -276,12 +273,36 @@ static const struct file_operations kvm_pgtable_levels_fops = {
	.release	= kvm_pgtable_debugfs_close,
};

void kvm_nested_s2_ptdump_create_debugfs(struct kvm_s2_mmu *mmu)
{
	struct dentry *dent;
	char file_name[S2FNAMESZ];

	snprintf(file_name, sizeof(file_name), "0x%016llx-0x%016llx-s2-%sabled",
		 mmu->tlb_vttbr,
		 mmu->tlb_vtcr,
		 mmu->nested_stage2_enabled ? "en" : "dis");

	dent = debugfs_create_file(file_name, 0400,
				   mmu->arch->debugfs_nv_dentry, mmu,
				   &kvm_ptdump_guest_fops);

	mmu->shadow_pt_debugfs_dentry = dent;
}

void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu)
{
	debugfs_remove(mmu->shadow_pt_debugfs_dentry);
}

void kvm_s2_ptdump_create_debugfs(struct kvm *kvm)
{
	debugfs_create_file("stage2_page_tables", 0400, kvm->debugfs_dentry,
			    kvm, &kvm_ptdump_guest_fops);
	debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry, kvm,
			    &kvm_pgtable_range_fops);
			    &kvm->arch.mmu, &kvm_ptdump_guest_fops);
	debugfs_create_file("ipa_range", 0400, kvm->debugfs_dentry,
			    &kvm->arch.mmu, &kvm_pgtable_range_fops);
	debugfs_create_file("stage2_levels", 0400, kvm->debugfs_dentry,
			    kvm, &kvm_pgtable_levels_fops);
			    &kvm->arch.mmu, &kvm_pgtable_levels_fops);
	if (cpus_have_final_cap(ARM64_HAS_NESTED_VIRT))
		kvm->arch.debugfs_nv_dentry = debugfs_create_dir("nested", kvm->debugfs_dentry);
}