Fix ia64-efi image generation on big-endian machines. Deduplicate

some code while on it.
	Reported by: Leif Lindholm.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2013-04-09 19:19:19 +02:00
parent 18866643f7
commit d5e2a158e1
6 changed files with 243 additions and 286 deletions

View file

@ -1,3 +1,9 @@
2013-04-08 Vladimir Serbinenko <phcoder@gmail.com>
Fix ia64-efi image generation on big-endian machines. Deduplicate
some code while on it.
Reported by: Leif Lindholm.
2013-04-08 Andrey Borzenkov <arvidjaar@gmail.com> 2013-04-08 Andrey Borzenkov <arvidjaar@gmail.com>
* grub-core/Makefile.core.def: Add kern/elfXX.c to elf module * grub-core/Makefile.core.def: Add kern/elfXX.c to elf module

View file

@ -23,6 +23,9 @@
#include <grub/err.h> #include <grub/err.h>
#include <grub/mm.h> #include <grub/mm.h>
#include <grub/i18n.h> #include <grub/i18n.h>
#include <grub/ia64/reloc.h>
#define MASK19 ((1 << 19) - 1)
/* Check if EHDR is a valid ELF header. */ /* Check if EHDR is a valid ELF header. */
grub_err_t grub_err_t
@ -41,126 +44,6 @@ grub_arch_dl_check_header (void *ehdr)
#pragma GCC diagnostic ignored "-Wcast-align" #pragma GCC diagnostic ignored "-Wcast-align"
#define MASK20 ((1 << 20) - 1)
#define MASK19 ((1 << 19) - 1)
struct unaligned_uint32
{
grub_uint32_t val;
} __attribute__ ((packed));
static void
add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value)
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
p = (struct unaligned_uint32 *) ((addr & ~3ULL) + 2);
p->val = ((((((p->val >> 2) & MASK20) + value) & MASK20) << 2)
| (p->val & ~(MASK20 << 2)));
break;
case 1:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 7);
p->val = ((((((p->val >> 3) & MASK20) + value) & MASK20) << 3)
| (p->val & ~(MASK20 << 3)));
break;
case 2:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 12);
p->val = ((((((p->val >> 4) & MASK20) + value) & MASK20) << 4)
| (p->val & ~(MASK20 << 4)));
break;
}
}
#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) )
static grub_uint32_t
add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value)
{
grub_uint32_t high, mid, low, c;
low = (a & 0x00007f);
mid = (a & 0x7fc000) >> 7;
high = (a & 0x003e00) << 7;
c = (low | mid | high) + value;
return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00
}
static void
add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
p = (struct unaligned_uint32 *) ((addr & ~3ULL) + 2);
p->val = ((add_value_to_slot_21_real (((p->val >> 2) & MASKF21), value) & MASKF21) << 2) | (p->val & ~(MASKF21 << 2));
break;
case 1:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 7);
p->val = ((add_value_to_slot_21_real (((p->val >> 3) & MASKF21), value) & MASKF21) << 3) | (p->val & ~(MASKF21 << 3));
break;
case 2:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 12);
p->val = ((add_value_to_slot_21_real (((p->val >> 4) & MASKF21), value) & MASKF21) << 4) | (p->val & ~(MASKF21 << 4));
break;
}
}
static const grub_uint8_t nopm[5] =
{
/* [MLX] nop.m 0x0 */
0x05, 0x00, 0x00, 0x00, 0x01
};
static const grub_uint8_t jump[0x20] =
{
/* ld8 r16=[r15],8 */
0x02, 0x80, 0x20, 0x1e, 0x18, 0x14,
/* mov r14=r1;; */
0xe0, 0x00, 0x04, 0x00, 0x42, 0x00,
/* nop.i 0x0 */
0x00, 0x00, 0x04, 0x00,
/* ld8 r1=[r15] */
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
/* mov b6=r16 */
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
/* br.few b6;; */
0x60, 0x00, 0x80, 0x00
};
struct ia64_trampoline
{
/* nop.m */
grub_uint8_t nop[5];
/* movl r15 = addr*/
grub_uint8_t addr_hi[6];
grub_uint8_t e0;
grub_uint8_t addr_lo[4];
grub_uint8_t jump[0x20];
};
static void
make_trampoline (struct ia64_trampoline *tr, grub_uint64_t addr)
{
COMPILE_TIME_ASSERT (sizeof (struct ia64_trampoline)
== GRUB_IA64_DL_TRAMP_SIZE);
grub_memcpy (tr->nop, nopm, sizeof (tr->nop));
tr->addr_hi[0] = ((addr & 0xc00000) >> 16);
tr->addr_hi[1] = (addr >> 24) & 0xff;
tr->addr_hi[2] = (addr >> 32) & 0xff;
tr->addr_hi[3] = (addr >> 40) & 0xff;
tr->addr_hi[4] = (addr >> 48) & 0xff;
tr->addr_hi[5] = (addr >> 56) & 0xff;
tr->e0 = 0xe0;
tr->addr_lo[0] = ((addr & 0x000f) << 4) | 0x01;
tr->addr_lo[1] = (((addr & 0x0070) >> 4) | ((addr & 0x070000) >> 11)
| ((addr & 0x200000) >> 17));
tr->addr_lo[2] = ((addr & 0x1f80) >> 5) | ((addr & 0x180000) >> 19);
tr->addr_lo[3] = ((addr & 0xe000) >> 13) | 0x60;
grub_memcpy (tr->jump, jump, sizeof (tr->jump));
}
/* Relocate symbols. */ /* Relocate symbols. */
grub_err_t grub_err_t
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
@ -170,7 +53,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
Elf_Word entsize; Elf_Word entsize;
unsigned i; unsigned i;
grub_uint64_t *gp, *gpptr; grub_uint64_t *gp, *gpptr;
struct ia64_trampoline *tr; struct grub_ia64_trampoline *tr;
gp = (grub_uint64_t *) mod->base; gp = (grub_uint64_t *) mod->base;
gpptr = (grub_uint64_t *) mod->got; gpptr = (grub_uint64_t *) mod->got;
@ -230,13 +113,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
case R_IA64_PCREL21B: case R_IA64_PCREL21B:
{ {
grub_uint64_t noff; grub_uint64_t noff;
make_trampoline (tr, value); grub_ia64_make_trampoline (tr, value);
noff = ((char *) tr - (char *) (addr & ~3)) >> 4; noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
tr++; tr = (struct grub_ia64_trampoline *) ((char *) tr + GRUB_IA64_DL_TRAMP_SIZE);
if (noff & ~MASK19) if (noff & ~MASK19)
return grub_error (GRUB_ERR_BAD_OS, return grub_error (GRUB_ERR_BAD_OS,
"trampoline offset too big (%lx)", noff); "trampoline offset too big (%lx)", noff);
add_value_to_slot_20b (addr, noff); grub_ia64_add_value_to_slot_20b (addr, noff);
} }
break; break;
case R_IA64_SEGREL64LSB: case R_IA64_SEGREL64LSB:
@ -250,7 +133,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
*(grub_uint64_t *) addr += value - addr; *(grub_uint64_t *) addr += value - addr;
break; break;
case R_IA64_GPREL22: case R_IA64_GPREL22:
add_value_to_slot_21 (addr, value - (grub_addr_t) gp); grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) gp);
break; break;
case R_IA64_LTOFF22X: case R_IA64_LTOFF22X:
@ -259,7 +142,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
value = *(grub_uint64_t *) sym->st_value + rel->r_addend; value = *(grub_uint64_t *) sym->st_value + rel->r_addend;
case R_IA64_LTOFF_FPTR22: case R_IA64_LTOFF_FPTR22:
*gpptr = value; *gpptr = value;
add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp); grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp);
gpptr++; gpptr++;
break; break;

