Add a fix-loongson2f-btb option to gas, from OpenBSD.
This works around a bug in the branch prediction logic of the CPU which can cause a hard hang. The existing fix-loongson2f-jump works around the same bug, but by 'fixing' the jump target to be within the same 256MB region as the kernel. This will (silently) cause a jump to the wrong address if the jump is intentionally to some other region (e.g. a call to pmon). fix-loongson2f-btb instead works around the issue by adding an explicit flush of the branch target buffer (via a write to a cop0 register) before every jump register instruction.
This commit is contained in:
parent
ad05e3fb85
commit
0c15c4be25
|
@ -778,6 +778,8 @@ static int mips_fix_vr4130;
|
|||
efficient expansion. */
|
||||
|
||||
static int mips_relax_branch;
|
||||
|
||||
static int mips_fix_loongson2f_btb;
|
||||
|
||||
/* The expansion of many macros depends on the type of symbol that
|
||||
they refer to. For example, when generating position-dependent code,
|
||||
|
@ -1051,6 +1053,7 @@ static void macro_build (expressionS *, const char *, const char *, ...);
|
|||
static void mips16_macro_build
|
||||
(expressionS *, const char *, const char *, va_list);
|
||||
static void load_register (int, expressionS *, int);
|
||||
static void macro_build (expressionS *, const char *, const char *, ...);
|
||||
static void macro_start (void);
|
||||
static void macro_end (void);
|
||||
static void macro (struct mips_cl_insn * ip);
|
||||
|
@ -3584,6 +3587,41 @@ macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
|
|||
r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
|
||||
}
|
||||
|
||||
/* Fix jump through register issue on loongson2f processor for kernel code:
|
||||
force a BTB clear before the jump to prevent it from being incorrectly
|
||||
prefetched by the branch prediction engine. */
|
||||
|
||||
static void
|
||||
macro_build_jrpatch (expressionS *ep, unsigned int sreg)
|
||||
{
|
||||
if (!mips_fix_loongson2f_btb)
|
||||
return;
|
||||
|
||||
if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == AT)
|
||||
return;
|
||||
|
||||
if (!mips_opts.at)
|
||||
{
|
||||
as_warn (_("unable to apply loongson2f BTB workaround when .set noat"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* li $at, COP_0_BTB_CLEAR | COP_0_RAS_DISABLE */
|
||||
ep->X_op = O_constant;
|
||||
ep->X_add_number = 3;
|
||||
macro_build (ep, "ori", "t,r,i", AT, ZERO, BFD_RELOC_LO16);
|
||||
|
||||
/* dmtc0 $at, COP_0_DIAG */
|
||||
macro_build (NULL, "dmtc0", "t,G", AT, 22);
|
||||
|
||||
/* Hide these two instructions to avoid getting a ``macro expanded into
|
||||
multiple instructions'' warning. */
|
||||
if (mips_relax.sequence != 2)
|
||||
mips_macro_warning.sizes[0] -= 2 * 4;
|
||||
if (mips_relax.sequence != 1)
|
||||
mips_macro_warning.sizes[1] -= 2 * 4;
|
||||
}
|
||||
|
||||
/* Build an instruction created by a macro expansion. This is passed
|
||||
a pointer to the count of instructions created so far, an
|
||||
expression, the name of the instruction to build, an operand format
|
||||
|
@ -3985,6 +4023,7 @@ macro_build_jalr (expressionS *ep)
|
|||
frag_grow (8);
|
||||
f = frag_more (0);
|
||||
}
|
||||
macro_build_jrpatch (ep, PIC_CALL_REG);
|
||||
macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
|
||||
if (HAVE_NEWABI)
|
||||
fix_new_exp (frag_now, f - frag_now->fr_literal,
|
||||
|
@ -6167,6 +6206,26 @@ macro (struct mips_cl_insn *ip)
|
|||
/* AT is not used, just return */
|
||||
return;
|
||||
|
||||
case M_JR_S:
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "jr", "s", sreg);
|
||||
return; /* didn't modify $at */
|
||||
|
||||
case M_J_S:
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "j", "s", sreg);
|
||||
return; /* didn't modify $at */
|
||||
|
||||
case M_JALR_S:
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "jalr", "s", sreg);
|
||||
return; /* didn't modify $at */
|
||||
|
||||
case M_JALR_DS:
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
||||
return; /* didn't modify $at */
|
||||
|
||||
case M_J_A:
|
||||
/* The j instruction may not be used in PIC code, since it
|
||||
requires an absolute address. We convert it to a b
|
||||
|
@ -6185,12 +6244,16 @@ macro (struct mips_cl_insn *ip)
|
|||
/* Fall through. */
|
||||
case M_JAL_2:
|
||||
if (mips_pic == NO_PIC)
|
||||
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
||||
{
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sreg != PIC_CALL_REG)
|
||||
as_warn (_("MIPS PIC call to register other than $25"));
|
||||
|
||||
macro_build_jrpatch (&expr1, sreg);
|
||||
macro_build (NULL, "jalr", "d,s", dreg, sreg);
|
||||
if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
|
||||
{
|
||||
|
@ -11324,9 +11387,14 @@ struct option md_longopts[] =
|
|||
#define OPTION_NO_FIX_LOONGSON2F_NOP (OPTION_FIX_BASE + 9)
|
||||
{"mfix-loongson2f-nop", no_argument, NULL, OPTION_FIX_LOONGSON2F_NOP},
|
||||
{"mno-fix-loongson2f-nop", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_NOP},
|
||||
#define OPTION_FIX_LOONGSON2F_BTB (OPTION_FIX_BASE + 10)
|
||||
#define OPTION_NO_FIX_LOONGSON2F_BTB (OPTION_FIX_BASE + 11)
|
||||
{"mfix-loongson2f-btb", no_argument, NULL, OPTION_FIX_LOONGSON2F_BTB},
|
||||
{"mno-fix-loongson2f-btb", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_BTB},
|
||||
|
||||
|
||||
/* Miscellaneous options. */
|
||||
#define OPTION_MISC_BASE (OPTION_FIX_BASE + 10)
|
||||
#define OPTION_MISC_BASE (OPTION_FIX_BASE + 12)
|
||||
#define OPTION_TRAP (OPTION_MISC_BASE + 0)
|
||||
{"trap", no_argument, NULL, OPTION_TRAP},
|
||||
{"no-break", no_argument, NULL, OPTION_TRAP},
|
||||
|
@ -11645,6 +11713,14 @@ md_parse_option (int c, char *arg)
|
|||
mips_fix_vr4130 = 0;
|
||||
break;
|
||||
|
||||
case OPTION_FIX_LOONGSON2F_BTB:
|
||||
mips_fix_loongson2f_btb = 1;
|
||||
break;
|
||||
|
||||
case OPTION_NO_FIX_LOONGSON2F_BTB:
|
||||
mips_fix_loongson2f_btb = 0;
|
||||
break;
|
||||
|
||||
case OPTION_RELAX_BRANCH:
|
||||
mips_relax_branch = 1;
|
||||
break;
|
||||
|
@ -15590,6 +15666,7 @@ MIPS options:\n\
|
|||
-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
|
||||
-mfix-vr4120 work around certain VR4120 errata\n\
|
||||
-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
|
||||
-mfix-loongson2f-btb work around Loongson2F BTB errata\n\
|
||||
-mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\
|
||||
-mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\
|
||||
-msym32 assume all symbols have 32-bit values\n\
|
||||
|
|
|
@ -182,6 +182,14 @@ all problems in hand-written assembler code.
|
|||
@itemx -no-mfix-vr4130
|
||||
Insert nops to work around the VR4130 @samp{mflo}/@samp{mfhi} errata.
|
||||
|
||||
@item -mfix-loongson2f-btb
|
||||
@itemx -mno-fix-loongson2f-btb
|
||||
Clear the Branch Target Buffer before any jump through a register. This
|
||||
option is intended to be used on kernel code for the Loongson 2F processor
|
||||
only; userland code compiled with this option will fault, and kernel code
|
||||
compiled with this option run on another processor than Loongson 2F will
|
||||
yield unpredictable results.
|
||||
|
||||
@item -m4010
|
||||
@itemx -no-m4010
|
||||
Generate code for the LSI @sc{r4010} chip. This tells the assembler to
|
||||
|
|
|
@ -762,7 +762,11 @@ enum
|
|||
M_DSUB_I,
|
||||
M_DSUBU_I,
|
||||
M_DSUBU_I_2,
|
||||
M_JR_S,
|
||||
M_J_S,
|
||||
M_J_A,
|
||||
M_JALR_S,
|
||||
M_JALR_DS,
|
||||
M_JAL_1,
|
||||
M_JAL_2,
|
||||
M_JAL_A,
|
||||
|
|
|
@ -712,10 +712,12 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
|||
{"floor.w.s", "D,S", 0x4600000f, 0xffff003f, WR_D|RD_S|FP_S, 0, I2 },
|
||||
{"hibernate","", 0x42000023, 0xffffffff, 0, 0, V1 },
|
||||
{"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, 0, I33 },
|
||||
{"jr", "s", 0, (int) M_JR_S, INSN_MACRO, 0, I1 },
|
||||
{"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 },
|
||||
/* jr.hb is officially MIPS{32,64}R2, but it works on R1 as jr with
|
||||
the same hazard barrier effect. */
|
||||
{"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, 0, I32 },
|
||||
{"j", "s", 0, (int) M_J_S, INSN_MACRO, 0, I1 },
|
||||
{"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, 0, I1 }, /* jr */
|
||||
/* SVR4 PIC code requires special handling for j, so it must be a
|
||||
macro. */
|
||||
|
@ -724,7 +726,9 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
|||
assembler, but will never match user input (because the line above
|
||||
will match first). */
|
||||
{"j", "a", 0x08000000, 0xfc000000, UBD, 0, I1 },
|
||||
{"jalr", "s", 0, (int) M_JALR_S, INSN_MACRO, 0, I1 },
|
||||
{"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, 0, I1 },
|
||||
{"jalr", "d,s", 0, (int) M_JALR_DS, INSN_MACRO, 0, I1 },
|
||||
{"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, 0, I1 },
|
||||
/* jalr.hb is officially MIPS{32,64}R2, but it works on R1 as jalr
|
||||
with the same hazard barrier effect. */
|
||||
|
|
Loading…
Reference in New Issue