File grub2-ppc64le-17-Add-ppc64-relocations.patch of Package grub2.2174

From 96d6b8d370e653386982b808f10a2a67849f73f1 Mon Sep 17 00:00:00 2001
From: Anton Blanchard <anton@samba.org>
Date: Wed, 29 Jan 2014 10:44:46 +1100
Subject: [PATCH 17/23] Add ppc64 relocations

 Add ppc64 relocations

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
From:    Anton Blanchard <anton@samba.org>
---
 grub-core/kern/powerpc/dl.c | 185 ++++++++++++++++++++++++++++++++++++++++----
 include/grub/elf.h          |   3 +
 2 files changed, 174 insertions(+), 14 deletions(-)

diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
index 7677e5a..cc496d3 100644
--- a/grub-core/kern/powerpc/dl.c
+++ b/grub-core/kern/powerpc/dl.c
@@ -23,6 +23,20 @@
 #include <grub/err.h>
 #include <grub/i18n.h>
 
+#if defined( __powerpc64__ ) || defined( __powerpc64le__ )
+#define ELFCLASSXX ELFCLASS64
+#define ELFMACHINEXX EM_PPC64
+#else
+#define ELFCLASSXX ELFCLASS32
+#define ELFMACHINEXX EM_PPC
+#endif
+
+#if defined( __powerpc64le__ )
+#define ELFDATA2XSB ELFDATA2LSB
+#else
+#define ELFDATA2XSB ELFDATA2MSB
+#endif
+
 /* Check if EHDR is a valid ELF header.  */
 grub_err_t
 grub_arch_dl_check_header (void *ehdr)
@@ -30,14 +44,86 @@ grub_arch_dl_check_header (void *ehdr)
   Elf_Ehdr *e = ehdr;
 
   /* Check the magic numbers.  */
-  if (e->e_ident[EI_CLASS] != ELFCLASS32
-      || e->e_ident[EI_DATA] != ELFDATA2MSB
-      || e->e_machine != EM_PPC)
+  if (e->e_ident[EI_CLASS] != ELFCLASSXX
+      || e->e_ident[EI_DATA] != ELFDATA2XSB
+      || e->e_machine != ELFMACHINEXX)
     return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
 
   return GRUB_ERR_NONE;
 }
 
+
+
+
+#if defined( __powerpc64le__ )
+struct trampoline
+{
+  grub_uint32_t std;
+  grub_uint32_t addis;
+  grub_uint32_t addi;
+  grub_uint32_t mtctr;
+  grub_uint32_t bctr;
+};
+
+static const struct trampoline trampoline_template =
+  {
+       0xf8410018,       /* std     r2,24(r1) */
+       0x3d800000,       /* addis   r12,0,0 */
+       0x398c0000,       /* addi    r12,r12,0 */
+       0x7d8903a6,       /* mtctr   r12 */
+       0x4e800420,       /* bctr */
+  };
+
+#define PPC_NOP 0x60000000
+#define	RESTORE_TOC 0xe8410018       /* ld      r2,24(r1) */
+
+#define STO_PPC64_LOCAL_BIT             5
+#define STO_PPC64_LOCAL_MASK            (7 << STO_PPC64_LOCAL_BIT)
+
+static unsigned long grub_arch_dl_get_toc (grub_dl_t mod, void *ehdr)
+{
+  unsigned long i = (unsigned long)grub_dl_find_section_addr(mod, ehdr, ".toc");
+  if (!i)
+    return 0;
+
+  return i;
+}
+
+static inline unsigned int
+ppc64_decode_local_entry(unsigned int other)
+{
+  return ((1 << other) >> 2) << 2;
+}
+
+#define PPC64_LOCAL_ENTRY_OFFSET(other)                         \
+  ppc64_decode_local_entry (((other) & STO_PPC64_LOCAL_MASK)    \
+                            >> STO_PPC64_LOCAL_BIT)
+
+
+
+#elif defined( __powerpc64__ )
+
+#error "NOT IMPLEMENTED YET"
+
+static int grub_arch_dl_is_in_opd (grub_dl_t mod, void *ehdr, unsigned long addr)
+{
+  unsigned long start, end;
+  Elf_Shdr *s = grub_dl_find_section(ehdr, ".opd");
+
+  if (!s)
+	return 0;
+
+  start = (unsigned long)grub_dl_find_section_addr(mod, ehdr, ".opd");
+  end = start + s->sh_size;
+
+  if ((start <= addr) && (addr < end))
+    return 1;
+  else
+    return 0;
+}
+
+#else
+
 /* For low-endian reverse lis and addr_high as well as ori and addr_low. */
 struct trampoline
 {
@@ -47,7 +133,7 @@ struct trampoline
   grub_uint32_t bctr;
 };
 
-static const struct trampoline trampoline_template = 
+static const struct trampoline trampoline_template =
   {
     0x3d800000,
     0x618c0000,
@@ -55,6 +141,8 @@ static const struct trampoline trampoline_template =
     0x4e800420,
   };
 
+#endif
+
 #pragma GCC diagnostic ignored "-Wcast-align"
 
 grub_err_t
@@ -74,14 +162,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
     if (s->sh_type == SHT_RELA)
       {
 	const Elf_Rela *rel, *max;
-	
+
 	for (rel = (const Elf_Rela *) ((const char *) e + s->sh_offset),
 	       max = rel + s->sh_size / s->sh_entsize;
 	     rel < max;
 	     rel++)
 	  if (ELF_R_TYPE (rel->r_info) == GRUB_ELF_R_PPC_REL24)
 	    (*tramp)++;
-	
       }
 
   *tramp *= sizeof (struct trampoline);
