@@ -303,11 +303,12 @@ struct arm_insn {
u8 type;
u8 Rt, Rn, Rm;
u8 shift_n;
+ u32 offset_addr;
/* Common decoding */
u8 len;
bool sign_extend;
- bool w;
+ bool w, W, U, P;
};
struct arm_decode {
@@ -315,7 +316,7 @@ struct arm_decode {
u32 opc;
u32 opc_mask;
- bool (*decode)(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+ bool (*decode)(struct kvm_vcpu *vcpu,
unsigned long instr, struct arm_insn *ai);
struct arm_insn template;
@@ -385,31 +386,24 @@ u32 shift(u32 value, u8 N, enum SRType type, u8 amount, bool carry_in)
return value & mask;
}
-static bool decode_arm_wb(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
- unsigned long instr, const struct arm_insn *ai)
+static bool decode_arm_wb(struct kvm_vcpu *vcpu, unsigned long instr,
+ struct arm_insn *ai)
{
- u8 Rt = (instr >> 12) & 0xf;
- u8 Rn = (instr >> 16) & 0xf;
- u8 W = (instr >> 21) & 1;
- u8 U = (instr >> 23) & 1;
- u8 P = (instr >> 24) & 1;
- u32 base_addr = *vcpu_reg(vcpu, Rn);
- u32 offset_addr, offset;
+ u32 base_addr, offset;
- /*
- * Technically this is allowed in certain circumstances,
- * but we don't support it.
- */
- if (Rt == 15 || Rn == 15)
- return false;
+ ai->Rt = (instr >> 12) & 0xf;
+ ai->Rn = (instr >> 16) & 0xf;
+ ai->W = (instr >> 21) & 1;
+ ai->U = (instr >> 23) & 1;
+ ai->P = (instr >> 24) & 1;
+
+ base_addr = *vcpu_reg(vcpu, ai->Rn);
- if (P && !W) {
+ if (ai->P && !ai->W) {
kvm_err("Decoding operation with valid ISV?\n");
return false;
}
- vcpu->arch.mmio.rd = Rt;
-
if (ai->register_form) {
/* Register operation */
enum SRType s_type;
@@ -425,46 +419,56 @@ static bool decode_arm_wb(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
}
/* Handle Writeback */
- if (U)
- offset_addr = base_addr + offset;
+ if (ai->U)
+ ai->offset_addr = base_addr + offset;
else
- offset_addr = base_addr - offset;
- *vcpu_reg(vcpu, Rn) = offset_addr;
+ ai->offset_addr = base_addr - offset;
return true;
}
-static bool decode_arm_ls(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+static bool decode_arm_ls(struct kvm_vcpu *vcpu,
unsigned long instr, struct arm_insn *ai)
{
u8 A = (instr >> 25) & 1;
- mmio->is_write = ai->w;
- mmio->len = ai->len;
- vcpu->arch.mmio.sign_extend = false;
-
ai->register_form = A;
ai->imm = instr & 0xfff;
ai->Rm = instr & 0xf;
ai->type = (instr >> 5) & 0x3;
ai->shift_n = (instr >> 7) & 0x1f;
- return decode_arm_wb(vcpu, mmio, instr, ai);
+ return decode_arm_wb(vcpu, instr, ai);
}
-static bool decode_arm_extra(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+static bool decode_arm_extra(struct kvm_vcpu *vcpu,
unsigned long instr, struct arm_insn *ai)
{
- mmio->is_write = ai->w;
- mmio->len = ai->len;
- vcpu->arch.mmio.sign_extend = ai->sign_extend;
-
ai->register_form = !((instr >> 22) & 1);
ai->imm = ((instr >> 4) & 0xf0) | (instr & 0xf);
ai->Rm = instr & 0xf;
ai->type = 0; /* SRType_LSL */
ai->shift_n = 0;
- return decode_arm_wb(vcpu, mmio, instr, ai);
+ return decode_arm_wb(vcpu, instr, ai);
+}
+
+static bool execute(struct kvm_vcpu *vcpu, struct kvm_exit_mmio *mmio,
+ const struct arm_insn *ai)
+{
+ /*
+ * Technically this is allowed in certain circumstances,
+ * but we don't support it.
+ */
+ if (ai->Rt == 15 || ai->Rn == 15)
+ return false;
+
+ mmio->is_write = ai->w;
+ mmio->len = ai->len;
+ vcpu->arch.mmio.sign_extend = ai->sign_extend;
+
+ vcpu->arch.mmio.rd = ai->Rt;
+ *vcpu_reg(vcpu, ai->Rn) = ai->offset_addr;
+ return true;
}
/*
@@ -553,7 +557,9 @@ static bool kvm_decode_arm_ls(struct kvm_vcpu *vcpu, unsigned long instr,
const struct arm_decode *d = &arm_decode[i];
if ((instr & d->opc_mask) == d->opc) {
struct arm_insn ai = d->template;
- return d->decode(vcpu, mmio, instr, &ai);
+ if (!d->decode(vcpu, instr, &ai))
+ return false;
+ return execute(vcpu, mmio, &ai);
}
}
return false;