]> git.proxmox.com Git - grub2.git/commitdiff
Support trampoline jumps on powerpc.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 8 Nov 2011 10:23:56 +0000 (11:23 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Tue, 8 Nov 2011 10:23:56 +0000 (11:23 +0100)
* grub-core/kern/dl.c (grub_dl_load_segments) [__powerpc__]: Follow
__ia64__ path.
(grub_dl_load_segments): Set mod->sz.
(grub_dl_flush_cache): Flush whole space occupied by module, not just
segments.
* grub-core/kern/ia64/dl.c (nopm): Make const while on it.
(jump): Likewise.
* grub-core/kern/powerpc/dl.c (grub_arch_dl_get_tramp_got_size): New
function.
(trampoline): New struct.
(trampoline_template): New const.
(grub_arch_dl_relocate_symbols): Create trampolines on overflow.
* include/grub/dl.h (grub_dl): Add sz element.
[__powerpc__]: Follow __ia64__.
(GRUB_ARCH_DL_TRAMP_ALIGN): Define on ppc.
(GRUB_ARCH_DL_GOT_ALIGN): Likewise.
(GRUB_ARCH_DL_TRAMP_SIZE): Likewise.
(grub_arch_dl_get_tramp_got_size) [__powerpc__]: New proto.

ChangeLog
grub-core/kern/dl.c
grub-core/kern/ia64/dl.c
grub-core/kern/powerpc/dl.c
include/grub/dl.h

index 89112679272ed3b8d331516e7d352d82d3c0e209..48799984d9065a2724679c844aa13ef6a72f50d2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2011-11-08  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Support trampoline jumps on powerpc.
+
+       * grub-core/kern/dl.c (grub_dl_load_segments) [__powerpc__]: Follow
+       __ia64__ path.
+       (grub_dl_load_segments): Set mod->sz.
+       (grub_dl_flush_cache): Flush whole space occupied by module, not just
+       segments.
+       * grub-core/kern/ia64/dl.c (nopm): Make const while on it.
+       (jump): Likewise.
+       * grub-core/kern/powerpc/dl.c (grub_arch_dl_get_tramp_got_size): New
+       function.
+       (trampoline): New struct.
+       (trampoline_template): New const.
+       (grub_arch_dl_relocate_symbols): Create trampolines on overflow.
+       * include/grub/dl.h (grub_dl): Add sz element.
+       [__powerpc__]: Follow __ia64__.
+       (GRUB_ARCH_DL_TRAMP_ALIGN): Define on ppc.
+       (GRUB_ARCH_DL_GOT_ALIGN): Likewise.
+       (GRUB_ARCH_DL_TRAMP_SIZE): Likewise.
+       (grub_arch_dl_get_tramp_got_size) [__powerpc__]: New proto.
+
 2011-11-06  Vladimir Serbinenko  <phcoder@gmail.com>
 
        ZFS crypto support.
index 1841bf1f5e1acc8f4dcc3c4815d1b2c3ad10b8b9..b6fb537e88a6616d29fbf4e43206ccba6a68e948 100644 (file)
@@ -233,7 +233,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
   unsigned i;
   Elf_Shdr *s;
   grub_size_t tsize = 0, talign = 1;
-#ifdef __ia64__
+#if defined (__ia64__) || defined (__powerpc__)
   grub_size_t tramp;
   grub_size_t got;
 #endif
@@ -248,9 +248,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
        talign = s->sh_addralign;
     }
 
-#ifdef __ia64__
+#if defined (__ia64__) || defined (__powerpc__)
   grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
-  tramp *= GRUB_IA64_DL_TRAMP_SIZE;
+  tramp *= GRUB_ARCH_DL_TRAMP_SIZE;
   got *= sizeof (grub_uint64_t);
   tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN);
   if (talign < GRUB_ARCH_DL_TRAMP_ALIGN)
@@ -269,6 +269,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
   mod->base = grub_memalign (talign, tsize);
   if (!mod->base)
     return grub_errno;
+  mod->sz = tsize;
   ptr = mod->base;
 
 #ifdef GRUB_MACHINE_EMU
@@ -316,7 +317,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
          mod->segment = seg;
        }
     }
-#ifdef __ia64__
+#if defined (__ia64__) || defined (__powerpc__)
   ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
   mod->tramp = ptr;
   ptr += tramp;
@@ -575,15 +576,9 @@ grub_dl_unref (grub_dl_t mod)
 static void
 grub_dl_flush_cache (grub_dl_t mod)
 {
-  grub_dl_segment_t seg;
-
-  for (seg = mod->segment; seg; seg = seg->next) {
-    if (seg->size) {
-      grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
-                   (unsigned long) seg->size, seg->addr);
-      grub_arch_sync_caches (seg->addr, seg->size);
-    }
-  }
+  grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n",
+               (unsigned long) mod->sz, mod->base);
+  grub_arch_sync_caches (mod->base, mod->sz);
 }
 
 /* Load a module from core memory.  */
index 3904f73b7c2f4f9c548ed89208ff23d7edf753df..31b5f94cfd8efd1208d2ce302d5ffbbe78c8c8f1 100644 (file)
@@ -104,13 +104,13 @@ add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
     }
 }
 
-static grub_uint8_t nopm[5] =
+static const grub_uint8_t nopm[5] =
   {
     /* [MLX]       nop.m 0x0 */
     0x05, 0x00, 0x00, 0x00, 0x01
   };
 
-static grub_uint8_t jump[0x20] =
+static const grub_uint8_t jump[0x20] =
   {
     /* ld8 r16=[r15],8 */
     0x02, 0x80, 0x20, 0x1e, 0x18, 0x14,
index ad19e560013dd2833529f879dd08751ff4ff507f..34e780a450dbeabc91e3ca16ef8a922cd1711a06 100644 (file)
@@ -37,6 +37,65 @@ grub_arch_dl_check_header (void *ehdr)
   return GRUB_ERR_NONE;
 }
 
+void
+grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+                                grub_size_t *got)
+{
+  const Elf_Ehdr *e = ehdr;
+  const Elf_Shdr *s;
+  Elf_Word entsize;
+  unsigned i;
+
+  *tramp = 0;
+  *got = 0;
+
+  /* Find a symbol table.  */
+  for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
+       i < e->e_shnum;
+       i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
+    if (s->sh_type == SHT_SYMTAB)
+      break;
+
+  if (i == e->e_shnum)
+    return;
+
+  entsize = s->sh_entsize;
+
+  for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
+       i < e->e_shnum;
+       i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
+    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) == R_PPC_REL24)
+           (*tramp)++;
+       
+      }
+
+  return;
+}
+
+/* For low-endian reverse lis and addr_high as well as ori and addr_low. */
+struct trampoline
+{
+  grub_uint32_t lis;
+  grub_uint32_t ori;
+  grub_uint32_t mtctr;
+  grub_uint32_t bctr;
+};
+
+static const struct trampoline trampoline_template = 
+  {
+    0x3c000000,
+    0x60000000,
+    0x7c0903a6,
+    0x4e800420,
+  };
 
 /* Relocate symbols.  */
 grub_err_t
@@ -46,6 +105,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
   Elf_Shdr *s;
   Elf_Word entsize;
   unsigned i;
+  struct trampoline *tptr = mod->tramp;
 
   /* Find a symbol table.  */
   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
@@ -106,7 +166,20 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
                      Elf_Sword delta = value - (Elf_Word) addr;
 
                      if (delta << 6 >> 6 != delta)
-                       return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow");
+                       {
+                         COMPILE_TIME_ASSERT (sizeof (struct trampoline)
+                                              == GRUB_ARCH_DL_TRAMP_SIZE);
+                         grub_memcpy (tptr, &trampoline_template,
+                                      sizeof (*tptr));
+                         delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr;
+                         tptr->lis |= (((value) >> 16) & 0xffff);
+                         tptr->ori |= ((value) & 0xffff);
+                         tptr++;
+                       }
+                       
+                     if (delta << 6 >> 6 != delta)
+                       return grub_error (GRUB_ERR_BAD_MODULE,
+                                          "relocation overflow");
                      *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc);
                      break;
                    }
index 75cd71758998ac86c0a4cb8337fcc16d4d879933..886e966cc37cdf8c30ae58093cdcdda8e1acf045 100644 (file)
@@ -136,11 +136,12 @@ struct grub_dl
   Elf_Sym *symtab;
   void (*init) (struct grub_dl *mod);
   void (*fini) (void);
-#ifdef __ia64__
+#if defined (__ia64__) || defined (__powerpc__)
   void *got;
   void *tramp;
 #endif
   void *base;
+  grub_size_t sz;
   struct grub_dl *next;
 };
 typedef struct grub_dl *grub_dl_t;
@@ -176,10 +177,20 @@ void
 grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
                                 grub_size_t *got);
 
-#ifdef __ia64__
+#if defined (__ia64__)
 #define GRUB_ARCH_DL_TRAMP_ALIGN 16
 #define GRUB_ARCH_DL_GOT_ALIGN 16
 #define grub_arch_dl_get_tramp_got_size grub_ia64_dl_get_tramp_got_size
+#else
+void
+grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+                                grub_size_t *got);
+#endif
+
+#ifdef __powerpc__
+#define GRUB_ARCH_DL_TRAMP_SIZE 16
+#define GRUB_ARCH_DL_TRAMP_ALIGN 4
+#define GRUB_ARCH_DL_GOT_ALIGN 4
 #endif
 
 #endif