From 0c15c4be2544dc7ea73b14e2cc7c3b3e54395a9e Mon Sep 17 00:00:00 2001 From: bouyer Date: Sat, 27 Aug 2011 13:19:39 +0000 Subject: [PATCH] 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. --- .../gpl3/binutils/dist/gas/config/tc-mips.c | 81 ++++++++++++++++++- .../gpl3/binutils/dist/gas/doc/c-mips.texi | 8 ++ .../gpl3/binutils/dist/include/opcode/mips.h | 4 + .../gpl3/binutils/dist/opcodes/mips-opc.c | 4 + 4 files changed, 95 insertions(+), 2 deletions(-) diff --git a/external/gpl3/binutils/dist/gas/config/tc-mips.c b/external/gpl3/binutils/dist/gas/config/tc-mips.c index 595afff3f3a7..8f600a9f14c9 100644 --- a/external/gpl3/binutils/dist/gas/config/tc-mips.c +++ b/external/gpl3/binutils/dist/gas/config/tc-mips.c @@ -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\ diff --git a/external/gpl3/binutils/dist/gas/doc/c-mips.texi b/external/gpl3/binutils/dist/gas/doc/c-mips.texi index 2313b1af0e8c..e049ff8d0865 100644 --- a/external/gpl3/binutils/dist/gas/doc/c-mips.texi +++ b/external/gpl3/binutils/dist/gas/doc/c-mips.texi @@ -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 diff --git a/external/gpl3/binutils/dist/include/opcode/mips.h b/external/gpl3/binutils/dist/include/opcode/mips.h index 7b9549da9ad6..b7746605fe8f 100644 --- a/external/gpl3/binutils/dist/include/opcode/mips.h +++ b/external/gpl3/binutils/dist/include/opcode/mips.h @@ -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, diff --git a/external/gpl3/binutils/dist/opcodes/mips-opc.c b/external/gpl3/binutils/dist/opcodes/mips-opc.c index b1f809157fbe..ff9a4f5f6844 100644 --- a/external/gpl3/binutils/dist/opcodes/mips-opc.c +++ b/external/gpl3/binutils/dist/opcodes/mips-opc.c @@ -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. */