diff mbox

[03/10] kvm: split out instruction decode from emulation (thumb instructions).

Message ID 1351077923-17977-4-git-send-email-rusty@rustcorp.com.au (mailing list archive)
State New, archived
Headers show

Commit Message

Rusty Russell Oct. 24, 2012, 11:25 a.m. UTC
From: Rusty Russell <rusty.russell@linaro.org>

Add thumb info to struct arm_insn, and use that to store decode
information for later handling in execute_thumb().

Signed-off-by: Rusty Russell <rusty.russell@linaro.org>
---
 arch/arm/kvm/emulate.c |  120 +++++++++++++++++++++++++++++-------------------
 1 file changed, 74 insertions(+), 46 deletions(-)
diff mbox

Patch

diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index 05b534f..882db33 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -301,7 +301,7 @@  struct arm_insn {
 	bool register_form;
 	u32 imm;
 	u8 type;
-	u8 Rt, Rn, Rm;
+	u8 Rt, Rn, Rm, Rd;
 	u8 shift_n;
 	u32 offset_addr;
 
@@ -309,6 +309,21 @@  struct arm_insn {
 	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 {
@@ -565,7 +580,7 @@  static bool kvm_decode_arm_ls(struct kvm_vcpu *vcpu, unsigned long instr,
 	return false;
 }
 
-struct thumb_instr {
+struct thumb_decode {
 	bool is32;
 
 	union {
@@ -581,86 +596,95 @@  struct thumb_instr {
 		} t32;
 	};
 
-	bool (*decode)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
-		       unsigned long instr, const struct thumb_instr *ti);
+	bool (*decode)(struct kvm_vcpu *vcpu,
+		       unsigned long instr, struct arm_insn *ti);
 };
 
-static bool decode_thumb_wb(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
-			    unsigned long instr)
+static bool decode_thumb_wb(struct kvm_vcpu *vcpu,
+			    unsigned long instr, struct arm_insn *ti)
 {
-	bool P = (instr >> 10) & 1;
-	bool U = (instr >> 9) & 1;
 	u8 imm8 = instr & 0xff;
 	u32 offset_addr = vcpu->arch.hxfar;
-	u8 Rn = (instr >> 16) & 0xf;
-
-	vcpu->arch.mmio.rd = (instr >> 12) & 0xf;
 
-	if (kvm_vcpu_reg_is_pc(vcpu, Rn))
-		return false;
+	ti->P = (instr >> 10) & 1;
+	ti->U = (instr >> 9) & 1;
+	ti->Rn = (instr >> 16) & 0xf;
+	ti->Rd = (instr >> 12) & 0xf;
 
 	/* Handle Writeback */
-	if (!P && U)
-		*vcpu_reg(vcpu, Rn) = offset_addr + imm8;
-	else if (!P && !U)
-		*vcpu_reg(vcpu, Rn) = offset_addr - imm8;
+	if (!ti->P && ti->U)
+		ti->offset_addr = offset_addr + imm8;
+	else if (!ti->P && !ti->U)
+		ti->offset_addr = offset_addr - imm8;
 	return true;
 }
 
-static bool decode_thumb_str(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
-			     unsigned long instr, const struct thumb_instr *ti)
+static bool decode_thumb_str(struct kvm_vcpu *vcpu,
+			     unsigned long instr, struct arm_insn *ti)
 {
 	u8 op1 = (instr >> (16 + 5)) & 0x7;
 	u8 op2 = (instr >> 6) & 0x3f;
 
-	mmio->is_write = true;
-	vcpu->arch.mmio.sign_extend = false;
+	ti->W = true;
+	ti->sign_extend = false;
 
 	switch (op1) {
-	case 0x0: mmio->len = 1; break;
-	case 0x1: mmio->len = 2; break;
-	case 0x2: mmio->len = 4; break;
+	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, mmio, instr);
+		return decode_thumb_wb(vcpu, instr, ti);
 	}
 
 	return false;
 }
 
-static bool decode_thumb_ldr(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
-			     unsigned long instr, const struct thumb_instr *ti)
+static bool decode_thumb_ldr(struct kvm_vcpu *vcpu,
+			     unsigned long instr, struct arm_insn *ti)
 {
 	u8 op1 = (instr >> (16 + 7)) & 0x3;
 	u8 op2 = (instr >> 6) & 0x3f;
 
-	mmio->is_write = false;
+	ti->W = false;
 
 	switch (ti->t32.op2 & 0x7) {
-	case 0x1: mmio->len = 1; break;
-	case 0x3: mmio->len = 2; break;
-	case 0x5: mmio->len = 4; break;
+	case 0x1: ti->len = 1; break;
+	case 0x3: ti->len = 2; break;
+	case 0x5: ti->len = 4; break;
 	}
 
 	if (op1 == 0x0)
-		vcpu->arch.mmio.sign_extend = false;
+		ti->sign_extend = false;
 	else if (op1 == 0x2 && (ti->t32.op2 & 0x7) != 0x5)
-		vcpu->arch.mmio.sign_extend = true;
+		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, mmio, instr);
+		return decode_thumb_wb(vcpu, instr, ti);
 	}
 
 	return false;
 }
 
+static bool execute_thumb(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+			  const struct arm_insn *ti)
+{
+	if (kvm_vcpu_reg_is_pc(vcpu, ti->Rn))
+		return false;
+
+	if (!ti->P)
+		*vcpu_reg(vcpu, ti->Rn) = ti->offset_addr;
+	vcpu->arch.mmio.sign_extend = ti->sign_extend;
+	return true;
+}
+
 /*
  * We only support instruction decoding for valid reasonable MMIO operations
  * where trapping them do not provide sufficient information in the HSR (no
@@ -673,7 +697,7 @@  static bool decode_thumb_ldr(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
  *  - any load/store dual
  *  - anything with the PC as the dest register
  */
-static const struct thumb_instr thumb_instr[] = {
+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	},
@@ -686,15 +710,17 @@  static const struct thumb_instr thumb_instr[] = {
 };
 
 
-
 static bool kvm_decode_thumb_ls(struct kvm_vcpu *vcpu, unsigned long instr,
 				struct kvm_exit_mmio *mmio)
 {
-	bool is32 = is_wide_instruction(instr);
-	bool is16 = !is32;
-	struct thumb_instr tinstr; /* re-use to pass on already decoded info */
+	struct arm_insn tinstr; /* re-use to pass on already decoded info */
+	bool is16;
 	int i;
 
+	tinstr.is_thumb = true;
+	tinstr.is_thumb32 = is_wide_instruction(instr);
+
+	is16 = !tinstr.is_thumb32;
 	if (is16) {
 		tinstr.t16.opcode = (instr >> 10) & 0x3f;
 	} else {
@@ -702,22 +728,24 @@  static bool kvm_decode_thumb_ls(struct kvm_vcpu *vcpu, unsigned long instr,
 		tinstr.t32.op2 = (instr >> (16 + 4)) & 0x7f;
 	}
 
-	for (i = 0; i < ARRAY_SIZE(thumb_instr); i++) {
-		const struct thumb_instr *ti = &thumb_instr[i];
-		if (ti->is32 != is32)
+	for (i = 0; i < ARRAY_SIZE(thumb_decode); i++) {
+		const struct thumb_decode *td = &thumb_decode[i];
+		if (td->is32 != tinstr.is_thumb32)
 			continue;
 
 		if (is16) {
-			if ((tinstr.t16.opcode & ti->t16.mask) != ti->t16.opcode)
+			if ((tinstr.t16.opcode & td->t16.mask) != td->t16.opcode)
 				continue;
 		} else {
-			if (ti->t32.op1 != tinstr.t32.op1)
+			if (td->t32.op1 != tinstr.t32.op1)
 				continue;
-			if ((ti->t32.op2_mask & tinstr.t32.op2) != ti->t32.op2)
+			if ((td->t32.op2_mask & tinstr.t32.op2) != td->t32.op2)
 				continue;
 		}
 
-		return ti->decode(vcpu, mmio, instr, &tinstr);
+		if (!td->decode(vcpu, instr, &tinstr))
+			return false;
+		return execute_thumb(vcpu, mmio, &tinstr);
 	}
 
 	return false;