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