]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
ppc476 icache workaround fix for bctr
authorAlan Modra <amodra@gmail.com>
Sat, 5 Apr 2014 07:13:36 +0000 (17:43 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 16 Apr 2014 02:38:35 +0000 (12:08 +0930)
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
bfd/elf32-ppc.c
ld/ChangeLog
ld/emultempl/ppc32elf.em

index 1e54a3cf3eacd6bcfa21cf397a23f3f10b82cc85..b2492039aa880a3127e89ec25372a134b8adbea1 100644 (file)
@@ -1,6 +1,11 @@
 2014-04-16  Alan Modra  <amodra@gmail.com>
 
        Apply mainline patches for ppc476 workaround
+       2014-04-09  Alan Modra  <amodra@gmail.com>
+       * 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-02-27  Alan Modra  <amodra@gmail.com>
        * elf32-ppc.c (ppc_elf_link_hash_table_create): Provide default
        params for targets that don't use ppc32elf.em.
index 22f3511432a011162ebfbb2d1a58db0f124d787c..746d0f5f51d3c0ad4c2f151c01899285078fed55 100644 (file)
@@ -9227,27 +9227,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
index b4b155343315da79f6d1e39ac76f713ca2c51dae..6344f4b07052ed4f3be1f33e07754953cbc4e3ea 100644 (file)
@@ -1,6 +1,10 @@
 2014-04-16  Alan Modra  <amodra@gmail.com>
 
        Apply mainline patches for ppc476 workaround
+       2014-04-09  Alan Modra  <amodra@gmail.com>
+       * emultempl/ppc32elf.em (no_zero_padding, ppc_finish): New functions.
+       (LDEMUL_FINISH): Define.
+
        2014-02-27  Alan Modra  <amodra@gmail.com>
        * emulparams/elf32ppcvxworks.sh: Source plt_unwind.sh and
        use ppc32elf.em.
index fc796a2295a977998bbdb2cc87a531ad600a4834..67a37e63000c8b1350d53779fabd965832c460b8 100644 (file)
@@ -28,6 +28,7 @@ fragment <<EOF
 #include "libbfd.h"
 #include "elf32-ppc.h"
 #include "ldlex.h"
+#include "ldlang.h"
 
 #define is_ppc_elf(bfd) \
   (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
@@ -174,6 +175,45 @@ ppc_before_allocation (void)
     ENABLE_RELAXATION;
 }
 
+/* Replaces default zero fill padding in executable sections with
+   "ba 0" instructions.  This works around the ppc476 icache bug if we
+   have a function pointer tail call near the end of a page, some
+   small amount of padding, then the function called at the beginning
+   of the next page.  If the "ba 0" is ever executed we should hit a
+   segv, so it's almost as good as an illegal instruction (zero).  */
+
+static void
+no_zero_padding (lang_statement_union_type *l)
+{
+  if (l->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
@@ -304,3 +344,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