[PATCH 10/10] opcode: move generic instruction decode out of KVM.

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

 



From: Rusty Russell <rusty.russell@xxxxxxxxxx>

We add some callbacks and use them instead of using struct kvm_vcpu or
vcpu_reg() directly.  We also remove the kvm_ prefixes, and call the
top-level function decode_insn() instead of kvm_decode().

Signed-off-by: Rusty Russell <rusty.russell@xxxxxxxxxx>
---
 arch/arm/include/asm/opcodes.h |   38 ++++
 arch/arm/kernel/opcodes.c      |  438 +++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c         |  473 +---------------------------------------
 3 files changed, 484 insertions(+), 465 deletions(-)

diff --git a/arch/arm/include/asm/opcodes.h b/arch/arm/include/asm/opcodes.h
index 74e211a..7ceb7fb 100644
--- a/arch/arm/include/asm/opcodes.h
+++ b/arch/arm/include/asm/opcodes.h
@@ -11,6 +11,44 @@
 
 #ifndef __ASSEMBLY__
 extern asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr);
+
+struct arm_insn {
+	/* Encoded instruction. */
+	u32 instr;
+
+	/* Decoding for the register write back */
+	bool register_form;
+	u32 imm;
+	u8 type;
+	u8 Rt, Rn, Rm, Rd;
+	u8 shift_n;
+	u32 offset_addr;
+
+	/* Common decoding */
+	u8 len;
+	bool sign_extend;
+	bool w, W, U, P;
+
+	/* Thumb encoding */
+	bool is_thumb, is_thumb32;
+	union {
+		struct {
+			u8 opcode;
+			u8 mask;
+		} t16;
+
+		struct {
+			u8 op1;
+			u8 op2;
+			u8 op2_mask;
+		} t32;
+	};
+};
+
+int decode_insn(struct arm_insn *ai, unsigned long pc, u32 psr,
+		int (*copy_from)(void *dst, unsigned long addr, size_t, void *),
+		u32 (*get_regval)(int reg, void *),
+		void *priv);
 #endif
 
 #define ARM_OPCODE_CONDTEST_FAIL   0
diff --git a/arch/arm/kernel/opcodes.c b/arch/arm/kernel/opcodes.c
index f8179c6..b6c7940 100644
--- a/arch/arm/kernel/opcodes.c
+++ b/arch/arm/kernel/opcodes.c
@@ -70,3 +70,441 @@ asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr)
 	return ret;
 }
 EXPORT_SYMBOL_GPL(arm_check_condition);