View file

@ -22,9 +22,154 @@
#include <grub/misc.h> #include <grub/misc.h>
#include <grub/err.h> #include <grub/err.h>
#include <grub/mm.h> #include <grub/mm.h>
#include <grub/i18n.h>
#include <grub/ia64/reloc.h>
#pragma GCC diagnostic ignored "-Wcast-align" #pragma GCC diagnostic ignored "-Wcast-align"
#define MASK20 ((1 << 20) - 1)
#define MASK3 (~(grub_addr_t) 3)
void
grub_ia64_add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value)
{
grub_uint32_t val;
switch (addr & 3)
{
case 0:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 2)));
val = (((((val & MASK20) + value) & MASK20) << 2)
| (val & ~(MASK20 << 2)));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 2),
grub_cpu_to_le32 (val));
break;
case 1:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 7)));
val = ((((((val >> 3) & MASK20) + value) & MASK20) << 3)
| (val & ~(MASK20 << 3)));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 7),
grub_cpu_to_le32 (val));
break;
case 2:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 12)));
val = ((((((val >> 4) & MASK20) + value) & MASK20) << 4)
| (val & ~(MASK20 << 4)));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 12),
grub_cpu_to_le32 (val));
break;
}
}
#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) )
static grub_uint32_t
add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value)
{
grub_uint32_t high, mid, low, c;
low = (a & 0x00007f);
mid = (a & 0x7fc000) >> 7;
high = (a & 0x003e00) << 7;
c = (low | mid | high) + value;
return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00
}
void
grub_ia64_add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
{
grub_uint32_t val;
switch (addr & 3)
{
case 0:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 2)));
val = ((add_value_to_slot_21_real (((val >> 2) & MASKF21), value)
& MASKF21) << 2) | (val & ~(MASKF21 << 2));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 2),
grub_cpu_to_le32 (val));
break;
case 1:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 7)));
val = ((add_value_to_slot_21_real (((val >> 3) & MASKF21), value)
& MASKF21) << 3) | (val & ~(MASKF21 << 3));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 7),
grub_cpu_to_le32 (val));
break;
case 2:
val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *)
(addr & MASK3) + 12)));
val = ((add_value_to_slot_21_real (((val >> 4) & MASKF21), value)
& MASKF21) << 4) | (val & ~(MASKF21 << 4));
grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 12),
grub_cpu_to_le32 (val));
break;
}
}
static const grub_uint8_t nopm[5] =
{
/* [MLX] nop.m 0x0 */
0x05, 0x00, 0x00, 0x00, 0x01
};
#ifdef GRUB_UTIL
static grub_uint8_t jump[0x20] =
{
/* [MMI] add r15=r15,r1;; */
0x0b, 0x78, 0x3c, 0x02, 0x00, 0x20,
/* ld8 r16=[r15],8 */
0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0,
/* mov r14=r1;; */
0x01, 0x08, 0x00, 0x84,
/* [MIB] ld8 r1=[r15] */
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
/* mov b6=r16 */
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
/* br.few b6;; */
0x60, 0x00, 0x80, 0x00
};
#else
static const grub_uint8_t jump[0x20] =
{
/* ld8 r16=[r15],8 */
0x02, 0x80, 0x20, 0x1e, 0x18, 0x14,
/* mov r14=r1;; */
0xe0, 0x00, 0x04, 0x00, 0x42, 0x00,
/* nop.i 0x0 */
0x00, 0x00, 0x04, 0x00,
/* ld8 r1=[r15] */
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
/* mov b6=r16 */
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
/* br.few b6;; */
0x60, 0x00, 0x80, 0x00
};
#endif
void
grub_ia64_make_trampoline (struct grub_ia64_trampoline *tr, grub_uint64_t addr)
{
COMPILE_TIME_ASSERT (sizeof (struct grub_ia64_trampoline)
== GRUB_IA64_DL_TRAMP_SIZE);
grub_memcpy (tr->nop, nopm, sizeof (tr->nop));
tr->addr_hi[0] = ((addr & 0xc00000) >> 16);
tr->addr_hi[1] = (addr >> 24) & 0xff;
tr->addr_hi[2] = (addr >> 32) & 0xff;
tr->addr_hi[3] = (addr >> 40) & 0xff;
tr->addr_hi[4] = (addr >> 48) & 0xff;
tr->addr_hi[5] = (addr >> 56) & 0xff;
tr->e0 = 0xe0;
tr->addr_lo[0] = ((addr & 0x000f) << 4) | 0x01;
tr->addr_lo[1] = (((addr & 0x0070) >> 4) | ((addr & 0x070000) >> 11)
| ((addr & 0x200000) >> 17));
tr->addr_lo[2] = ((addr & 0x1f80) >> 5) | ((addr & 0x180000) >> 19);
tr->addr_lo[3] = ((addr & 0xe000) >> 13) | 0x60;
grub_memcpy (tr->jump, jump, sizeof (tr->jump));
}
void void
grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
grub_size_t *got) grub_size_t *got)
@ -35,26 +180,26 @@ grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
unsigned i; unsigned i;
/* Find a symbol table. */ /* Find a symbol table. */
for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu32 (e->e_shoff)); for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff));
i < grub_le_to_cpu16 (e->e_shnum); i < grub_le_to_cpu16 (e->e_shnum);
i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize))) i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize)))
if (grub_le_to_cpu32 (s->sh_type) == SHT_SYMTAB) if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_SYMTAB))
break; break;
if (i == grub_le_to_cpu16 (e->e_shnum)) if (i == grub_le_to_cpu16 (e->e_shnum))
return; return;
for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu32 (e->e_shoff)); for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff));
i < grub_le_to_cpu16 (e->e_shnum); i < grub_le_to_cpu16 (e->e_shnum);
i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize))) i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize)))
if (grub_le_to_cpu32 (s->sh_type) == SHT_RELA) if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_RELA))
{ {
Elf64_Rela *rel, *max; Elf64_Rela *rel, *max;
for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu32 (s->sh_offset)), for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 (s->sh_offset)),
max = rel + grub_le_to_cpu32 (s->sh_size) / grub_le_to_cpu16 (s->sh_entsize); max = rel + grub_le_to_cpu64 (s->sh_size) / grub_le_to_cpu64 (s->sh_entsize);
rel < max; rel++) rel < max; rel++)
switch (ELF64_R_TYPE (grub_le_to_cpu32 (rel->r_info))) switch (ELF64_R_TYPE (grub_le_to_cpu64 (rel->r_info)))
{ {
case R_IA64_PCREL21B: case R_IA64_PCREL21B:
cntt++; cntt++;

42
include/grub/ia64/reloc.h Normal file
View file

@ -0,0 +1,42 @@
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GRUB_IA64_RELOC_H
#define GRUB_IA64_RELOC_H 1
struct grub_ia64_trampoline;
void
grub_ia64_add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value);
void
grub_ia64_add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value);
void
grub_ia64_make_trampoline (struct grub_ia64_trampoline *tr, grub_uint64_t addr);
struct grub_ia64_trampoline
{
/* nop.m */
grub_uint8_t nop[5];
/* movl r15 = addr*/
grub_uint8_t addr_hi[6];
grub_uint8_t e0;
grub_uint8_t addr_lo[4];
grub_uint8_t jump[0x20];
};
#endif

