uasm.c 18.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * A small micro-assembler. It is intentionally kept simple, does only
 * support a subset of instructions, and does not try to hide pipeline
 * effects like branch delay slots.
 *
 * Copyright (C) 2004, 2005, 2006, 2008  Thiemo Seufer
 * Copyright (C) 2005, 2007  Maciej W. Rozycki
 * Copyright (C) 2006  Ralf Baechle (ralf@linux-mips.org)
 */

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>

#include <asm/inst.h>
#include <asm/elf.h>
#include <asm/bugs.h>
22
#include <asm/uasm.h>
23 24 25 26 27 28 29 30 31 32 33

enum fields {
	RS = 0x001,
	RT = 0x002,
	RD = 0x004,
	RE = 0x008,
	SIMM = 0x010,
	UIMM = 0x020,
	BIMM = 0x040,
	JIMM = 0x080,
	FUNC = 0x100,
D
David Daney 已提交
34 35
	SET = 0x200,
	SCIMM = 0x400
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
};

#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
D
David Daney 已提交
56 57
#define SCIMM_MASK	0xfffff
#define SCIMM_SH	6
58 59 60

enum opcode {
	insn_invalid,
61 62 63 64 65
	insn_addiu, insn_addu, insn_and, insn_andi, insn_bbit0, insn_bbit1,
	insn_beq, insn_beql, insn_bgez, insn_bgezl, insn_bltz, insn_bltzl,
	insn_bne, insn_cache, insn_daddiu, insn_daddu, insn_dins, insn_dinsm,
	insn_dmfc0, insn_dmtc0, insn_drotr, insn_drotr32, insn_dsll,
	insn_dsll32, insn_dsra, insn_dsrl, insn_dsrl32, insn_dsubu, insn_eret,
66 67 68 69 70 71
	insn_ext, insn_ins, insn_j, insn_jal, insn_jr, insn_ld, insn_ldx,
	insn_ll, insn_lld, insn_lui, insn_lw, insn_lwx, insn_mfc0, insn_mtc0,
	insn_or, insn_ori, insn_pref, insn_rfe, insn_rotr, insn_sc, insn_scd,
	insn_sd, insn_sll, insn_sra, insn_srl, insn_subu, insn_sw,
	insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_xor,
	insn_xori,
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
};

struct insn {
	enum opcode opcode;
	u32 match;
	enum fields fields;
};

