From patchwork Tue Jul 3 09:01:38 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christoffer Dall X-Patchwork-Id: 1149601 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 3D23740135 for ; Tue, 3 Jul 2012 09:01:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754195Ab2GCJBl (ORCPT ); Tue, 3 Jul 2012 05:01:41 -0400 Received: from mail-qc0-f174.google.com ([209.85.216.174]:56412 "EHLO mail-qc0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754071Ab2GCJBk (ORCPT ); Tue, 3 Jul 2012 05:01:40 -0400 Received: by mail-qc0-f174.google.com with SMTP id o28so3431456qcr.19 for ; Tue, 03 Jul 2012 02:01:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=subject:to:from:cc:date:message-id:in-reply-to:references :user-agent:mime-version:content-type:content-transfer-encoding :x-gm-message-state; bh=VsFyUPDu1OzxvSdddZE4WYrpLJlBhiaqN0r7FfT2TqI=; b=GuauvPVqWRYvyUxq4aG5PGcNukHcAZNgtSBg+WU7EH9TonIUEypai6b4+Vc+I/nesz t7kkXmga2w3Z2Ko8SZGTYhhyjkN2kt7mVjXAgIe/SuCcbl5Q6rIc7BKnOa+LIQpQLK5d o4bNGmR6iC12C5nlZiVll4UgQ2PiZW4msCCDftf2Fpwiy4jzIbgWzzo45YO9f8NpACUT WtuSFbKCeDHWJchDnmLTQ0ugHNo3z7pvd2dC5RKHDild+bPTwitqAitMUb9NaSDlaW6v /r0lhVVqaPDquYzbx1SCr8n2KOGKXmE+lL1Kh1cDPfER/2UJ12JbjLeqFSQ+pdTWKwno Cz0g== Received: by 10.224.219.211 with SMTP id hv19mr28397619qab.86.1341306099620; Tue, 03 Jul 2012 02:01:39 -0700 (PDT) Received: from [127.0.1.1] (fireball.cs.columbia.edu. [128.59.13.10]) by mx.google.com with ESMTPS id w2sm37048720qao.2.2012.07.03.02.01.38 (version=TLSv1/SSLv3 cipher=OTHER); Tue, 03 Jul 2012 02:01:39 -0700 (PDT) Subject: [PATCH v9 13/16] ARM: KVM: Emulation framework and CP15 emulation To: android-virt@lists.cs.columbia.edu, kvm@vger.kernel.org From: Christoffer Dall Cc: tech@virtualopensystems.com Date: Tue, 03 Jul 2012 05:01:38 -0400 Message-ID: <20120703090138.27746.22965.stgit@ubuntu> In-Reply-To: <20120703085841.27746.82730.stgit@ubuntu> References: <20120703085841.27746.82730.stgit@ubuntu> User-Agent: StGit/0.15 MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkEeVFZwewQ+vOEK4p+4Q9JGACjP8YE5OhJBj5hwWXn0JIphY4jHiXoUEUxOJZG4v3BxzH0 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Adds a new important function in the main KVM/ARM code called handle_exit() which is called from kvm_arch_vcpu_ioctl_run() on returns from guest execution. This function examines the Hyp-Syndrome-Register (HSR), which contains information telling KVM what caused the exit from the guest. Some of the reasons for an exit are CP15 accesses, which are not allowed from the guest and this commit handles these exits by emulating the intended operation in software and skip the guest instruction. Signed-off-by: Christoffer Dall --- arch/arm/include/asm/kvm_arm.h | 5 arch/arm/include/asm/kvm_emulate.h | 10 + arch/arm/include/asm/kvm_host.h | 3 arch/arm/kvm/arm.c | 114 +++++++++ arch/arm/kvm/emulate.c | 455 ++++++++++++++++++++++++++++++++++++ arch/arm/kvm/trace.h | 28 ++ 6 files changed, 614 insertions(+), 1 deletion(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 232117c..0d1e895 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -77,6 +77,11 @@ HCR_SWIO) #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) +/* System Control Register (SCTLR) bits */ +#define SCTLR_TE (1 << 30) +#define SCTLR_EE (1 << 25) +#define SCTLR_V (1 << 13) + /* Hyp System Control Register (HSCTLR) bits */ #define HSCTLR_TE (1 << 30) #define HSCTLR_EE (1 << 25) diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h index 9e29335..f2e973c 100644 --- a/arch/arm/include/asm/kvm_emulate.h +++ b/arch/arm/include/asm/kvm_emulate.h @@ -51,6 +51,16 @@ static inline enum vcpu_mode vcpu_mode(struct kvm_vcpu *vcpu) return mode; } +int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run); +void kvm_adjust_itstate(struct kvm_vcpu *vcpu); +void kvm_inject_undefined(struct kvm_vcpu *vcpu); + /* * Return the SPSR for the specified mode of the virtual CPU. */ diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index f6b4c02..c58865b 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -112,6 +112,9 @@ struct kvm_vcpu_arch { u64 pc_ipa2; /* same as above, but for non-aligned wide thumb instructions */ + /* dcache set/way operation pending */ + cpumask_t require_dcache_flush; + /* IO related fields */ bool mmio_sign_extend; /* for byte/halfword loads */ u32 mmio_rd; diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 4687690..5e6465b 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +272,15 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { vcpu->cpu = cpu; + + /* + * Check whether this vcpu requires the cache to be flushed on + * this physical CPU. This is a consequence of doing dcache + * operations by set/way on this vcpu. We do it here in order + * to be in a non-preemptible section. + */ + if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush)) + flush_cache_all(); /* We'd really want v7_flush_dcache_all() */ } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -375,6 +385,69 @@ static void update_vttbr(struct kvm *kvm) spin_unlock(&kvm_vmid_lock); } +static int handle_svc_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* SVC called from Hyp mode should never get here */ + kvm_debug("SVC called from Hyp mode shouldn't go here\n"); + BUG(); + return -EINVAL; /* Squash warning */ +} + +static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* + * Guest called HVC instruction: + * Let it know we don't want that by injecting an undefined exception. + */ + kvm_debug("hvc: %x (at %08x)", vcpu->arch.hsr & ((1 << 16) - 1), + vcpu->arch.regs.pc); + kvm_debug(" HSR: %8x", vcpu->arch.hsr); + kvm_inject_undefined(vcpu); + return 0; +} + +static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* We don't support SMC; don't do that. */ + kvm_debug("smc: at %08x", vcpu->arch.regs.pc); + return -EINVAL; +} + +static int handle_pabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* The hypervisor should never cause aborts */ + kvm_err("Prefetch Abort taken from Hyp mode at %#08x (HSR: %#08x)\n", + vcpu->arch.hifar, vcpu->arch.hsr); + return -EFAULT; +} + +static int handle_dabt_hyp(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + /* This is either an error in the ws. code or an external abort */ + kvm_err("Data Abort taken from Hyp mode at %#08x (HSR: %#08x)\n", + vcpu->arch.hdfar, vcpu->arch.hsr); + return -EFAULT; +} + +typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *); +static exit_handle_fn arm_exit_handlers[] = { + [HSR_EC_WFI] = kvm_handle_wfi, + [HSR_EC_CP15_32] = kvm_handle_cp15_32, + [HSR_EC_CP15_64] = kvm_handle_cp15_64, + [HSR_EC_CP14_MR] = kvm_handle_cp14_access, + [HSR_EC_CP14_LS] = kvm_handle_cp14_load_store, + [HSR_EC_CP14_64] = kvm_handle_cp14_access, + [HSR_EC_CP_0_13] = kvm_handle_cp_0_13_access, + [HSR_EC_CP10_ID] = kvm_handle_cp10_id, + [HSR_EC_SVC_HYP] = handle_svc_hyp, + [HSR_EC_HVC] = handle_hvc, + [HSR_EC_SMC] = handle_smc, + [HSR_EC_IABT] = kvm_handle_guest_abort, + [HSR_EC_IABT_HYP] = handle_pabt_hyp, + [HSR_EC_DABT] = kvm_handle_guest_abort, + [HSR_EC_DABT_HYP] = handle_dabt_hyp, +}; + /* * Return 0 to return to guest, < 0 on error, exit_reason ( > 0) on proper * exit to QEMU. @@ -382,7 +455,36 @@ static void update_vttbr(struct kvm *kvm) static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, int exception_index) { - return -EINVAL; + unsigned long hsr_ec; + + switch (exception_index) { + case ARM_EXCEPTION_IRQ: + vcpu->stat.irq_exits++; + return 0; + case ARM_EXCEPTION_UNDEFINED: + kvm_err("Undefined exception in Hyp mode at: %#08x\n", + vcpu->arch.hyp_pc); + BUG(); + panic("KVM: Hypervisor undefined exception!\n"); + case ARM_EXCEPTION_DATA_ABORT: + case ARM_EXCEPTION_PREF_ABORT: + case ARM_EXCEPTION_HVC: + hsr_ec = (vcpu->arch.hsr & HSR_EC) >> HSR_EC_SHIFT; + + if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) + || !arm_exit_handlers[hsr_ec]) { + kvm_err("Unkown exception class: %#08lx, " + "hsr: %#08x\n", hsr_ec, + (unsigned int)vcpu->arch.hsr); + BUG(); + } + + return arm_exit_handlers[hsr_ec](vcpu, run); + default: + kvm_pr_unimpl("Unsupported exception type: %d", + exception_index); + return -EINVAL; + } } /* @@ -432,6 +534,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) update_vttbr(vcpu->kvm); + /* + * Make sure preemption is disabled while calling handle_exit + * as exit handling touches CPU-specific resources, such as + * caches, and we must stay on the same CPU. + * + * Code that might sleep must disable preemption and access + * CPU-specific resources first. + */ + preempt_disable(); local_irq_disable(); /* Re-check atomic conditions */ @@ -462,6 +573,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) *************************************************************/ ret = handle_exit(vcpu, run, ret); + preempt_enable(); if (ret < 0) { kvm_err("Error in handle_exit\n"); break; diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c index c0bacc6..b430924 100644 --- a/arch/arm/kvm/emulate.c +++ b/arch/arm/kvm/emulate.c @@ -16,7 +16,13 @@ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include +#include +#include #include +#include + +#include "trace.h" #define REG_OFFSET(_reg) \ (offsetof(struct kvm_vcpu_regs, _reg) / sizeof(u32)) @@ -125,3 +131,452 @@ u32 *vcpu_reg_mode(struct kvm_vcpu *vcpu, u8 reg_num, u32 mode) return reg_array + vcpu_reg_offsets[mode][reg_num]; } + +/****************************************************************************** + * Co-processor emulation + */ + +struct coproc_params { + unsigned long CRn; + unsigned long CRm; + unsigned long Op1; + unsigned long Op2; + unsigned long Rt1; + unsigned long Rt2; + bool is_64bit; + bool is_write; +}; + +static void print_cp_instr(const struct coproc_params *p) +{ + /* Look, we even formatted it for you to paste into the table! */ + if (p->is_64bit) { + kvm_err("{ CRn(DF), CRm(%2lu), Op1(%2lu), Op2(DF), is64, %-6s" + " func, arg},\n", + p->CRm, p->Op1, p->is_write ? "WRITE," : "READ,"); + } else { + kvm_err("{ CRn(%2lu), CRm(%2lu), Op1(%2lu), Op2(%2lu), is32," + " %-6s func, arg},\n", + p->CRn, p->CRm, p->Op1, p->Op2, + p->is_write ? "WRITE," : "READ,"); + } +} + +int kvm_handle_cp10_id(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 0; +} + +int kvm_handle_cp_0_13_access(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 0; +} + +int kvm_handle_cp14_load_store(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 0; +} + +int kvm_handle_cp14_access(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + kvm_inject_undefined(vcpu); + return 0; +} + +static bool ignore_write(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + if (arg) + trace_kvm_emulate_cp15_imp(p->Op1, p->Rt1, p->CRn, p->CRm, + p->Op2, p->is_write); + return true; +} + +static bool read_zero(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + if (arg) + trace_kvm_emulate_cp15_imp(p->Op1, p->Rt1, p->CRn, p->CRm, + p->Op2, p->is_write); + *vcpu_reg(vcpu, p->Rt1) = 0; + return true; +} + +static bool read_l2ctlr(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + u32 l2ctlr, ncores; + + switch (kvm_target_cpu()) { + case CORTEX_A15: + asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr)); + l2ctlr &= ~(3 << 24); + ncores = atomic_read(&vcpu->kvm->online_vcpus) - 1; + l2ctlr |= (ncores & 3) << 24; + *vcpu_reg(vcpu, p->Rt1) = l2ctlr; + return true; + default: + return false; + } +} + +static bool write_l2ctlr(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + return false; +} + +static bool access_l2ectlr(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + switch (kvm_target_cpu()) { + case CORTEX_A15: + if (!p->is_write) + *vcpu_reg(vcpu, p->Rt1) = 0; + return true; + default: + return false; + } +} + +static bool read_actlr(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long arg) +{ + u32 actlr; + + switch (kvm_target_cpu()) { + case CORTEX_A15: + asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); + /* Make the SMP bit consistent with the guest configuration */ + if (atomic_read(&vcpu->kvm->online_vcpus) > 1) + actlr |= 1U << 6; + else + actlr &= ~(1U << 6); + *vcpu_reg(vcpu, p->Rt1) = actlr; + break; + default: + asm volatile("mrc p15, 0, %0, c1, c0, 1\n" : "=r" (actlr)); + *vcpu_reg(vcpu, p->Rt1) = actlr; + break; + } + + return true; +} + +static bool write_dcsw(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long cp15_reg) +{ + u32 val; + + val = *vcpu_reg(vcpu, p->Rt1); + + switch (p->CRm) { + case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */ + case 14: /* DCCISW */ + asm volatile("mcr p15, 0, %0, c7, c14, 2" : : "r" (val)); + break; + + case 10: /* DCCSW */ + asm volatile("mcr p15, 0, %0, c7, c10, 2" : : "r" (val)); + break; + } + + cpumask_setall(&vcpu->arch.require_dcache_flush); + cpumask_clear_cpu(vcpu->cpu, &vcpu->arch.require_dcache_flush); + + return true; +} + +static bool access_cp15_reg(struct kvm_vcpu *vcpu, + const struct coproc_params *p, + unsigned long cp15_reg) +{ + if (p->is_write) + vcpu->arch.cp15[cp15_reg] = *vcpu_reg(vcpu, p->Rt1); + else + *vcpu_reg(vcpu, p->Rt1) = vcpu->arch.cp15[cp15_reg]; + return true; +} + +/* Any field which is 0xFFFFFFFF == DF */ +struct coproc_emulate { + unsigned long CRn; + unsigned long CRm; + unsigned long Op1; + unsigned long Op2; + + unsigned long is_64; + unsigned long is_w; + + bool (*f)(struct kvm_vcpu *, + const struct coproc_params *, + unsigned long); + unsigned long arg; +}; + +#define DF (-1UL) /* Default: If nothing else fits, use this one */ +#define CRn(_x) .CRn = _x +#define CRm(_x) .CRm = _x +#define Op1(_x) .Op1 = _x +#define Op2(_x) .Op2 = _x +#define is64 .is_64 = true +#define is32 .is_64 = false +#define READ .is_w = false +#define WRITE .is_w = true +#define RW .is_w = DF + +static const struct coproc_emulate coproc_emulate[] = { + /* + * ACTRL access: + * + * Ignore writes, and read returns the host settings. + */ + { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, WRITE, ignore_write}, + { CRn( 1), CRm( 0), Op1( 0), Op2( 1), is32, READ, read_actlr}, + /* + * DC{C,I,CI}SW operations: + */ + { CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, WRITE, write_dcsw}, + { CRn( 7), CRm( 6), Op1( 0), Op2( 2), is32, READ, read_zero}, + { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, WRITE, write_dcsw}, + { CRn( 7), CRm(10), Op1( 0), Op2( 2), is32, READ, read_zero}, + { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, WRITE, write_dcsw}, + { CRn( 7), CRm(14), Op1( 0), Op2( 2), is32, READ, read_zero}, + /* + * L2CTLR access (guest wants to know #CPUs). + * + * FIXME: Hack Alert: Read zero as default case. + */ + { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, READ, read_l2ctlr}, + { CRn( 9), CRm( 0), Op1( 1), Op2( 2), is32, WRITE, write_l2ctlr}, + { CRn( 9), CRm( 0), Op1( 1), Op2( 3), is32, READ, access_l2ectlr}, + { CRn( 9), CRm(DF), Op1(DF), Op2(DF), is32, WRITE, ignore_write}, + { CRn( 9), CRm(DF), Op1(DF), Op2(DF), is32, READ, read_zero}, + + /* + * These CRn == 10 entries may not need to exist - if we can + * ignore guest attempts to tamper with TLB lockdowns then it + * should be enough to store/restore the host/guest PRRR and + * NMRR memory remap registers and allow guest direct access + * to these registers. + * + * TLB Lockdown operations - ignored + */ + { CRn(10), CRm( 0), Op1(DF), Op2(DF), is32, WRITE, ignore_write}, + { CRn(10), CRm( 2), Op1( 0), Op2( 0), is32, RW, access_cp15_reg, + c10_PRRR}, + { CRn(10), CRm( 2), Op1( 0), Op2( 1), is32, RW, access_cp15_reg, + c10_NMRR}, + + /* + * The CP15 c15 register is architecturally implementation + * defined, but some guest kernels attempt to read/write a + * diagnostics register here. We always return 0 and ignore + * writes and hope for the best. + */ + { CRn(15), CRm(DF), Op1(DF), Op2(DF), is32, WRITE, ignore_write, 1}, + { CRn(15), CRm(DF), Op1(DF), Op2(DF), is32, READ, read_zero, 1}, +}; + +#undef is64 +#undef is32 +#undef READ +#undef WRITE +#undef RW + +static inline bool match(unsigned long val, unsigned long param) +{ + return param == DF || val == param; +} + +static int emulate_cp15(struct kvm_vcpu *vcpu, + const struct coproc_params *params) +{ + unsigned long instr_len, i; + + for (i = 0; i < ARRAY_SIZE(coproc_emulate); i++) { + const struct coproc_emulate *e = &coproc_emulate[i]; + + if (!match(params->is_64bit, e->is_64)) + continue; + if (!match(params->is_write, e->is_w)) + continue; + if (!match(params->CRn, e->CRn)) + continue; + if (!match(params->CRm, e->CRm)) + continue; + if (!match(params->Op1, e->Op1)) + continue; + if (!match(params->Op2, e->Op2)) + continue; + + /* If function fails, it should complain. */ + if (!e->f(vcpu, params, e->arg)) + goto undef; + + /* Skip instruction, since it was emulated */ + instr_len = ((vcpu->arch.hsr >> 25) & 1) ? 4 : 2; + *vcpu_pc(vcpu) += instr_len; + kvm_adjust_itstate(vcpu); + return 0; + } + + kvm_err("Unsupported guest CP15 access at: %08x\n", + vcpu->arch.regs.pc); + print_cp_instr(params); +undef: + kvm_inject_undefined(vcpu); + return 0; +} + +/** + * kvm_handle_cp15_64 -- handles a mrrc/mcrr trap on a guest CP15 access + * @vcpu: The VCPU pointer + * @run: The kvm_run struct + */ +int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + struct coproc_params params; + + params.CRm = (vcpu->arch.hsr >> 1) & 0xf; + params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf; + params.is_write = ((vcpu->arch.hsr & 1) == 0); + params.is_64bit = true; + + params.Op1 = (vcpu->arch.hsr >> 16) & 0xf; + params.Op2 = 0; + params.Rt2 = (vcpu->arch.hsr >> 10) & 0xf; + params.CRn = 0; + + return emulate_cp15(vcpu, ¶ms); +} + +/** + * kvm_handle_cp15_32 -- handles a mrc/mcr trap on a guest CP15 access + * @vcpu: The VCPU pointer + * @run: The kvm_run struct + */ +int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + struct coproc_params params; + + params.CRm = (vcpu->arch.hsr >> 1) & 0xf; + params.Rt1 = (vcpu->arch.hsr >> 5) & 0xf; + params.is_write = ((vcpu->arch.hsr & 1) == 0); + params.is_64bit = false; + + params.CRn = (vcpu->arch.hsr >> 10) & 0xf; + params.Op1 = (vcpu->arch.hsr >> 14) & 0x7; + params.Op2 = (vcpu->arch.hsr >> 17) & 0x7; + params.Rt2 = 0; + + return emulate_cp15(vcpu, ¶ms); +} + +int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + vcpu->stat.wfi_exits++; + return 0; +} + +/** + * adjust_itstate - adjust ITSTATE when emulating instructions in IT-block + * @vcpu: The VCPU pointer + * + * When exceptions occur while instructions are executed in Thumb IF-THEN + * blocks, the ITSTATE field of the CPSR is not advanved (updated), so we have + * to do this little bit of work manually. The fields map like this: + * + * IT[7:0] -> CPSR[26:25],CPSR[15:10] + */ +void kvm_adjust_itstate(struct kvm_vcpu *vcpu) +{ + unsigned long itbits, cond; + unsigned long cpsr = *vcpu_cpsr(vcpu); + bool is_arm = !(cpsr & PSR_T_BIT); + + BUG_ON(is_arm && (cpsr & PSR_IT_MASK)); + + if (!(cpsr & PSR_IT_MASK)) + return; + + cond = (cpsr & 0xe000) >> 13; + itbits = (cpsr & 0x1c00) >> (10 - 2); + itbits |= (cpsr & (0x3 << 25)) >> 25; + + /* Perform ITAdvance (see page A-52 in ARM DDI 0406C) */ + if ((itbits & 0x7) == 0) + itbits = cond = 0; + else + itbits = (itbits << 1) & 0x1f; + + cpsr &= ~PSR_IT_MASK; + cpsr |= cond << 13; + cpsr |= (itbits & 0x1c) << (10 - 2); + cpsr |= (itbits & 0x3) << 25; + *vcpu_cpsr(vcpu) = cpsr; +} + +/****************************************************************************** + * Inject exceptions into the guest + */ + +static u32 exc_vector_base(struct kvm_vcpu *vcpu) +{ + u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; + u32 vbar = vcpu->arch.cp15[c12_VBAR]; + + if (sctlr & SCTLR_V) + return 0xffff0000; + else /* always have security exceptions */ + return vbar; +} + +/** + * kvm_inject_undefined - inject an undefined exception into the guest + * @vcpu: The VCPU to receive the undefined exception + * + * It is assumed that this code is called from the VCPU thread and that the + * VCPU therefore is not currently executing guest code. + * + * Modelled after TakeUndefInstrException() pseudocode. + */ +void kvm_inject_undefined(struct kvm_vcpu *vcpu) +{ + u32 new_lr_value; + u32 new_spsr_value; + u32 cpsr = *vcpu_cpsr(vcpu); + u32 sctlr = vcpu->arch.cp15[c1_SCTLR]; + bool is_thumb = (cpsr & PSR_T_BIT); + u32 vect_offset = 4; + u32 return_offset = (is_thumb) ? 2 : 4; + + new_spsr_value = cpsr; + new_lr_value = *vcpu_pc(vcpu) - return_offset; + + *vcpu_cpsr(vcpu) = (cpsr & ~MODE_MASK) | UND_MODE; + *vcpu_cpsr(vcpu) |= PSR_I_BIT; + *vcpu_cpsr(vcpu) &= ~(PSR_IT_MASK | PSR_J_BIT | PSR_E_BIT | PSR_T_BIT); + + if (sctlr & SCTLR_TE) + *vcpu_cpsr(vcpu) |= PSR_T_BIT; + if (sctlr & SCTLR_EE) + *vcpu_cpsr(vcpu) |= PSR_E_BIT; + + /* Note: These now point to UND banked copies */ + *vcpu_spsr(vcpu) = cpsr; + *vcpu_reg(vcpu, 14) = new_lr_value; + + /* Branch to exception vector */ + *vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset; +} diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index f8869c1..e474a0a 100644 --- a/arch/arm/kvm/trace.h +++ b/arch/arm/kvm/trace.h @@ -39,7 +39,35 @@ TRACE_EVENT(kvm_exit, TP_printk("PC: 0x%08lx", __entry->vcpu_pc) ); +/* Architecturally implementation defined CP15 register access */ +TRACE_EVENT(kvm_emulate_cp15_imp, + TP_PROTO(unsigned long Op1, unsigned long Rt1, unsigned long CRn, + unsigned long CRm, unsigned long Op2, bool is_write), + TP_ARGS(Op1, Rt1, CRn, CRm, Op2, is_write), + TP_STRUCT__entry( + __field( unsigned int, Op1 ) + __field( unsigned int, Rt1 ) + __field( unsigned int, CRn ) + __field( unsigned int, CRm ) + __field( unsigned int, Op2 ) + __field( bool, is_write ) + ), + + TP_fast_assign( + __entry->is_write = is_write; + __entry->Op1 = Op1; + __entry->Rt1 = Rt1; + __entry->CRn = CRn; + __entry->CRm = CRm; + __entry->Op2 = Op2; + ), + + TP_printk("Implementation defined CP15: %s\tp15, %u, r%u, c%u, c%u, %u", + (__entry->is_write) ? "mcr" : "mrc", + __entry->Op1, __entry->Rt1, __entry->CRn, + __entry->CRm, __entry->Op2) +); #endif /* _TRACE_KVM_H */