View file

@ -40,6 +40,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <grub/efi/pe32.h> #include <grub/efi/pe32.h>
#include <grub/ia64/reloc.h>
#define _GNU_SOURCE 1 #define _GNU_SOURCE 1
#include <argp.h> #include <argp.h>
@ -1201,10 +1202,10 @@ generate_image (const char *dir, const char *prefix,
o->subsystem = grub_host_to_target16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION); o->subsystem = grub_host_to_target16 (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION);
/* Do these really matter? */ /* Do these really matter? */
o->stack_reserve_size = grub_host_to_target32 (0x10000); o->stack_reserve_size = grub_host_to_target64 (0x10000);
o->stack_commit_size = grub_host_to_target32 (0x10000); o->stack_commit_size = grub_host_to_target64 (0x10000);
o->heap_reserve_size = grub_host_to_target32 (0x10000); o->heap_reserve_size = grub_host_to_target64 (0x10000);
o->heap_commit_size = grub_host_to_target32 (0x10000); o->heap_commit_size = grub_host_to_target64 (0x10000);
o->num_data_directories o->num_data_directories
= grub_host_to_target32 (GRUB_PE32_NUM_DATA_DIRECTORIES); = grub_host_to_target32 (GRUB_PE32_NUM_DATA_DIRECTORIES);

View file

@ -117,7 +117,7 @@ SUFFIX (relocate_symbols) (Elf_Ehdr *e, Elf_Shdr *sections,
if (image_target->elf_target == EM_IA_64 && ELF_ST_TYPE (sym->st_info) if (image_target->elf_target == EM_IA_64 && ELF_ST_TYPE (sym->st_info)
== STT_FUNC) == STT_FUNC)
{ {
*jptr = sym->st_value; *jptr = grub_host_to_target64 (sym->st_value);
sym->st_value = (char *) jptr - (char *) jumpers + jumpers_addr; sym->st_value = (char *) jptr - (char *) jumpers + jumpers_addr;
jptr++; jptr++;
*jptr = 0; *jptr = 0;
@ -143,8 +143,8 @@ SUFFIX (get_symbol_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i,
Elf_Sym *sym; Elf_Sym *sym;
sym = (Elf_Sym *) ((char *) e sym = (Elf_Sym *) ((char *) e
+ grub_target_to_host32 (s->sh_offset) + grub_target_to_host (s->sh_offset)
+ i * grub_target_to_host32 (s->sh_entsize)); + i * grub_target_to_host (s->sh_entsize));
return sym->st_value; return sym->st_value;
} }
@ -153,7 +153,7 @@ static Elf_Addr *
SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset, SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset,
struct image_target_desc *image_target) struct image_target_desc *image_target)
{ {
return (Elf_Addr *) ((char *) e + grub_target_to_host32 (s->sh_offset) + offset); return (Elf_Addr *) ((char *) e + grub_target_to_host (s->sh_offset) + offset);
} }
#ifdef MKIMAGE_ELF64 #ifdef MKIMAGE_ELF64
@ -182,128 +182,6 @@ SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section,
} }
#endif #endif
#ifdef MKIMAGE_ELF64
struct unaligned_uint32
{
grub_uint32_t val;
} __attribute__ ((packed));
#define MASK20 ((1 << 20) - 1)
#define MASK19 ((1 << 19) - 1)
#define MASK3 (~(grub_addr_t) 3)
static void
add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value)
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
p = (struct unaligned_uint32 *) ((addr & MASK3) + 2);
p->val = ((((((p->val >> 2) & MASK20) + value) & MASK20) << 2)
| (p->val & ~(MASK20 << 2)));
break;
case 1:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & MASK3) + 7);
p->val = ((((((p->val >> 3) & MASK20) + value) & MASK20) << 3)
| (p->val & ~(MASK20 << 3)));
break;
case 2:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & MASK3) + 12);
p->val = ((((((p->val >> 4) & MASK20) + value) & MASK20) << 4)
| (p->val & ~(MASK20 << 4)));
break;
}
}
#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) )
static grub_uint32_t
add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value)
{
grub_uint32_t high, mid, low, c;
low = (a & 0x00007f);
mid = (a & 0x7fc000) >> 7;
high = (a & 0x003e00) << 7;
c = (low | mid | high) + value;
return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00
}
static void
add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
{
struct unaligned_uint32 *p;
switch (addr & 3)
{
case 0:
p = (struct unaligned_uint32 *) ((addr & MASK3) + 2);
p->val = ((add_value_to_slot_21_real (((p->val >> 2) & MASKF21), value) & MASKF21) << 2) | (p->val & ~(MASKF21 << 2));
break;
case 1:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & MASK3) + 7);
p->val = ((add_value_to_slot_21_real (((p->val >> 3) & MASKF21), value) & MASKF21) << 3) | (p->val & ~(MASKF21 << 3));
break;
case 2:
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & MASK3) + 12);
p->val = ((add_value_to_slot_21_real (((p->val >> 4) & MASKF21), value) & MASKF21) << 4) | (p->val & ~(MASKF21 << 4));
break;
}
}
struct ia64_kernel_trampoline
{
/* nop.m */
grub_uint8_t nop[5];
/* movl r15 = addr*/
grub_uint8_t addr_hi[6];
grub_uint8_t e0;
grub_uint8_t addr_lo[4];
grub_uint8_t jump[0x20];
};
static grub_uint8_t nopm[5] =
{
/* [MLX] nop.m 0x0 */
0x05, 0x00, 0x00, 0x00, 0x01
};
static grub_uint8_t jump[0x20] =
{
/* [MMI] add r15=r15,r1;; */
0x0b, 0x78, 0x3c, 0x02, 0x00, 0x20,
/* ld8 r16=[r15],8 */
0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0,
/* mov r14=r1;; */
0x01, 0x08, 0x00, 0x84,
/* [MIB] ld8 r1=[r15] */
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
/* mov b6=r16 */
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
/* br.few b6;; */
0x60, 0x00, 0x80, 0x00
};
static void
make_trampoline (struct ia64_kernel_trampoline *tr, grub_uint64_t addr)
{
grub_memcpy (tr->nop, nopm, sizeof (tr->nop));
tr->addr_hi[0] = ((addr & 0xc00000) >> 16);
tr->addr_hi[1] = (addr >> 24) & 0xff;
tr->addr_hi[2] = (addr >> 32) & 0xff;
tr->addr_hi[3] = (addr >> 40) & 0xff;
tr->addr_hi[4] = (addr >> 48) & 0xff;
tr->addr_hi[5] = (addr >> 56) & 0xff;
tr->e0 = 0xe0;
tr->addr_lo[0] = ((addr & 0x000f) << 4) | 0x01;
tr->addr_lo[1] = (((addr & 0x0070) >> 4) | ((addr & 0x070000) >> 11)
| ((addr & 0x200000) >> 17));
tr->addr_lo[2] = ((addr & 0x1f80) >> 5) | ((addr & 0x180000) >> 19);
tr->addr_lo[3] = ((addr & 0xe000) >> 13) | 0x60;
grub_memcpy (tr->jump, jump, sizeof (tr->jump));
}
#endif
/* Deal with relocation information. This function relocates addresses /* Deal with relocation information. This function relocates addresses
within the virtual address space starting from 0. So only relative within the virtual address space starting from 0. So only relative
addresses can be fully resolved. Absolute addresses must be relocated addresses can be fully resolved. Absolute addresses must be relocated
@ -320,8 +198,9 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
Elf_Half i; Elf_Half i;
Elf_Shdr *s; Elf_Shdr *s;
#ifdef MKIMAGE_ELF64 #ifdef MKIMAGE_ELF64
struct ia64_kernel_trampoline *tr = (void *) (pe_target + tramp_off); struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
grub_uint64_t *gpptr = (void *) (pe_target + got_off); grub_uint64_t *gpptr = (void *) (pe_target + got_off);
#define MASK19 ((1 << 19) - 1)
#endif #endif
for (i = 0, s = sections; for (i = 0, s = sections;
@ -352,9 +231,9 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
strtab + grub_target_to_host32 (s->sh_name), strtab + grub_target_to_host32 (s->sh_name),
strtab + grub_target_to_host32 (target_section->sh_name)); strtab + grub_target_to_host32 (target_section->sh_name));
rtab_size = grub_target_to_host32 (s->sh_size); rtab_size = grub_target_to_host (s->sh_size);
r_size = grub_target_to_host32 (s->sh_entsize); r_size = grub_target_to_host (s->sh_entsize);
rtab_offset = grub_target_to_host32 (s->sh_offset); rtab_offset = grub_target_to_host (s->sh_offset);
num_rs = rtab_size / r_size; num_rs = rtab_size / r_size;
for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset); for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset);
@ -375,7 +254,7 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
ELF_R_SYM (info), image_target); ELF_R_SYM (info), image_target);
addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ? addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
r->r_addend : 0; grub_target_to_host (r->r_addend) : 0;
switch (image_target->elf_target) switch (image_target->elf_target)
{ {
@ -461,14 +340,14 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
case R_IA64_PCREL21B: case R_IA64_PCREL21B:
{ {
grub_uint64_t noff; grub_uint64_t noff;
make_trampoline (tr, addend + sym_addr); grub_ia64_make_trampoline (tr, addend + sym_addr);
noff = ((char *) tr - (char *) pe_target noff = ((char *) tr - (char *) pe_target
- target_section_addr - (offset & ~3)) >> 4; - target_section_addr - (offset & ~3)) >> 4;
tr++; tr++;
if (noff & ~MASK19) if (noff & ~MASK19)
grub_util_error ("trampoline offset too big (%" grub_util_error ("trampoline offset too big (%"
PRIxGRUB_UINT64_T ")", noff); PRIxGRUB_UINT64_T ")", noff);
add_value_to_slot_20b ((grub_addr_t) target, noff); grub_ia64_add_value_to_slot_20b ((grub_addr_t) target, noff);
} }
break; break;
@ -478,8 +357,8 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
Elf_Sym *sym; Elf_Sym *sym;
sym = (Elf_Sym *) ((char *) e sym = (Elf_Sym *) ((char *) e
+ grub_target_to_host32 (symtab_section->sh_offset) + grub_target_to_host (symtab_section->sh_offset)
+ ELF_R_SYM (info) * grub_target_to_host32 (symtab_section->sh_entsize)); + ELF_R_SYM (info) * grub_target_to_host (symtab_section->sh_entsize));
if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
sym_addr = grub_target_to_host64 (*(grub_uint64_t *) (pe_target sym_addr = grub_target_to_host64 (*(grub_uint64_t *) (pe_target
+ sym->st_value + sym->st_value
@ -487,14 +366,14 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
} }
case R_IA64_LTOFF_FPTR22: case R_IA64_LTOFF_FPTR22:
*gpptr = grub_host_to_target64 (addend + sym_addr); *gpptr = grub_host_to_target64 (addend + sym_addr);
add_value_to_slot_21 ((grub_addr_t) target, grub_ia64_add_value_to_slot_21 ((grub_addr_t) target,
(char *) gpptr - (char *) pe_target (char *) gpptr - (char *) pe_target
+ image_target->vaddr_offset); + image_target->vaddr_offset);
gpptr++; gpptr++;
break; break;
case R_IA64_GPREL22: case R_IA64_GPREL22:
add_value_to_slot_21 ((grub_addr_t) target, grub_ia64_add_value_to_slot_21 ((grub_addr_t) target,
addend + sym_addr); addend + sym_addr);
break; break;
case R_IA64_PCREL64LSB: case R_IA64_PCREL64LSB:
@ -514,7 +393,8 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections,
+ addend + sym_addr); + addend + sym_addr);
grub_util_info ("relocating a direct entry to 0x%" grub_util_info ("relocating a direct entry to 0x%"
PRIxGRUB_UINT64_T " at the offset 0x%llx", PRIxGRUB_UINT64_T " at the offset 0x%llx",
*target, (unsigned long long) offset); grub_target_to_host64 (*target),
(unsigned long long) offset);
break; break;
/* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */
@ -650,8 +530,8 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
for (i = 0, s = sections; i < num_sections; for (i = 0, s = sections; i < num_sections;
i++, s = (Elf_Shdr *) ((char *) s + section_entsize)) i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) || if ((grub_target_to_host32 (s->sh_type) == SHT_REL) ||
(s->sh_type == grub_cpu_to_le32 (SHT_RELA))) (grub_target_to_host32 (s->sh_type) == SHT_RELA))
{ {
Elf_Rel *r; Elf_Rel *r;
Elf_Word rtab_size, r_size, num_rs; Elf_Word rtab_size, r_size, num_rs;
@ -662,9 +542,9 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
grub_util_info ("translating the relocation section %s", grub_util_info ("translating the relocation section %s",
strtab + grub_le_to_cpu32 (s->sh_name)); strtab + grub_le_to_cpu32 (s->sh_name));
rtab_size = grub_le_to_cpu32 (s->sh_size); rtab_size = grub_target_to_host (s->sh_size);
r_size = grub_le_to_cpu32 (s->sh_entsize); r_size = grub_target_to_host (s->sh_entsize);
rtab_offset = grub_le_to_cpu32 (s->sh_offset); rtab_offset = grub_target_to_host (s->sh_offset);
num_rs = rtab_size / r_size; num_rs = rtab_size / r_size;
section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)]; section_address = section_addresses[grub_le_to_cpu32 (s->sh_info)];
@ -676,8 +556,8 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out,
Elf_Addr info; Elf_Addr info;
Elf_Addr offset; Elf_Addr offset;
offset = grub_le_to_cpu32 (r->r_offset); offset = grub_target_to_host (r->r_offset);
info = grub_le_to_cpu32 (r->r_info); info = grub_target_to_host (r->r_info);
/* Necessary to relocate only absolute addresses. */ /* Necessary to relocate only absolute addresses. */
switch (image_target->elf_target) switch (image_target->elf_target)
@ -1027,7 +907,7 @@ SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size,
*kernel_sz = ALIGN_UP (*kernel_sz, 16); *kernel_sz = ALIGN_UP (*kernel_sz, 16);
grub_ia64_dl_get_tramp_got_size (e, &tramp, &got); grub_ia64_dl_get_tramp_got_size (e, &tramp, &got);
tramp *= sizeof (struct ia64_kernel_trampoline); tramp *= sizeof (struct grub_ia64_trampoline);
ia64_toff = *kernel_sz; ia64_toff = *kernel_sz;
*kernel_sz += ALIGN_UP (tramp, 16); *kernel_sz += ALIGN_UP (tramp, 16);