+
+/******************************************************************************
+ * Load-Store instruction emulation
+ *****************************************************************************/
+enum SRType {
+	SRType_LSL,
+	SRType_LSR,
+	SRType_ASR,
+	SRType_ROR,
+	SRType_RRX
+};
+
+struct arm_decode {
+	/* Instruction decoding */
+	u32 opc;
+	u32 opc_mask;
+
+	bool (*decode)(struct arm_insn *ai, u32 psr,
+		       u32 (*get_regval)(int reg, void *priv), void *priv);
+
+	struct arm_insn template;
+};
+
+/* Modelled after DecodeImmShift() in the ARM ARM */
+enum SRType decode_imm_shift(u8 type, u8 imm5, u8 *amount)
+{
+	switch (type) {
+	case 0x0:
+		*amount = imm5;
+		return SRType_LSL;
+	case 0x1:
+		*amount = (imm5 == 0) ? 32 : imm5;
+		return SRType_LSR;
+	case 0x2:
+		*amount = (imm5 == 0) ? 32 : imm5;
+		return SRType_ASR; 
+	case 0x3:
+		if (imm5 == 0) {
+			*amount = 1;
+			return SRType_RRX;
+		} else {
+			*amount = imm5;
+			return SRType_ROR;
+		}
+	}
+
+	return SRType_LSL;
+}
+
+/* Modelled after Shift() in the ARM ARM */
+u32 shift(u32 value, u8 N, enum SRType type, u8 amount, bool carry_in)
+{
+	u32 mask = (1 << N) - 1;
+	s32 svalue = (s32)value;
+
+	BUG_ON(N > 32);
+	BUG_ON(type == SRType_RRX && amount != 1);
+	BUG_ON(amount > N);
+
+	if (amount == 0)
+		return value;
+
+	switch (type) {
+	case SRType_LSL:
+		value <<= amount;
+		break;
+	case SRType_LSR:
+		 value >>= amount;
+		break;
+	case SRType_ASR:
+		if (value & (1 << (N - 1)))
+			svalue |= ((-1UL) << N);
+		value = svalue >> amount;
+		break;
+	case SRType_ROR:
+		value = (value >> amount) | (value << (N - amount));
+		break;
+	case SRType_RRX: {
+		u32 C = (carry_in) ? 1 : 0;
+		value = (value >> 1) | (C << (N - 1));
+		break;
+	}
+	}
+
+	return value & mask;
+}
+
+static bool decode_arm_wb(struct arm_insn *ai, u32 psr,
+			  u32 (*get_regval)(int reg, void *priv), void *priv)
+{
+	u32 base_addr, offset;
+
+	ai->Rt = (ai->instr >> 12) & 0xf;
+	ai->Rn = (ai->instr >> 16) & 0xf;
+	ai->W = (ai->instr >> 21) & 1;
+	ai->U = (ai->instr >> 23) & 1;
+	ai->P = (ai->instr >> 24) & 1;
+
+	base_addr = get_regval(ai->Rn, priv);
+
+	if (ai->P && !ai->W) {
+		pr_err("Decoding operation with valid ISV?\n");
+		return false;
+	}
+
+	if (ai->register_form) {
+		/* Register operation */
+		enum SRType s_type;
+		u8 shift_n;
+		bool c_bit = psr & PSR_C_BIT;
+		u32 s_reg = get_regval(ai->Rm, priv);
+
+		s_type = decode_imm_shift(ai->type, ai->shift_n, &shift_n);
+		offset = shift(s_reg, 5, s_type, shift_n, c_bit);
+	} else {
+		/* Immediate operation */
+		offset = ai->imm;
+	}
+
+	/* Handle Writeback */
+	if (ai->U)
+		ai->offset_addr = base_addr + offset;
+	else
+		ai->offset_addr = base_addr - offset;
+	return true;
+}
+
+static bool decode_arm_ls(struct arm_insn *ai, u32 psr,
+			  u32 (*get_regval)(int reg, void *priv), void *priv)
+{
+	u8 A = (ai->instr >> 25) & 1;
+
+	ai->register_form = A;
+	ai->imm = ai->instr & 0xfff;
+	ai->Rm = ai->instr & 0xf;
+	ai->type = (ai->instr >> 5) & 0x3;
+	ai->shift_n = (ai->instr >> 7) & 0x1f;
+
+	return decode_arm_wb(ai, psr, get_regval, priv);
+}
+
+static bool decode_arm_extra(struct arm_insn *ai, u32 psr,
+			     u32 (*get_regval)(int reg, void *priv), void *priv)
+{
+	ai->register_form = !((ai->instr >> 22) & 1);
+	ai->imm = ((ai->instr >> 4) & 0xf0) | (ai->instr & 0xf);
+	ai->Rm = ai->instr & 0xf;
+	ai->type = 0; /* SRType_LSL */
+	ai->shift_n = 0;
+
+	return decode_arm_wb(ai, psr, get_regval, priv);
+}
+
+/*
+ * The encodings in this table assumes that a fault was generated where the
+ * ISV field in the HSR was clear, and the decoding information was invalid,
+ * which means that a register write-back occurred, the PC was used as the
+ * destination or a load/store multiple operation was used. Since the latter
+ * two cases are crazy for MMIO on the guest side, we simply inject a fault
+ * when this happens and support the common case.
+ *
+ * We treat unpriviledged loads and stores of words and bytes like all other
+ * loads and stores as their encodings mandate the W bit set and the P bit
+ * clear.
+ */
+static const struct arm_decode arm_decode[] = {
+	/**************** Load/Store Word and Byte **********************/
+	/* Store word with writeback */
+	{ .opc = 0x04000000, .opc_mask = 0x0c500000,
+	  .decode = decode_arm_ls,
+	  .template =  { .len = 4, .w = true, .sign_extend = false, }, },
+	/* Store byte with writeback */
+	{ .opc = 0x04400000, .opc_mask = 0x0c500000,
+	  .decode = decode_arm_ls,
+	  .template = {  .len = 1, .w = true, .sign_extend = false, }, },
+	/* Load word with writeback */
+	{ .opc = 0x04100000, .opc_mask = 0x0c500000,
+	  .decode = decode_arm_ls,
+	  .template = {  .len = 4, .w = false, .sign_extend = false, }, },
+	/* Load byte with writeback */
+	{ .opc = 0x04500000, .opc_mask = 0x0c500000,
+	  .decode = decode_arm_ls,
+	  .template = { .len = 1, .w = false, .sign_extend = false, }, },
+
+	/*************** Extra load/store instructions ******************/
+
+	/* Store halfword with writeback */
+	{ .opc = 0x000000b0, .opc_mask = 0x0c1000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = true, .sign_extend = false, }, },
+	/* Load halfword with writeback */
+	{ .opc = 0x001000b0, .opc_mask = 0x0c1000f0, 
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = false, .sign_extend = false, }, },
+	/* Load dual with writeback */
+	{ .opc = 0x000000d0, .opc_mask = 0x0c1000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 8, .w = false, .sign_extend = false, }, },
+	/* Load signed byte with writeback */
+	{ .opc = 0x001000d0, .opc_mask = 0x0c1000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 1, .w = false, .sign_extend = true, }, },
+
+	/* Store dual with writeback */
+	{ .opc = 0x000000f0, .opc_mask = 0x0c1000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 8, .w = true, .sign_extend = false, }, },
+	/* Load signed halfword with writeback */
+	{ .opc = 0x001000f0, .opc_mask = 0x0c1000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = false, .sign_extend = true, }, },
+
+	/* Store halfword unprivileged */
+	{ .opc = 0x002000b0, .opc_mask = 0x0f3000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = true, .sign_extend = false, }, },
+	/* Load halfword unprivileged */
+	{ .opc = 0x003000b0, .opc_mask = 0x0f3000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = false, .sign_extend = false, }, },
+	/* Load signed byte unprivileged */
+	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 1, .w = false, .sign_extend = true, }, },
+	/* Load signed halfword unprivileged */
+	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0,
+	  .decode = decode_arm_extra,
+	  .template = { .len = 2, .w = false, .sign_extend = true, }, },
+};
+
+static bool decode_arm(struct arm_insn *ai, u32 psr,
+		       u32 (*get_regval)(int reg, void *), void *priv)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(arm_decode); i++) {
+		const struct arm_decode *d = &arm_decode[i];
+		if ((ai->instr & d->opc_mask) == d->opc) {
+			ai->len = d->template.len;
+			ai->w = d->template.w;
+			ai->sign_extend = d->template.sign_extend;
+			return d->decode(ai, psr, get_regval, priv);
+		}
+	}
+	return false;
+}
+
+struct thumb_decode {
+	bool is32;
+
+	union {
+		struct {
+			u8 opcode;
+			u8 mask;
+		} t16;
+
+		struct {
+			u8 op1;
+			u8 op2;
+			u8 op2_mask;
+		} t32;
+	};
+
+	bool (*decode)(struct arm_insn *ti);
+};
+
+static bool decode_thumb_wb(struct arm_insn *ti)
+{
+	u8 imm8 = ti->instr & 0xff;
+
+	ti->P = (ti->instr >> 10) & 1;
+	ti->U = (ti->instr >> 9) & 1;
+	ti->Rn = (ti->instr >> 16) & 0xf;
+	ti->Rd = (ti->instr >> 12) & 0xf;
+
+	/* Handle Writeback: offset_addr relative to fault address. */
+	if (!ti->P && ti->U)
+		ti->offset_addr = imm8;
+	else if (!ti->P && !ti->U)
+		ti->offset_addr = -imm8;
+	return true;
+}
+
+static bool decode_thumb_str(struct arm_insn *ti)
+{
+	u8 op1 = (ti->instr >> (16 + 5)) & 0x7;
+	u8 op2 = (ti->instr >> 6) & 0x3f;
+
+	ti->W = true;
+	ti->sign_extend = false;
+
+	switch (op1) {
+	case 0x0: ti->len = 1; break;
+	case 0x1: ti->len = 2; break;
+	case 0x2: ti->len = 4; break;
+	default:
+		  return false; /* Only register write-back versions! */
+	}
+
+	if ((op2 & 0x24) == 0x24) {
+		/* STRB (immediate, thumb, W=1) */
+		return decode_thumb_wb(ti);
+	}
+
+	return false;
+}
+
+static bool decode_thumb_ldr(struct arm_insn *ti)
+{
+	u8 op1 = (ti->instr >> (16 + 7)) & 0x3;
+	u8 op2 = (ti->instr >> 6) & 0x3f;
+
+	ti->W = false;
+
+	switch (ti->t32.op2 & 0x7) {
+	case 0x1: ti->len = 1; break;
+	case 0x3: ti->len = 2; break;
+	case 0x5: ti->len = 4; break;
+	}
+
+	if (op1 == 0x0)
+		ti->sign_extend = false;
+	else if (op1 == 0x2 && (ti->t32.op2 & 0x7) != 0x5)
+		ti->sign_extend = true;
+	else
+		return false; /* Only register write-back versions! */
+
+	if ((op2 & 0x24) == 0x24) {
+		/* LDR{S}X (immediate, thumb, W=1) */
+		return decode_thumb_wb(ti);
+	}
+
+	return false;
+}
+
+/*
+ * We only support instruction decoding for valid reasonable MMIO operations
+ * where trapping them do not provide sufficient information in the HSR (no
+ * 16-bit Thumb instructions provide register writeback that we care about).
+ *
+ * The following instruciton types are NOT supported for MMIO operations
+ * despite the HSR not containing decode info:
+ *  - any Load/Store multiple
+ *  - any load/store exclusive
+ *  - any load/store dual
+ *  - anything with the PC as the dest register
+ */
+static const struct thumb_decode thumb_decode[] = {
+	/**************** 32-bit Thumb instructions **********************/
+	/* Store single data item:	Op1 == 11, Op2 == 000xxx0 */
+	{ .is32 = true,  .t32 = { 3, 0x00, 0x71}, decode_thumb_str	},
+	/* Load byte:			Op1 == 11, Op2 == 00xx001 */
+	{ .is32 = true,  .t32 = { 3, 0x01, 0x67}, decode_thumb_ldr	},
+	/* Load halfword:		Op1 == 11, Op2 == 00xx011 */
+	{ .is32 = true,  .t32 = { 3, 0x03, 0x67}, decode_thumb_ldr	},
+	/* Load word:			Op1 == 11, Op2 == 00xx101 */
+	{ .is32 = true,  .t32 = { 3, 0x05, 0x67}, decode_thumb_ldr	},
+};
+
+
+static bool decode_thumb(struct arm_insn *ti)
+{
+	bool is16;
+	int i;
+
+	is16 = !ti->is_thumb32;
+	if (is16) {
+		ti->t16.opcode = (ti->instr >> 10) & 0x3f;
+	} else {
+		ti->t32.op1 = (ti->instr >> (16 + 11)) & 0x3;
+		ti->t32.op2 = (ti->instr >> (16 + 4)) & 0x7f;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(thumb_decode); i++) {
+		const struct thumb_decode *td = &thumb_decode[i];
+		if (td->is32 != ti->is_thumb32)
+			continue;
+
+		if (is16) {
+			if ((ti->t16.opcode & td->t16.mask) != td->t16.opcode)
+				continue;
+		} else {
+			if (td->t32.op1 != ti->t32.op1)
+				continue;
+			if ((td->t32.op2_mask & ti->t32.op2) != td->t32.op2)
+				continue;
+		}
+
+		return td->decode(ti);
+	}
+
+	return false;
+}
+
+int decode_insn(struct arm_insn *ai, unsigned long pc, u32 psr,
+		int (*copy_from)(void *dst, unsigned long addr, size_t, void *),
+		u32 (*get_regval)(int reg, void *),
+		void *priv)
+{
+	int err;
+	unsigned int instr_len;
+
+	memset(&ai, 0, sizeof(ai));
+
+	ai->is_thumb = psr & PSR_T_BIT;
+	instr_len = ai->is_thumb ? 2 : 4;
+
+	BUG_ON(!ai->is_thumb && (pc & 0x3));
+
+	/* Zero out high bits for thumb case, and so it's set on error. */
+	ai->instr = 0;
+
+	/* Now guest isn't running, we can va->pa map and copy atomically. */
+	err = copy_from(&ai->instr, pc, instr_len, priv);
+	if (err)
+		return err;
+
+	if (ai->is_thumb) {
+		/*
+		 * Is it a 32 bit thumb instruction?  Can't get it all
+		 * in 1 go, since it can actually go over a page
+		 * boundary.
+		 */
+		ai->is_thumb32 = is_wide_instruction(ai->instr);
+
+		if (ai->is_thumb32) {
+			ai->instr = ai->instr << 16;
+			err = copy_from(&ai->instr, pc + instr_len, instr_len,
+					priv);
+			if (err)
+				return err;
+		}
+		return decode_thumb(ai);
+	}
+
+	return decode_arm(ai, psr, get_regval, priv);
+}
+EXPORT_SYMBOL_GPL(decode_insn);
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index c0014e1..eeebd33 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -20,6 +20,7 @@
 #include <linux/kvm_host.h>
 #include <asm/kvm_arm.h>
 #include <asm/kvm_emulate.h>
