diff --git a/h/out.h b/h/out.h index 45289f313..6aeffc446 100644 --- a/h/out.h +++ b/h/out.h @@ -65,9 +65,9 @@ struct outname { #define RELO1 1 /* 1 byte */ #define RELO2 2 /* 2 bytes */ #define RELO4 3 /* 4 bytes */ -#define RELOPPC 4 /* PowerPC 26-bit address */ -/* relo 5 is unused */ -#define RELOVC4 6 /* VideoCore IV address in 32-bit instruction */ +#define RELOPPC 4 /* PowerPC 26-bit address */ +#define RELOLIS 5 /* PowerPC lis */ +#define RELOVC4 6 /* VideoCore IV address in 32-bit instruction */ #define RELPC 0x2000 /* pc relative */ #define RELBR 0x4000 /* High order byte lowest address. */ diff --git a/mach/powerpc/as/mach1.c b/mach/powerpc/as/mach1.c index 44b415ff8..50f799684 100644 --- a/mach/powerpc/as/mach1.c +++ b/mach/powerpc/as/mach1.c @@ -7,5 +7,6 @@ * Do not #include anything here. Do it in mach/proto/as/comm0.h */ -extern word_t emit_hi(struct expr_t* expr, int is_signed); -extern word_t emit_lo(struct expr_t* expr); +void no_hl(void); +word_t eval_hl(struct expr_t* expr, int token); +void emit_hl(word_t in); diff --git a/mach/powerpc/as/mach4.c b/mach/powerpc/as/mach4.c index 640573fa1..8a0cca9de 100644 --- a/mach/powerpc/as/mach4.c +++ b/mach/powerpc/as/mach4.c @@ -10,11 +10,11 @@ operation | OP_BF_BFA CR ',' CR { emit4($1 | ($2<<23) | ($4<<18)); } | OP_BF_FRA_FRB CR ',' FPR ',' FPR { emit4($1 | ($2<<23) | ($4<<16) | ($6<<11)); } | OP_BF_L_RA_RB CR ',' u1 ',' GPR ',' GPR { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | ($8<<11)); } - | OP_BF_L_RA_SI CR ',' u1 ',' GPR ',' e16 { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); } - | OP_BF_L_RA_UI CR ',' u1 ',' GPR ',' e16 { emit4($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); } + | OP_BF_L_RA_SI CR ',' u1 ',' GPR ',' e16 { emit_hl($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); } + | OP_BF_L_RA_UI CR ',' u1 ',' GPR ',' e16 { emit_hl($1 | ($2<<23) | ($4<<21) | ($6<<16) | $8); } | OP_BF_RA_RB cr_opt GPR ',' GPR { emit4($1 | ($2<<23) | ($3<<16) | ($5<<11)); } - | OP_BF_RA_SI cr_opt GPR ',' e16 { emit4($1 | ($2<<23) | ($3<<16) | $5); } - | OP_BF_RA_UI cr_opt GPR ',' e16 { emit4($1 | ($2<<23) | ($3<<16) | $5); } + | OP_BF_RA_SI cr_opt GPR ',' e16 { emit_hl($1 | ($2<<23) | ($3<<16) | $5); } + | OP_BF_RA_UI cr_opt GPR ',' e16 { emit_hl($1 | ($2<<23) | ($3<<16) | $5); } | OP_BF_U_C c CR ',' u4 { emit4($1 | $2 | ($3<<23) | ($5<<12)); } | OP_BH { emit4($1); } | OP_BH u2 { emit4($1 | ($2<<11)); } @@ -33,13 +33,13 @@ operation | OP_BT_BT_BT u5 { emit4($1 | ($2<<21) | ($2<<16) | ($2<<11)); } | OP_BT_C c u5 { emit4($1 | $2 | ($3<<21)); } | OP_FLM_FRB_C c u8 ',' FPR { emit4($1 | $2 | ($3<<17) | ($5<<11)); } - | OP_FRS_RA_D FPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } + | OP_FRS_RA_D FPR ',' e16 '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } | OP_FRS_RA_RB FPR ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_FRT_FRA_FRB_C c FPR ',' FPR ',' FPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); } | OP_FRT_FRA_FRC_FRB_C c FPR ',' FPR ',' FPR ',' FPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($9<<11) | ($7<<6)); } | OP_FRT_FRA_FRC_C c FPR ',' FPR ',' FPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<6)); } | OP_FRT_FRB_C c FPR ',' FPR { emit4($1 | $2 | ($3<<21) | ($5<<11)); } - | OP_FRT_RA_D FPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } + | OP_FRT_RA_D FPR ',' e16 '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } | OP_FRT_RA_RB FPR ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_FRT_C c FPR { emit4($1 | $2 | ($3<<21)); } | OP_RA_RS_C c GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16)); } @@ -59,34 +59,34 @@ operation { emit4($1 | $2 | ($5<<21) | ($3<<16) | SH6($7)); } | OP_RA_RS_SH6_MB6_C c GPR ',' GPR ',' u6 ',' u6 { emit4($1 | $2 | ($5<<21) | ($3<<16) | SH6($7) | MB6($9)); } - | OP_RA_RS_UI GPR ',' GPR ',' e16 { emit4($1 | ($4<<21) | ($2<<16) | $6); } - | OP_RA_RS_UI_CC C GPR ',' GPR ',' e16 { emit4($1 | ($5<<21) | ($3<<16) | $7); } + | OP_RA_RS_UI GPR ',' GPR ',' e16 { emit_hl($1 | ($4<<21) | ($2<<16) | $6); } + | OP_RA_RS_UI_CC C GPR ',' GPR ',' e16 { emit_hl($1 | ($5<<21) | ($3<<16) | $7); } | OP_RT GPR { emit4($1 | ($2<<21)); } | OP_RT_RA_C c GPR ',' GPR { emit4($1 | $2 | ($3<<21) | ($5<<16)); } - | OP_RT_RA_D GPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } - | OP_RT_RA_DS GPR ',' ds '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } + | OP_RT_RA_D GPR ',' e16 '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } + | OP_RT_RA_DS GPR ',' ds '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } | OP_RT_RA_NB GPR ',' GPR ',' nb { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_RT_RA_RB GPR ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_RT_RA_RB_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($3<<21) | ($5<<16) | ($7<<11)); } - | OP_RT_RA_SI GPR ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } - | OP_RT_RA_SI_addic c GPR ',' GPR ',' e16 { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); } + | OP_RT_RA_SI GPR ',' GPR ',' e16 { emit_hl($1 | ($2<<21) | ($4<<16) | $6); } + | OP_RT_RA_SI_addic c GPR ',' GPR ',' e16 { emit_hl($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); } | OP_RT_RA_SI_subi GPR ',' GPR ',' negate16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } | OP_RT_RA_SI_subic c GPR ',' GPR ',' negate16 { emit4($1 | ($2<<26) | ($3<<21) | ($5<<16) | $7); } | OP_RT_RB_RA_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($3<<21) | ($7<<16) | ($5<<11)); } - | OP_RT_SI GPR ',' e16 { emit4($1 | ($2<<21) | $4); } + | OP_RT_SI GPR ',' e16 { emit_hl($1 | ($2<<21) | $4); } | OP_RT_SPR GPR ',' spr_num { emit4($1 | ($2<<21) | ($4<<11)); } | OP_RS_FXM u7 ',' GPR { emit4($1 | ($4<<21) | ($2<<12)); } - | OP_RS_RA_D GPR ',' e16 '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } - | OP_RS_RA_DS GPR ',' ds '(' GPR ')' { emit4($1 | ($2<<21) | ($6<<16) | $4); } + | OP_RS_RA_D GPR ',' e16 '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } + | OP_RS_RA_DS GPR ',' ds '(' GPR ')' { emit_hl($1 | ($2<<21) | ($6<<16) | $4); } | OP_RS_RA_NB GPR ',' GPR ',' nb { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_RS_RA_RB GPR ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } | OP_RS_RA_RB_C c GPR ',' GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($7<<11)); } | OP_RS_RA_RA_C c GPR ',' GPR { emit4($1 | $2 | ($5<<21) | ($3<<16) | ($5<<11)); } | OP_RS_SPR spr_num ',' GPR { emit4($1 | ($4<<21) | ($2<<11)); } | OP_TO_RA_RB u5 ',' GPR ',' GPR { emit4($1 | ($2<<21) | ($4<<16) | ($6<<11)); } - | OP_TO_RA_SI u5 ',' GPR ',' e16 { emit4($1 | ($2<<21) | ($4<<16) | $6); } + | OP_TO_RA_SI u5 ',' GPR ',' e16 { emit_hl($1 | ($2<<21) | ($4<<16) | $6); } | OP_TOX_RA_RB GPR ',' GPR { emit4($1 | ($2<<16) | ($4<<11)); } - | OP_TOX_RA_SI GPR ',' e16 { emit4($1 | ($2<<16) | $4); } + | OP_TOX_RA_SI GPR ',' e16 { emit_hl($1 | ($2<<16) | $4); } | OP_LEV { emit4($1); } | OP_LEV u7 { emit4($1 | ($2<<5)); } | OP_LIA lia { emit4($1 | $2); } @@ -217,10 +217,11 @@ e16 if (($1 < -0x8000) || ($1 > 0xffff)) serror("16-bit value out of range"); $$ = (uint16_t) $1; + no_hl(); } - | OP_HI ASC_LPAR expr ASC_RPAR { $$ = emit_hi(&$3, 0); } - | OP_HA ASC_LPAR expr ASC_RPAR { $$ = emit_hi(&$3, 1); } - | OP_LO ASC_LPAR expr ASC_RPAR { $$ = emit_lo(&$3); } + | OP_HI ASC_LPAR expr ASC_RPAR { $$ = eval_hl(&$3, OP_HI); } + | OP_HA ASC_LPAR expr ASC_RPAR { $$ = eval_hl(&$3, OP_HA); } + | OP_LO ASC_LPAR expr ASC_RPAR { $$ = eval_hl(&$3, OP_LO); } ; negate16 diff --git a/mach/powerpc/as/mach5.c b/mach/powerpc/as/mach5.c index 47460790c..d72b8514a 100644 --- a/mach/powerpc/as/mach5.c +++ b/mach/powerpc/as/mach5.c @@ -1,38 +1,75 @@ +static int hl_token; +static expr_t hl_expr; -word_t emit_hi(struct expr_t* expr, int is_signed) -{ - /* If this is a symbol reference, discard the symbol and keep only the - * offset part. */ - word_t type = expr->typ & S_TYP; - word_t val = expr->val; - uint16_t hi = val >> 16; - uint16_t lo = val & 0xffff; - - if (type != S_ABS) - newrelo(expr->typ, RELOPPC | FIXUPFLAGS); - - /* If the low half of this relocation is going to be a memory operation, - * then it'll be treated as a signed value. That means that values greater - * than 0x7fff will cause the high word to have 1 subtracted from it; so - * we apply an adjustment here. - */ - - if (is_signed && (lo > 0x7fff)) - hi++; - - return hi; +void no_hl(void) { + hl_token = 0; } -word_t emit_lo(struct expr_t* expr) +word_t eval_hl(expr_t* expr, int token) { - word_t type = expr->typ & S_TYP; - word_t val = expr->val; + word_t val = expr->val; + uint16_t hi = val >> 16; + uint16_t lo = val & 0xffff; - /* If the assembler stored a symbol for relocation later, we need to - * abandon it (because the relocation was generated by emit_ha). */ + hl_token = token; + hl_expr = *expr; - if (type != S_ABS) - relonami = 0; - - return val & 0xffff; + switch (token) { + case OP_HI: /* hi16[expr] */ + return hi; + case OP_HA: /* ha16[expr]*/ + /* + * If the low half will be treated as a signed value, + * then values greater than 0x7fff will cause the high + * half to have 1 subtracted from it; so we apply an + * adjustment here. + */ + if (lo > 0x7fff) + hi++; + return hi; + case OP_LO: /* lo16[expr] */ + return lo; + } +} + +void emit_hl(word_t in) +{ + word_t reg; + int type; + + switch (hl_token) { + case OP_HI: /* hi16[expr] */ + case OP_HA: /* ha16[expr] */ + if (PASS_RELO && (hl_expr.typ & S_TYP) != S_ABS) { + /* + * RELOLIS only works with lis _, _ (same as + * addis _, r0, _). Check if instruction + * isn't addis or register RA isn't r0. + */ + if ((in & 0xfc1f0000) != (0x3c000000)) + serror("relocation only works with lis"); + + /* + * High bit: ha16 flag + * Next 5 bits: register RT + * Low 26 bits: signed offset + */ + fit(fitx(hl_expr.val, 26)); + newrelo(hl_expr.typ, RELOLIS | FIXUPFLAGS); + reg = (in >> 21) & 0x1f; + in = (hl_token == OP_HA) << 31; + in |= reg << 26; + in |= hl_expr.val & 0x03ffffff; + } + break; + case OP_LO: /* lo16[expr] */ + if (PASS_RELO && (hl_expr.typ & S_TYP) != S_ABS) { + DOTVAL += 2; + newrelo(hl_expr.typ, RELO2 | FIXUPFLAGS); + DOTVAL -= 2; + } + break; + } + + emit4(in); } diff --git a/util/amisc/ashow.c b/util/amisc/ashow.c index 498ae3980..ec85de30d 100644 --- a/util/amisc/ashow.c +++ b/util/amisc/ashow.c @@ -140,6 +140,9 @@ showrelo() case RELOPPC: printf("\tPowerPC 26-bit address\n"); break; + case RELOLIS: + printf("\tPowerPC lis instruction\n"); + break; case RELOVC4: printf("\tVideoCore IV address in 32-bit instruction\n"); break; diff --git a/util/led/relocate.c b/util/led/relocate.c index 036b7dbb8..1b8960938 100644 --- a/util/led/relocate.c +++ b/util/led/relocate.c @@ -165,8 +165,17 @@ static uint32_t get_powerpc_valu(char* addr, uint16_t type) return ((hi << 16) | lo); } - fatal("Don't know how to read from PowerPC fixup on instructions 0x%08x+0x%08x", - opcode1, opcode2); + fatal("Don't know how to read from PowerPC fixup on instructions 0x%08lx+0x%08lx", + (unsigned long)opcode1, (unsigned long)opcode2); +} + +/* RELOLIS stores a signed 26-bit offset in the low bits. */ +static uint32_t get_lis_valu(char *addr, uint16_t type) +{ + uint32_t valu = read4(addr, type) & 0x03ffffff; + if (valu & 0x02000000) + valu |= 0xfc000000; /* sign extension */ + return valu; } /* @@ -184,6 +193,8 @@ static uint32_t getvalu(char* addr, uint16_t type) return read4(addr, type); case RELOPPC: return get_powerpc_valu(addr, type); + case RELOLIS: + return get_lis_valu(addr, type); case RELOVC4: return get_vc4_valu(addr); default: @@ -327,8 +338,34 @@ static void put_powerpc_valu(char* addr, uint32_t value, uint16_t type) } else - fatal("Don't know how to write a PowerPC fixup to instructions 0x%08x+0x%08x", - opcode1, opcode2); + fatal("Don't know how to write a PowerPC fixup to instructions 0x%08lx+0x%08lx", + (unsigned long)opcode1, (unsigned long)opcode2); +} + +/* Writes a PowerPC lis instruction. */ +static void put_lis_valu(char* addr, uint32_t value, uint16_t type) +{ + uint32_t opcode, reg; + uint16_t hi, lo; + bool ha16; + + /* ha16 flag in high bit, register in next 5 bits */ + opcode = read4(addr, type); + ha16 = opcode >> 31; + reg = (opcode >> 26) & 0x1f; + + /* + * Apply the sign adjustment if the ha16 flag is set and the + * low half is a negative signed 16-bit integer. + */ + hi = value >> 16; + lo = value & 0xffff; + if (ha16 && lo > 0x7fff) + hi++; + + /* Assemble lis reg, hi == addis reg, r0, hi. */ + opcode = (15 << 26) | (reg << 21) | (0 << 16) | hi; + write4(opcode, addr, type); } /* @@ -352,6 +389,9 @@ static putvalu(uint32_t valu, char* addr, uint16_t type) case RELOPPC: put_powerpc_valu(addr, valu, type); break; + case RELOLIS: + put_lis_valu(addr, valu, type); + break; case RELOVC4: put_vc4_valu(addr, valu); break;