From 15a463d7420ecbe58ac595e3411f4cc466b10a9e Mon Sep 17 00:00:00 2001 From: Leif Lindholm Date: Sat, 16 Nov 2013 20:52:55 +0100 Subject: [PATCH] ARM 64 port by Leif Lindholm --- Makefile.util.def | 12 +- conf/Makefile.common | 3 + configure.ac | 8 ++ gentpl.py | 7 +- grub-core/Makefile.am | 5 + grub-core/Makefile.core.def | 14 ++ grub-core/kern/arm64/cache.c | 72 +++++++++++ grub-core/kern/arm64/cache_flush.S | 67 ++++++++++ grub-core/kern/arm64/dl.c | 199 +++++++++++++++++++++++++++++ grub-core/kern/arm64/dl_helper.c | 70 ++++++++++ grub-core/kern/arm64/efi/startup.S | 34 +++++ grub-core/lib/arm64/setjmp.S | 53 ++++++++ grub-core/lib/efi/halt.c | 2 +- grub-core/lib/setjmp.S | 2 + include/grub/arm64/efi/memory.h | 1 + include/grub/arm64/reloc.h | 24 ++++ include/grub/arm64/setjmp.h | 27 ++++ include/grub/arm64/time.h | 29 +++++ include/grub/arm64/types.h | 34 +++++ include/grub/efi/api.h | 3 +- include/grub/efi/pe32.h | 1 + include/grub/elf.h | 16 +++ include/grub/util/install.h | 1 + util/grub-install.c | 16 +++ util/grub-mkimagexx.c | 59 ++++++++- util/mkimage.c | 17 +++ 26 files changed, 759 insertions(+), 17 deletions(-) create mode 100644 grub-core/kern/arm64/cache.c create mode 100644 grub-core/kern/arm64/cache_flush.S create mode 100644 grub-core/kern/arm64/dl.c create mode 100644 grub-core/kern/arm64/dl_helper.c create mode 100644 grub-core/kern/arm64/efi/startup.S create mode 100644 grub-core/lib/arm64/setjmp.S create mode 100644 include/grub/arm64/efi/memory.h create mode 100644 include/grub/arm64/reloc.h create mode 100644 include/grub/arm64/setjmp.h create mode 100644 include/grub/arm64/time.h create mode 100644 include/grub/arm64/types.h diff --git a/Makefile.util.def b/Makefile.util.def index ede74687e..b17c89bc5 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -156,6 +156,8 @@ library = { common = grub-core/io/gzio.c; common = grub-core/io/lzopio.c; common = grub-core/kern/ia64/dl_helper.c; + common = grub-core/kern/arm/dl_helper.c; + common = grub-core/kern/arm64/dl_helper.c; common = grub-core/lib/minilzo/minilzo.c; common = grub-core/lib/xzembed/xz_dec_bcj.c; common = grub-core/lib/xzembed/xz_dec_lzma2.c; @@ -176,8 +178,6 @@ program = { extra_dist = grub-core/osdep/unix/config.c; common = util/config.c; - common = grub-core/kern/arm/dl_helper.c; - extra_dist = util/grub-mkimagexx.c; ldadd = libgrubmods.a; @@ -511,8 +511,6 @@ program = { common = grub-core/kern/emu/hostfs.c; common = grub-core/disk/host.c; - common = grub-core/kern/arm/dl_helper.c; - common = util/resolve.c; common = grub-core/kern/emu/argp_common.c; @@ -557,8 +555,6 @@ program = { common = grub-core/kern/emu/hostfs.c; common = grub-core/disk/host.c; - common = grub-core/kern/arm/dl_helper.c; - common = util/resolve.c; common = grub-core/kern/emu/argp_common.c; @@ -594,8 +590,6 @@ program = { common = grub-core/osdep/config.c; common = util/config.c; - common = grub-core/kern/arm/dl_helper.c; - common = util/resolve.c; enable = noemu; common = grub-core/kern/emu/argp_common.c; @@ -631,8 +625,6 @@ program = { common = grub-core/osdep/config.c; common = util/config.c; - common = grub-core/kern/arm/dl_helper.c; - common = util/resolve.c; common = grub-core/kern/emu/argp_common.c; common = grub-core/osdep/init.c; diff --git a/conf/Makefile.common b/conf/Makefile.common index 39d2f4d60..866998a15 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -23,6 +23,9 @@ if COND_arm CCASFLAGS_PLATFORM = -mthumb-interwork LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache endif +if COND_arm64 + CFLAGS_PLATFORM += -mcmodel=large +endif #FIXME: discover and check XEN headers CPPFLAGS_XEN = -I/usr/include diff --git a/configure.ac b/configure.ac index 9f8fb8a35..b7af65c70 100644 --- a/configure.ac +++ b/configure.ac @@ -99,6 +99,9 @@ case "$target_cpu" in arm*) target_cpu=arm; ;; + aarch64*) + target_cpu=arm64; + ;; esac # Specify the platform (such as firmware). @@ -120,6 +123,7 @@ if test "x$with_platform" = x; then mips-*) platform=arc ;; ia64-*) platform=efi ;; arm-*) platform=uboot ;; + arm64-*) platform=efi ;; *) AC_MSG_ERROR([unsupported CPU: "$target_cpu"]) ;; esac else @@ -160,6 +164,7 @@ case "$target_cpu"-"$platform" in mipsel-loongson) ;; arm-uboot) ;; arm-efi) ;; + arm64-efi) ;; *-emu) ;; *) AC_MSG_ERROR([platform "$platform" is not supported for target CPU "$target_cpu"]) ;; esac @@ -210,6 +215,7 @@ case "$platform" in esac case "$target_cpu" in arm) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_ARM=1" ;; + arm64) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_ARM64=1" ;; mips |mipsel) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MIPS=1" ;; sparc64) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_SPARC64=1" ;; esac @@ -1434,6 +1440,8 @@ AM_CONDITIONAL([COND_mipseb], [test x$target_cpu = xmips]) AM_CONDITIONAL([COND_arm], [test x$target_cpu = xarm ]) AM_CONDITIONAL([COND_arm_uboot], [test x$target_cpu = xarm -a x$platform = xuboot]) AM_CONDITIONAL([COND_arm_efi], [test x$target_cpu = xarm -a x$platform = xefi]) +AM_CONDITIONAL([COND_arm64], [test x$target_cpu = xarm64 ]) +AM_CONDITIONAL([COND_arm64_efi], [test x$target_cpu = xarm64 -a x$platform = xefi]) AM_CONDITIONAL([COND_HOST_HURD], [test x$host_kernel = xhurd]) AM_CONDITIONAL([COND_HOST_LINUX], [test x$host_kernel = xlinux]) diff --git a/gentpl.py b/gentpl.py index 3ec853ea0..19fab42e2 100644 --- a/gentpl.py +++ b/gentpl.py @@ -24,7 +24,7 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_xen", "x86_64_xen", "mips_loongson", "sparc64_ieee1275", "powerpc_ieee1275", "mips_arc", "ia64_efi", - "mips_qemu_mips", "arm_uboot", "arm_efi" ] + "mips_qemu_mips", "arm_uboot", "arm_efi", "arm64_efi" ] GROUPS = {} @@ -38,9 +38,10 @@ GROUPS["mips"] = [ "mips_loongson", "mips_qemu_mips", "mips_arc" ] GROUPS["sparc64"] = [ "sparc64_ieee1275" ] GROUPS["powerpc"] = [ "powerpc_ieee1275" ] GROUPS["arm"] = [ "arm_uboot", "arm_efi" ] +GROUPS["arm64"] = [ "arm64_efi" ] # Groups based on firmware -GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi" ] +GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] GROUPS["uboot"] = [ "arm_uboot" ] GROUPS["xen"] = [ "i386_xen", "x86_64_xen" ] @@ -66,7 +67,7 @@ GROUPS["terminfomodule"] = GRUB_PLATFORMS[:]; for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i) # Flattened Device Trees (FDT) -GROUPS["fdt"] = [ "arm_uboot", "arm_efi" ] +GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi" ] # Miscelaneous groups schedulded to disappear in future GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"] diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 151b9339a..ec76ca0e1 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -234,6 +234,11 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h endif +if COND_arm64_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +endif + if COND_emu KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/datetime.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/misc.h diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 4fff57f16..74aa7470e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -67,6 +67,9 @@ kernel = { arm_efi_ldflags = '-Wl,-r,-d'; arm_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + arm64_efi_ldflags = '-Wl,-r,-d'; + arm64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)'; i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x9000'; @@ -106,6 +109,7 @@ kernel = { powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S; arm_uboot_startup = kern/arm/uboot/startup.S; arm_efi_startup = kern/arm/efi/startup.S; + arm64_efi_startup = kern/arm64/efi/startup.S; common = kern/command.c; common = kern/corecmd.c; @@ -194,6 +198,8 @@ kernel = { arm_efi = kern/arm/efi/init.c; arm_efi = kern/arm/efi/misc.c; + arm64_efi = kern/arm/efi/init.c; + i386_pc = kern/i386/pc/init.c; i386_pc = kern/i386/pc/mmap.c; i386_pc = term/i386/pc/console.c; @@ -254,6 +260,11 @@ kernel = { arm = kern/arm/cache.c; arm = kern/arm/misc.S; + arm64 = kern/arm64/cache.c; + arm64 = kern/arm64/cache_flush.S; + arm64 = kern/arm64/dl.c; + arm64 = kern/arm64/dl_helper.c; + emu = disk/host.c; emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; @@ -750,6 +761,7 @@ module = { enable = mips_arc; enable = ia64_efi; enable = arm_efi; + enable = arm64_efi; enable = arm_uboot; }; @@ -845,6 +857,7 @@ module = { ia64_efi = lib/efi/reboot.c; x86_64_efi = lib/efi/reboot.c; arm_efi = lib/efi/reboot.c; + arm64_efi = lib/efi/reboot.c; powerpc_ieee1275 = lib/ieee1275/reboot.c; sparc64_ieee1275 = lib/ieee1275/reboot.c; mips_arc = lib/mips/arc/reboot.c; @@ -1707,6 +1720,7 @@ module = { enable = x86; enable = ia64_efi; enable = arm_efi; + enable = arm64_efi; enable = mips; }; diff --git a/grub-core/kern/arm64/cache.c b/grub-core/kern/arm64/cache.c new file mode 100644 index 000000000..c13ad2163 --- /dev/null +++ b/grub-core/kern/arm64/cache.c @@ -0,0 +1,72 @@ +/* + * 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 . + */ + +#include +#include + +grub_int64_t grub_arch_cache_dlinesz; +grub_int64_t grub_arch_cache_ilinesz; + +/* Prototypes for asm functions. */ +void grub_arch_sync_caches_real (grub_uint64_t address, grub_uint64_t end); + +static void +probe_caches (void) +{ + grub_uint32_t cache_type; + + /* Read Cache Type Register */ + asm volatile ("mrs %0, ctr_el0": "=r"(cache_type)); + + grub_arch_cache_dlinesz = 8 << ((cache_type >> 16) & 0xf); + grub_arch_cache_ilinesz = 8 << (cache_type & 0xf); + + grub_dprintf("cache", "D$ line size: %lld\n", + (long long) grub_arch_cache_dlinesz); + grub_dprintf("cache", "I$ line size: %lld\n", + (long long) grub_arch_cache_ilinesz); +} + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + grub_uint64_t start, end; + + if (grub_arch_cache_dlinesz == 0) + probe_caches(); + if (grub_arch_cache_dlinesz == 0) + grub_fatal ("Unknown cache line size!"); + + grub_dprintf("cache", "syncing caches for %p-%lx\n", + address, (grub_addr_t) address + len); + + /* Align here to both cache lines. Saves a tiny bit of asm complexity and + most of potential problems with different line sizes. */ + start = (grub_uint64_t) address; + end = (grub_uint64_t) address + len; + start = ALIGN_DOWN (start, grub_arch_cache_dlinesz); + start = ALIGN_DOWN (start, grub_arch_cache_ilinesz); + + end = ALIGN_UP (end, grub_arch_cache_dlinesz); + end = ALIGN_UP (end, grub_arch_cache_ilinesz); + + grub_dprintf("cache", "aligned to: %lx-%lx\n", + start, end); + + grub_arch_sync_caches_real (start, end); +} diff --git a/grub-core/kern/arm64/cache_flush.S b/grub-core/kern/arm64/cache_flush.S new file mode 100644 index 000000000..1a35120cf --- /dev/null +++ b/grub-core/kern/arm64/cache_flush.S @@ -0,0 +1,67 @@ +/* + * 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 . + */ + +#include + + .file "cache_flush.S" + .text + +/* + * Simple cache maintenance functions + */ + +// r0 - *beg (inclusive) +// r1 - *end (exclusive) +clean_dcache_range: + // Clean data cache for range to point-of-unification + ldr x2, =EXT_C(grub_arch_cache_dlinesz) + ldr x2, [x2] +1: cmp x0, x1 + bge 2f + dc cvau, x0 // Clean Virtual Address to PoU + add x0, x0, x2 // Next line + b 1b +2: dsb ish + ret + +// r0 - *beg (inclusive) +// r1 - *end (exclusive) +invalidate_icache_range: + // Invalidate instruction cache for range to point-of-unification + ldr x2, =EXT_C(grub_arch_cache_ilinesz) + ldr x2, [x2] +1: cmp x0, x1 + bge 2f + ic ivau, x0 // Invalidate Virtual Address to PoU + add x0, x0, x2 // Next line + b 1b + // Branch predictor invalidation not needed on AArch64 +2: dsb ish + isb + ret + +// void grub_arch_sync_caches_real (void *address, grub_size_t len) +FUNCTION(grub_arch_sync_caches_real) + dsb ish + stp x0, x30, [sp, #-16]! + stp x0, x1, [sp, #-16]! + bl clean_dcache_range + ldp x0, x1, [sp], #16 + bl invalidate_icache_range + ldp x0, x30, [sp], #16 + ret diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c new file mode 100644 index 000000000..afd0de2f3 --- /dev/null +++ b/grub-core/kern/arm64/dl.c @@ -0,0 +1,199 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Check if EHDR is a valid ELF header. + */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_AARCH64) + return grub_error (GRUB_ERR_BAD_OS, + N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* + * Unified function for both REL and RELA + */ +static grub_err_t +do_relX (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod) +{ + grub_err_t retval; + grub_dl_segment_t segment; + Elf_Rel *rel; + Elf_Rela *rela; + Elf_Sym *symbol; + int i, entnum; + unsigned long long entsize; + + /* Find the target segment for this relocation section. */ + for (segment = mod->segment ; segment != 0 ; segment = segment->next) + if (segment->section == relhdr->sh_info) + break; + if (!segment) + return grub_error (GRUB_ERR_EOF, N_("relocation segment not found")); + + rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset); + rela = (Elf_Rela *) rel; + if (relhdr->sh_type == SHT_RELA) + entsize = sizeof (Elf_Rela); + else + entsize = sizeof (Elf_Rel); + + entnum = relhdr->sh_size / entsize; + retval = GRUB_ERR_NONE; + + grub_dprintf("dl", "Processing %d relocation entries.\n", entnum); + + /* Step through all relocations */ + for (i = 0, symbol = mod->symtab; i < entnum; i++) + { + void *place; + grub_uint64_t sym_addr, symidx, reltype; + + if (rel->r_offset >= segment->size) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + symidx = ELF_R_SYM (rel->r_info); + reltype = ELF_R_TYPE (rel->r_info); + + sym_addr = symbol[symidx].st_value; + if (relhdr->sh_type == SHT_RELA) + sym_addr += rela->r_addend; + + place = (void *) ((grub_addr_t) segment->addr + rel->r_offset); + + switch (reltype) + { + case R_AARCH64_ABS64: + { + grub_uint64_t *abs_place = place; + + grub_dprintf ("dl", " reloc_abs64 %p => 0x%016llx\n", + place, (unsigned long long) sym_addr); + + *abs_place = (grub_uint64_t) sym_addr; + } + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + retval = grub_arm64_reloc_xxxx26 (place, sym_addr); + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + reltype); + } + + if (retval != GRUB_ERR_NONE) + break; + + rel = (Elf_Rel *) ((grub_addr_t) rel + entsize); + rela++; + } + + return retval; +} + +/* + * Verify that provided ELF header contains reference to a symbol table + */ +static int +has_symtab (Elf_Ehdr * e) +{ + int i; + Elf_Shdr *s; + + for (i = 0, s = (Elf_Shdr *) ((grub_addr_t) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize)) + if (s->sh_type == SHT_SYMTAB) + return 1; + + return 0; +} + +/* + * grub_arch_dl_relocate_symbols(): + * Locates the relocations section of the ELF object, and calls + * do_relX() to deal with it. + */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +{ + Elf_Ehdr *e = ehdr; + Elf_Shdr *s; + unsigned i; + + if (!has_symtab (e)) + return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); + +#define FIRST_SHDR(x) ((Elf_Shdr *) ((grub_addr_t)(x) + (x)->e_shoff)) +#define NEXT_SHDR(x, y) ((Elf_Shdr *) ((grub_addr_t)(y) + (x)->e_shentsize)) + + for (i = 0, s = FIRST_SHDR (e); i < e->e_shnum; i++, s = NEXT_SHDR (e, s)) + { + grub_err_t ret; + + switch (s->sh_type) + { + case SHT_REL: + case SHT_RELA: + { + ret = do_relX (s, e, mod); + if (ret != GRUB_ERR_NONE) + return ret; + } + break; + case SHT_ARM_ATTRIBUTES: + case SHT_NOBITS: + case SHT_NULL: + case SHT_PROGBITS: + case SHT_SYMTAB: + case SHT_STRTAB: + break; + default: + { + grub_dprintf ("dl", "unhandled section_type: %d (0x%08x)\n", + s->sh_type, s->sh_type); + return GRUB_ERR_NOT_IMPLEMENTED_YET; + }; + } + } + +#undef FIRST_SHDR +#undef NEXT_SHDR + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c new file mode 100644 index 000000000..b2f7ee591 --- /dev/null +++ b/grub-core/kern/arm64/dl_helper.c @@ -0,0 +1,70 @@ +/* dl_helper.c - relocation helper functions for modules and grub-mkimage */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static grub_ssize_t +sign_compress_offset (grub_ssize_t offset, int bitpos) +{ + return offset & ((1LL << (bitpos + 1)) - 1); +} + +/* + * grub_arm64_reloc_xxxx26(): + * + * JUMP26/CALL26 relocations for B and BL instructions. + */ + +grub_err_t +grub_arm64_reloc_xxxx26 (grub_uint32_t *place, Elf64_Addr adjust) +{ + grub_uint32_t insword, insmask; + grub_ssize_t offset; + const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1; + + insword = grub_le_to_cpu32 (*place); + insmask = 0xfc000000; + + offset = adjust; +#ifndef GRUB_UTIL + offset -= (grub_addr_t) place; +#endif + + if ((offset < offset_low) || (offset > offset_high)) + { + return grub_error (GRUB_ERR_BAD_MODULE, + N_("CALL26 Relocation out of range")); + } + + grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%llx\n", + place, offset > 0 ? '+' : '-', + offset < 0 ? (long long) -(unsigned long long) offset : offset); + + offset = sign_compress_offset (offset, 27) >> 2; + + *place = grub_cpu_to_le32 ((insword & insmask) | offset); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm64/efi/startup.S b/grub-core/kern/arm64/efi/startup.S new file mode 100644 index 000000000..781c2b099 --- /dev/null +++ b/grub-core/kern/arm64/efi/startup.S @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +#include + + .file "startup.S" + .text +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0. + */ + ldr x2, =EXT_C(grub_efi_image_handle) + str x0, [x2] + ldr x2, =EXT_C(grub_efi_system_table) + str x1, [x2] + ldr x2, =EXT_C(grub_main) + br x2 + + .end diff --git a/grub-core/lib/arm64/setjmp.S b/grub-core/lib/arm64/setjmp.S new file mode 100644 index 000000000..adaafe40f --- /dev/null +++ b/grub-core/lib/arm64/setjmp.S @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +#include + + .file "setjmp.S" + .text + +/* + * int grub_setjmp (grub_jmp_buf env) + */ +FUNCTION(grub_setjmp) + stp x19, x20, [x0], #16 + stp x21, x22, [x0], #16 + stp x23, x24, [x0], #16 + stp x25, x26, [x0], #16 + stp x27, x28, [x0], #16 + stp x29, x30, [x0], #16 + mov x1, sp + str x1, [x0] + mov x0, #0 + ret + +/* + * int grub_longjmp (grub_jmp_buf env, int val) + */ +FUNCTION(grub_longjmp) + ldp x19, x20, [x0], #16 + ldp x21, x22, [x0], #16 + ldp x23, x24, [x0], #16 + ldp x25, x26, [x0], #16 + ldp x27, x28, [x0], #16 + ldp x29, x30, [x0], #16 + ldr x2, [x0] + mov sp, x2 + cmp x1, #0 + csel x0, x1, x0, ne + ret diff --git a/grub-core/lib/efi/halt.c b/grub-core/lib/efi/halt.c index ce93db39f..e9441c844 100644 --- a/grub-core/lib/efi/halt.c +++ b/grub-core/lib/efi/halt.c @@ -29,7 +29,7 @@ void grub_halt (void) { grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); -#if !defined(__ia64__) && !defined(__arm__) +#if !defined(__ia64__) && !defined(__arm__) && !defined(__aarch64__) grub_acpi_halt (); #endif efi_call_4 (grub_efi_system_table->runtime_services->reset_system, diff --git a/grub-core/lib/setjmp.S b/grub-core/lib/setjmp.S index feb7b431c..f6e4905e2 100644 --- a/grub-core/lib/setjmp.S +++ b/grub-core/lib/setjmp.S @@ -13,6 +13,8 @@ #include "./ia64/longjmp.S" #elif defined(__arm__) #include "./arm/setjmp.S" +#elif defined(__aarch64__) +#include "./arm64/setjmp.S" #else #error "Unknown target cpu type" #endif diff --git a/include/grub/arm64/efi/memory.h b/include/grub/arm64/efi/memory.h new file mode 100644 index 000000000..c9a61bb77 --- /dev/null +++ b/include/grub/arm64/efi/memory.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/arm64/reloc.h b/include/grub/arm64/reloc.h new file mode 100644 index 000000000..606d71c77 --- /dev/null +++ b/include/grub/arm64/reloc.h @@ -0,0 +1,24 @@ +/* + * 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 . + */ + +#ifndef GRUB_ARM64_RELOC_H +#define GRUB_ARM64_RELOC_H 1 + +grub_err_t grub_arm64_reloc_xxxx26 (grub_uint32_t *target, Elf64_Addr sym_addr); + +#endif diff --git a/include/grub/arm64/setjmp.h b/include/grub/arm64/setjmp.h new file mode 100644 index 000000000..3ff7dfbf3 --- /dev/null +++ b/include/grub/arm64/setjmp.h @@ -0,0 +1,27 @@ +/* + * 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 . + */ + +#ifndef GRUB_SETJMP_CPU_HEADER +#define GRUB_SETJMP_CPU_HEADER 1 + +typedef unsigned long long grub_jmp_buf[13]; + +int grub_setjmp (grub_jmp_buf env) RETURNS_TWICE; +void grub_longjmp (grub_jmp_buf env, int val) __attribute__ ((noreturn)); + +#endif /* ! GRUB_SETJMP_CPU_HEADER */ diff --git a/include/grub/arm64/time.h b/include/grub/arm64/time.h new file mode 100644 index 000000000..4128506cb --- /dev/null +++ b/include/grub/arm64/time.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#ifndef KERNEL_CPU_TIME_HEADER +#define KERNEL_CPU_TIME_HEADER 1 + +static __inline void +grub_cpu_idle (void) +{ + /* FIXME: this can't work until we handle interrupts. */ +/* __asm__ __volatile__ ("wfi"); */ +} + +#endif /* ! KERNEL_CPU_TIME_HEADER */ diff --git a/include/grub/arm64/types.h b/include/grub/arm64/types.h new file mode 100644 index 000000000..d132c5eab --- /dev/null +++ b/include/grub/arm64/types.h @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +#ifndef GRUB_TYPES_CPU_HEADER +#define GRUB_TYPES_CPU_HEADER 1 + +/* The size of void *. */ +#define GRUB_TARGET_SIZEOF_VOID_P 8 + +/* The size of long. */ +#define GRUB_TARGET_SIZEOF_LONG 8 + +/* currently only support little-endian. */ +#undef GRUB_TARGET_WORDS_BIGENDIAN + +/* Unaligned accesses only supported if MMU enabled */ +#undef GRUB_HAVE_UNALIGNED_ACCESS + +#endif /* ! GRUB_TYPES_CPU_HEADER */ diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index f7503406c..3af09111a 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -1534,7 +1534,8 @@ struct grub_efi_block_io }; typedef struct grub_efi_block_io grub_efi_block_io_t; -#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) +#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \ + || defined (__aarch64__) #define efi_call_0(func) func() #define efi_call_1(func, a) func(a) diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 7cacabd45..4adea603b 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -67,6 +67,7 @@ struct grub_pe32_coff_header #define GRUB_PE32_MACHINE_IA64 0x200 #define GRUB_PE32_MACHINE_X86_64 0x8664 #define GRUB_PE32_MACHINE_ARMTHUMB_MIXED 0x01c2 +#define GRUB_PE32_MACHINE_ARM64 0xAA64 #define GRUB_PE32_RELOCS_STRIPPED 0x0001 #define GRUB_PE32_EXECUTABLE_IMAGE 0x0002 diff --git a/include/grub/elf.h b/include/grub/elf.h index f64d6a891..140d24d02 100644 --- a/include/grub/elf.h +++ b/include/grub/elf.h @@ -246,6 +246,7 @@ typedef struct #define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ #define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ #define EM_NUM 95 +#define EM_AARCH64 183 /* ARM 64-bit architecture */ /* If it is necessary to assign new unofficial EM_* values, please pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the @@ -2062,6 +2063,21 @@ typedef Elf32_Addr Elf32_Conflict; #define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ +/* AArch64 relocs. */ +#define R_AARCH64_NONE 0 /* No relocation. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_JUMP26 282 /* 26-bit relative. */ +#define R_AARCH64_CALL26 283 /* 26-bit relative. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD64 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL64 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL64 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ + /* ARM relocs. */ #define R_ARM_NONE 0 /* No reloc */ #define R_ARM_PC24 1 /* PC relative 26 bit branch */ diff --git a/include/grub/util/install.h b/include/grub/util/install.h index c1cd6b339..0aead0a8a 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -87,6 +87,7 @@ enum grub_install_plat GRUB_INSTALL_PLATFORM_ARM_EFI, GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS, GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS, + GRUB_INSTALL_PLATFORM_ARM64_EFI, GRUB_INSTALL_PLATFORM_MAX }; diff --git a/util/grub-install.c b/util/grub-install.c index 100f09379..b9b6e6911 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -256,6 +256,8 @@ get_default_platform (void) return "ia64-efi"; #elif defined (__arm__) return "arm-uboot"; +#elif defined (__aarch64__) + return "arm64-efi"; #elif defined (__amd64__) || defined (__x86_64__) || defined (__i386__) return grub_install_get_default_x86_platform (); #else @@ -389,6 +391,7 @@ have_bootdev (enum grub_install_plat pl) case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_I386_IEEE1275: case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275: case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: @@ -736,6 +739,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: case GRUB_INSTALL_PLATFORM_I386_IEEE1275: case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275: @@ -773,6 +777,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: case GRUB_INSTALL_PLATFORM_I386_IEEE1275: case GRUB_INSTALL_PLATFORM_ARM_UBOOT: @@ -818,6 +823,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: is_efi = 1; break; @@ -922,6 +928,9 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_ARM_EFI: efi_file = "BOOTARM.EFI"; break; + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + efi_file = "BOOTAARCH64.EFI"; + break; default: grub_util_error ("%s", _("You've found a bug")); break; @@ -946,6 +955,9 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_ARM_EFI: efi_file = "grubarm.efi"; break; + case GRUB_INSTALL_PLATFORM_ARM64_EFI: + efi_file = "grubarm64.efi"; + break; default: efi_file = "grub.efi"; break; @@ -1171,6 +1183,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: g = grub_util_guess_efi_drive (*curdev); break; @@ -1258,6 +1271,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: core_name = "core.efi"; snprintf (mkimage_target, sizeof (mkimage_target), @@ -1358,6 +1372,7 @@ main (int argc, char *argv[]) } break; case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: case GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS: case GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS: @@ -1488,6 +1503,7 @@ main (int argc, char *argv[]) case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: + case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: { char *dst = grub_util_path_concat (2, efidir, efi_file); diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 36a683d6f..b88e9b980 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -718,6 +718,35 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, break; } break; + case EM_AARCH64: + { + sym_addr += addend; + switch (ELF_R_TYPE (info)) + { + case R_AARCH64_ABS64: + { + *target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr); + } + break; + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + { + grub_err_t err; + sym_addr -= offset; + sym_addr -= SUFFIX (entry_point); + err = grub_arm64_reloc_xxxx26((grub_uint32_t *)target, + sym_addr); + if (err) + grub_util_error ("%s", grub_errmsg); + } + break; + default: + grub_util_error (_("relocation %d is not implemented yet"), + (unsigned long long) ELF_R_TYPE (info)); + break; + } + break; + } #endif #if defined(MKIMAGE_ELF32) case EM_ARM: @@ -995,6 +1024,32 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out, break; } break; + case EM_AARCH64: + switch (ELF_R_TYPE (info)) + { + case R_AARCH64_ABS64: + { + Elf_Addr addr; + + addr = section_address + offset; + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_DIR64, + addr, 0, current_address, + image_target); + } + break; + /* Relative relocations do not require fixup entries. */ + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + break; + default: + grub_util_error (_("fixup for relocation %d is not implemented yet"), + (unsigned long long) ELF_R_TYPE (info)); + break; + } + break; + break; #if defined(MKIMAGE_ELF32) case EM_ARM: switch (ELF_R_TYPE (info)) @@ -1358,7 +1413,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, image_target); if (*start == 0) grub_util_error ("start symbol is not defined"); - + SUFFIX (entry_point) = (Elf_Addr) *start; /* Resolve addresses in the virtual address space. */ @@ -1367,7 +1422,7 @@ SUFFIX (load_image) (const char *kernel_path, size_t *exec_size, num_sections, strtab, out_img, ia64_toff, ia64_got_off, image_target); - + *reloc_size = SUFFIX (make_reloc_section) (e, reloc_section, section_vaddresses, sections, section_entsize, num_sections, diff --git a/util/mkimage.c b/util/mkimage.c index 4a510228c..4bc82df2f 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -578,6 +579,22 @@ static const struct grub_install_image_target_desc image_targets[] = .pe_target = GRUB_PE32_MACHINE_ARMTHUMB_MIXED, .elf_target = EM_ARM, }, + { + .dirname = "arm64-efi", + .names = { "arm64-efi", NULL }, + .voidp_sizeof = 8, + .bigendian = 0, + .id = IMAGE_EFI, + .flags = PLATFORM_FLAGS_NONE, + .total_module_size = TARGET_NO_FIELD, + .decompressor_compressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_addr = TARGET_NO_FIELD, + .section_align = GRUB_PE32_SECTION_ALIGNMENT, + .vaddr_offset = EFI64_HEADER_SIZE, + .pe_target = GRUB_PE32_MACHINE_ARM64, + .elf_target = EM_AARCH64, + }, }; #define grub_target_to_host32(x) (grub_target_to_host32_real (image_target, (x)))