2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
6 * A small micro-assembler. It is intentionally kept simple, does only
7 * support a subset of instructions, and does not try to hide pipeline
8 * effects like branch delay slots.
10 * Copyright (C) 2004, 2005, 2006, 2008 Thiemo Seufer
11 * Copyright (C) 2005, 2007 Maciej W. Rozycki
12 * Copyright (C) 2006 Ralf Baechle (ralf@linux-mips.org)
13 * Copyright (C) 2012, 2013 MIPS Technologies, Inc. All rights reserved.
16 #include <linux/kernel.h>
17 #include <linux/types.h>
22 #define UASM_ISA _UASM_ISA_CLASSIC
29 #define SCIMM_MASK 0xfffff
32 /* This macro sets the non-variable bits of an instruction. */
33 #define M(a, b, c, d, e, f) \
41 /* This macro sets the non-variable bits of an R6 instruction. */
42 #define M6(a, b, c, d, e) \
51 static struct insn insn_table
[] = {
52 { insn_addiu
, M(addiu_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
53 { insn_addu
, M(spec_op
, 0, 0, 0, 0, addu_op
), RS
| RT
| RD
},
54 { insn_andi
, M(andi_op
, 0, 0, 0, 0, 0), RS
| RT
| UIMM
},
55 { insn_and
, M(spec_op
, 0, 0, 0, 0, and_op
), RS
| RT
| RD
},
56 { insn_bbit0
, M(lwc2_op
, 0, 0, 0, 0, 0), RS
| RT
| BIMM
},
57 { insn_bbit1
, M(swc2_op
, 0, 0, 0, 0, 0), RS
| RT
| BIMM
},
58 { insn_beql
, M(beql_op
, 0, 0, 0, 0, 0), RS
| RT
| BIMM
},
59 { insn_beq
, M(beq_op
, 0, 0, 0, 0, 0), RS
| RT
| BIMM
},
60 { insn_bgezl
, M(bcond_op
, 0, bgezl_op
, 0, 0, 0), RS
| BIMM
},
61 { insn_bgez
, M(bcond_op
, 0, bgez_op
, 0, 0, 0), RS
| BIMM
},
62 { insn_bltzl
, M(bcond_op
, 0, bltzl_op
, 0, 0, 0), RS
| BIMM
},
63 { insn_bltz
, M(bcond_op
, 0, bltz_op
, 0, 0, 0), RS
| BIMM
},
64 { insn_bne
, M(bne_op
, 0, 0, 0, 0, 0), RS
| RT
| BIMM
},
65 #ifndef CONFIG_CPU_MIPSR6
66 { insn_cache
, M(cache_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
68 { insn_cache
, M6(spec3_op
, 0, 0, 0, cache6_op
), RS
| RT
| SIMM9
},
70 { insn_cfc1
, M(cop1_op
, cfc_op
, 0, 0, 0, 0), RT
| RD
},
71 { insn_cfcmsa
, M(msa_op
, 0, msa_cfc_op
, 0, 0, msa_elm_op
), RD
| RE
},
72 { insn_ctc1
, M(cop1_op
, ctc_op
, 0, 0, 0, 0), RT
| RD
},
73 { insn_ctcmsa
, M(msa_op
, 0, msa_ctc_op
, 0, 0, msa_elm_op
), RD
| RE
},
74 { insn_daddiu
, M(daddiu_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
75 { insn_daddu
, M(spec_op
, 0, 0, 0, 0, daddu_op
), RS
| RT
| RD
},
76 { insn_dinsm
, M(spec3_op
, 0, 0, 0, 0, dinsm_op
), RS
| RT
| RD
| RE
},
77 { insn_di
, M(cop0_op
, mfmc0_op
, 0, 12, 0, 0), RT
},
78 { insn_dins
, M(spec3_op
, 0, 0, 0, 0, dins_op
), RS
| RT
| RD
| RE
},
79 { insn_divu
, M(spec_op
, 0, 0, 0, 0, divu_op
), RS
| RT
},
80 { insn_dmfc0
, M(cop0_op
, dmfc_op
, 0, 0, 0, 0), RT
| RD
| SET
},
81 { insn_dmtc0
, M(cop0_op
, dmtc_op
, 0, 0, 0, 0), RT
| RD
| SET
},
82 { insn_drotr32
, M(spec_op
, 1, 0, 0, 0, dsrl32_op
), RT
| RD
| RE
},
83 { insn_drotr
, M(spec_op
, 1, 0, 0, 0, dsrl_op
), RT
| RD
| RE
},
84 { insn_dsll32
, M(spec_op
, 0, 0, 0, 0, dsll32_op
), RT
| RD
| RE
},
85 { insn_dsll
, M(spec_op
, 0, 0, 0, 0, dsll_op
), RT
| RD
| RE
},
86 { insn_dsra
, M(spec_op
, 0, 0, 0, 0, dsra_op
), RT
| RD
| RE
},
87 { insn_dsrl32
, M(spec_op
, 0, 0, 0, 0, dsrl32_op
), RT
| RD
| RE
},
88 { insn_dsrl
, M(spec_op
, 0, 0, 0, 0, dsrl_op
), RT
| RD
| RE
},
89 { insn_dsubu
, M(spec_op
, 0, 0, 0, 0, dsubu_op
), RS
| RT
| RD
},
90 { insn_eret
, M(cop0_op
, cop_op
, 0, 0, 0, eret_op
), 0 },
91 { insn_ext
, M(spec3_op
, 0, 0, 0, 0, ext_op
), RS
| RT
| RD
| RE
},
92 { insn_ins
, M(spec3_op
, 0, 0, 0, 0, ins_op
), RS
| RT
| RD
| RE
},
93 { insn_j
, M(j_op
, 0, 0, 0, 0, 0), JIMM
},
94 { insn_jal
, M(jal_op
, 0, 0, 0, 0, 0), JIMM
},
95 { insn_jalr
, M(spec_op
, 0, 0, 0, 0, jalr_op
), RS
| RD
},
96 { insn_j
, M(j_op
, 0, 0, 0, 0, 0), JIMM
},
97 #ifndef CONFIG_CPU_MIPSR6
98 { insn_jr
, M(spec_op
, 0, 0, 0, 0, jr_op
), RS
},
100 { insn_jr
, M(spec_op
, 0, 0, 0, 0, jalr_op
), RS
},
102 { insn_lb
, M(lb_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
103 { insn_ld
, M(ld_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
104 { insn_ldx
, M(spec3_op
, 0, 0, 0, ldx_op
, lx_op
), RS
| RT
| RD
},
105 { insn_lh
, M(lh_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
106 #ifndef CONFIG_CPU_MIPSR6
107 { insn_lld
, M(lld_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
108 { insn_ll
, M(ll_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
110 { insn_lld
, M6(spec3_op
, 0, 0, 0, lld6_op
), RS
| RT
| SIMM9
},
111 { insn_ll
, M6(spec3_op
, 0, 0, 0, ll6_op
), RS
| RT
| SIMM9
},
113 { insn_lui
, M(lui_op
, 0, 0, 0, 0, 0), RT
| SIMM
},
114 { insn_lw
, M(lw_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
115 { insn_lwx
, M(spec3_op
, 0, 0, 0, lwx_op
, lx_op
), RS
| RT
| RD
},
116 { insn_mfc0
, M(cop0_op
, mfc_op
, 0, 0, 0, 0), RT
| RD
| SET
},
117 { insn_mfhc0
, M(cop0_op
, mfhc0_op
, 0, 0, 0, 0), RT
| RD
| SET
},
118 { insn_mfhi
, M(spec_op
, 0, 0, 0, 0, mfhi_op
), RD
},
119 { insn_mflo
, M(spec_op
, 0, 0, 0, 0, mflo_op
), RD
},
120 { insn_mtc0
, M(cop0_op
, mtc_op
, 0, 0, 0, 0), RT
| RD
| SET
},
121 { insn_mthc0
, M(cop0_op
, mthc0_op
, 0, 0, 0, 0), RT
| RD
| SET
},
122 { insn_mthi
, M(spec_op
, 0, 0, 0, 0, mthi_op
), RS
},
123 { insn_mtlo
, M(spec_op
, 0, 0, 0, 0, mtlo_op
), RS
},
124 #ifndef CONFIG_CPU_MIPSR6
125 { insn_mul
, M(spec2_op
, 0, 0, 0, 0, mul_op
), RS
| RT
| RD
},
127 { insn_mul
, M(spec_op
, 0, 0, 0, mult_mul_op
, mult_op
), RS
| RT
| RD
},
129 { insn_ori
, M(ori_op
, 0, 0, 0, 0, 0), RS
| RT
| UIMM
},
130 { insn_or
, M(spec_op
, 0, 0, 0, 0, or_op
), RS
| RT
| RD
},
131 #ifndef CONFIG_CPU_MIPSR6
132 { insn_pref
, M(pref_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
134 { insn_pref
, M6(spec3_op
, 0, 0, 0, pref6_op
), RS
| RT
| SIMM9
},
136 { insn_rfe
, M(cop0_op
, cop_op
, 0, 0, 0, rfe_op
), 0 },
137 { insn_rotr
, M(spec_op
, 1, 0, 0, 0, srl_op
), RT
| RD
| RE
},
138 #ifndef CONFIG_CPU_MIPSR6
139 { insn_scd
, M(scd_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
140 { insn_sc
, M(sc_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
142 { insn_scd
, M6(spec3_op
, 0, 0, 0, scd6_op
), RS
| RT
| SIMM9
},
143 { insn_sc
, M6(spec3_op
, 0, 0, 0, sc6_op
), RS
| RT
| SIMM9
},
145 { insn_sd
, M(sd_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
146 { insn_sll
, M(spec_op
, 0, 0, 0, 0, sll_op
), RT
| RD
| RE
},
147 { insn_sllv
, M(spec_op
, 0, 0, 0, 0, sllv_op
), RS
| RT
| RD
},
148 { insn_slt
, M(spec_op
, 0, 0, 0, 0, slt_op
), RS
| RT
| RD
},
149 { insn_sltiu
, M(sltiu_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
150 { insn_sltu
, M(spec_op
, 0, 0, 0, 0, sltu_op
), RS
| RT
| RD
},
151 { insn_sra
, M(spec_op
, 0, 0, 0, 0, sra_op
), RT
| RD
| RE
},
152 { insn_srl
, M(spec_op
, 0, 0, 0, 0, srl_op
), RT
| RD
| RE
},
153 { insn_srlv
, M(spec_op
, 0, 0, 0, 0, srlv_op
), RS
| RT
| RD
},
154 { insn_subu
, M(spec_op
, 0, 0, 0, 0, subu_op
), RS
| RT
| RD
},
155 { insn_sw
, M(sw_op
, 0, 0, 0, 0, 0), RS
| RT
| SIMM
},
156 { insn_sync
, M(spec_op
, 0, 0, 0, 0, sync_op
), RE
},
157 { insn_syscall
, M(spec_op
, 0, 0, 0, 0, syscall_op
), SCIMM
},
158 { insn_tlbp
, M(cop0_op
, cop_op
, 0, 0, 0, tlbp_op
), 0 },
159 { insn_tlbr
, M(cop0_op
, cop_op
, 0, 0, 0, tlbr_op
), 0 },
160 { insn_tlbwi
, M(cop0_op
, cop_op
, 0, 0, 0, tlbwi_op
), 0 },
161 { insn_tlbwr
, M(cop0_op
, cop_op
, 0, 0, 0, tlbwr_op
), 0 },
162 { insn_wait
, M(cop0_op
, cop_op
, 0, 0, 0, wait_op
), SCIMM
},
163 { insn_wsbh
, M(spec3_op
, 0, 0, 0, wsbh_op
, bshfl_op
), RT
| RD
},
164 { insn_xori
, M(xori_op
, 0, 0, 0, 0, 0), RS
| RT
| UIMM
},
165 { insn_xor
, M(spec_op
, 0, 0, 0, 0, xor_op
), RS
| RT
| RD
},
166 { insn_yield
, M(spec3_op
, 0, 0, 0, 0, yield_op
), RS
| RD
},
167 { insn_ldpte
, M(lwc2_op
, 0, 0, 0, ldpte_op
, mult_op
), RS
| RD
},
168 { insn_lddir
, M(lwc2_op
, 0, 0, 0, lddir_op
, mult_op
), RS
| RT
| RD
},
169 { insn_invalid
, 0, 0 }
174 static inline u32
build_bimm(s32 arg
)
176 WARN(arg
> 0x1ffff || arg
< -0x20000,
177 KERN_WARNING
"Micro-assembler field overflow\n");
179 WARN(arg
& 0x3, KERN_WARNING
"Invalid micro-assembler branch target\n");
181 return ((arg
< 0) ? (1 << 15) : 0) | ((arg
>> 2) & 0x7fff);
184 static inline u32
build_jimm(u32 arg
)
186 WARN(arg
& ~(JIMM_MASK
<< 2),
187 KERN_WARNING
"Micro-assembler field overflow\n");
189 return (arg
>> 2) & JIMM_MASK
;
193 * The order of opcode arguments is implicitly left to right,
194 * starting with RS and ending with FUNC or IMM.
196 static void build_insn(u32
**buf
, enum opcode opc
, ...)
198 struct insn
*ip
= NULL
;
203 for (i
= 0; insn_table
[i
].opcode
!= insn_invalid
; i
++)
204 if (insn_table
[i
].opcode
== opc
) {
209 if (!ip
|| (opc
== insn_daddiu
&& r4k_daddiu_bug()))
210 panic("Unsupported Micro-assembler instruction %d", opc
);
215 op
|= build_rs(va_arg(ap
, u32
));
217 op
|= build_rt(va_arg(ap
, u32
));
219 op
|= build_rd(va_arg(ap
, u32
));
221 op
|= build_re(va_arg(ap
, u32
));
222 if (ip
->fields
& SIMM
)
223 op
|= build_simm(va_arg(ap
, s32
));
224 if (ip
->fields
& UIMM
)
225 op
|= build_uimm(va_arg(ap
, u32
));
226 if (ip
->fields
& BIMM
)
227 op
|= build_bimm(va_arg(ap
, s32
));
228 if (ip
->fields
& JIMM
)
229 op
|= build_jimm(va_arg(ap
, u32
));
230 if (ip
->fields
& FUNC
)
231 op
|= build_func(va_arg(ap
, u32
));
232 if (ip
->fields
& SET
)
233 op
|= build_set(va_arg(ap
, u32
));
234 if (ip
->fields
& SCIMM
)
235 op
|= build_scimm(va_arg(ap
, u32
));
236 if (ip
->fields
& SIMM9
)
237 op
|= build_scimm9(va_arg(ap
, u32
));
245 __resolve_relocs(struct uasm_reloc
*rel
, struct uasm_label
*lab
)
247 long laddr
= (long)lab
->addr
;
248 long raddr
= (long)rel
->addr
;
252 *rel
->addr
|= build_bimm(laddr
- (raddr
+ 4));
256 panic("Unsupported Micro-assembler relocation %d",