linux/arch/mips/mm/uasm.c
<<
>>
Prefs
   1/*
   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
   4 * for more details.
   5 *
   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.
   9 *
  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 */
  14
  15#include <linux/kernel.h>
  16#include <linux/types.h>
  17#include <linux/init.h>
  18
  19#include <asm/inst.h>
  20#include <asm/elf.h>
  21#include <asm/bugs.h>
  22#include <asm/uasm.h>
  23
  24enum fields {
  25        RS = 0x001,
  26        RT = 0x002,
  27        RD = 0x004,
  28        RE = 0x008,
  29        SIMM = 0x010,
  30        UIMM = 0x020,
  31        BIMM = 0x040,
  32        JIMM = 0x080,
  33        FUNC = 0x100,
  34        SET = 0x200,
  35        SCIMM = 0x400
  36};
  37
  38#define OP_MASK         0x3f
  39#define OP_SH           26
  40#define RS_MASK         0x1f
  41#define RS_SH           21
  42#define RT_MASK         0x1f
  43#define RT_SH           16
  44#define RD_MASK         0x1f
  45#define RD_SH           11
  46#define RE_MASK         0x1f
  47#define RE_SH           6
  48#define IMM_MASK        0xffff
  49#define IMM_SH          0
  50#define JIMM_MASK       0x3ffffff
  51#define JIMM_SH         0
  52#define FUNC_MASK       0x3f
  53#define FUNC_SH         0
  54#define SET_MASK        0x7
  55#define SET_SH          0
  56#define SCIMM_MASK      0xfffff
  57#define SCIMM_SH        6
  58
  59enum opcode {
  60        insn_invalid,
  61        insn_addu, insn_addiu, insn_and, insn_andi, insn_beq,
  62        insn_beql, insn_bgez, insn_bgezl, insn_bltz, insn_bltzl,
  63        insn_bne, insn_cache, insn_daddu, insn_daddiu, insn_dmfc0,
  64        insn_dmtc0, insn_dsll, insn_dsll32, insn_dsra, insn_dsrl,
  65        insn_dsrl32, insn_drotr, insn_drotr32, insn_dsubu, insn_eret,
  66        insn_j, insn_jal, insn_jr, insn_ld, insn_ll, insn_lld,
  67        insn_lui, insn_lw, insn_mfc0, insn_mtc0, insn_or, insn_ori,
  68        insn_pref, insn_rfe, insn_sc, insn_scd, insn_sd, insn_sll,
  69        insn_sra, insn_srl, insn_rotr, insn_subu, insn_sw, insn_tlbp,
  70        insn_tlbr, insn_tlbwi, insn_tlbwr, insn_xor, insn_xori,
  71        insn_dins, insn_dinsm, insn_syscall, insn_bbit0, insn_bbit1,
  72        insn_lwx, insn_ldx
  73};
  74
  75struct insn {
  76        enum opcode opcode;
  77        u32 match;
  78        enum fields fields;
  79};
  80
  81/* This macro sets the non-variable bits of an instruction. */
  82#define M(a, b, c, d, e, f)                                     \
  83        ((a) << OP_SH                                           \
  84         | (b) << RS_SH                                         \
  85         | (c) << RT_SH                                         \
  86         | (d) << RD_SH                                         \
  87         | (e) << RE_SH                                         \
  88         | (f) << FUNC_SH)
  89
  90static struct insn insn_table[] __uasminitdata = {
  91        { insn_addiu, M(addiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
  92        { insn_addu, M(spec_op, 0, 0, 0, 0, addu_op), RS | RT | RD },
  93        { insn_and, M(spec_op, 0, 0, 0, 0, and_op), RS | RT | RD },
  94        { insn_andi, M(andi_op, 0, 0, 0, 0, 0), RS | RT | UIMM },
  95        { insn_beq, M(beq_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_bgez, M(bcond_op, 0, bgez_op, 0, 0, 0), RS | BIMM },
  98        { insn_bgezl, M(bcond_op, 0, bgezl_op, 0, 0, 0), RS | BIMM },
  99        { insn_bltz, M(bcond_op, 0, bltz_op, 0, 0, 0), RS | BIMM },
 100        { insn_bltzl, M(bcond_op, 0, bltzl_op, 0, 0, 0), RS | BIMM },
 101        { insn_bne, M(bne_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
 102        { insn_cache,  M(cache_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 103        { insn_daddiu, M(daddiu_op, 0, 0, 0, 0, 0), RS | RT | SIMM },
 104        { insn_daddu, M(spec_op, 0, 0, 0, 0, daddu_op), RS | RT | RD },
 105        { insn_dmfc0, M(cop0_op, dmfc_op, 0, 0, 0, 0), RT | RD | SET},
 106        { insn_dmtc0, M(cop0_op, dmtc_op, 0, 0, 0, 0), RT | RD | SET},
 107        { insn_dsll, M(spec_op, 0, 0, 0, 0, dsll_op), RT | RD | RE },
 108        { insn_dsll32, M(spec_op, 0, 0, 0, 0, dsll32_op), RT | RD | RE },
 109        { insn_dsra, M(spec_op, 0, 0, 0, 0, dsra_op), RT | RD | RE },
 110        { insn_dsrl, M(spec_op, 0, 0, 0, 0, dsrl_op), RT | RD | RE },
 111        { insn_dsrl32, M(spec_op, 0, 0, 0, 0, dsrl32_op), RT | RD | RE },
 112        { insn_drotr, M(spec_op, 1, 0, 0, 0, dsrl_op), RT | RD | RE },
 113        { insn_drotr32, M(spec_op, 1, 0, 0, 0, dsrl32_op), RT | RD | RE },
 114        { insn_dsubu, M(spec_op, 0, 0, 0, 0, dsubu_op), RS | RT | RD },
 115        { insn_eret,  M(cop0_op, cop_op, 0, 0, 0, eret_op),  0 },
 116        { insn_j,  M(j_op, 0, 0, 0, 0, 0),  JIMM },
 117        { insn_jal,  M(jal_op, 0, 0, 0, 0, 0),  JIMM },
 118        { insn_jr,  M(spec_op, 0, 0, 0, 0, jr_op),  RS },
 119        { insn_ld,  M(ld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 120        { insn_ll,  M(ll_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 121        { insn_lld,  M(lld_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 122        { insn_lui,  M(lui_op, 0, 0, 0, 0, 0),  RT | SIMM },
 123        { insn_lw,  M(lw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 124        { insn_mfc0,  M(cop0_op, mfc_op, 0, 0, 0, 0),  RT | RD | SET},
 125        { insn_mtc0,  M(cop0_op, mtc_op, 0, 0, 0, 0),  RT | RD | SET},
 126        { insn_or,  M(spec_op, 0, 0, 0, 0, or_op),  RS | RT | RD },
 127        { insn_ori,  M(ori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
 128        { insn_pref,  M(pref_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 129        { insn_rfe,  M(cop0_op, cop_op, 0, 0, 0, rfe_op),  0 },
 130        { insn_sc,  M(sc_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 131        { insn_scd,  M(scd_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 132        { insn_sd,  M(sd_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 133        { insn_sll,  M(spec_op, 0, 0, 0, 0, sll_op),  RT | RD | RE },
 134        { insn_sra,  M(spec_op, 0, 0, 0, 0, sra_op),  RT | RD | RE },
 135        { insn_srl,  M(spec_op, 0, 0, 0, 0, srl_op),  RT | RD | RE },
 136        { insn_rotr,  M(spec_op, 1, 0, 0, 0, srl_op),  RT | RD | RE },
 137        { insn_subu,  M(spec_op, 0, 0, 0, 0, subu_op),  RS | RT | RD },
 138        { insn_sw,  M(sw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM },
 139        { insn_tlbp,  M(cop0_op, cop_op, 0, 0, 0, tlbp_op),  0 },
 140        { insn_tlbr,  M(cop0_op, cop_op, 0, 0, 0, tlbr_op),  0 },
 141        { insn_tlbwi,  M(cop0_op, cop_op, 0, 0, 0, tlbwi_op),  0 },
 142        { insn_tlbwr,  M(cop0_op, cop_op, 0, 0, 0, tlbwr_op),  0 },
 143        { insn_xor,  M(spec_op, 0, 0, 0, 0, xor_op),  RS | RT | RD },
 144        { insn_xori,  M(xori_op, 0, 0, 0, 0, 0),  RS | RT | UIMM },
 145        { insn_dins, M(spec3_op, 0, 0, 0, 0, dins_op), RS | RT | RD | RE },
 146        { insn_dinsm, M(spec3_op, 0, 0, 0, 0, dinsm_op), RS | RT | RD | RE },
 147        { insn_syscall, M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
 148        { insn_bbit0, M(lwc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
 149        { insn_bbit1, M(swc2_op, 0, 0, 0, 0, 0), RS | RT | BIMM },
 150        { insn_lwx, M(spec3_op, 0, 0, 0, lwx_op, lx_op), RS | RT | RD },
 151        { insn_ldx, M(spec3_op, 0, 0, 0, ldx_op, lx_op), RS | RT | RD },
 152        { insn_invalid, 0, 0 }
 153};
 154
 155#undef M
 156
 157static inline __uasminit u32 build_rs(u32 arg)
 158{
 159        WARN(arg & ~RS_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 160
 161        return (arg & RS_MASK) << RS_SH;
 162}
 163
 164static inline __uasminit u32 build_rt(u32 arg)
 165{
 166        WARN(arg & ~RT_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 167
 168        return (arg & RT_MASK) << RT_SH;
 169}
 170
 171static inline __uasminit u32 build_rd(u32 arg)
 172{
 173        WARN(arg & ~RD_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 174
 175        return (arg & RD_MASK) << RD_SH;
 176}
 177
 178static inline __uasminit u32 build_re(u32 arg)
 179{
 180        WARN(arg & ~RE_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 181
 182        return (arg & RE_MASK) << RE_SH;
 183}
 184
 185static inline __uasminit u32 build_simm(s32 arg)
 186{
 187        WARN(arg > 0x7fff || arg < -0x8000,
 188             KERN_WARNING "Micro-assembler field overflow\n");
 189
 190        return arg & 0xffff;
 191}
 192
 193static inline __uasminit u32 build_uimm(u32 arg)
 194{
 195        WARN(arg & ~IMM_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 196
 197        return arg & IMM_MASK;
 198}
 199
 200static inline __uasminit u32 build_bimm(s32 arg)
 201{
 202        WARN(arg > 0x1ffff || arg < -0x20000,
 203             KERN_WARNING "Micro-assembler field overflow\n");
 204
 205        WARN(arg & 0x3, KERN_WARNING "Invalid micro-assembler branch target\n");
 206
 207        return ((arg < 0) ? (1 << 15) : 0) | ((arg >> 2) & 0x7fff);
 208}
 209
 210static inline __uasminit u32 build_jimm(u32 arg)
 211{
 212        WARN(arg & ~(JIMM_MASK << 2),
 213             KERN_WARNING "Micro-assembler field overflow\n");
 214
 215        return (arg >> 2) & JIMM_MASK;
 216}
 217
 218static inline __uasminit u32 build_scimm(u32 arg)
 219{
 220        WARN(arg & ~SCIMM_MASK,
 221             KERN_WARNING "Micro-assembler field overflow\n");
 222
 223        return (arg & SCIMM_MASK) << SCIMM_SH;
 224}
 225
 226static inline __uasminit u32 build_func(u32 arg)
 227{
 228        WARN(arg & ~FUNC_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 229
 230        return arg & FUNC_MASK;
 231}
 232
 233static inline __uasminit u32 build_set(u32 arg)
 234{
 235        WARN(arg & ~SET_MASK, KERN_WARNING "Micro-assembler field overflow\n");
 236
 237        return arg & SET_MASK;
 238}
 239
 240/*
 241 * The order of opcode arguments is implicitly left to right,
 242 * starting with RS and ending with FUNC or IMM.
 243 */
 244static void __uasminit build_insn(u32 **buf, enum opcode opc, ...)
 245{
 246        struct insn *ip = NULL;
 247        unsigned int i;
 248        va_list ap;
 249        u32 op;
 250
 251        for (i = 0; insn_table[i].opcode != insn_invalid; i++)
 252                if (insn_table[i].opcode == opc) {
 253                        ip = &insn_table[i];
 254                        break;
 255                }
 256
 257        if (!ip || (opc == insn_daddiu && r4k_daddiu_bug()))
 258                panic("Unsupported Micro-assembler instruction %d", opc);
 259
 260        op = ip->match;
 261        va_start(ap, opc);
 262        if (ip->fields & RS)
 263                op |= build_rs(va_arg(ap, u32));
 264        if (ip->fields & RT)
 265                op |= build_rt(va_arg(ap, u32));
 266        if (ip->fields & RD)
 267                op |= build_rd(va_arg(ap, u32));
 268        if (ip->fields & RE)
 269                op |= build_re(va_arg(ap, u32));
 270        if (ip->fields & SIMM)
 271                op |= build_simm(va_arg(ap, s32));
 272        if (ip->fields & UIMM)
 273                op |= build_uimm(va_arg(ap, u32));
 274        if (ip->fields & BIMM)
 275                op |= build_bimm(va_arg(ap, s32));
 276        if (ip->fields & JIMM)
 277                op |= build_jimm(va_arg(ap, u32));
 278        if (ip->fields & FUNC)
 279                op |= build_func(va_arg(ap, u32));
 280        if (ip->fields & SET)
 281                op |= build_set(va_arg(ap, u32));
 282        if (ip->fields & SCIMM)
 283                op |= build_scimm(va_arg(ap, u32));
 284        va_end(ap);
 285
 286        **buf = op;
 287        (*buf)++;
 288}
 289
 290#define I_u1u2u3(op)                                    \
 291Ip_u1u2u3(op)                                           \
 292{                                                       \
 293        build_insn(buf, insn##op, a, b, c);             \
 294}                                                       \
 295UASM_EXPORT_SYMBOL(uasm_i##op);
 296
 297#define I_u2u1u3(op)                                    \
 298Ip_u2u1u3(op)                                           \
 299{                                                       \
 300        build_insn(buf, insn##op, b, a, c);             \
 301}                                                       \
 302UASM_EXPORT_SYMBOL(uasm_i##op);
 303
 304#define I_u3u1u2(op)                                    \
 305Ip_u3u1u2(op)                                           \
 306{                                                       \
 307        build_insn(buf, insn##op, b, c, a);             \
 308}                                                       \
 309UASM_EXPORT_SYMBOL(uasm_i##op);
 310
 311#define I_u1u2s3(op)                                    \
 312Ip_u1u2s3(op)                                           \
 313{                                                       \
 314        build_insn(buf, insn##op, a, b, c);             \
 315}                                                       \
 316UASM_EXPORT_SYMBOL(uasm_i##op);
 317
 318#define I_u2s3u1(op)                                    \
 319Ip_u2s3u1(op)                                           \
 320{                                                       \
 321        build_insn(buf, insn##op, c, a, b);             \
 322}                                                       \
 323UASM_EXPORT_SYMBOL(uasm_i##op);
 324
 325#define I_u2u1s3(op)                                    \
 326Ip_u2u1s3(op)                                           \
 327{                                                       \
 328        build_insn(buf, insn##op, b, a, c);             \
 329}                                                       \
 330UASM_EXPORT_SYMBOL(uasm_i##op);
 331
 332#define I_u2u1msbu3(op)                                 \
 333Ip_u2u1msbu3(op)                                        \
 334{                                                       \
 335        build_insn(buf, insn##op, b, a, c+d-1, c);      \
 336}                                                       \
 337UASM_EXPORT_SYMBOL(uasm_i##op);
 338
 339#define I_u2u1msb32u3(op)                               \
 340Ip_u2u1msbu3(op)                                        \
 341{                                                       \
 342        build_insn(buf, insn##op, b, a, c+d-33, c);     \
 343}                                                       \
 344UASM_EXPORT_SYMBOL(uasm_i##op);
 345
 346#define I_u1u2(op)                                      \
 347Ip_u1u2(op)                                             \
 348{                                                       \
 349        build_insn(buf, insn##op, a, b);                \
 350}                                                       \
 351UASM_EXPORT_SYMBOL(uasm_i##op);
 352
 353#define I_u1s2(op)                                      \
 354Ip_u1s2(op)                                             \
 355{                                                       \
 356        build_insn(buf, insn##op, a, b);                \
 357}                                                       \
 358UASM_EXPORT_SYMBOL(uasm_i##op);
 359
 360#define I_u1(op)                                        \
 361Ip_u1(op)                                               \
 362{                                                       \
 363        build_insn(buf, insn##op, a);                   \
 364}                                                       \
 365UASM_EXPORT_SYMBOL(uasm_i##op);
 366
 367#define I_0(op)                                         \
 368Ip_0(op)                                                \
 369{                                                       \
 370        build_insn(buf, insn##op);                      \
 371}                                                       \
 372UASM_EXPORT_SYMBOL(uasm_i##op);
 373
 374I_u2u1s3(_addiu)
 375I_u3u1u2(_addu)
 376I_u2u1u3(_andi)
 377I_u3u1u2(_and)
 378I_u1u2s3(_beq)
 379I_u1u2s3(_beql)
 380I_u1s2(_bgez)
 381I_u1s2(_bgezl)
 382I_u1s2(_bltz)
 383I_u1s2(_bltzl)
 384I_u1u2s3(_bne)
 385I_u2s3u1(_cache)
 386I_u1u2u3(_dmfc0)
 387I_u1u2u3(_dmtc0)
 388I_u2u1s3(_daddiu)
 389I_u3u1u2(_daddu)
 390I_u2u1u3(_dsll)
 391I_u2u1u3(_dsll32)
 392I_u2u1u3(_dsra)
 393I_u2u1u3(_dsrl)
 394I_u2u1u3(_dsrl32)
 395I_u2u1u3(_drotr)
 396I_u2u1u3(_drotr32)
 397I_u3u1u2(_dsubu)
 398I_0(_eret)
 399I_u1(_j)
 400I_u1(_jal)
 401I_u1(_jr)
 402I_u2s3u1(_ld)
 403I_u2s3u1(_ll)
 404I_u2s3u1(_lld)
 405I_u1s2(_lui)
 406I_u2s3u1(_lw)
 407I_u1u2u3(_mfc0)
 408I_u1u2u3(_mtc0)
 409I_u2u1u3(_ori)
 410I_u3u1u2(_or)
 411I_0(_rfe)
 412I_u2s3u1(_sc)
 413I_u2s3u1(_scd)
 414I_u2s3u1(_sd)
 415I_u2u1u3(_sll)
 416I_u2u1u3(_sra)
 417I_u2u1u3(_srl)
 418I_u2u1u3(_rotr)
 419I_u3u1u2(_subu)
 420I_u2s3u1(_sw)
 421I_0(_tlbp)
 422I_0(_tlbr)
 423I_0(_tlbwi)
 424I_0(_tlbwr)
 425I_u3u1u2(_xor)
 426I_u2u1u3(_xori)
 427I_u2u1msbu3(_dins);
 428I_u2u1msb32u3(_dinsm);
 429I_u1(_syscall);
 430I_u1u2s3(_bbit0);
 431I_u1u2s3(_bbit1);
 432I_u3u1u2(_lwx)
 433I_u3u1u2(_ldx)
 434
 435#ifdef CONFIG_CPU_CAVIUM_OCTEON
 436#include <asm/octeon/octeon.h>
 437void __uasminit uasm_i_pref(u32 **buf, unsigned int a, signed int b,
 438                            unsigned int c)
 439{
 440        if (OCTEON_IS_MODEL(OCTEON_CN63XX_PASS1_X) && a <= 24 && a != 5)
 441                /*
 442                 * As per erratum Core-14449, replace prefetches 0-4,
 443                 * 6-24 with 'pref 28'.
 444                 */
 445                build_insn(buf, insn_pref, c, 28, b);
 446        else
 447                build_insn(buf, insn_pref, c, a, b);
 448}
 449UASM_EXPORT_SYMBOL(uasm_i_pref);
 450#else
 451I_u2s3u1(_pref)
 452#endif
 453
 454/* Handle labels. */
 455void __uasminit uasm_build_label(struct uasm_label **lab, u32 *addr, int lid)
 456{
 457        (*lab)->addr = addr;
 458        (*lab)->lab = lid;
 459        (*lab)++;
 460}
 461UASM_EXPORT_SYMBOL(uasm_build_label);
 462
 463int __uasminit uasm_in_compat_space_p(long addr)
 464{
 465        /* Is this address in 32bit compat space? */
 466#ifdef CONFIG_64BIT
 467        return (((addr) & 0xffffffff00000000L) == 0xffffffff00000000L);
 468#else
 469        return 1;
 470#endif
 471}
 472UASM_EXPORT_SYMBOL(uasm_in_compat_space_p);
 473
 474static int __uasminit uasm_rel_highest(long val)
 475{
 476#ifdef CONFIG_64BIT
 477        return ((((val + 0x800080008000L) >> 48) & 0xffff) ^ 0x8000) - 0x8000;
 478#else
 479        return 0;
 480#endif
 481}
 482
 483static int __uasminit uasm_rel_higher(long val)
 484{
 485#ifdef CONFIG_64BIT
 486        return ((((val + 0x80008000L) >> 32) & 0xffff) ^ 0x8000) - 0x8000;
 487#else
 488        return 0;
 489#endif
 490}
 491
 492int __uasminit uasm_rel_hi(long val)
 493{
 494        return ((((val + 0x8000L) >> 16) & 0xffff) ^ 0x8000) - 0x8000;
 495}
 496UASM_EXPORT_SYMBOL(uasm_rel_hi);
 497
 498int __uasminit uasm_rel_lo(long val)
 499{
 500        return ((val & 0xffff) ^ 0x8000) - 0x8000;
 501}
 502UASM_EXPORT_SYMBOL(uasm_rel_lo);
 503
 504void __uasminit UASM_i_LA_mostly(u32 **buf, unsigned int rs, long addr)
 505{
 506        if (!uasm_in_compat_space_p(addr)) {
 507                uasm_i_lui(buf, rs, uasm_rel_highest(addr));
 508                if (uasm_rel_higher(addr))
 509                        uasm_i_daddiu(buf, rs, rs, uasm_rel_higher(addr));
 510                if (uasm_rel_hi(addr)) {
 511                        uasm_i_dsll(buf, rs, rs, 16);
 512                        uasm_i_daddiu(buf, rs, rs, uasm_rel_hi(addr));
 513                        uasm_i_dsll(buf, rs, rs, 16);
 514                } else
 515                        uasm_i_dsll32(buf, rs, rs, 0);
 516        } else
 517                uasm_i_lui(buf, rs, uasm_rel_hi(addr));
 518}
 519UASM_EXPORT_SYMBOL(UASM_i_LA_mostly);
 520
 521void __uasminit UASM_i_LA(u32 **buf, unsigned int rs, long addr)
 522{
 523        UASM_i_LA_mostly(buf, rs, addr);
 524        if (uasm_rel_lo(addr)) {
 525                if (!uasm_in_compat_space_p(addr))
 526                        uasm_i_daddiu(buf, rs, rs, uasm_rel_lo(addr));
 527                else
 528                        uasm_i_addiu(buf, rs, rs, uasm_rel_lo(addr));
 529        }
 530}
 531UASM_EXPORT_SYMBOL(UASM_i_LA);
 532
 533/* Handle relocations. */
 534void __uasminit
 535uasm_r_mips_pc16(struct uasm_reloc **rel, u32 *addr, int lid)
 536{
 537        (*rel)->addr = addr;
 538        (*rel)->type = R_MIPS_PC16;
 539        (*rel)->lab = lid;
 540        (*rel)++;
 541}
 542UASM_EXPORT_SYMBOL(uasm_r_mips_pc16);
 543
 544static inline void __uasminit
 545__resolve_relocs(struct uasm_reloc *rel, struct uasm_label *lab)
 546{
 547        long laddr = (long)lab->addr;
 548        long raddr = (long)rel->addr;
 549
 550        switch (rel->type) {
 551        case R_MIPS_PC16:
 552                *rel->addr |= build_bimm(laddr - (raddr + 4));
 553                break;
 554
 555        default:
 556                panic("Unsupported Micro-assembler relocation %d",
 557                      rel->type);
 558        }
 559}
 560
 561void __uasminit
 562uasm_resolve_relocs(struct uasm_reloc *rel, struct uasm_label *lab)
 563{
 564        struct uasm_label *l;
 565
 566        for (; rel->lab != UASM_LABEL_INVALID; rel++)
 567                for (l = lab; l->lab != UASM_LABEL_INVALID; l++)
 568                        if (rel->lab == l->lab)
 569                                __resolve_relocs(rel, l);
 570}
 571UASM_EXPORT_SYMBOL(uasm_resolve_relocs);
 572
 573void __uasminit
 574uasm_move_relocs(struct uasm_reloc *rel, u32 *first, u32 *end, long off)
 575{
 576        for (; rel->lab != UASM_LABEL_INVALID; rel++)
 577                if (rel->addr >= first && rel->addr < end)
 578                        rel->addr += off;
 579}
 580UASM_EXPORT_SYMBOL(uasm_move_relocs);
 581
 582void __uasminit
 583uasm_move_labels(struct uasm_label *lab, u32 *first, u32 *end, long off)
 584{
 585        for (; lab->lab != UASM_LABEL_INVALID; lab++)
 586                if (lab->addr >= first && lab->addr < end)
 587                        lab->addr += off;
 588}
 589UASM_EXPORT_SYMBOL(uasm_move_labels);
 590
 591void __uasminit
 592uasm_copy_handler(struct uasm_reloc *rel, struct uasm_label *lab, u32 *first,
 593                  u32 *end, u32 *target)
 594{
 595        long off = (long)(target - first);
 596
 597        memcpy(target, first, (end - first) * sizeof(u32));
 598
 599        uasm_move_relocs(rel, first, end, off);
 600        uasm_move_labels(lab, first, end, off);
 601}
 602UASM_EXPORT_SYMBOL(uasm_copy_handler);
 603
 604int __uasminit uasm_insn_has_bdelay(struct uasm_reloc *rel, u32 *addr)
 605{
 606        for (; rel->lab != UASM_LABEL_INVALID; rel++) {
 607                if (rel->addr == addr
 608                    && (rel->type == R_MIPS_PC16
 609                        || rel->type == R_MIPS_26))
 610                        return 1;
 611        }
 612
 613        return 0;
 614}
 615UASM_EXPORT_SYMBOL(uasm_insn_has_bdelay);
 616
 617/* Convenience functions for labeled branches. */
 618void __uasminit
 619uasm_il_bltz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 620{
 621        uasm_r_mips_pc16(r, *p, lid);
 622        uasm_i_bltz(p, reg, 0);
 623}
 624UASM_EXPORT_SYMBOL(uasm_il_bltz);
 625
 626void __uasminit
 627uasm_il_b(u32 **p, struct uasm_reloc **r, int lid)
 628{
 629        uasm_r_mips_pc16(r, *p, lid);
 630        uasm_i_b(p, 0);
 631}
 632UASM_EXPORT_SYMBOL(uasm_il_b);
 633
 634void __uasminit
 635uasm_il_beqz(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 636{
 637        uasm_r_mips_pc16(r, *p, lid);
 638        uasm_i_beqz(p, reg, 0);
 639}
 640UASM_EXPORT_SYMBOL(uasm_il_beqz);
 641
 642void __uasminit
 643uasm_il_beqzl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 644{
 645        uasm_r_mips_pc16(r, *p, lid);
 646        uasm_i_beqzl(p, reg, 0);
 647}
 648UASM_EXPORT_SYMBOL(uasm_il_beqzl);
 649
 650void __uasminit
 651uasm_il_bne(u32 **p, struct uasm_reloc **r, unsigned int reg1,
 652        unsigned int reg2, int lid)
 653{
 654        uasm_r_mips_pc16(r, *p, lid);
 655        uasm_i_bne(p, reg1, reg2, 0);
 656}
 657UASM_EXPORT_SYMBOL(uasm_il_bne);
 658
 659void __uasminit
 660uasm_il_bnez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 661{
 662        uasm_r_mips_pc16(r, *p, lid);
 663        uasm_i_bnez(p, reg, 0);
 664}
 665UASM_EXPORT_SYMBOL(uasm_il_bnez);
 666
 667void __uasminit
 668uasm_il_bgezl(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 669{
 670        uasm_r_mips_pc16(r, *p, lid);
 671        uasm_i_bgezl(p, reg, 0);
 672}
 673UASM_EXPORT_SYMBOL(uasm_il_bgezl);
 674
 675void __uasminit
 676uasm_il_bgez(u32 **p, struct uasm_reloc **r, unsigned int reg, int lid)
 677{
 678        uasm_r_mips_pc16(r, *p, lid);
 679        uasm_i_bgez(p, reg, 0);
 680}
 681UASM_EXPORT_SYMBOL(uasm_il_bgez);
 682
 683void __uasminit
 684uasm_il_bbit0(u32 **p, struct uasm_reloc **r, unsigned int reg,
 685              unsigned int bit, int lid)
 686{
 687        uasm_r_mips_pc16(r, *p, lid);
 688        uasm_i_bbit0(p, reg, bit, 0);
 689}
 690UASM_EXPORT_SYMBOL(uasm_il_bbit0);
 691
 692void __uasminit
 693uasm_il_bbit1(u32 **p, struct uasm_reloc **r, unsigned int reg,
 694              unsigned int bit, int lid)
 695{
 696        uasm_r_mips_pc16(r, *p, lid);
 697        uasm_i_bbit1(p, reg, bit, 0);
 698}
 699UASM_EXPORT_SYMBOL(uasm_il_bbit1);
 700