From f094bccdbc0b1467707677533e8287908130793b Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Wed, 3 Feb 2010 21:44:21 +0000 Subject: Add a new option to the mips64 gas, -mfix-loongson2f-btb. This option is intended to be used when compiling kernel code which will run on a Loongson 2E or 2F processor, and inserts an explicit BTB clear operation before every jump through a register (jr or jalr instructions), unless that register is k0 or k1, or we are in .set noat. This is a reliable, although aggressive, workaround for the misbehaviour of the branch prediction engine of many Loongson 2F processors with regard to the Branch Translation Buffer, for which an official errata has yet to be published, and for which the Loongson suggested workaround apparently only works due to pipeline side effects, but requires all the executable kernel code to be located in CKSEG0/CKSEG1. These changes are inspired by a backport of the Loongson binutils 2.19 patch to binutils 2.18; the idea of doing an aggressive BTB clear is mine. This causes no functional change to code compiled without the -mfix-loongson2f-btb option. ``looks reasonable'' kettenis@ --- gnu/usr.bin/binutils/gas/config/tc-mips.c | 80 +++++++++++++++++++++++++++++- gnu/usr.bin/binutils/gas/doc/c-mips.texi | 8 +++ gnu/usr.bin/binutils/include/opcode/mips.h | 4 ++ gnu/usr.bin/binutils/opcodes/mips-opc.c | 4 ++ 4 files changed, 94 insertions(+), 2 deletions(-) (limited to 'gnu/usr.bin/binutils') diff --git a/gnu/usr.bin/binutils/gas/config/tc-mips.c b/gnu/usr.bin/binutils/gas/config/tc-mips.c index 7b6cee85df4..d7800823ea5 100644 --- a/gnu/usr.bin/binutils/gas/config/tc-mips.c +++ b/gnu/usr.bin/binutils/gas/config/tc-mips.c @@ -630,6 +630,8 @@ static int mips_fix_vr4120; 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, @@ -867,6 +869,7 @@ static void mips_no_prev_insn (int); 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); @@ -2968,6 +2971,41 @@ macro_end (void) } } +/* 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.noat) + { + 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 @@ -3336,6 +3374,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, @@ -5509,6 +5548,26 @@ macro (struct mips_cl_insn *ip) break; + 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 @@ -5528,12 +5587,16 @@ macro (struct mips_cl_insn *ip) case M_JAL_2: if (mips_pic == NO_PIC || mips_pic == EMBEDDED_PIC) - macro_build (NULL, "jalr", "d,s", dreg, sreg); + { + macro_build_jrpatch (&expr1, sreg); + macro_build (NULL, "jalr", "d,s", dreg, sreg); + } else if (mips_pic == SVR4_PIC) { 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 (! HAVE_NEWABI) { @@ -10258,9 +10321,13 @@ struct option md_longopts[] = #define OPTION_NO_FIX_VR4120 (OPTION_FIX_BASE + 3) {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120}, {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120}, +#define OPTION_FIX_LOONGSON2F_BTB (OPTION_FIX_BASE + 4) +#define OPTION_NO_FIX_LOONGSON2F_BTB (OPTION_FIX_BASE + 5) + {"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 + 4) +#define OPTION_MISC_BASE (OPTION_FIX_BASE + 6) #define OPTION_MEMBEDDED_PIC (OPTION_MISC_BASE + 0) {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC}, #define OPTION_TRAP (OPTION_MISC_BASE + 1) @@ -10507,6 +10574,14 @@ md_parse_option (int c, char *arg) mips_fix_vr4120 = 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; @@ -14374,6 +14449,7 @@ MIPS options:\n\ -no-mips16 do not generate mips16 instructions\n")); fprintf (stream, _("\ -mfix-vr4120 work around certain VR4120 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\ -O0 remove unneeded NOPs, do not swap branches\n\ diff --git a/gnu/usr.bin/binutils/gas/doc/c-mips.texi b/gnu/usr.bin/binutils/gas/doc/c-mips.texi index 1375230a673..f5a427ce2dc 100644 --- a/gnu/usr.bin/binutils/gas/doc/c-mips.texi +++ b/gnu/usr.bin/binutils/gas/doc/c-mips.texi @@ -128,6 +128,14 @@ Insert nops to work around certain VR4120 errata. This option is intended to be used on GCC-generated code: it is not designed to catch all problems in hand-written assembler code. +@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/gnu/usr.bin/binutils/include/opcode/mips.h b/gnu/usr.bin/binutils/include/opcode/mips.h index 5c3ddfcd7b5..f5a1c549186 100644 --- a/gnu/usr.bin/binutils/include/opcode/mips.h +++ b/gnu/usr.bin/binutils/include/opcode/mips.h @@ -582,7 +582,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/gnu/usr.bin/binutils/opcodes/mips-opc.c b/gnu/usr.bin/binutils/opcodes/mips-opc.c index 9a80e53d43d..d16b804c2be 100644 --- a/gnu/usr.bin/binutils/opcodes/mips-opc.c +++ b/gnu/usr.bin/binutils/opcodes/mips-opc.c @@ -612,8 +612,10 @@ const struct mips_opcode mips_builtin_opcodes[] = {"flushid", "", 0xbc030000, 0xffffffff, 0, L1 }, {"hibernate","", 0x42000023, 0xffffffff, 0, V1 }, {"ins", "t,r,+A,+B", 0x7c000004, 0xfc00003f, WR_t|RD_s, I33 }, +{"jr", "s", 0, (int) M_JR_S, INSN_MACRO, I1 }, {"jr", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, {"jr.hb", "s", 0x00000408, 0xfc1fffff, UBD|RD_s, I33 }, +{"j", "s", 0, (int) M_J_S, INSN_MACRO, I1 }, {"j", "s", 0x00000008, 0xfc1fffff, UBD|RD_s, I1 }, /* jr */ /* SVR4 PIC code requires special handling for j, so it must be a macro. */ @@ -622,7 +624,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, I1 }, +{"jalr", "s", 0, (int) M_JALR_S, INSN_MACRO, I1 }, {"jalr", "s", 0x0000f809, 0xfc1fffff, UBD|RD_s|WR_d, I1 }, +{"jalr", "d,s", 0, (int) M_JALR_DS, INSN_MACRO, I1 }, {"jalr", "d,s", 0x00000009, 0xfc1f07ff, UBD|RD_s|WR_d, I1 }, {"jalr.hb", "s", 0x0000fc09, 0xfc1fffff, UBD|RD_s|WR_d, I33 }, {"jalr.hb", "d,s", 0x00000409, 0xfc1f07ff, UBD|RD_s|WR_d, I33 }, -- cgit v1.2.3