/* 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)

89
static struct insn insn_table[] __uasminitdata = {
90 91 92
	{ insn_addiu, M(addiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
	{ insn_addu, M(spec_op, 0, 0, 0, 0, addu_op), RS | RT | RD },
	{ insn_andi, M(andi_op, 0, 0, 0, 0, 0), RS | RT | UIMM },
93 94 95
	{ insn_and, M(spec_op, 0, 0, 0, 0, and_op), RS | RT | RD },
	{ insn_bbit0, M(lwc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
	{ insn_bbit1, M(swc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
96
	{ insn_beql, M(beql_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
97
	{ insn_beq, M(beq_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
98
	{ insn_bgezl, M(bcond_op, 0, bgezl_op, 0, 0, 0), RS | BIMM },
99
	{ insn_bgez, M(bcond_op, 0, bgez_op, 0, 0, 0), RS | BIMM },
100
	{ insn_bltzl, M(bcond_op, 0, bltzl_op, 0, 0, 0), RS | BIMM },
101
	{ insn_bltz, M(bcond_op, 0, bltz_op, 0, 0, 0), RS | BIMM },
102
	{ insn_bne, M(bne_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
103
	{ insn_cache,  M(cache_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
104 105
	{ insn_daddiu, M(daddiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
	{ insn_daddu, M(spec_op, 0, 0, 0, 0, daddu_op), RS | RT | RD },
106 107
	{ insn_dinsm, M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE },
	{ insn_dins, M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE },
108 109
	{ insn_dmfc0, M(cop0_op, dmfc_op, 0, 0, 0, 0), RT | RD | SET},
	{ insn_dmtc0, M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
110 111
	{ insn_drotr32, M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE },
	{ insn_drotr, M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE },
112
	{ insn_dsll32, M(spec_op, 0, 0, 0, 0, dsll32_op), RT | RD | RE },
113
	{ insn_dsll, M(spec_op, 0, 0, 0, 0, dsll_op), RT | RD | RE },
114 115
	{ insn_dsra, M(spec_op, 0, 0, 0, 0, dsra_op), RT | RD | RE },
	{ insn_dsrl32, M(spec_op, 0, 0, 0, 0, dsrl32_op), RT | RD | RE },
116
	{ insn_dsrl, M(spec_op, 0, 0, 0, 0, dsrl_op), RT | RD | RE },
117 118
	{ insn_dsubu, M(spec_op, 0, 0, 0, 0, dsubu_op), RS | RT | RD },
	{ insn_eret,  M(cop0_op, cop_op, 0, 0, 0, eret_op),  0 },
119 120 121
	{ insn_ext, M(spec3_op, 0, 0, 0, 0, ext_op), RS | RT | RD | RE },
	{ insn_ins, M(spec3_op, 0, 0, 0, 0, ins_op), RS | RT | RD | RE },
	{ insn_j,  M(j_op, 0, 0, 0, 0, 0),  JIMM },
122
	{ insn_jal,  M(jal_op, 0, 0, 0, 0, 0),  JIMM },
123
	{ insn_j,  M(j_op, 0, 0, 0, 0, 0),  JIMM },
124 125
	{ insn_jr,  M(spec_op, 0, 0, 0, 0, jr_op),  RS },
	{ insn_ld,  M(ld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
126
	{ insn_ldx, M(spec3_op, 0, 0, 0, ldx_op, lx_op), RS | RT | RD },
127
	{ insn_lld,  M(lld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
128
	{ insn_ll,  M(ll_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
129 130
	{ insn_lui,  M(lui_op, 0, 0, 0, 0, 0),  RT | SIMM },
	{ insn_lw,  M(lw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
131
	{ insn_lwx, M(spec3_op, 0, 0, 0, lwx_op, lx_op), RS | RT | RD },
132 133 134
	{ insn_mfc0,  M(cop0_op, mfc_op, 0, 0, 0, 0),  RT | RD | SET},
	{ insn_mtc0,  M(cop0_op, mtc_op, 0, 0, 0, 0),  RT | RD | SET},
	{ insn_ori,  M(ori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
135
	{ insn_or,  M(spec_op, 0, 0, 0, 0, or_op),  RS | RT | RD },
136
	{ insn_pref,  M(pref_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
137
	{ insn_rfe,  M(cop0_op, cop_op, 0, 0, 0, rfe_op),  0 },
138
	{ insn_rotr,  M(spec_op, 1, 0, 0, 0, srl_op),  RT | RD | RE },
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 },
141 142 143 144 145 146
	{ insn_sd,  M(sd_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
	{ insn_sll,  M(spec_op, 0, 0, 0, 0, sll_op),  RT | RD | RE },
	{ insn_sra,  M(spec_op, 0, 0, 0, 0, sra_op),  RT | RD | RE },
	{ insn_srl,  M(spec_op, 0, 0, 0, 0, srl_op),  RT | RD | RE },
	{ insn_subu,  M(spec_op, 0, 0, 0, 0, subu_op),  RS | RT | RD },
	{ insn_sw,  M(sw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
147
	{ insn_syscall, M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
148
	{ insn_tlbp,  M(cop0_op, cop_op, 0, 0, 0, tlbp_op),  0 },
D
David Daney 已提交
149
	{ insn_tlbr,  M(cop0_op, cop_op, 0, 0, 0, tlbr_op),  0 },
150 151 152
	{ insn_tlbwi,  M(cop0_op, cop_op, 0, 0, 0, tlbwi_op),  0 },
	{ insn_tlbwr,  M(cop0_op, cop_op, 0, 0, 0, tlbwr_op),  0 },
	{ insn_xori,  M(xori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
153
	{ insn_xor,  M(spec_op, 0, 0, 0, 0, xor_op),  RS | RT | RD },
154 155 156 157 158
	{ insn_invalid, 0, 0 }
};

#undef M

159
static inline __uasminit u32 build_rs(u32 arg)
160
{
161
	WARN(arg & ~RS_MASK, KERN_WARNING "Micro-assembler field overflow\n");
162 163 164 165

	return (arg & RS_MASK) << RS_SH;
}

166
static inline __uasminit u32 build_rt(u32 arg)
167
{
168
	WARN(arg & ~RT_MASK, KERN_WARNING "Micro-assembler field overflow\n");
169 170 171 172

	return (arg & RT_MASK) << RT_SH;
}

173
static inline __uasminit u32 build_rd(u32 arg)
174
{
175
	WARN(arg & ~RD_MASK, KERN_WARNING "Micro-assembler field overflow\n");
176 177 178 179

	return (arg & RD_MASK) << RD_SH;
}

180
static inline __uasminit u32 build_re(u32 arg)
181
{
182
	WARN(arg & ~RE_MASK, KERN_WARNING "Micro-assembler field overflow\n");
183 184 185 186

	return (arg & RE_MASK) << RE_SH;
}

187
static inline __uasminit u32 build_simm(s32 arg)
188
{
189 190
	WARN(arg > 0x7fff || arg < -0x8000,
	     KERN_WARNING "Micro-assembler field overflow\n");
191 192 193 194

	return arg & 0xffff;
}

195
static inline __uasminit u32 build_uimm(u32 arg)
196
{
197
	WARN(arg & ~IMM_MASK, KERN_WARNING "Micro-assembler field overflow\n");
198 199 200 201

	return arg & IMM_MASK;
}

202
static inline __uasminit u32 build_bimm(s32 arg)
203
{
204 205
	WARN(arg > 0x1ffff || arg < -0x20000,
	     KERN_WARNING "Micro-assembler field overflow\n");
206

207
	WARN(arg & 0x3, KERN_WARNING "Invalid micro-assembler branch target\n");
208 209 210 211

	return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff);
}

212
static inline __uasminit u32 build_jimm(u32 arg)
213
{
214 215
	WARN(arg & ~(JIMM_MASK << 2),
	     KERN_WARNING "Micro-assembler field overflow\n");
216 217 218 219

	return (arg >> 2) & JIMM_MASK;
}

220
static inline __uasminit u32 build_scimm(u32 arg)
D
David Daney 已提交
221
{
222 223
	WARN(arg & ~SCIMM_MASK,
	     KERN_WARNING "Micro-assembler field overflow\n");
D
David Daney 已提交
224 225 226 227

	return (arg & SCIMM_MASK) << SCIMM_SH;
}

228
static inline __uasminit u32 build_func(u32 arg)
229
{
230
	WARN(arg & ~FUNC_MASK, KERN_WARNING "Micro-assembler field overflow\n");
231 232 233 234

	return arg & FUNC_MASK;
}

235
static inline __uasminit u32 build_set(u32 arg)
236
{
237
	WARN(arg & ~SET_MASK, KERN_WARNING "Micro-assembler field overflow\n");
238 239 240 241 242 243 244 245

	return arg & SET_MASK;
}

/*
 * The order of opcode arguments is implicitly left to right,
 * starting with RS and ending with FUNC or IMM.
 */
