On 01/29/2013 06:50 PM, Steven J. Hill wrote:
From: "Steven J. Hill" <sjhill@xxxxxxxx>
This adds microMIPS instruction support to the micro-assembler.
Signed-off-by: Steven J. Hill <sjhill@xxxxxxxx>
---
arch/mips/mm/uasm.c | 121 +++++++++++++++++++++++++++++++++------------------
1 file changed, 78 insertions(+), 43 deletions(-)
diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c
index 942ff6c..13f0ca7 100644
--- a/arch/mips/mm/uasm.c
+++ b/arch/mips/mm/uasm.c
@@ -10,6 +10,7 @@
* Copyright (C) 2004, 2005, 2006, 2008 Thiemo Seufer
* Copyright (C) 2005, 2007 Maciej W. Rozycki
* Copyright (C) 2006 Ralf Baechle (ralf@xxxxxxxxxxxxxx)
+ * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
*/
#include <linux/kernel.h>
@@ -35,27 +36,6 @@ enum fields {
SCIMM = 0x400
};
-#define OP_MASK 0x3f
-#define OP_SH 26
-#define RS_MASK 0x1f
-#define RS_SH 21
-#define RT_MASK 0x1f
-#define RT_SH 16
-#define RD_MASK 0x1f
-#define RD_SH 11
-#define RE_MASK 0x1f
-#define RE_SH 6
-#define IMM_MASK 0xffff
-#define IMM_SH 0
-#define JIMM_MASK 0x3ffffff
-#define JIMM_SH 0
-#define FUNC_MASK 0x3f
-#define FUNC_SH 0
-#define SET_MASK 0x7
-#define SET_SH 0
-#define SCIMM_MASK 0xfffff
-#define SCIMM_SH 6
-
enum opcode {
insn_invalid,
insn_addiu, insn_addu, insn_and, insn_andi, insn_bbit0, insn_bbit1,
@@ -77,14 +57,17 @@ struct insn {
enum fields fields;
};
+/* Register length mask. */
+#define RX_MASK 0x1f
+
/* This macro sets the non-variable bits of an instruction. */
#define M(a, b, c, d, e, f) \
- ((a) << OP_SH \
- | (b) << RS_SH \
- | (c) << RT_SH \
- | (d) << RD_SH \
- | (e) << RE_SH \
- | (f) << FUNC_SH)
+ ((a) << 26 \
+ | (b) << 21 \
+ | (c) << 16 \
+ | (d) << 11 \
+ | (e) << 6 \
+ | (f) << 0)
static struct insn insn_table[] __uasminitdata = {
{ insn_addiu, M(addiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
@@ -158,30 +141,46 @@ static struct insn insn_table[] __uasminitdata = {
static inline __uasminit u32 build_rs(u32 arg)
{
- WARN(arg & ~RS_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+#ifdef CONFIG_CPU_MICROMIPS
+#define RS_SH 16
+#else
+#define RS_SH 21
+#endif
I wonder if we could get rid of most of these #ifdefs by taking the
approach used in scripts/recordmcount.{c,h} or scripts/sortextable.{c,h}
That is, move all the code that varies between microMIPS and MIPS to an
included file. At the top of that file have a single
#if...#else...#endif block that defines the constants for each variety.
Then include this file as many times as needed (once or twice) to
produce the definitions required.
This would allow both a MIPS and microMIPS uasm to exist in the same kernel.
Use Case: Synthesizing VDSO code in a microMIPS kernel. The kernel
might be build for microMIPS, but the user space code in the VDSO must
(by the kernel ABI) be standard MIPS instructions.
- return (arg & RS_MASK) << RS_SH;
+ WARN(arg & ~RX_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+
+ return (arg & RX_MASK) << RS_SH;
}
static inline __uasminit u32 build_rt(u32 arg)
{
- WARN(arg & ~RT_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+#ifdef CONFIG_CPU_MICROMIPS
+#define RT_SH 21
+#else
+#define RT_SH 16
+#endif
+
+ WARN(arg & ~RX_MASK, KERN_WARNING "Micro-assembler field overflow\n");
- return (arg & RT_MASK) << RT_SH;
+ return (arg & RX_MASK) << RT_SH;
}
static inline __uasminit u32 build_rd(u32 arg)
{
- WARN(arg & ~RD_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+#define RD_SH 11
- return (arg & RD_MASK) << RD_SH;
+ WARN(arg & ~RX_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+
+ return (arg & RX_MASK) << RD_SH;
}
static inline __uasminit u32 build_re(u32 arg)
{
- WARN(arg & ~RE_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+#define RE_SH 6
- return (arg & RE_MASK) << RE_SH;
+ WARN(arg & ~RX_MASK, KERN_WARNING "Micro-assembler field overflow\n");
+
+ return (arg & RX_MASK) << RE_SH;
}
static inline __uasminit u32 build_simm(s32 arg)
@@ -194,6 +193,8 @@ static inline __uasminit u32 build_simm(s32 arg)
static inline __uasminit u32 build_uimm(u32 arg)
{
+#define IMM_MASK 0xffff
+
WARN(arg & ~IMM_MASK, KERN_WARNING "Micro-assembler field overflow\n");
return arg & IMM_MASK;
@@ -201,32 +202,54 @@ static inline __uasminit u32 build_uimm(u32 arg)
static inline __uasminit u32 build_bimm(s32 arg)
{
- WARN(arg > 0x1ffff || arg < -0x20000,
+#ifdef CONFIG_CPU_MICROMIPS
+#define BIMM_SHIFT 1
+#else
+#define BIMM_SHIFT 2
+#endif
+
+ WARN(arg > (0x1ffff >> (2 - BIMM_SHIFT)) ||
+ arg < (-0x20000 >> (2 - BIMM_SHIFT)),
KERN_WARNING "Micro-assembler field overflow\n");
WARN(arg & 0x3, KERN_WARNING "Invalid micro-assembler branch target\n");
- return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff);
+ return ((arg < 0) ? (1 << 15) : 0) | ((arg >> BIMM_SHIFT) & 0x7fff);
}
static inline __uasminit u32 build_jimm(u32 arg)
{
- WARN(arg & ~(JIMM_MASK << 2),
+#define JIMM_MASK 0x3ffffff
+#ifdef CONFIG_CPU_MICROMIPS
+ arg >>= 1;
+#else
+ arg >>= 2;
+#endif
+
+ WARN(arg & ~JIMM_MASK,
KERN_WARNING "Micro-assembler field overflow\n");
- return (arg >> 2) & JIMM_MASK;
+ return (arg & JIMM_MASK);
}
static inline __uasminit u32 build_scimm(u32 arg)
{
- WARN(arg & ~SCIMM_MASK,
+#ifdef CONFIG_CPU_MICROMIPS
+#define SCIMM_SH 6
+#else
+#define SCIMM_SH 0
+#endif
+
+ WARN(arg & ~(0xfffff >> SCIMM_SH),
KERN_WARNING "Micro-assembler field overflow\n");
- return (arg & SCIMM_MASK) << SCIMM_SH;
+ return (arg & ((0xfffff >> SCIMM_SH) << (SCIMM_SH + 10)));
}
static inline __uasminit u32 build_func(u32 arg)
{
+#define FUNC_MASK 0x3f
+
WARN(arg & ~FUNC_MASK, KERN_WARNING "Micro-assembler field overflow\n");
return arg & FUNC_MASK;
@@ -234,6 +257,8 @@ static inline __uasminit u32 build_func(u32 arg)
static inline __uasminit u32 build_set(u32 arg)
{
+#define SET_MASK 0x7
+
WARN(arg & ~SET_MASK, KERN_WARNING "Micro-assembler field overflow\n");
return arg & SET_MASK;
@@ -245,6 +270,11 @@ static inline __uasminit u32 build_set(u32 arg)
*/
static void __uasminit build_insn(u32 **buf, enum opcode opc, ...)
{
+#if defined(CONFIG_CPU_MICROMIPS) && defined(CONFIG_CPU_LITTLE_ENDIAN)
+#define OP_SHIFT 16
+#else
+#define OP_SHIFT 0
+#endif
struct insn *ip = NULL;
unsigned int i;
va_list ap;
@@ -285,7 +315,7 @@ static void __uasminit build_insn(u32 **buf, enum opcode opc, ...)
op |= build_scimm(va_arg(ap, u32));
va_end(ap);
- **buf = op;
+ **buf = ((op & 0xffff) << OP_SHIFT) | (op >> OP_SHIFT);
(*buf)++;
}
@@ -555,12 +585,17 @@ UASM_EXPORT_SYMBOL(uasm_r_mips_pc16);
static inline void __uasminit
__resolve_relocs(struct uasm_reloc *rel, struct uasm_label *lab)
{
+#if defined(CONFIG_CPU_MICROMIPS) && defined(CONFIG_CPU_LITTLE_ENDIAN)
+#define REL_SHIFT 16
+#else
+#define REL_SHIFT 0
+#endif
long laddr = (long)lab->addr;
long raddr = (long)rel->addr;
switch (rel->type) {
case R_MIPS_PC16:
- *rel->addr |= build_bimm(laddr - (raddr + 4));
+ *rel->addr |= (build_bimm(laddr - (raddr + 4)) << REL_SHIFT);
break;
default: