@@ -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)
@@ -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.
*/
@@ -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;
@@ -37,6 +37,7 @@
#include <asm/mman.h>
#include <asm/idmap.h>
#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_asm.h>
@@ -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;
@@ -16,7 +16,13 @@
* Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include <linux/mm.h>
+#include <asm/kvm_arm.h>
+#include <asm/kvm_host.h>
#include <asm/kvm_emulate.h>
+#include <trace/events/kvm.h>
+
+#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; }
@@ -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 */