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; +}