From b4ab4364239efcf0cec74e89a85d844d628138c0 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Sat, 5 Apr 2014 17:55:13 +1030 Subject: [PATCH] ppc476 icache workaround fix for bctr I got the ppc476 workaround wrong. bctr (and bctrl) as the last instruction in a page can hit the icache bug if the preceding mtctr insn is close by, and the destination is in the first few instructions on the next page. This scenario can occur with code generated by gcc to implement switch statements, or in code generated to call by function pointer. To prevent the bctr problem it is also necessary to remove other instructions that otherwise would be safe. bfd/ * elf32-ppc.c (ppc_elf_relocate_section): Remove bctr from list of safe ppc476 insns at end of page. Also remove non-branch insns. Expand comments. ld/ * emultempl/ppc32elf.em (no_zero_padding, ppc_finish): New functions. (LDEMUL_FINISH): Define. --- bfd/ChangeLog | 6 ++++ bfd/elf32-ppc.c | 67 ++++++++++++++++++++++++++++------------ ld/ChangeLog | 5 +++ ld/emultempl/ppc32elf.em | 41 ++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 19 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 70d23c50b15..b95d49c6f2f 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,9 @@ +2014-04-09 Alan Modra + + * elf32-ppc.c (ppc_elf_relocate_section): Remove bctr from list + of safe ppc476 insns at end of page. Also remove non-branch insns. + Expand comments. + 2014-04-08 Jon TURNEY * peXXigen.c (pe_print_debugdata): New function: Displays the diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index 868fe507009..61e94a045e9 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -9279,27 +9279,56 @@ ppc_elf_relocate_section (bfd *output_bfd, if (is_data) continue; - /* Some instructions can be left alone too. In this - category are most insns that unconditionally change - control flow, and isync. Of these, some *must* be left - alone, for example, the "bcl 20, 31, label" used in pic - sequences to give the address of the next insn. twui - and twu apparently are not safe. */ + /* Some instructions can be left alone too. Unconditional + branches, except for bcctr with BO=0x14 (bctr, bctrl), + avoid the icache failure. + + The problem occurs due to prefetch across a page boundary + where stale instructions can be fetched from the next + page, and the mechanism for flushing these bad + instructions fails under certain circumstances. The + unconditional branches: + 1) Branch: b, bl, ba, bla, + 2) Branch Conditional: bc, bca, bcl, bcla, + 3) Branch Conditional to Link Register: bclr, bclrl, + where (2) and (3) have BO=0x14 making them unconditional, + prevent the bad prefetch because the prefetch itself is + affected by these instructions. This happens even if the + instruction is not executed. + + A bctr example: + . + . lis 9,new_page@ha + . addi 9,9,new_page@l + . mtctr 9 + . bctr + . nop + . nop + . new_page: + . + The bctr is not predicted taken due to ctr not being + ready, so prefetch continues on past the bctr into the + new page which might have stale instructions. If they + fail to be flushed, then they will be executed after the + bctr executes. Either of the following modifications + prevent the bad prefetch from happening in the first + place: + . + . lis 9,new_page@ha lis 9,new_page@ha + . addi 9,9,new_page@l addi 9,9,new_page@l + . mtctr 9 mtctr 9 + . bctr bctr + . nop b somewhere_else + . b somewhere_else nop + . new_page: new_page: + . */ insn = bfd_get_32 (input_bfd, contents + offset); - if (insn == 0 - || (insn & (0x3f << 26)) == (18u << 26) /* b */ - || ((insn & (0x3f << 26)) == (16u << 26) /* bc always */ - && (insn & (0x14 << 21)) == (0x14 << 21)) - || ((insn & (0x3f << 26)) == (19u << 26) /* blr, bctr */ - && (insn & (0x14 << 21)) == (0x14 << 21) - && (insn & (0x1ff << 1)) == (16u << 1)) - || (insn & (0x3f << 26)) == (17u << 26) /* sc */ + if ((insn & (0x3f << 26)) == (18u << 26) /* b,bl,ba,bla */ + || ((insn & (0x3f << 26)) == (16u << 26) /* bc,bcl,bca,bcla*/ + && (insn & (0x14 << 21)) == (0x14 << 21)) /* with BO=0x14 */ || ((insn & (0x3f << 26)) == (19u << 26) - && ((insn & (0x3ff << 1)) == (38u << 1) /* rfmci */ - || (insn & (0x3ff << 1)) == (50u << 1) /* rfi */ - || (insn & (0x3ff << 1)) == (51u << 1) /* rfci */ - || (insn & (0x3ff << 1)) == (82u << 1) /* rfsvc */ - || (insn & (0x3ff << 1)) == (150u << 1))) /* isync */) + && (insn & (0x3ff << 1)) == (16u << 1) /* bclr,bclrl */ + && (insn & (0x14 << 21)) == (0x14 << 21)))/* with BO=0x14 */ continue; patch_addr = (start_addr + input_section->size diff --git a/ld/ChangeLog b/ld/ChangeLog index 54aa6cdd04d..b64c2e6af6b 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,8 @@ +2014-04-09 Alan Modra + + * emultempl/ppc32elf.em (no_zero_padding, ppc_finish): New functions. + (LDEMUL_FINISH): Define. + 2014-04-08 Nick Clifton * scripttempl/pe.sc (R_RSRC): Remove default manifest. diff --git a/ld/emultempl/ppc32elf.em b/ld/emultempl/ppc32elf.em index 00a29e2f0aa..069acd21dcc 100644 --- a/ld/emultempl/ppc32elf.em +++ b/ld/emultempl/ppc32elf.em @@ -27,6 +27,7 @@ fragment <header.type == lang_padding_statement_enum + && l->padding_statement.size != 0 + && l->padding_statement.output_section != NULL + && (l->padding_statement.output_section->flags & SEC_CODE) != 0 + && l->padding_statement.fill->size == 0) + { + struct _ppc_fill_type + { + size_t size; + unsigned char data[4]; + }; + static struct _ppc_fill_type fill_be = { 4, {0x48, 0, 0, 2} }; + static struct _ppc_fill_type fill_le = { 4, {2, 0, 0, 0x48} }; + + if (bfd_big_endian (link_info.output_bfd)) + l->padding_statement.fill = (struct _fill_type *) &fill_be; + else + l->padding_statement.fill = (struct _fill_type *) &fill_le; + } +} + +static void +ppc_finish (void) +{ + if (params.ppc476_workaround) + lang_for_each_statement (no_zero_padding); + finish_default (); +} + EOF if grep -q 'ld_elf32_spu_emulation' ldemul-list.h; then @@ -303,3 +343,4 @@ if test -z "$VXWORKS_BASE_EM_FILE" ; then LDEMUL_AFTER_OPEN=ppc_after_open fi LDEMUL_BEFORE_ALLOCATION=ppc_before_allocation +LDEMUL_FINISH=ppc_finish -- 2.30.2