[PATCH 2/7] ARM: KVM: Drive register reset from coproc table(s).

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This keeps the information in one place, but of course it means we
have entries in the register table which are never trapped.

Minor notes:
1) We reserve a value of 0 as an invalid cp15 offset, to catch bugs in our
   table, at cost of 4 bytes per vcpu.

2) Added comments on the table indicating how we handle each register, for
   simplicity of understanding.

Signed-off-by: Rusty Russell <rusty.russell@xxxxxxxxxx>

diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 9f9ec36..2e3b628 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -37,6 +37,7 @@ struct kvm_vcpu;
 u32 *kvm_vcpu_reg(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode);
 int kvm_target_cpu(void);
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu);
+void kvm_reset_coprocs(struct kvm_vcpu *vcpu);
 
 struct kvm_arch {
 	/* The VMID generation used for the virt. memory system */
@@ -72,8 +73,9 @@ struct kvm_vcpu_regs {
 	u32 cpsr;		/* The guest CPSR */
 } __packed;
 
+/* 0 is reserved as an invalid value. */
 enum cp15_regs {
-	c0_MIDR,		/* Main ID Register */
+	c0_MIDR=1,		/* Main ID Register */
 	c0_MPIDR,		/* MultiProcessor ID Register */
 	c1_SCTLR,		/* System Control Register */
 	c1_ACTLR,		/* Auxilliary Control Register */
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 42f8043..79e1d0b 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -21,6 +21,7 @@
 #include <asm/kvm_emulate.h>
 #include <asm/kvm_coproc.h>
 #include <asm/cacheflush.h>
+#include <asm/cputype.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
@@ -41,6 +42,7 @@ struct coproc_params {
 };
 
 struct coproc_reg {
+	/* MRC/MCR/MRRC/MCRR instruction which accesses it. */
 	unsigned long CRn;
 	unsigned long CRm;
 	unsigned long Op1;
@@ -48,9 +50,19 @@ struct coproc_reg {
 
 	bool is_64;
 
+	/* Trapped access from guest, if non-NULL. */
 	bool (*access)(struct kvm_vcpu *,
 		       const struct coproc_params *,
 		       const struct coproc_reg *);
+
+	/* Initialization for vcpu. */
+	void (*reset)(struct kvm_vcpu *, const struct coproc_reg *);
+
+	/* Index into vcpu->arch.cp15[], or 0 if we don't need to save it. */
+	enum cp15_regs reg;
+
+	/* Value (usually reset value) */
+	u64 val;
 };
 
 static void print_cp_instr(const struct coproc_params *p)
@@ -251,6 +263,43 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
 #define access_pmintenset pm_fake
 #define access_pmintenclr pm_fake
 
+/* Reset functions */
+static void reset_unknown(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+	BUG_ON(!r->reg);
+	BUG_ON(r->reg >= ARRAY_SIZE(vcpu->arch.cp15));
+	vcpu->arch.cp15[r->reg] = 0xdecafbad;
+}
+
+static void reset_val(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+	BUG_ON(!r->reg);
+	BUG_ON(r->reg >= ARRAY_SIZE(vcpu->arch.cp15));
+	vcpu->arch.cp15[r->reg] = r->val;
+}
+
+static void reset_unknown64(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+	BUG_ON(!r->reg);
+	BUG_ON(r->reg + 1 >= ARRAY_SIZE(vcpu->arch.cp15));
+
+	vcpu->arch.cp15[r->reg] = 0xdecafbad;
+	vcpu->arch.cp15[r->reg+1] = 0xd0c0ffee;
+}
+
+static void reset_mpidr(struct kvm_vcpu *vcpu, const struct coproc_reg *r)
+{
+	/*
+	 * Compute guest MPIDR:
+	 * (Even if we present only one VCPU to the guest on an SMP
+	 * host we don't set the U bit in the MPIDR, or vice versa, as
+	 * revealing the underlying hardware properties is likely to
+	 * be the best choice).
+	 */
+	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_CPUID)
+		| (vcpu->vcpu_id & MPIDR_CPUID);
+}
+
 #define CRn(_x)		.CRn = _x
 #define CRm(_x) 	.CRm = _x
 #define Op1(_x) 	.Op1 = _x
@@ -260,6 +309,33 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
 
 /* Architected CP15 registers. */
 static const struct coproc_reg cp15_regs[] = {
+	/* TTBCR: swapped by interrupt.S. */
+	{ CRn( 2), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_val, c2_TTBCR, 0x00000000 },
+
+	/* TTBR0/TTBR1: swapped by interrupt.S. */
+	{ CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
+	{ CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
+
+	/* DACR: swapped by interrupt.S. */
+	{ CRn( 3), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_unknown, c3_DACR },
+
+	/* DFSR/IFSR/ADFSR/AIFSR: swapped by interrupt.S. */
+	{ CRn( 5), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_unknown, c5_DFSR },
+	{ CRn( 5), CRm( 0), Op1( 0), Op2( 1), is32,
+			NULL, reset_unknown, c5_IFSR },
+	{ CRn( 5), CRm( 1), Op1( 0), Op2( 0), is32,
+			NULL, reset_unknown, c5_ADFSR },
+	{ CRn( 5), CRm( 1), Op1( 0), Op2( 1), is32,
+			NULL, reset_unknown, c5_AIFSR },
+
+	/* DFAR/IFAR: swapped by interrupt.S. */
+	{ CRn( 6), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_unknown, c6_DFAR },
+	{ CRn( 6), CRm( 0), Op1( 0), Op2( 2), is32,
+			NULL, reset_unknown, c6_IFAR },
 	/*
 	 * DC{C,I,CI}SW operations:
 	 */
@@ -282,14 +358,46 @@ static const struct coproc_reg cp15_regs[] = {
 	{ CRn( 9), CRm(14), Op1( 0), Op2( 0), is32, access_pmuserenr},
 	{ CRn( 9), CRm(14), Op1( 0), Op2( 1), is32, access_pmintenset},
 	{ CRn( 9), CRm(14), Op1( 0), Op2( 2), is32, access_pmintenclr},
+ 
+	/* PRRR/NMRR (aka MAIR0/MAIR1): swapped by interrupt.S. */
+	{ CRn(10), CRm( 2), Op1( 0), Op2( 0), is32,
+			NULL, reset_unknown, c10_PRRR},
+	{ CRn(10), CRm( 2), Op1( 0), Op2( 1), is32,
+			NULL, reset_unknown, c10_NMRR},
+
+	/* VBAR: swapped by interrupt.S. */
+	{ CRn(12), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_val, c12_VBAR, 0x00000000 },
+
+	/* CONTEXTIDR/TPIDRURW/TPIDRURO/TPIDRPRW: swapped by interrupt.S. */
+	{ CRn(13), CRm( 0), Op1( 0), Op2( 1), is32,
+			NULL, reset_val, c13_CID, 0x00000000 },
+	{ CRn(13), CRm( 0), Op1( 0), Op2( 2), is32,
+			NULL, reset_unknown, c13_TID_URW },
+	{ CRn(13), CRm( 0), Op1( 0), Op2( 3), is32,
+			NULL, reset_unknown, c13_TID_URO },
+	{ CRn(13), CRm( 0), Op1( 0), Op2( 4), is32,
+			NULL, reset_unknown, c13_TID_PRIV },
 };
 
 /* A15-specific CP15 registers. */
 static const struct coproc_reg cp15_cortex_a15_regs[] = {
-	/*
-	 * ACTRL access:
-	 */
+	/* MIDR: we use VPIDR for guest access. */
+	{ CRn( 0), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_val, c0_MIDR, 0x412FC0F0 },
+	/* MPIDR: we use VMPIDR for guest access. */
+	{ CRn( 0), CRm( 0), Op1( 0), Op2( 5), is32,
+			NULL, reset_mpidr, c0_MPIDR },
+
+	/* SCTLR: swapped by interrupt.S. */
+	{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
+			NULL, reset_val, c1_SCTLR, 0x00C50078 },
+	/* ACTLR: trapped by HCR.TAC bit. */
 	{ CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, access_actlr},
+	/* CPACR: swapped by interrupt.S. */
+	{ CRn( 1), CRm( 0), Op1( 0), Op2( 2), is32,
+			NULL, reset_val, c1_CPACR, 0x00000000 },
+
 	/*
 	 * L2CTLR access (guest wants to know #CPUs).
 	 */
@@ -352,6 +460,9 @@ static int emulate_cp15(struct kvm_vcpu *vcpu,
 		r = find_reg(params, cp15_regs, ARRAY_SIZE(cp15_regs));
 
 	if (likely(r)) {
+		/* If we don't have an accessor, we should never get here! */
+		BUG_ON(!r->access);
+
 		if (likely(r->access(vcpu, params, r))) {
 			/* Skip instruction, since it was emulated */
 			int instr_len = ((vcpu->arch.hsr >> 25) & 1) ? 4 : 2;
@@ -391,6 +502,41 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)
 	return emulate_cp15(vcpu, &params);
 }
 
+static void reset_coproc_regs(struct kvm_vcpu *vcpu,
+			      const struct coproc_reg *table, size_t num)
+{
+	unsigned long i;
+
+	for (i = 0; i < num; i++)
+		if (table[i].reset)
+			table[i].reset(vcpu, &table[i]);
+}
+
+/**
+ * kvm_reset_coprocs - sets cp15 registers to reset value
+ * @vcpu: The VCPU pointer
+ *
+ * This function finds the right table above and sets the registers on the
+ * virtual CPU struct to their architecturally defined reset values.
+ */
+void kvm_reset_coprocs(struct kvm_vcpu *vcpu)
+{
+	size_t num;
+	const struct coproc_reg *table;
+
+	/* Catch someone adding a register without putting in reset entry. */
+	memset(vcpu->arch.cp15, 0x42, sizeof(vcpu->arch.cp15));
+
+	/* Generic chip reset first (so target could override). */
+	reset_coproc_regs(vcpu, cp15_regs, ARRAY_SIZE(cp15_regs));
+
+	table = get_target_table(vcpu->arch.target, &num);
+	reset_coproc_regs(vcpu, table, num);
+
+	for (num = 1; num < nr_cp15_regs; num++)
+		if (vcpu->arch.cp15[num] == 0x42424242)
+			panic("Didn't reset vcpu->arch.cp15[%zi]", num);
+}
 /**
  * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access
  * @vcpu: The VCPU pointer
diff --git a/arch/arm/kvm/reset.c b/arch/arm/kvm/reset.c
index 4262d14..8eb0a56 100644
--- a/arch/arm/kvm/reset.c
+++ b/arch/arm/kvm/reset.c
@@ -23,14 +23,8 @@
 
 #include <asm/unified.h>
 #include <asm/ptrace.h>
-#include <asm/cputype.h>
 #include <asm/kvm_arm.h>
 
-#define CT_ASSERT(expr, name) extern char name[(expr) ? 1 : -1]
-#define CP15_REGS_ASSERT(_array, _name) \
-	CT_ASSERT((sizeof(_array) / sizeof(_array[0])) == nr_cp15_regs, _name)
-#define UNKNOWN 0xdecafbad
-
 /******************************************************************************
  * Cortex-A15 Register Reset Values
  */
@@ -41,47 +35,6 @@ static struct kvm_vcpu_regs a15_regs_reset = {
 	.cpsr = SVC_MODE | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT,
 };
 
-static u32 a15_cp15_regs_reset[][2] = {
-	{ c0_MIDR,		0x412FC0F0 },
-	{ c0_MPIDR,		0x00000000 }, /* see kvm_arch_vcpu_init */
-	{ c1_SCTLR,		0x00C50078 },
-	{ c1_ACTLR,		0x00000000 },
-	{ c1_CPACR,		0x00000000 },
-	{ c2_TTBR0,		UNKNOWN },
-	{ c2_TTBR0_high,	UNKNOWN },
-	{ c2_TTBR1,		UNKNOWN },
-	{ c2_TTBR1_high,	UNKNOWN },
-	{ c2_TTBCR,		0x00000000 },
-	{ c3_DACR,		UNKNOWN },
-	{ c5_DFSR,		UNKNOWN },
-	{ c5_IFSR,		UNKNOWN },
-	{ c5_ADFSR,		UNKNOWN },
-	{ c5_AIFSR,		UNKNOWN },
-	{ c6_DFAR,		UNKNOWN },
-	{ c6_IFAR,		UNKNOWN },
-	{ c10_PRRR,		0x00098AA4 },
-	{ c10_NMRR,		0x44E048E0 },
-	{ c12_VBAR,		0x00000000 },
-	{ c13_CID,		0x00000000 },
-	{ c13_TID_URW,		UNKNOWN },
-	{ c13_TID_URO,		UNKNOWN },
-	{ c13_TID_PRIV,		UNKNOWN },
-};
-CP15_REGS_ASSERT(a15_cp15_regs_reset, a15_cp15_regs_reset_init);
-
-static void a15_reset_vcpu(struct kvm_vcpu *vcpu)
-{
-	/*
-	 * Compute guest MPIDR:
-	 * (Even if we present only one VCPU to the guest on an SMP
-	 * host we don't set the U bit in the MPIDR, or vice versa, as
-	 * revealing the underlying hardware properties is likely to
-	 * be the best choice).
-	 */
-	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~MPIDR_CPUID)
-				    | (vcpu->vcpu_id & MPIDR_CPUID);
-}
-
 
 /*******************************************************************************
  * Exported reset function
@@ -96,18 +49,13 @@ static void a15_reset_vcpu(struct kvm_vcpu *vcpu)
  */
 int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
 {
-	unsigned int i;
 	struct kvm_vcpu_regs *cpu_reset;
-	u32 (*cp15_reset)[2];
-	void (*cpu_reset_vcpu)(struct kvm_vcpu *vcpu);
 
 	switch (vcpu->arch.target) {
 	case KVM_ARM_TARGET_CORTEX_A15:
 		if (vcpu->vcpu_id > a15_max_cpu_idx)
 			return -EINVAL;
 		cpu_reset = &a15_regs_reset;
-		cp15_reset = a15_cp15_regs_reset;
-		cpu_reset_vcpu = a15_reset_vcpu;
 		break;
 	default:
 		return -ENODEV;
@@ -117,17 +65,7 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu)
 	memcpy(&vcpu->arch.regs, cpu_reset, sizeof(vcpu->arch.regs));
 
 	/* Reset CP15 registers */
-	for (i = 0; i < nr_cp15_regs; i++) {
-		if (cp15_reset[i][0] != i) {
-			kvm_err("CP15 field %d is %d, expected %d\n",
-				i, cp15_reset[i][0], i);
-			return -ENXIO;
-		}
-		vcpu->arch.cp15[i] = cp15_reset[i][1];
-	}
-
-	/* Physical CPU specific runtime reset operations */
-	cpu_reset_vcpu(vcpu);
+	kvm_reset_coprocs(vcpu);
 
 	return 0;
 }
_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm


[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux