[PATCH 5/5] KVM: ARM: Factor out instruction decoding

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Move the instruction decoding to deocde.c and adapt the code slightly
to operate on a struct pt_regs structure to make it easier for someone
to take and reuse this code in a different setting some time.

Signed-off-by: Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx>
---
 arch/arm/include/asm/kvm_decode.h  |   37 +++
 arch/arm/include/asm/kvm_emulate.h |    1 
 arch/arm/include/asm/kvm_host.h    |    8 -
 arch/arm/kvm/Makefile              |    2 
 arch/arm/kvm/decode.c              |  463 ++++++++++++++++++++++++++++++++++++
 arch/arm/kvm/emulate.c             |  438 ----------------------------------
 arch/arm/kvm/mmio.c                |   13 +
 7 files changed, 516 insertions(+), 446 deletions(-)
 create mode 100644 arch/arm/include/asm/kvm_decode.h
 create mode 100644 arch/arm/kvm/decode.c

diff --git a/arch/arm/include/asm/kvm_decode.h b/arch/arm/include/asm/kvm_decode.h
new file mode 100644
index 0000000..6fd1e39
--- /dev/null
+++ b/arch/arm/include/asm/kvm_decode.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __ARM_KVM_DECODE_H__
+#define __ARM_KVM_DECODE_H__
+
+#include <linux/types.h>
+
+struct kvm_vcpu;
+struct kvm_exit_mmio;
+
+struct kvm_decode {
+	struct pt_regs *regs;
+	unsigned long hxfar;
+	unsigned long rt;
+	bool sign_extend;
+};
+
+int kvm_decode_load_store(struct kvm_decode *decode, unsigned long instr,
+			  struct kvm_exit_mmio *mmio);
+
+#endif /* __ARM_KVM_DECODE_H__ */
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
index 375795b..83a6fe1 100644
--- a/arch/arm/include/asm/kvm_emulate.h
+++ b/arch/arm/include/asm/kvm_emulate.h
@@ -33,7 +33,6 @@ void kvm_skip_instr(struct kvm_vcpu *vcpu, bool is_wide_instr);
 void kvm_inject_undefined(struct kvm_vcpu *vcpu);
 void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr);
 void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr);
-
 static inline u32 *vcpu_pc(struct kvm_vcpu *vcpu)
 {
 	return (u32 *)&vcpu->arch.regs.usr_regs.ARM_pc;
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 439deb0..41012ef 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -22,6 +22,7 @@
 #include <asm/kvm.h>
 #include <asm/kvm_asm.h>
 #include <asm/fpstate.h>
+#include <asm/kvm_decode.h>
 #include <asm/kvm_vgic.h>
 #include <asm/kvm_arch_timer.h>
 
@@ -78,13 +79,6 @@ struct kvm_mmu_memory_cache {
 	void *objects[KVM_NR_MEM_OBJS];
 };
 
-struct kvm_decode {
-	struct pt_regs *regs;
-	unsigned long hxfar;
-	unsigned long rt;	/* destination register for loads */
-	bool sign_extend;	/* for byte/halfword loads */
-};
-
 struct kvm_vcpu_arch {
 	struct kvm_regs regs;
 
diff --git a/arch/arm/kvm/Makefile b/arch/arm/kvm/Makefile
index 6b19e5c..7c5a635 100644
--- a/arch/arm/kvm/Makefile
+++ b/arch/arm/kvm/Makefile
@@ -19,6 +19,6 @@ obj-$(CONFIG_KVM_ARM_HOST) += init.o interrupts.o
 obj-$(CONFIG_KVM_ARM_HOST) += $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
 
 obj-$(CONFIG_KVM_ARM_HOST) += arm.o guest.o mmu.o emulate.o reset.o
-obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o
+obj-$(CONFIG_KVM_ARM_HOST) += coproc.o coproc_a15.o mmio.o decode.o
 obj-$(CONFIG_KVM_ARM_VGIC) += vgic.o
 obj-$(CONFIG_KVM_ARM_TIMER) += timer.o
diff --git a/arch/arm/kvm/decode.c b/arch/arm/kvm/decode.c
new file mode 100644
index 0000000..48e6bef
--- /dev/null
+++ b/arch/arm/kvm/decode.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@xxxxxxxxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include <linux/kvm_host.h>
+#include <asm/kvm_mmio.h>
+#include <asm/kvm_emulate.h>
+#include <asm/kvm_decode.h>
+#include <trace/events/kvm.h>
+
+#include "trace.h"
+
+struct arm_instr {
+	/* Instruction decoding */
+	u32 opc;
+	u32 opc_mask;
+
+	/* Decoding for the register write back */
+	bool register_form;
+	u32 imm;
+	u8 Rm;
+	u8 type;
+	u8 shift_n;
+
+	/* Common decoding */
+	u8 len;
+	bool sign_extend;
+	bool w;
+
+	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
+		       unsigned long instr, struct arm_instr *ai);
+};
+
+enum SRType {
+	SRType_LSL,
+	SRType_LSR,
+	SRType_ASR,
+	SRType_ROR,
+	SRType_RRX
+};
+
+/* Modelled after DecodeImmShift() in the ARM ARM */
+static 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 */
+static 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 kvm_decode *decode, struct kvm_exit_mmio *mmio,
+			  unsigned long instr, const struct arm_instr *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 = decode->regs->uregs[Rn];
+	u32 offset_addr, offset;
+
+	/*
+	 * Technically this is allowed in certain circumstances,
+	 * but we don't support it.
+	 */
+	if (Rt == 15 || Rn == 15)
+		return false;
+
+	if (P && !W) {
+		kvm_err("Decoding operation with valid ISV?\n");
+		return false;
+	}
+
+	decode->rt = Rt;
+
+	if (ai->register_form) {
+		/* Register operation */
+		enum SRType s_type;
+		u8 shift_n = 0;
+		bool c_bit = decode->regs->ARM_cpsr & PSR_C_BIT;
+		u32 s_reg = decode->regs->uregs[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 (U)
+		offset_addr = base_addr + offset;
+	else
+		offset_addr = base_addr - offset;
+	decode->regs->uregs[Rn] = offset_addr;
+	return true;
+}
+
+static bool decode_arm_ls(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
+			  unsigned long instr, struct arm_instr *ai)
+{
+	u8 A = (instr >> 25) & 1;
+
+	mmio->is_write = ai->w;
+	mmio->len = ai->len;
+	decode->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(decode, mmio, instr, ai);
+}
+
+static bool decode_arm_extra(struct kvm_decode *decode,
+			     struct kvm_exit_mmio *mmio,
+			     unsigned long instr, struct arm_instr *ai)
+{
+	mmio->is_write = ai->w;
+	mmio->len = ai->len;
+	decode->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(decode, mmio, instr, ai);
+}
+
+/*
+ * 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_instr arm_instr[] = {
+	/**************** Load/Store Word and Byte **********************/
+	/* Store word with writeback */
+	{ .opc = 0x04000000, .opc_mask = 0x0c500000, .len = 4, .w = true,
+		.sign_extend = false, .decode = decode_arm_ls },
+	/* Store byte with writeback */
+	{ .opc = 0x04400000, .opc_mask = 0x0c500000, .len = 1, .w = true,
+		.sign_extend = false, .decode = decode_arm_ls },
+	/* Load word with writeback */
+	{ .opc = 0x04100000, .opc_mask = 0x0c500000, .len = 4, .w = false,
+		.sign_extend = false, .decode = decode_arm_ls },
+	/* Load byte with writeback */
+	{ .opc = 0x04500000, .opc_mask = 0x0c500000, .len = 1, .w = false,
+		.sign_extend = false, .decode = decode_arm_ls },
+
+	/*************** Extra load/store instructions ******************/
+
+	/* Store halfword with writeback */
+	{ .opc = 0x000000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = true,
+		.sign_extend = false, .decode = decode_arm_extra },
+	/* Load halfword with writeback */
+	{ .opc = 0x001000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
+		.sign_extend = false, .decode = decode_arm_extra },
+
+	/* Load dual with writeback */
+	{ .opc = 0x000000d0, .opc_mask = 0x0c1000f0, .len = 8, .w = false,
+		.sign_extend = false, .decode = decode_arm_extra },
+	/* Load signed byte with writeback */
+	{ .opc = 0x001000d0, .opc_mask = 0x0c1000f0, .len = 1, .w = false,
+		.sign_extend = true,  .decode = decode_arm_extra },
+
+	/* Store dual with writeback */
+	{ .opc = 0x000000f0, .opc_mask = 0x0c1000f0, .len = 8, .w = true,
+		.sign_extend = false, .decode = decode_arm_extra },
+	/* Load signed halfword with writeback */
+	{ .opc = 0x001000f0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
+		.sign_extend = true,  .decode = decode_arm_extra },
+
+	/* Store halfword unprivileged */
+	{ .opc = 0x002000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = true,
+		.sign_extend = false, .decode = decode_arm_extra },
+	/* Load halfword unprivileged */
+	{ .opc = 0x003000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
+		.sign_extend = false, .decode = decode_arm_extra },
+	/* Load signed byte unprivileged */
+	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 1, .w = false,
+		.sign_extend = true , .decode = decode_arm_extra },
+	/* Load signed halfword unprivileged */
+	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
+		.sign_extend = true , .decode = decode_arm_extra },
+};
+
+static bool kvm_decode_arm_ls(struct kvm_decode *decode, unsigned long instr,
+			      struct kvm_exit_mmio *mmio)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(arm_instr); i++) {
+		const struct arm_instr *ai = &arm_instr[i];
+		if ((instr & ai->opc_mask) == ai->opc) {
+			struct arm_instr ai_copy = *ai;
+			return ai->decode(decode, mmio, instr, &ai_copy);
+		}
+	}
+	return false;
+}
+
+struct thumb_instr {
+	bool is32;
+
+	union {
+		struct {
+			u8 opcode;
+			u8 mask;
+		} t16;
+
+		struct {
+			u8 op1;
+			u8 op2;
+			u8 op2_mask;
+		} t32;
+	};
+
+	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
+		       unsigned long instr, const struct thumb_instr *ti);
+};
+
+static bool decode_thumb_wb(struct kvm_decode *decode,
+			    struct kvm_exit_mmio *mmio,
+			    unsigned long instr)
+{
+	bool P = (instr >> 10) & 1;
+	bool U = (instr >> 9) & 1;
+	u8 imm8 = instr & 0xff;
+	u32 offset_addr = decode->hxfar;
+	u8 Rn = (instr >> 16) & 0xf;
+
+	decode->rt = (instr >> 12) & 0xf;
+
+	if (Rn == 15)
+		return false;
+
+	/* Handle Writeback */
+	if (!P && U)
+		decode->regs->uregs[Rn] = offset_addr + imm8;
+	else if (!P && !U)
+		decode->regs->uregs[Rn] = offset_addr - imm8;
+	return true;
+}
+
+static bool decode_thumb_str(struct kvm_decode *decode,
+			     struct kvm_exit_mmio *mmio,
+			     unsigned long instr, const struct thumb_instr *ti)
+{
+	u8 op1 = (instr >> (16 + 5)) & 0x7;
+	u8 op2 = (instr >> 6) & 0x3f;
+
+	mmio->is_write = true;
+	decode->sign_extend = false;
+
+	switch (op1) {
+	case 0x0: mmio->len = 1; break;
+	case 0x1: mmio->len = 2; break;
+	case 0x2: mmio->len = 4; break;
+	default:
+		  return false; /* Only register write-back versions! */
+	}
+
+	if ((op2 & 0x24) == 0x24) {
+		/* STRB (immediate, thumb, W=1) */
+		return decode_thumb_wb(decode, mmio, instr);
+	}
+
+	return false;
+}
+
+static bool decode_thumb_ldr(struct kvm_decode *decode,
+			     struct kvm_exit_mmio *mmio,
+			     unsigned long instr, const struct thumb_instr *ti)
+{
+	u8 op1 = (instr >> (16 + 7)) & 0x3;
+	u8 op2 = (instr >> 6) & 0x3f;
+
+	mmio->is_write = false;
+
+	switch (ti->t32.op2 & 0x7) {
+	case 0x1: mmio->len = 1; break;
+	case 0x3: mmio->len = 2; break;
+	case 0x5: mmio->len = 4; break;
+	}
+
+	if (op1 == 0x0)
+		decode->sign_extend = false;
+	else if (op1 == 0x2 && (ti->t32.op2 & 0x7) != 0x5)
+		decode->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(decode, mmio, instr);
+	}
+
+	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_instr thumb_instr[] = {
+	/**************** 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_decode *decode, 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 */
+	int i;
+
+	if (is16) {
+		tinstr.t16.opcode = (instr >> 10) & 0x3f;
+	} else {
+		tinstr.t32.op1 = (instr >> (16 + 11)) & 0x3;
+		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)
+			continue;
+
+		if (is16) {
+			if ((tinstr.t16.opcode & ti->t16.mask) != ti->t16.opcode)
+				continue;
+		} else {
+			if (ti->t32.op1 != tinstr.t32.op1)
+				continue;
+			if ((ti->t32.op2_mask & tinstr.t32.op2) != ti->t32.op2)
+				continue;
+		}
+
+		return ti->decode(decode, mmio, instr, &tinstr);
+	}
+
+	return false;
+}
+
+/**
+ * kvm_decode_load_store - decodes load/store instructions
+ * @decode: reads regs and hxfar, writes rt and sign_extend
+ * @instr:  instruction to decode
+ * @mmio:   fills in len and is_write
+ *
+ * Decode load/store instructions with HSR ISV clear. The code assumes that
+ * this was indeed a KVM fault and therefore assumes registers write back for
+ * single load/store operations and does not support using the PC as the
+ * destination register.
+ */
+int kvm_decode_load_store(struct kvm_decode *decode, unsigned long instr,
+			  struct kvm_exit_mmio *mmio)
+{
+	bool is_thumb;
+
+	is_thumb = !!(decode->regs->ARM_cpsr & PSR_T_BIT);
+	if (!is_thumb)
+		return kvm_decode_arm_ls(decode, instr, mmio) ? 0 : 1;
+	else
+		return kvm_decode_thumb_ls(decode, instr, mmio) ? 0 : 1;
+}
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index 7cbdc68..fff1199 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/kvm_decode.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
@@ -283,426 +284,6 @@ out:
 	return ret;
 }
 
