From: Rusty Russell <rusty.russ...@linaro.org> 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.russ...@linaro.org> --- 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 majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html