diff --git a/h/out.h b/h/out.h index 4176fd341..cb7995133 100644 --- a/h/out.h +++ b/h/out.h @@ -68,9 +68,9 @@ struct outname { #define RELOPPC 4 /* PowerPC 26-bit address */ #define RELOPPC_LIS 5 /* PowerPC lis */ #define RELOVC4 6 /* VideoCore IV address in 32-bit instruction */ -#define RELOMIPS 7 /* MIPS, low half of word or other*/ -#define RELOMIPSHI 8 /* MIPS, high half of word */ +#define RELOMIPS 7 /* MIPS */ +#define RELS2 0x1000 /* shift result right 16 bits before writing back */ #define RELPC 0x2000 /* pc relative */ #define RELBR 0x4000 /* High order byte lowest address. */ #define RELWR 0x8000 /* High order word lowest address. */ diff --git a/mach/mips/as/mach3.c b/mach/mips/as/mach3.c index 519bb02e3..98e62c044 100644 --- a/mach/mips/as/mach3.c +++ b/mach/mips/as/mach3.c @@ -76,8 +76,8 @@ 0, OP_LI, 0, "li", 0, OP_LA, 0, "la", -0, HI, RELOMIPSHI, "hi", -0, LO, RELOMIPS, "lo", +0, HI, RELO2|RELS2,"hi", +0, LO, RELO2, "lo", #include "tokens.y" diff --git a/mach/mips/as/mach4.c b/mach/mips/as/mach4.c index 7b2e7e2e0..45c1bedc6 100644 --- a/mach/mips/as/mach4.c +++ b/mach/mips/as/mach4.c @@ -24,10 +24,10 @@ word_t val = $4.val; if (type != S_ABS) - newrelo($4.typ, RELOMIPSHI | FIXUPFLAGS); + newrelo($4.typ, RELO2 | RELS2 | FIXUPFLAGS); emit4(0x3c000000 | (reg<<16) | (val>>16)); /* lui reg, value */ if (type != S_ABS) - newrelo($4.typ, RELOMIPS | FIXUPFLAGS); + newrelo($4.typ, RELO2 | FIXUPFLAGS); emit4(0x34000000 | (reg<<16) | (reg<<21) | (val & 0xffff)); /* ori reg, reg, value */ } diff --git a/util/led/relocate.c b/util/led/relocate.c index f2e9f784d..8b34e8ec2 100644 --- a/util/led/relocate.c +++ b/util/led/relocate.c @@ -179,6 +179,30 @@ static uint32_t get_lis_valu(char *addr, uint16_t type) return valu; } +/* RELOMIPS is used for j and b instructions only. */ +static uint32_t get_mips_valu(char* addr) +{ + uint32_t value = read4(addr, 0); + switch (value >> 26) + { + case 2: /* j */ + case 3: /* jal */ + case 29: /* jalx */ + /* Unsigned 26-bit payload. */ + value = value & ((1<<26)-1); + break; + + default: /* assume everything else is a b, there are lots */ + /* Signed 16-bit payload. */ + value = ((int32_t)value << 16) >> 16; + break; + } + + /* The value has two implicit zero bits on the bottom. */ + value <<= 2; + return value; +} + /* * The bits in type indicate how many bytes the value occupies and what * significance should be attributed to each byte. @@ -198,8 +222,10 @@ static uint32_t getvalu(char* addr, uint16_t type) return get_lis_valu(addr, type); case RELOVC4: return get_vc4_valu(addr); + case RELOMIPS: + return get_mips_valu(addr); default: - fatal("bad relocation type %x", type & RELSZ); + fatal("can't read relocation type %x", type & RELSZ); } /* NOTREACHED */ } @@ -369,6 +395,36 @@ static void put_lis_valu(char* addr, uint32_t value, uint16_t type) write4(opcode, addr, type); } +/* RELOMIPS is used for j and b instructions only. */ +static void put_mips_valu(char* addr, uint32_t value) +{ + uint32_t opcode = read4(addr, 0); + + /* The two bottom zero bits are implicit. */ + if (value & 3) + fatal("invalid MIPS relocation value 0x%x", value); + value >>= 2; + + switch (value >> 26) + { + case 2: /* j */ + case 3: /* jal */ + case 29: /* jalx */ + /* Unsigned 26-bit payload. */ + value = value & ((1<<26)-1); + opcode = opcode & ~((1<<26)-1); + break; + + default: /* assume everything else is a b, there are lots */ + /* Signed 16-bit payload. */ + value = value & ((1<<16)-1); + opcode = opcode & ~((1<<16)-1); + break; + } + + write4(opcode | value, addr, 0); +} + /* * The bits in type indicate how many bytes the value occupies and what * significance should be attributed to each byte. @@ -396,8 +452,11 @@ static putvalu(uint32_t valu, char* addr, uint16_t type) case RELOVC4: put_vc4_valu(addr, valu); break; + case RELOMIPS: + put_mips_valu(addr, valu); + break; default: - fatal("bad relocation type %x", type & RELSZ); + fatal("can't write relocation type %x", type & RELSZ); } } @@ -506,6 +565,14 @@ relocate(head, emit, names, relo, off) if (relo->or_type & RELPC) valu -= relorig[sectindex].org_size+outsect[sectindex].os_base; + /* + * If RELS2 is set, right shift the value by sixteen bits; this + * allows 32-bit values to be fixed up as a high word and a low + * word. + */ + if (relo->or_type & RELS2) + valu >>= 16; + /* * Now put the value back. */