246
static void __uasminit build_insn(u32 **buf, enum opcode opc, ...)
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
{
	struct insn *ip = NULL;
	unsigned int i;
	va_list ap;
	u32 op;

	for (i = 0; insn_table[i].opcode != insn_invalid; i++)
		if (insn_table[i].opcode == opc) {
			ip = &insn_table[i];
			break;
		}

	if (!ip || (opc == insn_daddiu && r4k_daddiu_bug()))
		panic("Unsupported Micro-assembler instruction %d", opc);

	op = ip->match;
	va_start(ap, opc);
	if (ip->fields & RS)
		op |= build_rs(va_arg(ap, u32));
	if (ip->fields & RT)
		op |= build_rt(va_arg(ap, u32));
	if (ip->fields & RD)
		op |= build_rd(va_arg(ap, u32));
	if (ip->fields & RE)
		op |= build_re(va_arg(ap, u32));
	if (ip->fields & SIMM)
		op |= build_simm(va_arg(ap, s32));
	if (ip->fields & UIMM)
		op |= build_uimm(va_arg(ap, u32));
	if (ip->fields & BIMM)
		op |= build_bimm(va_arg(ap, s32));
	if (ip->fields & JIMM)
		op |= build_jimm(va_arg(ap, u32));
	if (ip->fields & FUNC)
		op |= build_func(va_arg(ap, u32));
	if (ip->fields & SET)
		op |= build_set(va_arg(ap, u32));
D
David Daney 已提交
284 285
	if (ip->fields & SCIMM)
		op |= build_scimm(va_arg(ap, u32));
286 287 288 289 290 291 292 293 294 295
	va_end(ap);

	**buf = op;
	(*buf)++;
}

