+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.
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
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)
mod->base = grub_memalign (talign, tsize);
if (!mod->base)
return grub_errno;
+ mod->sz = tsize;
ptr = mod->base;
#ifdef GRUB_MACHINE_EMU
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;
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. */
}
}
-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,
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
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);
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;
}
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;
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