@@ -89,12 +176,15 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
   return GRUB_ERR_NONE;
 }
 
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
 /* Relocate symbols.  */
 grub_err_t
 grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 			       Elf_Shdr *s, grub_dl_segment_t seg)
 {
-#ifdef powerpc
   Elf_Rela *rel, *max;
 
   for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
@@ -104,7 +194,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
     {
       Elf_Word *addr;
       Elf_Sym *sym;
-      grub_uint32_t value;
+      Elf_Addr value;
 
       if (seg->size < rel->r_offset)
 	return grub_error (GRUB_ERR_BAD_MODULE,
@@ -119,6 +209,76 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       value = sym->st_value + rel->r_addend;
       switch (ELF_R_TYPE (rel->r_info))
 	{
+#ifdef __powerpc64le__
+	case GRUB_ELF_R_PPC_REL24:
+	  {
+	    struct trampoline *tptr = mod->trampptr;
+	    Elf_Sword delta;
+	    if (sym->st_shndx == SHN_UNDEF)
+	    {
+                grub_memcpy (tptr, &trampoline_template, sizeof (*tptr));
+
+		tptr->addis |= PPC_HA(value);
+		tptr->addi |= PPC_LO(value);
+
+	        mod->trampptr = tptr + 1;
+	        delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr;
+
+	        if (*(addr+1) != PPC_NOP)
+	               return grub_error (GRUB_ERR_BAD_MODULE,
+			     "Missing NOP after PPC_REL24 got %x", *(addr+1));
+	        *(addr+1) = RESTORE_TOC;
+	    } else
+	        delta = (grub_uint8_t *)value - (grub_uint8_t *) addr +
+                     PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
+
+
+            if (delta << 6 >> 6 != delta)
+		      return grub_error (GRUB_ERR_BAD_MODULE,
+					 "relocation overflow");
+
+	    *(Elf_Word *) (addr) = (*addr & ~0x03fffffc) | (delta & 0x03fffffc);
+	  }
+	  break;
+
+	case GRUB_ELF_R_PPC64_ADDR64:
+	  *(Elf_Xword *) addr = value;
+	  break;
+
+	case GRUB_ELF_R_PPC64_TOC:
+	  *(Elf_Xword *) addr = grub_arch_dl_get_toc(mod, ehdr);
+	  break;
+
+	case GRUB_ELF_R_PPC64_TOC16_HA:
+          value -= grub_arch_dl_get_toc(mod, ehdr);
+          *(Elf_Half *) addr = PPC_HA(value);
+	  break;
+
+	case GRUB_ELF_R_PPC64_TOC16_LO:
+          value -= grub_arch_dl_get_toc(mod, ehdr);
+	  *(Elf_Half *) addr = PPC_LO(value);
+	  break;
+
+	case GRUB_ELF_R_PPC64_TOC16_LO_DS:
+	   value -= grub_arch_dl_get_toc(mod, ehdr);
+	   if (value & 3)
+	    return grub_error (GRUB_ERR_BAD_MODULE,
+			       "bad TOC16_LO_DS relocation");
+
+	  *(Elf_Half *) addr = ((*(Elf_Half *) addr) & ~0xfffc) | (value & 0xfffc);
+	  break;
+
+	case GRUB_ELF_R_PPC64_REL16_HA:
+	  value -=  (unsigned long) addr;
+	  *(Elf_Half *) addr = PPC_HA(value);
+	  break;
+
+	case GRUB_ELF_R_PPC64_REL16_LO:
+	  value -=  (unsigned long) addr;
+	  *(Elf_Half *) addr = PPC_LO(value);
+	  break;
+#else
+
 	case GRUB_ELF_R_PPC_ADDR16_LO:
 	  *(Elf_Half *) addr = value;
 	  break;
@@ -137,7 +297,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 		tptr->ori |= ((value) & 0xffff);
 		mod->trampptr = tptr + 1;
 	      }
-			
+
 	    if (delta << 6 >> 6 != delta)
 	      return grub_error (GRUB_ERR_BAD_MODULE,
 				 "relocation overflow");
@@ -156,6 +316,8 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 	case GRUB_ELF_R_PPC_REL32:
 	  *addr = value - (Elf_Word) addr;
 	  break;
+#endif
+
 	default:
 	  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 			     N_("relocation 0x%x is not implemented yet"),
@@ -164,9 +326,4 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
     }
 
   return GRUB_ERR_NONE;
-#else
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-     N_("relocation is not implemented yet for module=%llx ehdr=%llx Elf_Shdr=%llx seg=%llx"), 
-     mod, ehdr, s, seg);
-#endif
 }
diff --git a/include/grub/elf.h b/include/grub/elf.h
index bee7583..224d164 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -1998,6 +1998,9 @@ typedef Elf32_Addr Elf32_Conflict;
 #define GRUB_ELF_R_PPC_DIAB_RELSDA_HI	184	/* like EMB_RELSDA, but high 16 bit */
 #define GRUB_ELF_R_PPC_DIAB_RELSDA_HA	185	/* like EMB_RELSDA, adjusted high 16 */
 
+#define GRUB_ELF_R_PPC64_REL16_LO       250
+#define GRUB_ELF_R_PPC64_REL16_HA       252
+
 /* This is a phony reloc to handle any old fashioned TOC16 references
    that may still be in object files.  */
 #define GRUB_ELF_R_PPC_TOC16		255
-- 
1.8.3.1
openSUSE Build Service is sponsored by