#define I_u1u2u3(op)					\
Ip_u1u2u3(op)						\
{							\
	build_insn(buf, insn##op, a, b, c);		\
296 297
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
298 299 300 301 302

#define I_u2u1u3(op)					\
Ip_u2u1u3(op)						\
{							\
	build_insn(buf, insn##op, b, a, c);		\
303 304
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
305 306 307 308 309

#define I_u3u1u2(op)					\
Ip_u3u1u2(op)						\
{							\
	build_insn(buf, insn##op, b, c, a);		\
310 311
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
312 313 314 315 316

#define I_u1u2s3(op)					\
Ip_u1u2s3(op)						\
{							\
	build_insn(buf, insn##op, a, b, c);		\
317 318
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
319 320 321 322 323

#define I_u2s3u1(op)					\
Ip_u2s3u1(op)						\
{							\
	build_insn(buf, insn##op, c, a, b);		\
324 325
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
326 327 328 329 330

#define I_u2u1s3(op)					\
Ip_u2u1s3(op)						\
{							\
	build_insn(buf, insn##op, b, a, c);		\
331 332
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
333

334 335 336 337
#define I_u2u1msbu3(op)					\
Ip_u2u1msbu3(op)					\
{							\
	build_insn(buf, insn##op, b, a, c+d-1, c);	\
338 339
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
340

D
David Daney 已提交
341 342 343 344 345 346 347
#define I_u2u1msb32u3(op)				\
Ip_u2u1msbu3(op)					\
{							\
	build_insn(buf, insn##op, b, a, c+d-33, c);	\
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);

348 349 350 351 352 353 354
#define I_u2u1msbdu3(op) 				\
Ip_u2u1msbu3(op)					\
{							\
	build_insn(buf, insn##op, b, a, d-1, c);	\
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);

355 356 357 358
#define I_u1u2(op)					\
Ip_u1u2(op)						\
{							\
	build_insn(buf, insn##op, a, b);		\
359 360
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
361 362 363 364 365

#define I_u1s2(op)					\
Ip_u1s2(op)						\
{							\
	build_insn(buf, insn##op, a, b);		\
366 367
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
368 369 370 371 372

#define I_u1(op)					\
Ip_u1(op)						\
{							\
	build_insn(buf, insn##op, a);			\
373 374
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
375 376 377 378 379

#define I_0(op)						\
Ip_0(op)						\
{							\
	build_insn(buf, insn##op);			\
380 381
}							\
UASM_EXPORT_SYMBOL(uasm_i##op);
382 383 384 385 386 387 388 389 390 391 392 393

I_u2u1s3(_addiu)
I_u3u1u2(_addu)
I_u2u1u3(_andi)
I_u3u1u2(_and)
I_u1u2s3(_beq)
I_u1u2s3(_beql)
I_u1s2(_bgez)
I_u1s2(_bgezl)
I_u1s2(_bltz)
I_u1s2(_bltzl)
I_u1u2s3(_bne)
394
I_u2s3u1(_cache)
395 396 397 398 399 400 401 402 403
I_u1u2u3(_dmfc0)
I_u1u2u3(_dmtc0)
I_u2u1s3(_daddiu)
I_u3u1u2(_daddu)
I_u2u1u3(_dsll)
I_u2u1u3(_dsll32)
I_u2u1u3(_dsra)
I_u2u1u3(_dsrl)
I_u2u1u3(_dsrl32)
404
I_u2u1u3(_drotr)
405
I_u2u1u3(_drotr32)
406 407
I_u3u1u2(_dsubu)
I_0(_eret)
408 409
I_u2u1msbdu3(_ext)
I_u2u1msbu3(_ins)
410 411 412 413 414 415 416 417 418 419 420
I_u1(_j)
I_u1(_jal)
I_u1(_jr)
I_u2s3u1(_ld)
I_u2s3u1(_ll)
I_u2s3u1(_lld)
I_u1s2(_lui)
I_u2s3u1(_lw)
I_u1u2u3(_mfc0)
I_u1u2u3(_mtc0)
I_u2u1u3(_ori)
R
Ralf Baechle 已提交
421
I_u3u1u2(_or)
422 423 424 425 426 427 428
I_0(_rfe)
I_u2s3u1(_sc)
I_u2s3u1(_scd)
I_u2s3u1(_sd)
I_u2u1u3(_sll)
I_u2u1u3(_sra)
I_u2u1u3(_srl)
D
David Daney 已提交
429
I_u2u1u3(_rotr)
430 431 432
I_u3u1u2(_subu)
I_u2s3u1(_sw)
I_0(_tlbp)
D
David Daney 已提交
433
I_0(_tlbr)
434 435 436 437
I_0(_tlbwi)
I_0(_tlbwr)
I_u3u1u2(_xor)
I_u2u1u3(_xori)
438
I_u2u1msbu3(_dins);
D
David Daney 已提交
439
I_u2u1msb32u3(_dinsm);
D
David Daney 已提交
440
I_u1(_syscall);
441 442
I_u1u2s3(_bbit0);
I_u1u2s3(_bbit1);
443 444
I_u3u1u2(_lwx)
I_u3u1u2(_ldx)
445

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
#ifdef CONFIG_CPU_CAVIUM_OCTEON
#include <asm/octeon/octeon.h>
void __uasminit uasm_i_pref(u32 **buf, unsigned int a, signed int b,
			    unsigned int c)
{
	if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X) && a <= 24 && a != 5)
		/*
		 * As per erratum Core-14449, replace prefetches 0-4,
		 * 6-24 with 'pref 28'.
		 */
		build_insn(buf, insn_pref, c, 28, b);
	else
		build_insn(buf, insn_pref, c, a, b);
}
UASM_EXPORT_SYMBOL(uasm_i_pref);
#else
I_u2s3u1(_pref)
#endif

465
/* Handle labels. */
466
void __uasminit uasm_build_label(struct uasm_label **lab, u32 *addr, int lid)
467 468 469 470 471
{
	(*lab)->addr = addr;
	(*lab)->lab = lid;
	(*lab)++;
}
472
UASM_EXPORT_SYMBOL(uasm_build_label);
473

474
int __uasminit uasm_in_compat_space_p(long addr)
475 476 477 478 479 480 481 482
{
	/* Is this address in 32bit compat space? */
#ifdef CONFIG_64BIT
	return (((addr) & 0xffffffff00000000L) == 0xffffffff00000000L);
#else
	return 1;
#endif
}
483
UASM_EXPORT_SYMBOL(uasm_in_compat_space_p);
484

485
static int __uasminit uasm_rel_highest(long val)
486 487 488 489 490 491 492 493
{
#ifdef CONFIG_64BIT
	return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000;
#else
	return 0;
#endif
}

494
static int __uasminit uasm_rel_higher(long val)
495 496 497 498 499 500 501 502
{
#ifdef CONFIG_64BIT
	return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000;
#else
	return 0;
#endif
}

503
int __uasminit uasm_rel_hi(long val)
504 505 506
{
	return ((((val + 0x8000L) >> 16) & 0xffff) ^ 0x8000) - 0x8000;
}
507
UASM_EXPORT_SYMBOL(uasm_rel_hi);
508

509
int __uasminit uasm_rel_lo(long val)
510 511 512
{
	return ((val & 0xffff) ^ 0x8000) - 0x8000;
}
513
UASM_EXPORT_SYMBOL(uasm_rel_lo);
514

515
void __uasminit UASM_i_LA_mostly(u32 **buf, unsigned int rs, long addr)
516 517 518 519 520 521 522 523 524 525 526 527 528 529
{
	if (!uasm_in_compat_space_p(addr)) {
		uasm_i_lui(buf, rs, uasm_rel_highest(addr));
		if (uasm_rel_higher(addr))
			uasm_i_daddiu(buf, rs, rs, uasm_rel_higher(addr));
		if (uasm_rel_hi(addr)) {
			uasm_i_dsll(buf, rs, rs, 16);
			uasm_i_daddiu(buf, rs, rs, uasm_rel_hi(addr));
			uasm_i_dsll(buf, rs, rs, 16);
		} else
			uasm_i_dsll32(buf, rs, rs, 0);
	} else
		uasm_i_lui(buf, rs, uasm_rel_hi(addr));
}
530
UASM_EXPORT_SYMBOL(UASM_i_LA_mostly);
531

532
void __uasminit UASM_i_LA(u32 **buf, unsigned int rs, long addr)
533 534 535 536 537 538 539 540 541
{
	UASM_i_LA_mostly(buf, rs, addr);
	if (uasm_rel_lo(addr)) {
		if (!uasm_in_compat_space_p(addr))
			uasm_i_daddiu(buf, rs, rs, uasm_rel_lo(addr));
		else
			uasm_i_addiu(buf, rs, rs, uasm_rel_lo(addr));
	}
}
542
UASM_EXPORT_SYMBOL(UASM_i_LA);
543 544

/* Handle relocations. */
545
void __uasminit
546 547 548 549 550 551 552
uasm_r_mips_pc16(struct uasm_reloc **rel, u32 *addr, int lid)
{
	(*rel)->addr = addr;
	(*rel)->type = R_MIPS_PC16;
	(*rel)->lab = lid;
	(*rel)++;
}
553
UASM_EXPORT_SYMBOL(uasm_r_mips_pc16);
554

555
static inline void __uasminit
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
__resolve_relocs(struct uasm_reloc *rel, struct uasm_label *lab)
{
	long laddr = (long)lab->addr;
	long raddr = (long)rel->addr;

	switch (rel->type) {
	case R_MIPS_PC16:
		*rel->addr |= build_bimm(laddr - (raddr + 4));
		break;

	default:
		panic("Unsupported Micro-assembler relocation %d",
		      rel->type);
	}
}

572
void __uasminit
573 574 575 576 577 578 579 580 581
uasm_resolve_relocs(struct uasm_reloc *rel, struct uasm_label *lab)
{
	struct uasm_label *l;

	for (; rel->lab != UASM_LABEL_INVALID; rel++)
		for (l = lab; l->lab != UASM_LABEL_INVALID; l++)
			if (rel->lab == l->lab)
				__resolve_relocs(rel, l);
}
582
UASM_EXPORT_SYMBOL(uasm_resolve_relocs);
583

584
void __uasminit
585 586 587 588 589 590
uasm_move_relocs(struct uasm_reloc *rel, u32 *first, u32 *end, long off)
{
	for (; rel->lab != UASM_LABEL_INVALID; rel++)
		if (rel->addr >= first && rel->addr < end)
			rel->addr += off;
}
591
UASM_EXPORT_SYMBOL(uasm_move_relocs);
592

593
void __uasminit
594 595 596 597 598 599
uasm_move_labels(struct uasm_label *lab, u32 *first, u32 *end, long off)
{
	for (; lab->lab != UASM_LABEL_INVALID; lab++)
		if (lab->addr >= first && lab->addr < end)
			lab->addr += off;
}
600
UASM_EXPORT_SYMBOL(uasm_move_labels);
601

602
void __uasminit
603 604 605 606 607 608 609 610 611 612
uasm_copy_handler(struct uasm_reloc *rel, struct uasm_label *lab, u32 *first,
		  u32 *end, u32 *target)
{
	long off = (long)(target - first);

	memcpy(target, first, (end - first) * sizeof(u32));

	uasm_move_relocs(rel, first, end, off);
	uasm_move_labels(lab, first, end, off);
}
613
UASM_EXPORT_SYMBOL(uasm_copy_handler);
614

615
int __uasminit uasm_insn_has_bdelay(struct uasm_reloc *rel, u32 *addr)
616 617 618 619 620 621 622 623 624 625
{
	for (; rel->lab != UASM_LABEL_INVALID; rel++) {
		if (rel->addr == addr
		    && (rel->type == R_MIPS_PC16
			|| rel->type == R_MIPS_26))
			return 1;
	}

	return 0;
}
626
UASM_EXPORT_SYMBOL(uasm_insn_has_bdelay);
627 628

/* Convenience functions for labeled branches. */
629
void __uasminit
630 631 632 633 634
uasm_il_bltz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bltz(p, reg, 0);
}
635
UASM_EXPORT_SYMBOL(uasm_il_bltz);
636

637
void __uasminit
638 639 640 641 642
uasm_il_b(u32 **p, struct uasm_reloc **r, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_b(p, 0);
}
643
UASM_EXPORT_SYMBOL(uasm_il_b);
644

645
void __uasminit
646 647 648 649 650
uasm_il_beqz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_beqz(p, reg, 0);
}
651
UASM_EXPORT_SYMBOL(uasm_il_beqz);
652

653
void __uasminit
654 655 656 657 658
uasm_il_beqzl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_beqzl(p, reg, 0);
}
659
UASM_EXPORT_SYMBOL(uasm_il_beqzl);
660

661
void __uasminit
662 663 664 665 666 667
uasm_il_bne(u32 **p, struct uasm_reloc **r, unsigned int reg1,
	unsigned int reg2, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bne(p, reg1, reg2, 0);
}
668
UASM_EXPORT_SYMBOL(uasm_il_bne);
669

670
void __uasminit
671 672 673 674 675
uasm_il_bnez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bnez(p, reg, 0);
}
676
UASM_EXPORT_SYMBOL(uasm_il_bnez);
677

678
void __uasminit
679 680 681 682 683
uasm_il_bgezl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bgezl(p, reg, 0);
}
684
UASM_EXPORT_SYMBOL(uasm_il_bgezl);
685

686
void __uasminit
687 688 689 690 691
uasm_il_bgez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bgez(p, reg, 0);
}
692
UASM_EXPORT_SYMBOL(uasm_il_bgez);
693

694
void __uasminit
695 696 697 698 699 700
uasm_il_bbit0(u32 **p, struct uasm_reloc **r, unsigned int reg,
	      unsigned int bit, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bbit0(p, reg, bit, 0);
}
701
UASM_EXPORT_SYMBOL(uasm_il_bbit0);
702

703
void __uasminit
704 705 706 707 708 709
uasm_il_bbit1(u32 **p, struct uasm_reloc **r, unsigned int reg,
	      unsigned int bit, int lid)
{
	uasm_r_mips_pc16(r, *p, lid);
	uasm_i_bbit1(p, reg, bit, 0);
}
710
UASM_EXPORT_SYMBOL(uasm_il_bbit1);