From patchwork Wed Oct 24 11:25:16 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rusty Russell X-Patchwork-Id: 1637711 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork2.kernel.org (Postfix) with ESMTP id CC07DDF238 for ; Wed, 24 Oct 2012 11:31:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758438Ab2JXLbe (ORCPT ); Wed, 24 Oct 2012 07:31:34 -0400 Received: from ozlabs.org ([203.10.76.45]:40245 "EHLO ozlabs.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758267Ab2JXL34 (ORCPT ); Wed, 24 Oct 2012 07:29:56 -0400 Received: by ozlabs.org (Postfix, from userid 1011) id C61372C01C6; Wed, 24 Oct 2012 22:29:54 +1100 (EST) From: Rusty Russell To: Will Deacon Cc: Christoffer Dall , "kvm@vger.kernel.org" , dave.martin@linaro.org, Rusty Russell Subject: [PATCH 03/10] kvm: split out instruction decode from emulation (thumb instructions). Date: Wed, 24 Oct 2012 21:55:16 +1030 Message-Id: <1351077923-17977-4-git-send-email-rusty@rustcorp.com.au> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1351077923-17977-1-git-send-email-rusty@rustcorp.com.au> References: <20121022174555.GD26619@mudshark.cambridge.arm.com> <1351077923-17977-1-git-send-email-rusty@rustcorp.com.au> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Rusty Russell 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 --- 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;