+#include <asm/opcodes.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
@@ -231,11 +232,10 @@ static void do_nothing(void *info)
  * Fortunately this is so rare (we don't usually need the instruction), we
  * can go very slowly and noone will mind.
  */
-static int copy_from_guest(struct kvm_vcpu *vcpu,
-			   void *dest,
-			   unsigned long gva,
-			   size_t len)
+static int copy_from_guest(void *dest, unsigned long gva, size_t len,
+			   void *_vcpu)
 {
+	struct kvm_vcpu *vcpu = _vcpu;
 	int i;
 	bool ret;
 	struct kvm_vcpu *v;
@@ -269,467 +269,9 @@ static int copy_from_guest(struct kvm_vcpu *vcpu,
 	return ret ? 0 : -EFAULT;
 }
 
-/******************************************************************************
- * Load-Store instruction emulation
- *****************************************************************************/
-enum SRType {
-	SRType_LSL,
-	SRType_LSR,
-	SRType_ASR,
-	SRType_ROR,
-	SRType_RRX
-};
-
-struct arm_insn {
-	/* Encoded instruction. */
-	u32 instr;
-
-	/* Decoding for the register write back */
-	bool register_form;
-	u32 imm;
-	u8 type;
-	u8 Rt, Rn, Rm, Rd;
-	u8 shift_n;
-	u32 offset_addr;
-
-	/* Common decoding */
-	u8 len;
-	bool sign_extend;
-	bool w, W, U, P;
-
-	/* Thumb encoding */
-	bool is_thumb, is_thumb32;
-	union {
-		struct {
-			u8 opcode;
-			u8 mask;
-		} t16;
-
-		struct {
-			u8 op1;
-			u8 op2;
-			u8 op2_mask;
-		} t32;
-	};
-};
-
-struct arm_decode {
-	/* Instruction decoding */
-	u32 opc;
-	u32 opc_mask;
-
-	bool (*decode)(struct kvm_vcpu *vcpu, struct arm_insn *ai, u32 psr);
-
-	struct arm_insn template;
-};
-
-/* Modelled after DecodeImmShift() in the ARM ARM */
-enum SRType decode_imm_shift(u8 type, u8 imm5, u8 *amount)
-{
-	switch (type) {
-	case 0x0:
-		*amount = imm5;
-		return SRType_LSL;
-	case 0x1:
-		*amount = (imm5 == 0) ? 32 : imm5;
-		return SRType_LSR;
-	case 0x2:
-		*amount = (imm5 == 0) ? 32 : imm5;
-		return SRType_ASR; 
-	case 0x3:
-		if (imm5 == 0) {
-			*amount = 1;
-			return SRType_RRX;
-		} else {
-			*amount = imm5;
-			return SRType_ROR;
-		}
-	}
-
-	return SRType_LSL;
-}
-
-/* Modelled after Shift() in the ARM ARM */
-u32 shift(u32 value, u8 N, enum SRType type, u8 amount, bool carry_in)
+static u32 get_guest_reg(int reg, void *_vcpu)
 {
-	u32 mask = (1 << N) - 1;
-	s32 svalue = (s32)value;
-
-	BUG_ON(N > 32);
-	BUG_ON(type == SRType_RRX && amount != 1);
-	BUG_ON(amount > N);
-
-	if (amount == 0)
-		return value;
-
-	switch (type) {
-	case SRType_LSL:
-		value <<= amount;
-		break;
-	case SRType_LSR:
-		 value >>= amount;
-		break;
-	case SRType_ASR:
-		if (value & (1 << (N - 1)))
-			svalue |= ((-1UL) << N);
-		value = svalue >> amount;
-		break;
-	case SRType_ROR:
-		value = (value >> amount) | (value << (N - amount));
-		break;
-	case SRType_RRX: {
-		u32 C = (carry_in) ? 1 : 0;
-		value = (value >> 1) | (C << (N - 1));
-		break;
-	}
-	}
-
-	return value & mask;
-}
-
-static bool decode_arm_wb(struct kvm_vcpu *vcpu, struct arm_insn *ai, u32 psr)
-{
-	u32 base_addr, offset;
-
-	ai->Rt = (ai->instr >> 12) & 0xf;
-	ai->Rn = (ai->instr >> 16) & 0xf;
-	ai->W = (ai->instr >> 21) & 1;
-	ai->U = (ai->instr >> 23) & 1;
-	ai->P = (ai->instr >> 24) & 1;
-
-	base_addr = *vcpu_reg(vcpu, ai->Rn);
-
-	if (ai->P && !ai->W) {
-		kvm_err("Decoding operation with valid ISV?\n");
-		return false;
-	}
-
-	if (ai->register_form) {
-		/* Register operation */
-		enum SRType s_type;
-		u8 shift_n;
-		bool c_bit = psr & PSR_C_BIT;
-		u32 s_reg = *vcpu_reg(vcpu, ai->Rm);
-
-		s_type = decode_imm_shift(ai->type, ai->shift_n, &shift_n);
-		offset = shift(s_reg, 5, s_type, shift_n, c_bit);
-	} else {
-		/* Immediate operation */
-		offset = ai->imm;
-	}
-
-	/* Handle Writeback */
-	if (ai->U)
-		ai->offset_addr = base_addr + offset;
-	else
-		ai->offset_addr = base_addr - offset;
-	return true;
-}
-
-static bool decode_arm_ls(struct kvm_vcpu *vcpu, struct arm_insn *ai, u32 psr)
-{
-	u8 A = (ai->instr >> 25) & 1;
-
-	ai->register_form = A;
-	ai->imm = ai->instr & 0xfff;
-	ai->Rm = ai->instr & 0xf;
-	ai->type = (ai->instr >> 5) & 0x3;
-	ai->shift_n = (ai->instr >> 7) & 0x1f;
-
-	return decode_arm_wb(vcpu, ai, psr);
-}
-
-static bool decode_arm_extra(struct kvm_vcpu *vcpu, struct arm_insn *ai,
-			     u32 psr)
-{
-	ai->register_form = !((ai->instr >> 22) & 1);
-	ai->imm = ((ai->instr >> 4) & 0xf0) | (ai->instr & 0xf);
-	ai->Rm = ai->instr & 0xf;
-	ai->type = 0; /* SRType_LSL */
-	ai->shift_n = 0;
-
-	return decode_arm_wb(vcpu, ai, psr);
-}
-
-/*
- * The encodings in this table assumes that a fault was generated where the
- * ISV field in the HSR was clear, and the decoding information was invalid,
- * which means that a register write-back occurred, the PC was used as the
- * destination or a load/store multiple operation was used. Since the latter
- * two cases are crazy for MMIO on the guest side, we simply inject a fault
- * when this happens and support the common case.
- *
- * We treat unpriviledged loads and stores of words and bytes like all other
- * loads and stores as their encodings mandate the W bit set and the P bit
- * clear.
- */
-static const struct arm_decode arm_decode[] = {
-	/**************** Load/Store Word and Byte **********************/
-	/* Store word with writeback */
-	{ .opc = 0x04000000, .opc_mask = 0x0c500000,
-	  .decode = decode_arm_ls,
-	  .template =  { .len = 4, .w = true, .sign_extend = false, }, },
-	/* Store byte with writeback */
-	{ .opc = 0x04400000, .opc_mask = 0x0c500000,
-	  .decode = decode_arm_ls,
-	  .template = {  .len = 1, .w = true, .sign_extend = false, }, },
-	/* Load word with writeback */
-	{ .opc = 0x04100000, .opc_mask = 0x0c500000,
-	  .decode = decode_arm_ls,
-	  .template = {  .len = 4, .w = false, .sign_extend = false, }, },
-	/* Load byte with writeback */
-	{ .opc = 0x04500000, .opc_mask = 0x0c500000,
-	  .decode = decode_arm_ls,
-	  .template = { .len = 1, .w = false, .sign_extend = false, }, },
-
-	/*************** Extra load/store instructions ******************/
-
-	/* Store halfword with writeback */
-	{ .opc = 0x000000b0, .opc_mask = 0x0c1000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = true, .sign_extend = false, }, },
-	/* Load halfword with writeback */
-	{ .opc = 0x001000b0, .opc_mask = 0x0c1000f0, 
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = false, .sign_extend = false, }, },
-	/* Load dual with writeback */
-	{ .opc = 0x000000d0, .opc_mask = 0x0c1000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 8, .w = false, .sign_extend = false, }, },
-	/* Load signed byte with writeback */
-	{ .opc = 0x001000d0, .opc_mask = 0x0c1000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 1, .w = false, .sign_extend = true, }, },
-
-	/* Store dual with writeback */
-	{ .opc = 0x000000f0, .opc_mask = 0x0c1000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 8, .w = true, .sign_extend = false, }, },
-	/* Load signed halfword with writeback */
-	{ .opc = 0x001000f0, .opc_mask = 0x0c1000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = false, .sign_extend = true, }, },
-
-	/* Store halfword unprivileged */
-	{ .opc = 0x002000b0, .opc_mask = 0x0f3000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = true, .sign_extend = false, }, },
-	/* Load halfword unprivileged */
-	{ .opc = 0x003000b0, .opc_mask = 0x0f3000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = false, .sign_extend = false, }, },
-	/* Load signed byte unprivileged */
-	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 1, .w = false, .sign_extend = true, }, },
-	/* Load signed halfword unprivileged */
-	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0,
-	  .decode = decode_arm_extra,
-	  .template = { .len = 2, .w = false, .sign_extend = true, }, },
-};
-
-static bool kvm_decode_arm_ls(struct kvm_vcpu *vcpu, struct arm_insn *ai,
-			      u32 psr)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(arm_decode); i++) {
-		const struct arm_decode *d = &arm_decode[i];
-		if ((ai->instr & d->opc_mask) == d->opc) {
-			ai->len = d->template.len;
-			ai->w = d->template.w;
-			ai->sign_extend = d->template.sign_extend;
-			return d->decode(vcpu, ai, psr);
-		}
-	}
-	return false;
-}
-
-struct thumb_decode {
-	bool is32;
-
-	union {
-		struct {
-			u8 opcode;
-			u8 mask;
-		} t16;
-
-		struct {
-			u8 op1;
-			u8 op2;
-			u8 op2_mask;
-		} t32;
-	};
-
-	bool (*decode)(struct kvm_vcpu *vcpu, struct arm_insn *ti);
-};
-
-static bool decode_thumb_wb(struct kvm_vcpu *vcpu, struct arm_insn *ti)
-{
-	u8 imm8 = ti->instr & 0xff;
-
-	ti->P = (ti->instr >> 10) & 1;
-	ti->U = (ti->instr >> 9) & 1;
-	ti->Rn = (ti->instr >> 16) & 0xf;
-	ti->Rd = (ti->instr >> 12) & 0xf;
-
-	/* Handle Writeback: offset_addr relative to fault address. */
-	if (!ti->P && ti->U)
-		ti->offset_addr = imm8;
-	else if (!ti->P && !ti->U)
-		ti->offset_addr = -imm8;
-	return true;
-}
-
-static bool decode_thumb_str(struct kvm_vcpu *vcpu, struct arm_insn *ti)
-{
-	u8 op1 = (ti->instr >> (16 + 5)) & 0x7;
-	u8 op2 = (ti->instr >> 6) & 0x3f;
-
-	ti->W = true;
-	ti->sign_extend = false;
-
-	switch (op1) {
-	case 0x0: ti->len = 1; break;
-	case 0x1: ti->len = 2; break;
-	case 0x2: ti->len = 4; break;
-	default:
-		  return false; /* Only register write-back versions! */
-	}
-
-	if ((op2 & 0x24) == 0x24) {
-		/* STRB (immediate, thumb, W=1) */
-		return decode_thumb_wb(vcpu, ti);
-	}
-
-	return false;
-}
-
-static bool decode_thumb_ldr(struct kvm_vcpu *vcpu, struct arm_insn *ti)
-{
-	u8 op1 = (ti->instr >> (16 + 7)) & 0x3;
-	u8 op2 = (ti->instr >> 6) & 0x3f;
-
-	ti->W = false;
-
-	switch (ti->t32.op2 & 0x7) {
-	case 0x1: ti->len = 1; break;
-	case 0x3: ti->len = 2; break;
-	case 0x5: ti->len = 4; break;
-	}
-
-	if (op1 == 0x0)
-		ti->sign_extend = false;
-	else if (op1 == 0x2 && (ti->t32.op2 & 0x7) != 0x5)
-		ti->sign_extend = true;
-	else
-		return false; /* Only register write-back versions! */
-
-	if ((op2 & 0x24) == 0x24) {
-		/* LDR{S}X (immediate, thumb, W=1) */
-		return decode_thumb_wb(vcpu, ti);
-	}
-
-	return false;
-}
-
-/*
- * We only support instruction decoding for valid reasonable MMIO operations
- * where trapping them do not provide sufficient information in the HSR (no
- * 16-bit Thumb instructions provide register writeback that we care about).
- *
- * The following instruciton types are NOT supported for MMIO operations
- * despite the HSR not containing decode info:
- *  - any Load/Store multiple
- *  - any load/store exclusive
- *  - any load/store dual
- *  - anything with the PC as the dest register
- */
-static const struct thumb_decode thumb_decode[] = {
-	/**************** 32-bit Thumb instructions **********************/
-	/* Store single data item:	Op1 == 11, Op2 == 000xxx0 */
-	{ .is32 = true,  .t32 = { 3, 0x00, 0x71}, decode_thumb_str	},
-	/* Load byte:			Op1 == 11, Op2 == 00xx001 */
-	{ .is32 = true,  .t32 = { 3, 0x01, 0x67}, decode_thumb_ldr	},
-	/* Load halfword:		Op1 == 11, Op2 == 00xx011 */
-	{ .is32 = true,  .t32 = { 3, 0x03, 0x67}, decode_thumb_ldr	},
-	/* Load word:			Op1 == 11, Op2 == 00xx101 */
-	{ .is32 = true,  .t32 = { 3, 0x05, 0x67}, decode_thumb_ldr	},
-};
-
-
-static bool kvm_decode_thumb_ls(struct kvm_vcpu *vcpu, struct arm_insn *ti)
-{
-	bool is16;
-	int i;
-
-	is16 = !ti->is_thumb32;
-	if (is16) {
-		ti->t16.opcode = (ti->instr >> 10) & 0x3f;
-	} else {
-		ti->t32.op1 = (ti->instr >> (16 + 11)) & 0x3;
-		ti->t32.op2 = (ti->instr >> (16 + 4)) & 0x7f;
-	}
-
-	for (i = 0; i < ARRAY_SIZE(thumb_decode); i++) {
-		const struct thumb_decode *td = &thumb_decode[i];
-		if (td->is32 != ti->is_thumb32)
-			continue;
-
-		if (is16) {
-			if ((ti->t16.opcode & td->t16.mask) != td->t16.opcode)
-				continue;
-		} else {
-			if (td->t32.op1 != ti->t32.op1)
-				continue;
-			if ((td->t32.op2_mask & ti->t32.op2) != td->t32.op2)
-				continue;
-		}
-
-		return td->decode(vcpu, ti);
-	}
-
-	return false;
-}
-
-static int kvm_decode(struct kvm_vcpu *vcpu, unsigned long pc, u32 psr,
-		      struct arm_insn *ai)
-{
-	int err;
-	unsigned int instr_len;
-
-	ai->is_thumb = psr & PSR_T_BIT;
-	instr_len = ai->is_thumb ? 2 : 4;
-
-	BUG_ON(!ai->is_thumb && (pc & 0x3));
-
-	/* Zero out high bits for thumb case, and so it's set on error. */
-	ai->instr = 0;
-
-	/* Now guest isn't running, we can va->pa map and copy atomically. */
-	err = copy_from_guest(vcpu, &ai->instr, pc, instr_len);
-	if (err)
-		return err;
-
-	if (ai->is_thumb) {
-		/*
-		 * Is it a 32 bit thumb instruction?  Can't get it all
-		 * in 1 go, since it can actually go over a page
-		 * boundary.
-		 */
-		ai->is_thumb32 = is_wide_instruction(ai->instr);
-
-		if (ai->is_thumb32) {
-			ai->instr = ai->instr << 16;
-			err = copy_from_guest(vcpu, &ai->instr,
-					      pc + instr_len, instr_len);
-			if (err)
-				return err;
-		}
-		return kvm_decode_thumb_ls(vcpu, ai);
-	}
-
-	return kvm_decode_arm_ls(vcpu, ai, psr);
+	return *vcpu_reg(_vcpu, reg);
 }
 
 static bool execute(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
@@ -788,7 +330,8 @@ int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 
 	mmio->phys_addr = fault_ipa;
 
-	err = kvm_decode(vcpu, *vcpu_pc(vcpu), *vcpu_cpsr(vcpu), &insn);
+	err = decode_insn(&insn, *vcpu_pc(vcpu), *vcpu_cpsr(vcpu),
+			  copy_from_guest, get_guest_reg, vcpu);
 	if (err) {
 		kvm_debug("Unable to decode inst: %#08x (cpsr: %#08x (T=%i)"
 			  "pc: %#08x) - %i\n",
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux