From: Rusty Russell <[email protected]>

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 <[email protected]>
---
 arch/arm/kvm/emulate.c |  120 +++++++++++++++++++++++++++++-------------------
 1 file changed, 74 insertions(+), 46 deletions(-)

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;
-- 
1.7.10.4

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

Reply via email to