[RFC PATCH] ARM: Add generic instruction opcode manipulation helpers
Dave Martin
dave.martin at linaro.org
Fri Nov 25 06:28:13 EST 2011
This patch adds some endianness-agnostic helpers to convert machine
instructions between canonical integer form and in-memory
representation, and also provides a transparent way to read a
single Thumb instruction from memory, without the need to know the
size in advance or write explicit condition checks.
A canonical integer form for representing instructions is also
formalised here.
Signed-off-by: Dave Martin <dave.martin at linaro.org>
---
arch/arm/include/asm/opcodes.h | 162 ++++++++++++++++++++++++++++++++++++++++
1 files changed, 162 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/include/asm/opcodes.h
diff --git a/arch/arm/include/asm/opcodes.h b/arch/arm/include/asm/opcodes.h
new file mode 100644
index 0000000..5d18f92
--- /dev/null
+++ b/arch/arm/include/asm/opcodes.h
@@ -0,0 +1,162 @@
+/*
+ * arch/arm/include/asm/opcodes.h
+ *
+ * Copyright (C) 2011 Linaro Limited
+ *
+ * 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, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ARM_OPCODES_H
+#define __ARM_OPCODES_H
+
+#include <linux/types.h>
+#include <linux/swab.h>
+
+typedef u32 arm_opcode_t;
+
+/*
+ * Canonical instruction representation (arm_opcode_t):
+ *
+ * ARM: 0xKKLLMMNN
+ * Thumb 16-bit: 0x0000KKLL, where KK < 0xE8
+ * Thumb 32-bit: 0xKKLLMMNN, where KK >= 0xE8
+ *
+ * There is no way to distinguish an ARM instruction in canonical representation
+ * from a Thumb instruction (just as these cannot be distinguished in memory).
+ * Where this distinction is important, it needs to be tracked separately.
+ *
+ * Note that values in the range 0x0000E800..0xE7FFFFFF intentionally do not
+ * represent any valid Thumb-2 instruction. For this range,
+ * __opcode_is_thumb32() and __opcode_is_thumb16() will both be false.
+ */
+
+#ifdef CONFIG_CPU_ENDIAN_BE8
+#define __opcode_to_mem_arm(x) swab32(x)
+#define __opcode_to_mem_thumb16(x) swab16(x)
+#define __opcode_to_mem_thumb32(x) swahb32(x)
+#else
+#define __opcode_to_mem_arm(x) (x) ((u32)(x))
+#define __opcode_to_mem_thumb16(x) ((u16)(x))
+#define __opcode_to_mem_thumb32(x) swahw32(x)
+#endif
+
+#define __mem_to_opcode_arm(x) __opcode_to_mem_arm(x)
+#define __mem_to_opcode_thumb16(x) __opcode_to_mem_thumb16(x)
+#define __mem_to_opcode_thumb32(x) __opcode_to_mem_thumb32(x)
+
+/* Operations specific to Thumb opcodes */
+
+/* Instruction size checks: */
+#define __opcode_is_thumb32(x) ((u32)(x) >= 0xE8000000UL)
+#define __opcode_is_thumb16(x) ((u32)(x) < 0xE800UL)
+
+/* Operations to construct or split 32-bit Thumb instructions: */
+#define __opcode_thumb32_first(x) ((u16)((thumb_opcode) >> 16))
+#define __opcode_thumb32_second(x) ((u16)(thumb_opcode))
+#define __opcode_thumb32_compose(first, second) \
+ (((u32)(u16)(first) << 16) | (u32)(u16)(second))
+
+/*
+ * int __opcode_read_<isa>(
+ * arm_opcode_t *outp,
+ * void const **inpp,
+ * int (*readfn)(void *dst, void const *src, size_t size)
+ * )
+ *
+ * This helper reads one complete Thumb instruction and stores the canonicalised
+ * opcode to *outp.
+ *
+ * For maximum flexibility, the mechanism for reading the instruction is
+ * specified as an argument: read16fn(dst, src, size) must attempt to copy
+ * <size> bytes from <src> to <dst>. <readfn>() should return 0 if the copy
+ * was successful, or an error code otherwise.
+ *
+ * Return:
+ * 0 success;
+ * *outp contains the instruction read
+ * *inp points to the next instruction
+ * != 0 failure:
+ * *outp is undefined
+ * *inp contains the first address not successfully read
+ *
+ * Writing this is a macro means that <readfn> can also be implemented as a
+ * macro. This permits the simple case where no error checking is required to
+ * be heavily optimised.
+ */
+#define __opcode_read_thumb(outp, inpp, readfn) ({ \
+ u16 __t; \
+ \
+ BUILD_BUG_ON(sizeof(*(outp)) != sizeof(arm_opcode_t)); \
+ \
+ ___read_advance(&__t, inpp, sizeof(__t), readfn) \
+ || __opcode_is_thumb16(*(outp) = __mem_to_opcode_thumb16(__t)) ? 0 : \
+ ___read_advance(&__t, inpp, sizeof(__t), readfn) \
+ || (*(outp) = __opcode_thumb32_compose( \
+ *(outp), \
+ __mem_to_opcode_thumb16(__t)), \
+ 0); \
+})
+#define ___read_advance(outp, inpp, size, readfn) ({ \
+ int __status; \
+ \
+ __status = readfn(outp, *(inpp), size); \
+ if (!__status) \
+ *(inpp) = (typeof(*(inpp)))((uintptr_t)*(inpp) + (size)); \
+ \
+ __status; \
+})
+
+#define __opcode_read_arm(outp, inpp, readfn) ({ \
+ BUILD_BUG_ON(sizeof(*(outp)) != sizeof(arm_opcode_t)); \
+ \
+ ___read_advance(outp, inpp, sizeof(arm_opcode_t), readfn) \
+ || (*(outp) = __mem_to_opcode_arm(*(outp)), \
+ 0); \
+})
+
+/* __opcode_read_<isa>_simple(
+ * arm_opcode_t *outp,
+ * void const **inpp
+ * )
+ *
+ * Reads n Thumb-2 instruction from memory, without error checks.
+ * This macro will always succeed and return 0. Otherwise, it is similar
+ * to __opcode_read_thumb().
+ */
+#define __opcode_read_thumb_simple(outp, inp) \
+ __opcode_read_thumb(outp, inp, ___read16_simple)
+#define __opcode_read_arm_simple(outp, inp) \
+ __opcode_read_arm(outp, inp, ___read32_simple)
+
+#define ___read16_simple(outp, inp, size) \
+ (*(outp) = *(u16 *)(inp), 0)
+#define ___read32_simple(outp, inp, size) \
+ (*(outp) = *(u32 *)(inp), 0)
+
+
+#ifdef CONFIG_THUMB2_KERNEL
+#define __opcode_read(outp, inpp, readfn) \
+ __opcode_read_thumb(outp, inpp, readfn)
+#define __opcode_read_simple(outp, inpp) \
+ __opcode_read_thumb_simple(outp, inpp)
+#else
+#define __opcode_read(outp, inpp, readfn) \
+ __opcode_read_arm(outp, inpp, readfn)
+#define __opcode_read_simple(outp, inpp) \
+ __opcode_read_arm_simple(outp, inpp)
+#endif
+
+/* Maybe add some C static functions here, with proper type annotations */
+
+#endif /* ! __ARM_OPCODES_H */
--
1.7.4.1
More information about the linux-arm-kernel
mailing list