[Android-virt] [RFC PATCH 1/6] ARM: KVM: Reset VCPU registers properly

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

 



Reset all core and cp15 registers to their architecturally defined reset
values at VCPU init time.

Also provide a method to test which CPU we're emulating based on the
host physical CPU.

Signed-off-by: Christoffer Dall <c.dall at virtualopensystems.com>
---
 arch/arm/include/asm/kvm_arm.h  |    6 ++
 arch/arm/include/asm/kvm_host.h |    2 +
 arch/arm/kvm/Makefile           |    2 -
 arch/arm/kvm/arm.c              |   36 ++++++-----
 arch/arm/kvm/interrupts.S       |    4 +
 arch/arm/kvm/reset.c            |  132 +++++++++++++++++++++++++++++++++++++++
 6 files changed, 164 insertions(+), 18 deletions(-)
 create mode 100644 arch/arm/kvm/reset.c

diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h
index 5dc91f1..a68b5cc 100644
--- a/arch/arm/include/asm/kvm_arm.h
+++ b/arch/arm/include/asm/kvm_arm.h
@@ -21,6 +21,12 @@
 
 #include <asm/types.h>
 
+/* Supported Processor Types */
+#define CORTEX_A15	(0xC0F)
+
+/* Multiprocessor Affinity Register */
+#define MPIDR_CPUID	(0x3 << 0)
+
 /* Hyp Configuration Register (HCR) bits */
 #define HCR_TGE		(1 << 27)
 #define HCR_TVM		(1 << 26)
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 6bb172a..caeb687 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -31,6 +31,8 @@
 
 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);
 
 struct kvm_arch {
 	/* The VMID generation used for the virt. memory system */
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index e69a8e1..5f40c5a 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -12,6 +12,6 @@ obj-$(CONFIG_KVM_ARM_HOST) += init.o interrupts.o exports.o
 
 kvm-arm-y += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
-kvm-arm-y += arm.o guest.o mmu.o emulate.o
+kvm-arm-y += arm.o guest.o mmu.o emulate.o reset.o
 
 obj-$(CONFIG_KVM) += kvm-arm.o
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 118e660..62c0e5f 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -238,26 +238,27 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
 	return 0;
 }
 
-int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
+int __attribute_const__ kvm_target_cpu(void)
 {
-	unsigned long cpsr;
-	unsigned long sctlr;
+	unsigned int midr;
 
+	midr = read_cpuid_id();
+	switch ((midr >> 4) & 0xfff) {
+	case CORTEX_A15:
+		return CORTEX_A15;
+	default:
+		return -EINVAL;
+	}
+}
 
-	/* Init execution CPSR */
-	asm volatile ("mrs	%[cpsr], cpsr" :
-			[cpsr] "=r" (cpsr));
-	vcpu->arch.regs.cpsr = SVC_MODE | PSR_I_BIT | PSR_F_BIT | PSR_A_BIT |
-				(cpsr & PSR_E_BIT);
+int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
+{
+	int ret;
 
-	/* Init SCTLR with MMU disabled */
-	asm volatile ("mrc	p15, 0, %[sctlr], c1, c0, 0" :
-			[sctlr] "=r" (sctlr));
-	vcpu->arch.cp15[c1_SCTLR] = sctlr & ~1U;
+	ret = kvm_reset_vcpu(vcpu);
+	if (ret < 0)
+		return ret;
 
-	/* Compute guest MPIDR */
-	vcpu->arch.cp15[c0_MPIDR] = (read_cpuid_mpidr() & ~0xff)
-				    | vcpu->vcpu_id;
 	return 0;
 }
 
@@ -792,6 +793,11 @@ int kvm_arch_init(void *opaque)
 {
 	int err;
 
+	if (kvm_target_cpu() < 0) {
+		kvm_err("Target CPU not supported!\n");
+		return -EINVAL;
+	}
+
 	err = init_hyp_mode();
 	if (err)
 		goto out_err;
diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S
index 601f0a2..a93ba81 100644
--- a/arch/arm/kvm/interrupts.S
+++ b/arch/arm/kvm/interrupts.S
@@ -325,7 +325,7 @@ ENTRY(__kvm_vcpu_run)
 	@ Trap coprocessor CRx for all x except 2 and 14
 	set_hstr 1
 
-	@ Write standard A-9 CPU id in MIDR
+	@ Write configured ID register into MIDR alias
 	ldr	r1, [r0, #VCPU_MIDR]
 	mcr	p15, 4, r1, c0, c0, 0
 
@@ -375,7 +375,7 @@ __kvm_vcpu_return:
 	@ Reset Hyp-role
 	configure_hyp_role 0, r1
 
-	@ Let guest read hardware MIDR
+	@ Let host read hardware MIDR
 	mrc	p15, 0, r2, c0, c0, 0
 	mcr	p15, 4, r2, c0, c0, 0
 
diff --git a/arch/arm/kvm/reset.c b/arch/arm/kvm/reset.c
new file mode 100644
index 0000000..ce92d4b
--- /dev/null
+++ b/arch/arm/kvm/reset.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall at virtualopensystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+
+#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
+ */
+
+static const int a15_max_cpu_idx = 3;
+
+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 },
+	{ 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
+ */
+
+/**
+ * kvm_reset_vcpu - sets core registers and 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 architectually defined reset values.
+ */
+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 (kvm_target_cpu()) {
+	case 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;
+	}
+
+	/* Reset core registers */
+	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);
+
+	return 0;
+}



[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