-/******************************************************************************
- * Load-Store instruction emulation
- *****************************************************************************/
-
-struct arm_instr {
-	/* Instruction decoding */
-	u32 opc;
-	u32 opc_mask;
-
-	/* Decoding for the register write back */
-	bool register_form;
-	u32 imm;
-	u8 Rm;
-	u8 type;
-	u8 shift_n;
-
-	/* Common decoding */
-	u8 len;
-	bool sign_extend;
-	bool w;
-
-	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
-		       unsigned long instr, struct arm_instr *ai);
-};
-
-enum SRType {
-	SRType_LSL,
-	SRType_LSR,
-	SRType_ASR,
-	SRType_ROR,
-	SRType_RRX
-};
-
-/* 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 kvm_decode *decode, struct kvm_exit_mmio *mmio,
-			  unsigned long instr, const struct arm_instr *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 = decode->regs->uregs[Rn];
-	u32 offset_addr, offset;
-
-	/*
-	 * Technically this is allowed in certain circumstances,
-	 * but we don't support it.
-	 */
-	if (Rt == 15 || Rn == 15)
-		return false;
-
-	if (P && !W) {
-		kvm_err("Decoding operation with valid ISV?\n");
-		return false;
-	}
-
-	decode->rt = Rt;
-
-	if (ai->register_form) {
-		/* Register operation */
-		enum SRType s_type;
-		u8 shift_n;
-		bool c_bit = decode->regs->ARM_cpsr & PSR_C_BIT;
-		u32 s_reg = decode->regs->uregs[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 (U)
-		offset_addr = base_addr + offset;
-	else
-		offset_addr = base_addr - offset;
-	decode->regs->uregs[Rn] = offset_addr;
-	return true;
-}
-
-static bool decode_arm_ls(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
-			  unsigned long instr, struct arm_instr *ai)
-{
-	u8 A = (instr >> 25) & 1;
-
-	mmio->is_write = ai->w;
-	mmio->len = ai->len;
-	decode->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(decode, mmio, instr, ai);
-}
-
-static bool decode_arm_extra(struct kvm_decode *decode,
-			     struct kvm_exit_mmio *mmio,
-			     unsigned long instr, struct arm_instr *ai)
-{
-	mmio->is_write = ai->w;
-	mmio->len = ai->len;
-	decode->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(decode, mmio, instr, ai);
-}
-
-/*
- * 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_instr arm_instr[] = {
-	/**************** Load/Store Word and Byte **********************/
-	/* Store word with writeback */
-	{ .opc = 0x04000000, .opc_mask = 0x0c500000, .len = 4, .w = true,
-		.sign_extend = false, .decode = decode_arm_ls },
-	/* Store byte with writeback */
-	{ .opc = 0x04400000, .opc_mask = 0x0c500000, .len = 1, .w = true,
-		.sign_extend = false, .decode = decode_arm_ls },
-	/* Load word with writeback */
-	{ .opc = 0x04100000, .opc_mask = 0x0c500000, .len = 4, .w = false,
-		.sign_extend = false, .decode = decode_arm_ls },
-	/* Load byte with writeback */
-	{ .opc = 0x04500000, .opc_mask = 0x0c500000, .len = 1, .w = false,
-		.sign_extend = false, .decode = decode_arm_ls },
-
-	/*************** Extra load/store instructions ******************/
-
-	/* Store halfword with writeback */
-	{ .opc = 0x000000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = true,
-		.sign_extend = false, .decode = decode_arm_extra },
-	/* Load halfword with writeback */
-	{ .opc = 0x001000b0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
-		.sign_extend = false, .decode = decode_arm_extra },
-
-	/* Load dual with writeback */
-	{ .opc = 0x000000d0, .opc_mask = 0x0c1000f0, .len = 8, .w = false,
-		.sign_extend = false, .decode = decode_arm_extra },
-	/* Load signed byte with writeback */
-	{ .opc = 0x001000d0, .opc_mask = 0x0c1000f0, .len = 1, .w = false,
-		.sign_extend = true,  .decode = decode_arm_extra },
-
-	/* Store dual with writeback */
-	{ .opc = 0x000000f0, .opc_mask = 0x0c1000f0, .len = 8, .w = true,
-		.sign_extend = false, .decode = decode_arm_extra },
-	/* Load signed halfword with writeback */
-	{ .opc = 0x001000f0, .opc_mask = 0x0c1000f0, .len = 2, .w = false,
-		.sign_extend = true,  .decode = decode_arm_extra },
-
-	/* Store halfword unprivileged */
-	{ .opc = 0x002000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = true,
-		.sign_extend = false, .decode = decode_arm_extra },
-	/* Load halfword unprivileged */
-	{ .opc = 0x003000b0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
-		.sign_extend = false, .decode = decode_arm_extra },
-	/* Load signed byte unprivileged */
-	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 1, .w = false,
-		.sign_extend = true , .decode = decode_arm_extra },
-	/* Load signed halfword unprivileged */
-	{ .opc = 0x003000d0, .opc_mask = 0x0f3000f0, .len = 2, .w = false,
-		.sign_extend = true , .decode = decode_arm_extra },
-};
-
-static bool kvm_decode_arm_ls(struct kvm_decode *decode, unsigned long instr,
-			      struct kvm_exit_mmio *mmio)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(arm_instr); i++) {
-		const struct arm_instr *ai = &arm_instr[i];
-		if ((instr & ai->opc_mask) == ai->opc) {
-			struct arm_instr ai_copy = *ai;
-			return ai->decode(decode, mmio, instr, &ai_copy);
-		}
-	}
-	return false;
-}
-
-struct thumb_instr {
-	bool is32;
-
-	union {
-		struct {
-			u8 opcode;
-			u8 mask;
-		} t16;
-
-		struct {
-			u8 op1;
-			u8 op2;
-			u8 op2_mask;
-		} t32;
-	};
-
-	bool (*decode)(struct kvm_decode *decode, struct kvm_exit_mmio *mmio,
-		       unsigned long instr, const struct thumb_instr *ti);
-};
-
-static bool decode_thumb_wb(struct kvm_decode *decode,
-			    struct kvm_exit_mmio *mmio,
-			    unsigned long instr)
-{
-	bool P = (instr >> 10) & 1;
-	bool U = (instr >> 9) & 1;
-	u8 imm8 = instr & 0xff;
-	u32 offset_addr = decode->hxfar;
-	u8 Rn = (instr >> 16) & 0xf;
-
-	decode->rt = (instr >> 12) & 0xf;
-
-	if (Rn == 15)
-		return false;
-
-	/* Handle Writeback */
-	if (!P && U)
-		decode->regs->uregs[Rn] = offset_addr + imm8;
-	else if (!P && !U)
-		decode->regs->uregs[Rn] = offset_addr - imm8;
-	return true;
-}
-
-static bool decode_thumb_str(struct kvm_decode *decode,
-			     struct kvm_exit_mmio *mmio,
-			     unsigned long instr, const struct thumb_instr *ti)
-{
-	u8 op1 = (instr >> (16 + 5)) & 0x7;
-	u8 op2 = (instr >> 6) & 0x3f;
-
-	mmio->is_write = true;
-	decode->sign_extend = false;
-
-	switch (op1) {
-	case 0x0: mmio->len = 1; break;
-	case 0x1: mmio->len = 2; break;
-	case 0x2: mmio->len = 4; break;
-	default:
-		  return false; /* Only register write-back versions! */
-	}
-
-	if ((op2 & 0x24) == 0x24) {
-		/* STRB (immediate, thumb, W=1) */
-		return decode_thumb_wb(decode, mmio, instr);
-	}
-
-	return false;
-}
-
-static bool decode_thumb_ldr(struct kvm_decode *decode,
-			     struct kvm_exit_mmio *mmio,
-			     unsigned long instr, const struct thumb_instr *ti)
-{
-	u8 op1 = (instr >> (16 + 7)) & 0x3;
-	u8 op2 = (instr >> 6) & 0x3f;
-
-	mmio->is_write = false;
-
-	switch (ti->t32.op2 & 0x7) {
-	case 0x1: mmio->len = 1; break;
-	case 0x3: mmio->len = 2; break;
-	case 0x5: mmio->len = 4; break;
-	}
-
-	if (op1 == 0x0)
-		decode->sign_extend = false;
-	else if (op1 == 0x2 && (ti->t32.op2 & 0x7) != 0x5)
-		decode->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(decode, mmio, instr);
-	}
-
-	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_instr thumb_instr[] = {
-	/**************** 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_decode *decode, 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 */
-	int i;
-
-	if (is16) {
-		tinstr.t16.opcode = (instr >> 10) & 0x3f;
-	} else {
-		tinstr.t32.op1 = (instr >> (16 + 11)) & 0x3;
-		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)
-			continue;
-
-		if (is16) {
-			if ((tinstr.t16.opcode & ti->t16.mask) != ti->t16.opcode)
-				continue;
-		} else {
-			if (ti->t32.op1 != tinstr.t32.op1)
-				continue;
-			if ((ti->t32.op2_mask & tinstr.t32.op2) != ti->t32.op2)
-				continue;
-		}
-
-		return ti->decode(decode, mmio, instr, &tinstr);
-	}
-
-	return false;
-}
-
 /**
  * kvm_emulate_mmio_ls - emulates load/store instructions made to I/O memory
  * @vcpu:	The vcpu pointer
@@ -722,10 +303,10 @@ static bool kvm_decode_thumb_ls(struct kvm_decode *decode, unsigned long instr,
 int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 			struct kvm_exit_mmio *mmio)
 {
-	bool is_thumb;
 	unsigned long instr = 0;
 	struct pt_regs current_regs;
 	struct kvm_decode *decode = &vcpu->arch.mmio_decode;
+	int ret;
 
 	trace_kvm_mmio_emulate(*vcpu_pc(vcpu), instr, *vcpu_cpsr(vcpu));
 
@@ -741,20 +322,13 @@ int kvm_emulate_mmio_ls(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 
 	decode->regs = &current_regs;
 	decode->hxfar = vcpu->arch.hxfar;
-
-	is_thumb = !!(*vcpu_cpsr(vcpu) & PSR_T_BIT);
-	if (!is_thumb && !kvm_decode_arm_ls(decode, instr, mmio)) {
-		kvm_debug("Unable to decode inst: %#08lx (cpsr: %#08x (T=0)"
+	ret = kvm_decode_load_store(decode, instr, mmio);
+	if (ret) {
+		kvm_debug("Insrn. decode error: %#08lx (cpsr: %#08x"
 			  "pc: %#08x)\n",
 			  instr, *vcpu_cpsr(vcpu), *vcpu_pc(vcpu));
 		kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
-		return 1;
-	} else if (is_thumb && !kvm_decode_thumb_ls(decode, instr, mmio)) {
-		kvm_debug("Unable to decode inst: %#08lx (cpsr: %#08x (T=1)"
-			  "pc: %#08x)\n",
-			  instr, *vcpu_cpsr(vcpu), *vcpu_pc(vcpu));
-		kvm_inject_dabt(vcpu, vcpu->arch.hxfar);
-		return 1;
+		return ret;
 	}
 
 	memcpy(&vcpu->arch.regs.usr_regs, &current_regs, sizeof(current_regs));
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
index eefbead..ed6b441 100644
--- a/arch/arm/kvm/mmio.c
+++ b/arch/arm/kvm/mmio.c
@@ -18,6 +18,7 @@
 
 #include <asm/kvm_mmio.h>
 #include <asm/kvm_emulate.h>
+#include <asm/kvm_decode.h>
 #include <trace/events/kvm.h>
 
 #include "trace.h"
@@ -129,13 +130,15 @@ int io_mem_abort(struct kvm_vcpu *vcpu, struct kvm_run *run,
 	 * space do its magic.
 	 */
 
-	if (vcpu->arch.hsr & HSR_ISV)
+	if (vcpu->arch.hsr & HSR_ISV) {
 		ret = decode_hsr(vcpu, fault_ipa, &mmio);
-	else
+		if (ret)
+			return ret;
+	} else {
 		ret = kvm_emulate_mmio_ls(vcpu, fault_ipa, &mmio);
-
-	if (ret != 0)
-		return ret;
+		if (ret)
+			return ret;
+	}
 
 	rt = vcpu->arch.mmio_decode.rt;
 	trace_kvm_mmio((mmio.is_write) ? KVM_TRACE_MMIO_WRITE :

_______________________________________________
kvmarm mailing list
kvmarm@xxxxxxxxxxxxxxxxxxxxx
https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm


[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux