diff --git a/.bzrignore b/.bzrignore index 40c206522..d4e3d2dde 100644 --- a/.bzrignore +++ b/.bzrignore @@ -206,6 +206,8 @@ po/*.gmo po/LINGUAS include/grub/gcrypt/gcrypt.h include/grub/gcrypt/g10lib.h +grub-core/lib/dtc-grub +grub-core/Makefile.libfdt.def po/POTFILES.in po/POTFILES-shell.in grub-glue-efi diff --git a/ChangeLog b/ChangeLog index 69492ef85..dfd0558cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2013-07-18 Leif Lindholm +2013-07-18 Francesco Lavra +2013-07-18 Vladimir Serbinenko + + New ports to arm-uboot and arm-efi. + Mostly by Leif Lindholm with some additions from + Francesco Lavra and cleanup by Vladimir Serbinenko. + 2013-07-16 Vladimir Serbinenko * grub-core/loader/multiboot_elfxx.c: Check eip after v2p translation diff --git a/Makefile.util.def b/Makefile.util.def index 59de0c9d5..8c546e38d 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -167,6 +167,8 @@ program = { common = util/resolve.c; common = grub-core/kern/emu/argp_common.c; + common = grub-core/kern/arm/dl_helper.c; + extra_dist = util/grub-mkimagexx.c; ldadd = libgrubmods.a; diff --git a/acinclude.m4 b/acinclude.m4 index 6242fa699..08a05a03a 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -432,3 +432,23 @@ dnl program. AC_DEFUN([grub_TRANSFORM],[dnl AC_SUBST(AS_TR_SH([$1]), [`AS_ECHO([$1]) | sed "$program_transform_name"`])dnl ]) + +dnl Check if the C compiler supports `-mno-unaligned-access'. +AC_DEFUN([grub_CHECK_NO_UNALIGNED_ACCESS],[ +[# foobar +nua_possible=yes] +AC_MSG_CHECKING([whether `$CC' supports `-mno-unaligned-access']) +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +int main() { + return 0; +} +]])]) + +[if eval "$ac_compile -S -mno-unaligned-access -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [rm -f conftest.s +else + nua_possible=no] + AC_MSG_RESULT([no]) +[fi] +]) diff --git a/conf/Makefile.common b/conf/Makefile.common index 2e4d5ec88..c79e00608 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -37,6 +37,10 @@ if COND_sparc64_ieee1275 CFLAGS_PLATFORM += -mno-app-regs LDFLAGS_PLATFORM = -Wl,-melf64_sparc -mno-relax endif +if COND_arm + CFLAGS_PLATFORM += -mthumb-interwork -mlong-calls + LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache +endif # Other options @@ -115,6 +119,8 @@ CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/gnulib -I$(top_srcdir)/grub-core/g CFLAGS_POSIX = -fno-builtin CPPFLAGS_POSIX = -I$(top_srcdir)/grub-core/lib/posix_wrap +CPPFLAGS_LIBFDT = -I$(top_srcdir)/grub-core/lib/dtc-grub/libfdt $(CPPFLAGS_POSIX) + CFLAGS_GCRY = -Wno-error -Wno-missing-field-initializers $(CFLAGS_POSIX) CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -I$(top_srcdir)/include/grub/gcrypt diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index a76083bbe..ac5b90a80 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -18,6 +18,7 @@ EXTRA_DIST += conf/i386-pc-cygwin-img-ld.sc EXTRA_DIST += grub-core/Makefile.core.def EXTRA_DIST += grub-core/Makefile.gcry.def +EXTRA_DIST += grub-core/Makefile.libfdt.def EXTRA_DIST += grub-core/genmoddep.awk EXTRA_DIST += grub-core/genmod.sh.in diff --git a/configure.ac b/configure.ac index c9884d25b..4fe20a3e9 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,9 @@ case "$target_cpu" in target_cpu=mips; machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_CPU_MIPS=1"; ;; + arm*) + target_cpu=arm; + ;; esac # Specify the platform (such as firmware). @@ -116,6 +119,7 @@ if test "x$with_platform" = x; then mipsel-*) platform=loongson ;; mips-*) platform=arc ;; ia64-*) platform=efi ;; + arm-*) platform=uboot ;; *) AC_MSG_ERROR([unsupported CPU: "$target_cpu"]) ;; esac else @@ -151,6 +155,8 @@ case "$target_cpu"-"$platform" in mipsel-yeeloong) platform=loongson ;; mipsel-fuloong) platform=loongson ;; mipsel-loongson) ;; + arm-uboot) ;; + arm-efi) ;; *-emu) ;; *) AC_MSG_ERROR([platform "$platform" is not supported for target CPU "$target_cpu"]) ;; esac @@ -182,6 +188,7 @@ case "$platform" in multiboot) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MULTIBOOT=1" ;; efi) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_EFI=1" ;; ieee1275) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_IEEE1275=1" ;; + uboot) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_UBOOT=1" ;; qemu) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_QEMU=1" ;; pc) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_PCBIOS=1" ;; emu) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_EMU=1" ;; @@ -190,6 +197,7 @@ case "$platform" in arc) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_ARC=1" ;; esac case "$target_cpu" in + arm) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_ARM=1" ;; mips |mipsel) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MIPS=1" ;; sparc64) machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_SPARC64=1" ;; esac @@ -675,6 +683,14 @@ if test x"$sap_possible" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -mno-stack-arg-probe" fi +# -mno-unaligned-access +if test "$target_cpu" = arm; then + grub_CHECK_NO_UNALIGNED_ACCESS + if test x"$nua_possible" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-unaligned-access" + fi +fi + AC_ARG_ENABLE([werror], [AS_HELP_STRING([--disable-werror], [do not use -Werror when building GRUB])]) @@ -1160,6 +1176,9 @@ AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platf AM_CONDITIONAL([COND_mips], [test x$target_cpu = xmips -o x$target_cpu = xmipsel]) AM_CONDITIONAL([COND_mipsel], [test x$target_cpu = xmipsel]) 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_HOST_HURD], [test x$host_kernel = xhurd]) AM_CONDITIONAL([COND_HOST_LINUX], [test x$host_kernel = xlinux]) diff --git a/docs/grub.texi b/docs/grub.texi index 1e533641d..42e17e61c 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3436,6 +3436,7 @@ you forget a command, you can run the command @command{help} * crc:: Compute or check CRC32 checksums * cryptomount:: Mount a crypto device * date:: Display or set current date and time +* devicetree:: Load a device tree blob * drivemap:: Map a drive to another * echo:: Display a line of text * eval:: Evaluate agruments as GRUB commands @@ -3747,6 +3748,17 @@ hour, minute, and second unchanged. @end deffn +@node devicetree +@subsection linux + +@deffn Command devicetree file +Load a device tree blob (.dtb) from a filesystem, for later use by a Linux +kernel. Does not perform merging with any device tree supplied by firmware, +but rather replaces it completely. +@ref{GNU/Linux}. +@end deffn + + @node drivemap @subsection drivemap diff --git a/gentpl.py b/gentpl.py index 776b4067e..817f2b13d 100644 --- a/gentpl.py +++ b/gentpl.py @@ -23,7 +23,7 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_multiboot", "i386_ieee1275", "x86_64_efi", "mips_loongson", "sparc64_ieee1275", "powerpc_ieee1275", "mips_arc", "ia64_efi", - "mips_qemu_mips" ] + "mips_qemu_mips", "arm_uboot", "arm_efi" ] GROUPS = {} @@ -36,10 +36,12 @@ GROUPS["x86"] = GROUPS["i386"] + GROUPS["x86_64"] GROUPS["mips"] = [ "mips_loongson", "mips_qemu_mips", "mips_arc" ] GROUPS["sparc64"] = [ "sparc64_ieee1275" ] GROUPS["powerpc"] = [ "powerpc_ieee1275" ] +GROUPS["arm"] = [ "arm_uboot", "arm_efi" ] # Groups based on firmware -GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi" ] +GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] +GROUPS["uboot"] = [ "arm_uboot" ] # emu is a special case so many core functionality isn't needed on this platform GROUPS["noemu"] = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu") @@ -57,10 +59,13 @@ GROUPS["videomodules"] = GRUB_PLATFORMS[:]; for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i) # Similar for terminfo -GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["ieee1275"]; +GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["ieee1275"] + GROUPS["uboot"]; GROUPS["terminfomodule"] = GRUB_PLATFORMS[:]; for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i) +# Flattened Device Trees (FDT) +GROUPS["fdt"] = [ "arm_uboot", "arm_efi" ] + # Miscelaneous groups schedulded to disappear in future GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"] GROUPS["nopc"] = GRUB_PLATFORMS[:]; GROUPS["nopc"].remove("i386_pc") diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 5145d180d..feca25ece 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -203,6 +203,19 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h endif +if COND_arm_uboot +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/uboot.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +endif + +if COND_arm_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/efi/loader.h +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 ed9e251b2..06617d79a 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -59,6 +59,9 @@ kernel = { ia64_efi_ldflags = '-Wl,-r,-d'; ia64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + arm_efi_ldflags = '-Wl,-r,-d'; + arm_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'; @@ -81,6 +84,8 @@ kernel = { i386_qemu_cppflags = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)'; emu_cflags = '$(CFLAGS_GNULIB)'; emu_cppflags = '$(CPPFLAGS_GNULIB)'; + arm_uboot_ldflags = '-Wl,-Ttext=0x08000000'; + arm_uboot_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; i386_pc_startup = kern/i386/pc/startup.S; i386_efi_startup = kern/i386/efi/startup.S; @@ -92,6 +97,8 @@ kernel = { mips_startup = kern/mips/startup.S; sparc64_ieee1275_startup = kern/sparc64/ieee1275/crt0.S; powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S; + arm_uboot_startup = kern/arm/uboot/startup.S; + arm_efi_startup = kern/arm/efi/startup.S; common = kern/command.c; common = kern/corecmd.c; @@ -127,6 +134,12 @@ kernel = { ieee1275 = term/ieee1275/console.c; ieee1275 = kern/ieee1275/init.c; + uboot = disk/uboot/ubootdisk.c; + uboot = kern/uboot/uboot.c; + uboot = kern/uboot/init.c; + uboot = kern/uboot/hw.c; + uboot = term/uboot/console.c; + terminfoinkernel = term/terminfo.c; terminfoinkernel = term/tparm.c; terminfoinkernel = commands/extcmd.c; @@ -161,6 +174,9 @@ kernel = { ia64_efi = kern/ia64/dl.c; ia64_efi = kern/ia64/dl_helper.c; + arm_efi = kern/arm/efi/init.c; + arm_efi = kern/arm/efi/misc.c; + i386_pc = kern/i386/pc/init.c; i386_pc = kern/i386/pc/mmap.c; i386_pc = term/i386/pc/console.c; @@ -213,6 +229,13 @@ kernel = { sparc64_ieee1275 = kern/sparc64/dl.c; sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c; + arm = kern/arm/dl.c; + arm = kern/arm/dl_helper.c; + arm = kern/arm/cache_armv6.S; + arm = kern/arm/cache_armv7.S; + arm = kern/arm/cache.c; + arm = kern/arm/misc.S; + emu = disk/host.c; emu = gnulib/progname.c; emu = kern/emu/error.c; @@ -673,6 +696,8 @@ module = { enable = powerpc_ieee1275; enable = mips_arc; enable = ia64_efi; + enable = arm_efi; + enable = arm_uboot; }; module = { @@ -753,6 +778,7 @@ module = { efi = lib/efi/halt.c; ieee1275 = lib/ieee1275/halt.c; emu = lib/emu/halt.c; + uboot = lib/uboot/halt.c; }; module = { @@ -761,11 +787,13 @@ module = { i386 = lib/i386/reboot_trampoline.S; ia64_efi = lib/efi/reboot.c; x86_64_efi = lib/efi/reboot.c; + arm_efi = lib/efi/reboot.c; powerpc_ieee1275 = lib/ieee1275/reboot.c; sparc64_ieee1275 = lib/ieee1275/reboot.c; mips_arc = lib/mips/arc/reboot.c; mips_loongson = lib/mips/loongson/reboot.c; mips_qemu_mips = lib/mips/qemu_mips/reboot.c; + uboot = lib/uboot/reboot.c; common = commands/reboot.c; }; @@ -1441,6 +1469,7 @@ module = { name = datetime; cmos = lib/cmos_datetime.c; efi = lib/efi/datetime.c; + uboot = lib/uboot/datetime.c; sparc64_ieee1275 = lib/ieee1275/datetime.c; powerpc_ieee1275 = lib/ieee1275/datetime.c; sparc64_ieee1275 = lib/ieee1275/cmos.c; @@ -1460,6 +1489,7 @@ module = { extra_dist = lib/powerpc/setjmp.S; extra_dist = lib/ia64/setjmp.S; extra_dist = lib/ia64/longjmp.S; + extra_dist = lib/arm/setjmp.S; }; module = { @@ -1539,6 +1569,8 @@ module = { powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c; sparc64_ieee1275 = loader/sparc64/ieee1275/linux.c; ia64_efi = loader/ia64/efi/linux.c; + arm = loader/arm/linux.c; + arm = lib/fdt.c; common = loader/linux.c; common = lib/cmdline.c; enable = noemu; @@ -1590,6 +1622,7 @@ module = { enable = x86; enable = ia64_efi; + enable = arm_efi; enable = mips; }; @@ -1922,6 +1955,12 @@ module = { enable = ieee1275; }; +module = { + name = ubootnet; + common = net/drivers/uboot/ubootnet.c; + enable = uboot; +}; + module = { name = efinet; common = net/drivers/efi/efinet.c; diff --git a/grub-core/disk/uboot/ubootdisk.c b/grub-core/disk/uboot/ubootdisk.c new file mode 100644 index 000000000..9b9fc6b4a --- /dev/null +++ b/grub-core/disk/uboot/ubootdisk.c @@ -0,0 +1,299 @@ +/* ubootdisk.c - disk subsystem support for U-Boot platforms */ +/* + * 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 +#include +#include +#include + +static struct ubootdisk_data *hd_devices; +static int hd_num; +static int hd_max; + +/* + * grub_ubootdisk_register(): + * Called for each disk device enumerated as part of U-Boot initialization + * code. + */ +grub_err_t +grub_ubootdisk_register (struct device_info *newdev) +{ + struct ubootdisk_data *d; + +#define STOR_TYPE(x) ((x) & 0x0ff0) + switch (STOR_TYPE (newdev->type)) + { + case DT_STOR_IDE: + case DT_STOR_SATA: + case DT_STOR_MMC: + case DT_STOR_USB: + /* hd */ + if (hd_num == hd_max) + { + int new_num; + new_num = (hd_max ? hd_max * 2 : 1); + d = grub_realloc(hd_devices, + sizeof (struct ubootdisk_data) * new_num); + if (!d) + return grub_errno; + hd_devices = d; + hd_max = new_num; + } + + d = &hd_devices[hd_num]; + hd_num++; + break; + default: + return GRUB_ERR_BAD_DEVICE; + break; + } + + d->dev = newdev; + d->cookie = newdev->cookie; + d->opencount = 0; + + return 0; +} + +/* + * uboot_disk_iterate(): + * Iterator over enumerated disk devices. + */ +static int +uboot_disk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + char buf[16]; + int count; + + switch (pull) + { + case GRUB_DISK_PULL_NONE: + /* "hd" - built-in mass-storage */ + for (count = 0 ; count < hd_num; count++) + { + grub_snprintf (buf, sizeof (buf) - 1, "hd%d", count); + grub_dprintf ("ubootdisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + break; + default: + return 0; + } + + return 0; +} + +/* Helper function for uboot_disk_open. */ +static struct ubootdisk_data * +get_hd_device (int num) +{ + if (num < hd_num) + return &hd_devices[num]; + + return NULL; +} + +/* + * uboot_disk_open(): + * Opens a disk device already enumerated. + */ +static grub_err_t +uboot_disk_open (const char *name, struct grub_disk *disk) +{ + struct ubootdisk_data *d; + struct device_info *devinfo; + int num; + int retval; + + grub_dprintf ("ubootdisk", "Opening '%s'\n", name); + + num = grub_strtoul (name + 2, 0, 10); + if (grub_errno != GRUB_ERR_NONE) + { + grub_dprintf ("ubootdisk", "Opening '%s' failed, invalid number\n", + name); + goto fail; + } + + if (name[1] != 'd') + { + grub_dprintf ("ubootdisk", "Opening '%s' failed, invalid name\n", name); + goto fail; + } + + switch (name[0]) + { + case 'h': + d = get_hd_device (num); + break; + default: + goto fail; + } + + if (!d) + goto fail; + + /* + * Subsystems may call open on the same device recursively - but U-Boot + * does not deal with this. So simply keep track of number of calls and + * return success if already open. + */ + if (d->opencount > 0) + { + grub_dprintf ("ubootdisk", "(%s) already open\n", disk->name); + d->opencount++; + retval = 0; + } + else + { + retval = grub_uboot_dev_open (d->dev); + if (retval != 0) + goto fail; + d->opencount = 1; + } + + grub_dprintf ("ubootdisk", "cookie: 0x%08x\n", (grub_addr_t) d->cookie); + disk->id = (grub_addr_t) d->cookie; + + devinfo = d->dev; + + d->block_size = devinfo->di_stor.block_size; + if (d->block_size == 0) + { + grub_printf ("%s: no block size!\n", __FUNCTION__); + return GRUB_ERR_IO; + } + + for (disk->log_sector_size = 0; + (1U << disk->log_sector_size) < d->block_size; + disk->log_sector_size++); + + grub_dprintf ("ubootdisk", "(%s) blocksize=%d, log_sector_size=%d\n", + disk->name, d->block_size, disk->log_sector_size); + + if (devinfo->di_stor.block_count) + disk->total_sectors = devinfo->di_stor.block_count; + else + disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + + disk->data = d; + + return GRUB_ERR_NONE; + +fail: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); +} + +static void +uboot_disk_close (struct grub_disk *disk) +{ + struct ubootdisk_data *d; + int retval; + + d = disk->data; + + /* + * In mirror of open function, keep track of number of calls to close and + * send on to U-Boot only when opencount would decrease to 0. + */ + if (d->opencount > 1) + { + grub_dprintf ("ubootdisk", "Closed (%s)\n", disk->name); + + d->opencount--; + } + else if (d->opencount == 1) + { + retval = grub_uboot_dev_close (d->dev); + d->opencount--; + grub_dprintf ("ubootdisk", "closed %s (%d)\n", disk->name, retval); + } + else + { + grub_dprintf ("ubootdisk", "device %s not open!\n", disk->name); + } +} + +/* + * uboot_disk_read(): + * Called from within disk subsystem to read a sequence of blocks into the + * disk cache. Maps directly on top of U-Boot API, only wrap in some error + * handling. + */ +static grub_err_t +uboot_disk_read (struct grub_disk *disk, + grub_disk_addr_t offset, grub_size_t numblocks, char *buf) +{ + struct ubootdisk_data *d; + grub_size_t real_size; + int retval; + + d = disk->data; + + retval = grub_uboot_dev_read (d->dev, buf, numblocks, offset, &real_size); + grub_dprintf ("ubootdisk", + "retval=%d, numblocks=%d, real_size=%llu, sector=%llu\n", + retval, numblocks, (grub_uint64_t) real_size, + (grub_uint64_t) offset); + if (retval != 0) + return grub_error (GRUB_ERR_IO, "U-Boot disk read error"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +uboot_disk_write (struct grub_disk *disk __attribute__ ((unused)), + grub_disk_addr_t sector __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused)), + const char *buf __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "attempt to write (not supported)"); +} + +static struct grub_disk_dev grub_ubootdisk_dev = { + .name = "ubootdisk", + .id = GRUB_DISK_DEVICE_UBOOTDISK_ID, + .iterate = uboot_disk_iterate, + .open = uboot_disk_open, + .close = uboot_disk_close, + .read = uboot_disk_read, + .write = uboot_disk_write, + .next = 0 +}; + +void +grub_ubootdisk_init (void) +{ + grub_disk_dev_register (&grub_ubootdisk_dev); +} + +void +grub_ubootdisk_fini (void) +{ + grub_disk_dev_unregister (&grub_ubootdisk_dev); +} diff --git a/grub-core/kern/arm/cache.S b/grub-core/kern/arm/cache.S new file mode 100644 index 000000000..cd28dd908 --- /dev/null +++ b/grub-core/kern/arm/cache.S @@ -0,0 +1,130 @@ +/* + * 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.S" + .text + .syntax unified + .arm +#if !defined (ARMV6) && !defined (ARMV7) +# error Unsupported architecture version! +#endif + + .align 2 + +/* + * Simple cache maintenance functions + */ + +@ r0 - *beg (inclusive) +@ r1 - *end (exclusive) +clean_dcache_range: + @ Clean data cache for range to point-of-unification + ldr r2, =EXT_C(grub_arch_cache_dlinesz) + sub r3, r2, #1 @ align "beg" to start of line + mvn r3, r3 + and r0, r0, r3 +1: cmp r0, r1 + bge 2f +#ifdef ARMV6 + mcr p15, 0, r0, c7, c10, 1 @ Clean data cache line by MVA +#else + mcr p15, 0, r0, c7, c11, 1 @ DCCMVAU +#endif + add r0, r0, r2 @ Next line + b 1b +2: DSB + bx lr + +@ r0 - *beg (inclusive) +@ r1 - *end (exclusive) +invalidate_icache_range: + @ Invalidate instruction cache for range to point-of-unification + ldr r2, =EXT_C(grub_arch_cache_ilinesz) + sub r3, r2, #1 @ align "beg" to start of line + mvn r3, r3 + and r0, r0, r3 +1: cmp r0, r1 + bge 2f + mcr p15, 0, r0, c7, c5, 1 @ ICIMVAU + add r0, r0, r2 @ Next line + b 1b + @ Branch predictor invalidate all +2: mcr p15, 0, r0, c7, c5, 6 @ BPIALL + DSB + ISB + bx lr + +@void grub_arch_sync_caches (void *address, grub_size_t len) +#ifdef ARMV6 +FUNCTION(grub_arch_sync_caches_armv6) +#else +FUNCTION(grub_arch_sync_caches_armv7) +#endif + add r1, r0, r1 + DSB + push {r0-r1, r4-r6, lr} + ldrdeq r0, r1, [sp] + bl clean_dcache_range + pop {r0, r1} + bl invalidate_icache_range + pop {r4-r6, pc} + +#ifdef ARMV6 +FUNCTION(grub_arm_disable_caches_mmu_armv6) +#else +FUNCTION(grub_arm_disable_caches_mmu_armv7) +#endif + + push {r4, lr} + + @ disable D-cache + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) + mcr p15, 0, r0, c1, c0, 0 + DSB + ISB + + @ clean/invalidate D-cache + bl clean_invalidate_dcache + + @ disable I-cache + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 12) + mcr p15, 0, r0, c1, c0, 0 + DSB + ISB + + @ invalidate I-cache (also invalidates branch predictors) + mcr p15, 0, r0, c7, c5, 0 + DSB + ISB + + @ clear SCTLR M bit + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 0) + mcr p15, 0, r0, c1, c0, 0 + + mcr p15, 0, r0, c8, c7, 0 @ invalidate TLB + mcr p15, 0, r0, c7, c5, 6 @ invalidate branch predictor + DSB + ISB + + pop {r4, pc} + diff --git a/grub-core/kern/arm/cache.c b/grub-core/kern/arm/cache.c new file mode 100644 index 000000000..88054f3fc --- /dev/null +++ b/grub-core/kern/arm/cache.c @@ -0,0 +1,108 @@ +#include +#include +#include + +/* This is only about cache architecture. It doesn't imply + the CPU architecture. */ +static enum + { + ARCH_UNKNOWN, + ARCH_ARMV6, + ARCH_ARMV6_UNIFIED, + ARCH_ARMV7 + } type = ARCH_UNKNOWN; + +grub_uint32_t grub_arch_cache_dlinesz; +grub_uint32_t grub_arch_cache_ilinesz; + +/* Prototypes for asm functions. */ +void grub_arch_sync_caches_armv6 (void *address, grub_size_t len); +void grub_arch_sync_caches_armv7 (void *address, grub_size_t len); +void grub_arm_disable_caches_mmu_armv6 (void); +void grub_arm_disable_caches_mmu_armv7 (void); + +static void +probe_caches (void) +{ + grub_uint32_t main_id, cache_type; + + /* Read main ID Register */ + asm volatile ("mrc p15, 0, %0, c0, c0, 0": "=r"(main_id)); + + if (((main_id >> 16) & 0x7) != 0x7) + grub_fatal ("Unsupported ARM ID 0x%x", main_id); + + /* Read Cache Type Register */ + asm volatile ("mrc p15, 0, %0, c0, c0, 1": "=r"(cache_type)); + + switch (cache_type >> 24) + { + case 0x04: + case 0x0a: + case 0x0c: + case 0x0e: + case 0x1c: + grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); + grub_arch_cache_ilinesz = 8 << (cache_type & 3); + type = ARCH_ARMV6_UNIFIED; + break; + case 0x05: + case 0x0b: + case 0x0d: + case 0x0f: + case 0x1d: + grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); + grub_arch_cache_ilinesz = 8 << (cache_type & 3); + type = ARCH_ARMV6; + break; + case 0x80 ... 0x8f: + grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf); + grub_arch_cache_ilinesz = 4 << (cache_type & 0xf); + type = ARCH_ARMV7; + break; + default: + grub_fatal ("Unsupported cache type 0x%x", cache_type); + } +} + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + if (type == ARCH_UNKNOWN) + probe_caches (); + switch (type) + { + case ARCH_ARMV6: + grub_arch_sync_caches_armv6 (address, len); + break; + case ARCH_ARMV7: + grub_arch_sync_caches_armv7 (address, len); + break; + /* Nothing to do. */ + case ARCH_ARMV6_UNIFIED: + break; + /* Pacify GCC. */ + case ARCH_UNKNOWN: + break; + } +} + +void +grub_arm_disable_caches_mmu (void) +{ + if (type == ARCH_UNKNOWN) + probe_caches (); + switch (type) + { + case ARCH_ARMV6_UNIFIED: + case ARCH_ARMV6: + grub_arm_disable_caches_mmu_armv6 (); + break; + case ARCH_ARMV7: + grub_arm_disable_caches_mmu_armv7 (); + break; + /* Pacify GCC. */ + case ARCH_UNKNOWN: + break; + } +} diff --git a/grub-core/kern/arm/cache_armv6.S b/grub-core/kern/arm/cache_armv6.S new file mode 100644 index 000000000..e9da423dd --- /dev/null +++ b/grub-core/kern/arm/cache_armv6.S @@ -0,0 +1,35 @@ +/* + * 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_armv6.S" + .text + .syntax unified + .arm + .arch armv6 +# define DMB mcr p15, 0, r0, c7, c10, 5 +# define DSB mcr p15, 0, r0, c7, c10, 4 +# define ISB mcr p15, 0, r0, c7, c5, 4 +#define ARMV6 1 + +clean_invalidate_dcache: + mcr p15, 0, r0, c7, c14, 0 @ Clean/Invalidate D-cache + bx lr + +#include "cache.S" \ No newline at end of file diff --git a/grub-core/kern/arm/cache_armv7.S b/grub-core/kern/arm/cache_armv7.S new file mode 100644 index 000000000..0c16b1047 --- /dev/null +++ b/grub-core/kern/arm/cache_armv7.S @@ -0,0 +1,114 @@ +/* + * 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_armv7.S" + .text + .syntax unified + .arm + .arch armv7a +# define DMB dmb +# define DSB dsb +# define ISB isb +#define ARMV7 1 + + @ r0 - CLIDR + @ r1 - LoC + @ r2 - current level + @ r3 - num sets + @ r4 - num ways + @ r5 - current set + @ r6 - current way + @ r7 - line size + @ r8 - scratch + @ r9 - scratch + @ r10 - scratch + @ r11 - scratch +clean_invalidate_dcache: + push {r4-r12, lr} + mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR + lsr r1, r0, #24 @ Extract LoC + and r1, r1, #0x7 + + mov r2, #0 @ First level, L1 +2: and r8, r0, #7 @ cache type at current level + cmp r8, #2 + blt 5f @ instruction only, or none, skip level + + @ set current cache level/type (for CCSIDR read) + lsl r8, r2, #1 + mcr p15, 2, r8, c0, c0, 0 @ Write CSSELR (level, type: data/uni) + + @ read current cache information + mrc p15, 1, r8, c0, c0, 0 @ Read CCSIDR + lsr r3, r8, #13 @ Number of sets -1 + ldr r9, =0x3fff + and r3, r3, r9 + lsr r4, r8, #3 @ Number of ways -1 + ldr r9, =0x1ff + and r4, r4, r9 + and r7, r8, #7 @ log2(line size in words) - 2 + add r7, r7, #2 @ adjust + mov r8, #1 + lsl r7, r8, r7 @ -> line size in words + lsl r7, r7, #2 @ -> bytes + + @ set loop + mov r5, #0 @ current set = 0 +3: lsl r8, r2, #1 @ insert level + clz r9, r7 @ calculate set field offset + mov r10, #31 + sub r9, r10, r9 + lsl r10, r5, r9 + orr r8, r8, r10 @ insert set field + + @ way loop + @ calculate way field offset + mov r6, #0 @ current way = 0 + add r10, r4, #1 + clz r9, r10 @ r9 = way field offset + add r9, r9, #1 +4: lsl r10, r6, r9 + orr r11, r8, r10 @ insert way field + + @ clean and invalidate line by set/way + mcr p15, 0, r11, c7, c14, 2 @ DCCISW + + @ next way + add r6, r6, #1 + cmp r6, r4 + ble 4b + + @ next set + add r5, r5, #1 + cmp r5, r3 + ble 3b + + @ next level +5: lsr r0, r0, #3 @ align next level CLIDR 'type' field + add r2, r2, #1 @ increment cache level counter + cmp r2, r1 + blt 2b @ outer loop + + @ return +6: DSB + ISB + pop {r4-r12, pc} + +#include "cache.S" \ No newline at end of file diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c new file mode 100644 index 000000000..4d7f34b2b --- /dev/null +++ b/grub-core/kern/arm/dl.c @@ -0,0 +1,201 @@ +/* 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 + +/************************************************* + * Runtime dynamic linker with helper functions. * + *************************************************/ +static grub_err_t +do_relocations (Elf_Shdr * relhdr, Elf_Ehdr * e, grub_dl_t mod) +{ + grub_dl_segment_t seg; + Elf_Rel *rel; + Elf_Sym *sym; + int i, entnum; + + entnum = relhdr->sh_size / sizeof (Elf_Rel); + + /* Find the target segment for this relocation section. */ + for (seg = mod->segment ; seg ; seg = seg->next) + if (seg->section == relhdr->sh_info) + break; + if (!seg) + return grub_error (GRUB_ERR_EOF, N_("relocation segment not found")); + + rel = (Elf_Rel *) ((grub_addr_t) e + relhdr->sh_offset); + + /* Step through all relocations */ + for (i = 0, sym = mod->symtab; i < entnum; i++) + { + Elf_Addr *target, sym_addr; + int relsym, reltype; + grub_err_t retval; + + if (seg->size < rel[i].r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + relsym = ELF_R_SYM (rel[i].r_info); + reltype = ELF_R_TYPE (rel[i].r_info); + target = (void *) ((grub_addr_t) seg->addr + rel[i].r_offset); + + sym_addr = sym[relsym].st_value; + + switch (reltype) + { + case R_ARM_ABS32: + { + /* Data will be naturally aligned */ + retval = grub_arm_reloc_abs32 (target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + case R_ARM_CALL: + case R_ARM_JUMP24: + { + retval = grub_arm_reloc_jump24 (target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + { + /* Thumb instructions can be 16-bit aligned */ + retval = grub_arm_reloc_thm_call ((grub_uint16_t *) target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + case R_ARM_THM_JUMP19: + { + /* Thumb instructions can be 16-bit aligned */ + retval = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + reltype); + } + } + + return GRUB_ERR_NONE; +} + + +/* + * 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] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_ARM) + return grub_error (GRUB_ERR_BAD_OS, + N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* + * 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_uint32_t) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((grub_uint32_t) s + e->e_shentsize)) + if (s->sh_type == SHT_SYMTAB) + return 1; + + return 0; +} + +/* + * grub_arch_dl_relocate_symbols(): + * Only externally visible function in this file. + * Locates the relocations section of the ELF object, and calls + * do_relocations() 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: + { + /* Relocations, no addends */ + ret = do_relocations (s, e, mod); + if (ret != GRUB_ERR_NONE) + return ret; + } + break; + case SHT_NULL: + case SHT_PROGBITS: + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_NOBITS: + case SHT_ARM_ATTRIBUTES: + break; + case SHT_RELA: + 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/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c new file mode 100644 index 000000000..951019b58 --- /dev/null +++ b/grub-core/kern/arm/dl_helper.c @@ -0,0 +1,222 @@ +/* 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 + +/* + * R_ARM_ABS32 + * + * Simple relocation of 32-bit value (in literal pool) + */ +grub_err_t +grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr) +{ + Elf32_Addr tmp; + + tmp = grub_le_to_cpu32 (*target); + tmp += sym_addr; + *target = grub_cpu_to_le32 (tmp); + grub_dprintf ("dl", " %s: reloc_abs32 0x%08x => 0x%08x", __FUNCTION__, + (unsigned int) sym_addr, (unsigned int) tmp); + + return GRUB_ERR_NONE; +} + +/******************************************************************** + * Thumb (T32) relocations: * + * * + * 32-bit Thumb instructions can be 16-bit aligned, and are fetched * + * little-endian, requiring some additional fiddling. * + ********************************************************************/ + +/* + * R_ARM_THM_CALL/THM_JUMP24 + * + * Relocate Thumb (T32) instruction set relative branches: + * B.W, BL and BLX + */ +grub_err_t +grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr) +{ + grub_int32_t offset, offset_low, offset_high; + grub_uint32_t sign, j1, j2, is_blx; + grub_uint32_t insword, insmask; + + /* Extract instruction word in alignment-safe manner */ + insword = (grub_le_to_cpu16 (*target) << 16) + | (grub_le_to_cpu16(*(target + 1))); + insmask = 0xf800d000; + + /* B.W/BL or BLX? Affects range and expected target state */ + if (((insword >> 12) & 0xd) == 0xc) + is_blx = 1; + else + is_blx = 0; + + /* If BLX, target symbol must be ARM (target address LSB == 0) */ + if (is_blx && (sym_addr & 1)) + return grub_error (GRUB_ERR_BAD_MODULE, + N_("Relocation targeting wrong execution state")); + + offset_low = -16777216; + offset_high = is_blx ? 16777212 : 16777214; + + /* Extract bitfields from instruction words */ + sign = (insword >> 26) & 1; + j1 = (insword >> 13) & 1; + j2 = (insword >> 11) & 1; + offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) | + ((~(j2 ^ sign) & 1) << 22) | + ((insword & 0x03ff0000) >> 4) | ((insword & 0x000007ff) << 1); + + /* Sign adjust and calculate offset */ + if (offset & (1 << 24)) + offset -= (1 << 25); + + grub_dprintf ("dl", " sym_addr = 0x%08x", sym_addr); + + offset += sym_addr; +#ifndef GRUB_UTIL + offset -= (grub_uint32_t) target; +#endif + + grub_dprintf("dl", " %s: target=%p, sym_addr=0x%08x, offset=%d\n", + is_blx ? "BLX" : "BL", target, sym_addr, offset); + + if ((offset < offset_low) || (offset > offset_high)) + return grub_error (GRUB_ERR_BAD_MODULE, + N_("THM_CALL Relocation out of range.")); + + grub_dprintf ("dl", " relative destination = 0x%08lx", + (unsigned long)target + offset); + + /* Reassemble instruction word */ + sign = (offset >> 24) & 1; + j1 = sign ^ (~(offset >> 23) & 1); + j2 = sign ^ (~(offset >> 22) & 1); + insword = (insword & insmask) | + (sign << 26) | + (((offset >> 12) & 0x03ff) << 16) | + (j1 << 13) | (j2 << 11) | ((offset >> 1) & 0x07ff); + + /* Write instruction word back in alignment-safe manner */ + *target = grub_cpu_to_le16 ((insword >> 16) & 0xffff); + *(target + 1) = grub_cpu_to_le16 (insword & 0xffff); + + grub_dprintf ("dl", " *insword = 0x%08x", insword); + + return GRUB_ERR_NONE; +} + +/* + * R_ARM_THM_JUMP19 + * + * Relocate conditional Thumb (T32) B.W + */ +grub_err_t +grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr) +{ + grub_int32_t offset; + grub_uint32_t insword, insmask; + + /* Extract instruction word in alignment-safe manner */ + insword = grub_le_to_cpu16 ((*target)) << 16 + | grub_le_to_cpu16 (*(target + 1)); + insmask = 0xfbc0d000; + + /* Extract and sign extend offset */ + offset = ((insword >> 26) & 1) << 19 + | ((insword >> 11) & 1) << 18 + | ((insword >> 13) & 1) << 17 + | ((insword >> 16) & 0x3f) << 11 + | (insword & 0x7ff); + offset <<= 1; + if (offset & (1 << 20)) + offset -= (1 << 21); + + /* Adjust and re-truncate offset */ + offset += sym_addr; +#ifndef GRUB_UTIL + offset -= (grub_uint32_t) target; +#endif + if ((offset > 1048574) || (offset < -1048576)) + return grub_error (GRUB_ERR_BAD_MODULE, + N_("THM_JUMP19 Relocation out of range.")); + + offset >>= 1; + offset &= 0xfffff; + + /* Reassemble instruction word and write back */ + insword &= insmask; + insword |= ((offset >> 19) & 1) << 26 + | ((offset >> 18) & 1) << 11 + | ((offset >> 17) & 1) << 13 + | ((offset >> 11) & 0x3f) << 16 + | (offset & 0x7ff); + *target = grub_cpu_to_le16 (insword >> 16); + *(target + 1) = grub_cpu_to_le16 (insword & 0xffff); + return GRUB_ERR_NONE; +} + + + +/*********************************************************** + * ARM (A32) relocations: * + * * + * ARM instructions are 32-bit in size and 32-bit aligned. * + ***********************************************************/ + +/* + * R_ARM_JUMP24 + * + * Relocate ARM (A32) B + */ +grub_err_t +grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr) +{ + grub_uint32_t insword; + grub_int32_t offset; + + if (sym_addr & 1) + return grub_error (GRUB_ERR_BAD_MODULE, + N_("Relocation targeting wrong execution state")); + + insword = grub_le_to_cpu32 (*target); + + offset = (insword & 0x00ffffff) << 2; + if (offset & 0x02000000) + offset -= 0x04000000; + offset += sym_addr; +#ifndef GRUB_UTIL + offset -= (grub_uint32_t) target; +#endif + + insword &= 0xff000000; + insword |= (offset >> 2) & 0x00ffffff; + + *target = grub_cpu_to_le32 (insword); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm/efi/init.c b/grub-core/kern/arm/efi/init.c new file mode 100644 index 000000000..3a2b74dcc --- /dev/null +++ b/grub-core/kern/arm/efi/init.c @@ -0,0 +1,68 @@ +/* init.c - initialize an arm-based EFI system */ +/* + * 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 + +/* + * A bit ugly, but functional - and should be completely portable. + */ +static grub_uint64_t +grub_efi_get_time_ms(void) +{ + grub_efi_time_t now; + grub_uint64_t retval; + grub_efi_status_t status; + + status = efi_call_2 (grub_efi_system_table->runtime_services->get_time, + &now, NULL); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf("No time!\n"); + return 0; + } + retval = now.year * 365 * 24 * 60 * 60 * 1000; + retval += now.month * 30 * 24 * 60 * 60 * 1000; + retval += now.day * 24 * 60 * 60 * 1000; + retval += now.hour * 60 * 60 * 1000; + retval += now.minute * 60 * 1000; + retval += now.second * 1000; + retval += now.nanosecond / 1000; + + grub_dprintf("timer", "timestamp: 0x%llx\n", retval); + + return retval; +} + +void +grub_machine_init (void) +{ + grub_efi_init (); + grub_install_get_time_ms (grub_efi_get_time_ms); +} + +void +grub_machine_fini (void) +{ + grub_efi_fini (); +} diff --git a/grub-core/kern/arm/efi/misc.c b/grub-core/kern/arm/efi/misc.c new file mode 100644 index 000000000..efec98061 --- /dev/null +++ b/grub-core/kern/arm/efi/misc.c @@ -0,0 +1,203 @@ +/* misc.c - various system functions for an arm-based EFI system */ +/* + * 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 + +static inline grub_size_t +page_align (grub_size_t size) +{ + return (size + (1 << 12) - 1) & (~((1 << 12) - 1)); +} + +/* Find the optimal number of pages for the memory map. Is it better to + move this code to efi/mm.c? */ +static grub_efi_uintn_t +find_mmap_size (void) +{ + static grub_efi_uintn_t mmap_size = 0; + + if (mmap_size != 0) + return mmap_size; + + mmap_size = (1 << 12); + while (1) + { + int ret; + grub_efi_memory_descriptor_t *mmap; + grub_efi_uintn_t desc_size; + + mmap = grub_malloc (mmap_size); + if (! mmap) + return 0; + + ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0); + grub_free (mmap); + + if (ret < 0) + { + grub_error (GRUB_ERR_IO, "cannot get memory map"); + return 0; + } + else if (ret > 0) + break; + + mmap_size += (1 << 12); + } + + /* Increase the size a bit for safety, because GRUB allocates more on + later, and EFI itself may allocate more. */ + mmap_size += (1 << 12); + + return page_align (mmap_size); +} + +#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) +#define PAGE_SHIFT 12 + +void * +grub_efi_allocate_loader_memory (grub_uint32_t min_offset, grub_uint32_t size) +{ + grub_efi_uintn_t desc_size; + grub_efi_memory_descriptor_t *mmap, *mmap_end; + grub_efi_uintn_t mmap_size, tmp_mmap_size; + grub_efi_memory_descriptor_t *desc; + void *mem = NULL; + grub_addr_t min_start = 0; + + mmap_size = find_mmap_size(); + if (!mmap_size) + return NULL; + + mmap = grub_malloc(mmap_size); + if (!mmap) + return NULL; + + tmp_mmap_size = mmap_size; + if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0) + { + grub_error (GRUB_ERR_IO, "cannot get memory map"); + goto fail; + } + + mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size); + /* Find lowest accessible RAM location */ + { + int found = 0; + for (desc = mmap ; !found && (desc < mmap_end) ; + desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size)) + { + switch (desc->type) + { + case GRUB_EFI_CONVENTIONAL_MEMORY: + case GRUB_EFI_LOADER_CODE: + case GRUB_EFI_LOADER_DATA: + min_start = desc->physical_start + min_offset; + found = 1; + break; + default: + break; + } + } + } + + /* First, find free pages for the real mode code + and the memory map buffer. */ + for (desc = mmap ; desc < mmap_end ; + desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size)) + { + grub_uint64_t start, end; + + grub_dprintf("mm", "%s: 0x%08x bytes @ 0x%08x\n", + __FUNCTION__, + (grub_uint32_t) (desc->num_pages << PAGE_SHIFT), + (grub_uint32_t) (desc->physical_start)); + + if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY) + continue; + + start = desc->physical_start; + end = start + (desc->num_pages << PAGE_SHIFT); + grub_dprintf("mm", "%s: start=0x%016llx, end=0x%016llx\n", + __FUNCTION__, start, end); + start = start < min_start ? min_start : start; + if (start + size > end) + continue; + grub_dprintf("mm", "%s: let's allocate some (0x%x) pages @ 0x%08x...\n", + __FUNCTION__, (size >> PAGE_SHIFT), (grub_addr_t) start); + mem = grub_efi_allocate_pages (start, (size >> PAGE_SHIFT) + 1); + grub_dprintf("mm", "%s: retval=0x%08x\n", + __FUNCTION__, (grub_addr_t) mem); + if (! mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory"); + goto fail; + } + break; + } + + if (! mem) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory"); + goto fail; + } + + grub_free (mmap); + return mem; + + fail: + grub_free (mmap); + return NULL; +} + +grub_err_t +grub_efi_prepare_platform (void) +{ + grub_efi_uintn_t mmap_size; + grub_efi_uintn_t map_key; + grub_efi_uintn_t desc_size; + grub_efi_uint32_t desc_version; + grub_efi_memory_descriptor_t *mmap_buf; + grub_err_t err; + + /* + * Cloned from IA64 + * Must be done after grub_machine_fini because map_key is used by + *exit_boot_services. + */ + mmap_size = find_mmap_size (); + if (! mmap_size) + return GRUB_ERR_OUT_OF_MEMORY; + mmap_buf = grub_efi_allocate_pages (0, page_align (mmap_size) >> 12); + if (! mmap_buf) + return GRUB_ERR_OUT_OF_MEMORY; + + err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key, + &desc_size, &desc_version); + if (err != GRUB_ERR_NONE) + return err; + + grub_arm_disable_caches_mmu(); + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S new file mode 100644 index 000000000..557ec6c3d --- /dev/null +++ b/grub-core/kern/arm/efi/startup.S @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2013 Free Software Foundation + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include + + .file "startup.S" + .text + .arm +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0. + */ + ldr ip, =EXT_C(grub_efi_image_handle) + str r0, [ip] + ldr ip, =EXT_C(grub_efi_system_table) + str r1, [ip] + ldr ip, =EXT_C(grub_main) + bx ip + .thumb @ For relocation debugging + blx _start + .end diff --git a/grub-core/kern/arm/misc.S b/grub-core/kern/arm/misc.S new file mode 100644 index 000000000..c2170f669 --- /dev/null +++ b/grub-core/kern/arm/misc.S @@ -0,0 +1,44 @@ +/* + * 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 + + .file "misc.S" + .text + .syntax unified +#if !defined (__thumb2__) + .arm +#define ARM(x...) x +#define THUMB(x...) +#else + .thumb +#define THUMB(x...) x +#define ARM(x...) +#endif + + .align 2 + +/* + * Null divide-by-zero handler + */ +FUNCTION(raise) + mov r0, #0 + bx lr + + .end diff --git a/grub-core/kern/arm/uboot/startup.S b/grub-core/kern/arm/uboot/startup.S new file mode 100644 index 000000000..256613674 --- /dev/null +++ b/grub-core/kern/arm/uboot/startup.S @@ -0,0 +1,169 @@ +/* + * 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 + +/* + * GRUB is called from U-Boot as a Linux Kernel type image, which + * means among other things that it always enters in ARM state. + * + * + * Overview of GRUB image layout: + * + * _start: + * Entry point (1 ARM branch instruction, to "codestart") + * grub_total_module_size: + * Data field: Size of included module blob + * (when generated by grub-mkimage) + * codestart: + * Remainder of statically-linked executable code and data. + * __bss_start: + * Start of included module blob. + * Also where global/static variables are located. + * _end: + * End of bss region (but not necessarily module blob). + * : + * : + * Loadable modules, post relocation. + * : + */ + + .text + .arm +FUNCTION(_start) + b codestart + + @ Size of final image integrated module blob - set by grub-mkimage + . = _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE +VARIABLE(grub_total_module_size) + .long 0 + +FUNCTION(codestart) + @ Store context: Machine ID, atags/dtb, ... + @ U-Boot API signature is stored on the U-Boot heap + @ Stack pointer used as start address for signature probing + mov r12, sp + ldr sp, =entry_state + push {r4-r12,lr} @ store U-Boot context (sp in r12) + + ldr r12, =EXT_C(grub_uboot_machine_type) + str r1, [r12] + ldr r12, =EXT_C(grub_uboot_boot_data) + str r2, [r12] + + @ Modules have been stored as a blob in BSS, + @ they need to be manually relocated to _end + ldr r0, =EXT_C(__bss_start) @ src + add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + mvn r1, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + and r0, r0, r1 + + ldr r1, =EXT_C(_end) @ dst = End of BSS + ldr r2, grub_total_module_size @ blob size + + add r1, r1, #GRUB_KERNEL_MACHINE_STACK_SIZE + and r1, r1, #~0x7 @ Ensure 8-byte alignment + sub sp, r1, #8 + add r1, r1, #1024 + + ldr r12, =EXT_C(grub_modbase) + str r1, [r12] + + add r1, r1, r2 + add r0, r0, r2 + sub r1, r1, #4 + sub r0, r0, #4 + +1: ldr r3, [r0], #-4 @ r3 = *src-- + str r3, [r1], #-4 @ *dst-- = r3 + subs r2, #4 @ remaining -= 4 + bne 1b @ while remaining != 0 + + @ Since we _are_ the C run-time, we need to manually zero the BSS + @ region before continuing + ldr r0, =EXT_C(__bss_start) @ zero from here + @ If unaligned, bytewise zero until base address aligned. + mov r2, #0 +1: tst r0, #3 + beq 2f + strb r2, [r0], #1 + b 1b +2: ldr r1, =EXT_C(_end) @ to here +1: str r2, [r0], #4 + cmp r0, r1 + bne 1b + + b EXT_C(grub_main) + + /* + * uboot_syscall(): + * This function is effectively a veneer, so it cannot + * modify the stack or corrupt any registers other than + * r12 (ip). Furthermore it needs to restore r8 for + * U-Boot (Global Data Pointer) and preserve it for Grub. + */ +FUNCTION(grub_uboot_syscall) + ldr ip, =transition_space + stm ip, {r8, lr} + ldr ip, =gd_backup + ldr r8, [ip] + ldr ip, =grub_uboot_syscall_ptr + mov lr, pc + ldr pc, [ip] + ldr ip, =gd_backup + str r8, [ip] + ldr ip, =transition_space + ldm ip, {r8, lr} + bx lr + +FUNCTION(grub_uboot_return) + ldr sp, =entry_state_end + pop {r4-r12, lr} + mov sp, r12 + bx lr + + + .data + .align 3 @ 8-byte alignment for stack +@ U-boot context stack space +entry_state_end: + .long 0 @ r4 + .long 0 @ r5 + .long 0 @ r6 + .long 0 @ r7 +gd_backup: + .long 0 @ r8 - U-Boot global data pointer + .long 0 @ r9 + .long 0 @ r10 + .long 0 @ r11 +VARIABLE(grub_uboot_search_hint)@ U-Boot stack pointer - + .long 0 @ also API signature address hint. + .long 0 @ lr +entry_state: @ backup for U-Boot context + +@ GRUB context stack space +transition_space: + .long 0 @ r8 + .long 0 @ lr + +VARIABLE(grub_uboot_syscall_ptr) + .long 0 @ + + .end diff --git a/grub-core/kern/uboot/hw.c b/grub-core/kern/uboot/hw.c new file mode 100644 index 000000000..272b83bd7 --- /dev/null +++ b/grub-core/kern/uboot/hw.c @@ -0,0 +1,112 @@ +/* hw.c - U-Boot hardware discovery */ +/* + * 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 +#include +#include + +grub_addr_t start_of_ram; + +/* + * grub_uboot_probe_memory(): + * Queries U-Boot for available memory regions. + * + * Sets up heap near the image in memory and sets up "start_of_ram". + */ +void +grub_uboot_mm_init (void) +{ + struct sys_info *si = grub_uboot_get_sys_info (); + + grub_mm_init_region ((void *) grub_modules_get_end (), + GRUB_KERNEL_MACHINE_HEAP_SIZE); + + if (si && (si->mr_no != 0)) + { + int i; + start_of_ram = GRUB_UINT_MAX; + + for (i = 0; i < si->mr_no; i++) + if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM) + if (si->mr[i].start < start_of_ram) + start_of_ram = si->mr[i].start; + } +} + +/* + * grub_uboot_probe_hardware(): + */ +grub_err_t +grub_uboot_probe_hardware (void) +{ + int devcount, i; + + devcount = grub_uboot_dev_enum (); + grub_dprintf ("init", "%d devices found\n", devcount); + + for (i = 0; i < devcount; i++) + { + struct device_info *devinfo = grub_uboot_dev_get (i); + + grub_dprintf ("init", "device handle: %d\n", i); + grub_dprintf ("init", " cookie\t= 0x%08x\n", + (grub_uint32_t) devinfo->cookie); + + if (devinfo->type & DEV_TYP_STOR) + { + grub_dprintf ("init", " type\t\t= DISK\n"); + grub_ubootdisk_register (devinfo); + } + else if (devinfo->type & DEV_TYP_NET) + { + /* Dealt with in ubootnet module. */ + grub_dprintf ("init", " type\t\t= NET (not supported yet)\n"); + } + else + { + grub_dprintf ("init", "%s: unknown device type", __FUNCTION__); + } + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + int i; + struct sys_info *si = grub_uboot_get_sys_info (); + + if (!si || (si->mr_no < 1)) + return GRUB_ERR_BUG; + + /* Iterate and call `hook'. */ + for (i = 0; i < si->mr_no; i++) + if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM) + hook (si->mr[i].start, si->mr[i].size, GRUB_MEMORY_AVAILABLE, + hook_data); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/uboot/init.c b/grub-core/kern/uboot/init.c new file mode 100644 index 000000000..4b8c2e2ac --- /dev/null +++ b/grub-core/kern/uboot/init.c @@ -0,0 +1,157 @@ +/* init.c - generic U-Boot initialization and finalization */ +/* + * 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 +#include +#include +#include +#include +#include + +extern char __bss_start[]; +extern char _end[]; +extern grub_size_t grub_total_module_size; +extern int (*grub_uboot_syscall_ptr) (int, int *, ...); + +/* Set to anything other than zero so it lands in .data and not .bss. */ +grub_addr_t grub_modbase = 0x55aa55aa; +grub_uint32_t grub_uboot_machine_type = 0x55aa55aa; +grub_addr_t grub_uboot_boot_data = 0x55aa55aa; + +static unsigned long timer_start; + +void +grub_exit (void) +{ + grub_uboot_return (0); +} + +grub_uint32_t +grub_uboot_get_machine_type (void) +{ + return grub_uboot_machine_type; +} + +grub_addr_t +grub_uboot_get_boot_data (void) +{ + return grub_uboot_boot_data; +} + +static grub_uint64_t +uboot_timer_ms (void) +{ + return (grub_uint64_t) grub_uboot_get_timer (timer_start) / 1000; +} + +void +grub_machine_init (void) +{ + int ver; + + /* First of all - establish connection with U-Boot */ + ver = grub_uboot_api_init (); + if (!ver) + { + /* Don't even have a console to log errors to... */ + grub_exit (); + } + else if (ver > API_SIG_VERSION) + { + /* Try to print an error message */ + grub_uboot_puts ("invalid U-Boot API version\n"); + } + + /* Initialize the console so that GRUB can display messages. */ + grub_console_init_early (); + + /* Enumerate memory and initialize the memory management system. */ + grub_uboot_mm_init (); + + grub_dprintf ("init", "__bss_start: %p\n", __bss_start); + grub_dprintf ("init", "_end: %p\n", _end); + grub_dprintf ("init", "grub_modbase: %p\n", (void *) grub_modbase); + grub_dprintf ("init", "grub_modules_get_end(): %p\n", + (void *) grub_modules_get_end ()); + + /* Initialise full terminfo support */ + grub_console_init_lately (); + + /* Enumerate uboot devices */ + grub_uboot_probe_hardware (); + + /* Initialise timer */ + timer_start = grub_uboot_get_timer (0); + grub_install_get_time_ms (uboot_timer_ms); + + /* Initialize */ + grub_ubootdisk_init (); +} + + +void +grub_machine_fini (void) +{ +} + +/* + * grub_machine_get_bootlocation(): + * Called from kern/main.c, which expects a device name (minus parentheses) + * and a filesystem path back, if any are known. + * Any returned values must be pointers to dynamically allocated strings. + */ +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *tmp; + + tmp = grub_uboot_env_get ("grub_bootdev"); + if (tmp) + { + *device = grub_strdup (tmp); + if (*device == NULL) + return; + } + else + *device = NULL; + + tmp = grub_uboot_env_get ("grub_bootpath"); + if (tmp) + { + *path = grub_strdup (tmp); + if (*path == NULL) + return; + } + else + *path = NULL; +} + +void +grub_uboot_fini (void) +{ + grub_ubootdisk_fini (); + grub_console_fini (); +} diff --git a/grub-core/kern/uboot/uboot.c b/grub-core/kern/uboot/uboot.c new file mode 100644 index 000000000..6800a4beb --- /dev/null +++ b/grub-core/kern/uboot/uboot.c @@ -0,0 +1,326 @@ +/* + * 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 + +/* + * The main syscall entry point is not reentrant, only one call is + * serviced until finished. + * + * int syscall(int call, int *retval, ...) + * e.g. syscall(1, int *, u_int32_t, u_int32_t, u_int32_t, u_int32_t); + * + * call: syscall number + * + * retval: points to the return value placeholder, this is the place the + * syscall puts its return value, if NULL the caller does not + * expect a return value + * + * ... syscall arguments (variable number) + * + * returns: 0 if the call not found, 1 if serviced + */ + +extern int (*grub_uboot_syscall_ptr) (int, int *, ...); +extern int grub_uboot_syscall (int, int *, ...); +extern grub_addr_t grub_uboot_search_hint; + +static struct sys_info uboot_sys_info; +static struct mem_region uboot_mem_info[5]; +static struct device_info * devices; +static int num_devices; + +int +grub_uboot_api_init (void) +{ + struct api_signature *start, *end; + struct api_signature *p; + + if (grub_uboot_search_hint) + { + /* Extended search range to work around Trim Slice U-Boot issue */ + start = (struct api_signature *) ((grub_uboot_search_hint & ~0x000fffff) + - 0x00500000); + end = + (struct api_signature *) ((grub_addr_t) start + UBOOT_API_SEARCH_LEN - + API_SIG_MAGLEN + 0x00500000); + } + else + { + start = 0; + end = (struct api_signature *) (256 * 1024 * 1024); + } + + /* Structure alignment is (at least) 8 bytes */ + for (p = start; p < end; p = (void *) ((grub_addr_t) p + 8)) + { + if (grub_memcmp (&(p->magic), API_SIG_MAGIC, API_SIG_MAGLEN) == 0) + { + grub_uboot_syscall_ptr = p->syscall; + return p->version; + } + } + + return 0; +} + +/* + * All functions below are wrappers around the grub_uboot_syscall() function + */ + +int +grub_uboot_getc (void) +{ + int c; + if (!grub_uboot_syscall (API_GETC, NULL, &c)) + return -1; + + return c; +} + +int +grub_uboot_tstc (void) +{ + int c; + if (!grub_uboot_syscall (API_TSTC, NULL, &c)) + return -1; + + return c; +} + +void +grub_uboot_putc (int c) +{ + grub_uboot_syscall (API_PUTC, NULL, &c); +} + +void +grub_uboot_puts (const char *s) +{ + grub_uboot_syscall (API_PUTS, NULL, s); +} + +void +grub_uboot_reset (void) +{ + grub_uboot_syscall (API_RESET, NULL, 0); +} + +struct sys_info * +grub_uboot_get_sys_info (void) +{ + int retval; + + grub_memset (&uboot_sys_info, 0, sizeof (uboot_sys_info)); + grub_memset (&uboot_mem_info, 0, sizeof (uboot_mem_info)); + uboot_sys_info.mr = uboot_mem_info; + uboot_sys_info.mr_no = sizeof (uboot_mem_info) / sizeof (struct mem_region); + + if (grub_uboot_syscall (API_GET_SYS_INFO, &retval, &uboot_sys_info)) + if (retval == 0) + return &uboot_sys_info; + + return NULL; +} + +void +grub_uboot_udelay (grub_uint32_t usec) +{ + grub_uboot_syscall (API_UDELAY, NULL, &usec); +} + +grub_uint32_t +grub_uboot_get_timer (grub_uint32_t base) +{ + grub_uint32_t current; + + if (!grub_uboot_syscall (API_GET_TIMER, NULL, ¤t, &base)) + return 0; + + return current; +} + +int +grub_uboot_dev_enum (void) +{ + struct device_info * enum_devices; + int num_enum_devices, max_devices; + + if (num_devices) + return num_devices; + + max_devices = 2; + enum_devices = grub_malloc (sizeof(struct device_info) * max_devices); + if (!enum_devices) + return 0; + + /* + * The API_DEV_ENUM call starts a fresh enumeration when passed a + * struct device_info with a NULL cookie, and then depends on having + * the previously enumerated device cookie "seeded" into the target + * structure. + */ + + enum_devices[0].cookie = NULL; + num_enum_devices = 0; + + if (grub_uboot_syscall (API_DEV_ENUM, NULL, + &enum_devices[num_enum_devices]) == 0) + goto error; + + num_enum_devices++; + + while (enum_devices[num_enum_devices - 1].cookie != NULL) + { + if (num_enum_devices == max_devices) + { + struct device_info *tmp; + int new_max; + new_max = max_devices * 2; + tmp = grub_realloc (enum_devices, + sizeof (struct device_info) * new_max); + if (!tmp) + { + /* Failed to realloc, so return what we have */ + break; + } + enum_devices = tmp; + max_devices = new_max; + } + + enum_devices[num_enum_devices].cookie = + enum_devices[num_enum_devices - 1].cookie; + if (grub_uboot_syscall (API_DEV_ENUM, NULL, + &enum_devices[num_enum_devices]) == 0) + goto error; + + if (enum_devices[num_enum_devices].cookie == NULL) + break; + + num_enum_devices++; + } + + devices = enum_devices; + return num_devices = num_enum_devices; + + error: + grub_free (enum_devices); + return 0; +} + +#define VALID_DEV(x) (((x) < num_devices) && ((x) >= 0)) +#define OPEN_DEV(x) ((x->state == DEV_STA_OPEN)) + +struct device_info * +grub_uboot_dev_get (int index) +{ + if (VALID_DEV (index)) + return &devices[index]; + + return NULL; +} + + +int +grub_uboot_dev_open (struct device_info *dev) +{ + int retval; + + if (!grub_uboot_syscall (API_DEV_OPEN, &retval, dev)) + return -1; + + return retval; +} + +int +grub_uboot_dev_close (struct device_info *dev) +{ + int retval; + + if (!grub_uboot_syscall (API_DEV_CLOSE, &retval, dev)) + return -1; + + return retval; +} + + +int +grub_uboot_dev_read (struct device_info *dev, void *buf, grub_size_t blocks, + grub_uint32_t start, grub_size_t * real_blocks) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_READ, &retval, dev, buf, + &blocks, &start, real_blocks)) + return -1; + + return retval; +} + +int +grub_uboot_dev_recv (struct device_info *dev, void *buf, + int size, int *real_size) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_READ, &retval, dev, buf, &size, real_size)) + return -1; + + return retval; + +} + +int +grub_uboot_dev_send (struct device_info *dev, void *buf, int size) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_WRITE, &retval, dev, buf, &size)) + return -1; + + return retval; +} + +char * +grub_uboot_env_get (const char *name) +{ + char *value; + + if (!grub_uboot_syscall (API_ENV_GET, NULL, name, &value)) + return NULL; + + return value; +} + +void +grub_uboot_env_set (const char *name, const char *value) +{ + grub_uboot_syscall (API_ENV_SET, NULL, name, value); +} diff --git a/grub-core/lib/arm/setjmp.S b/grub-core/lib/arm/setjmp.S new file mode 100644 index 000000000..9054e073e --- /dev/null +++ b/grub-core/lib/arm/setjmp.S @@ -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 . + */ + +#include + + .file "setjmp.S" + .syntax unified + .arm + + .text + +/* + * int grub_setjmp (grub_jmp_buf env) + */ +FUNCTION(grub_setjmp) + stm r0, { r4-r11, sp, lr } + mov r0, #0 + bx lr + +/* + * int grub_longjmp (grub_jmp_buf env, int val) + */ +FUNCTION(grub_longjmp) + ldm r0, { r4-r11, sp, lr } + movs r0, r1 + moveq r0, #1 + bx lr diff --git a/grub-core/lib/efi/halt.c b/grub-core/lib/efi/halt.c index 5ebf2cd1d..3e1ea47f4 100644 --- a/grub-core/lib/efi/halt.c +++ b/grub-core/lib/efi/halt.c @@ -28,7 +28,7 @@ void grub_halt (void) { grub_machine_fini (); -#ifndef __ia64__ +#if !defined(__ia64__) && !defined(__arm__) grub_acpi_halt (); #endif efi_call_4 (grub_efi_system_table->runtime_services->reset_system, diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c new file mode 100644 index 000000000..57528c58f --- /dev/null +++ b/grub-core/lib/fdt.c @@ -0,0 +1,389 @@ +/* + * 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 + +#define FDT_SUPPORTED_VERSION 17 + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +#define struct_end(fdt) \ + ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) \ + + grub_fdt_get_size_dt_struct(fdt)) + +/* Size needed by a node entry: 2 tokens (FDT_BEGIN_NODE and FDT_END_NODE), plus + the NULL-terminated string containing the name, plus padding if needed. */ +#define node_entry_size(node_name) \ + (2 * sizeof(grub_uint32_t) \ + + ALIGN_UP (grub_strlen (name) + 1, sizeof(grub_uint32_t))) + +/* Size needed by a property entry: 1 token (FDT_PROPERTY), plus len and nameoff + fields, plus the property value, plus padding if needed. */ +#define prop_entry_size(prop_len) \ + (3 * sizeof(grub_uint32_t) + ALIGN_UP(prop_len, sizeof(grub_uint32_t))) + +static grub_uint32_t *get_next_node (const void *fdt, char *node_name) +{ + grub_uint32_t *end = (void *) struct_end (fdt); + grub_uint32_t *token; + + if (node_name >= (char *) end) + return NULL; + while (*node_name) + { + if (++node_name >= (char *) end) + return NULL; + } + token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4); + while (token < end) + { + switch (grub_be_to_cpu32(*token)) + { + case FDT_BEGIN_NODE: + token = get_next_node (fdt, (char *) (token + 1)); + if (!token) + return NULL; + break; + case FDT_END_NODE: + token++; + if (token >= end) + return NULL; + return token; + case FDT_PROP: + /* Skip property token and following data (len, nameoff and property + value). */ + token += 3 + grub_be_to_cpu32 (*(token + 1)); + break; + case FDT_NOP: + token++; + break; + default: + return NULL; + } + } + return NULL; +} + +static int get_mem_rsvmap_size (const void *fdt) +{ + int size = 0; + grub_uint64_t *ptr = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_mem_rsvmap (fdt)); + + do + { + size += 2 * sizeof(*ptr); + if (!*ptr && !*(ptr + 1)) + return size; + ptr += 2; + } while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt) + - 2 * sizeof(grub_uint64_t)); + return -1; +} + +static grub_uint32_t get_free_space (void *fdt) +{ + int mem_rsvmap_size = get_mem_rsvmap_size (fdt); + + if (mem_rsvmap_size < 0) + /* invalid memory reservation block */ + return 0; + return (grub_fdt_get_totalsize (fdt) - sizeof(grub_fdt_header_t) + - mem_rsvmap_size - grub_fdt_get_size_dt_strings (fdt) + - grub_fdt_get_size_dt_struct (fdt)); +} + +static int add_subnode (void *fdt, int parentoffset, const char *name) +{ + grub_uint32_t *begin = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct(fdt) + + parentoffset); + grub_uint32_t *end = (void *) struct_end (fdt); + unsigned int entry_size = node_entry_size (name); + grub_uint32_t *token = begin; + + /* Insert the new subnode just after the properties of the parent node (if + any).*/ + while (1) + { + if (token >= end) + return -1; + switch (grub_be_to_cpu32(*token)) + { + case FDT_PROP: + /* Skip len and nameoff. */ + token += 2; + break; + case FDT_BEGIN_NODE: + case FDT_END_NODE: + goto insert; + case FDT_NOP: + break; + default: + /* invalid token */ + return -1; + } + token++; + } +insert: + grub_memmove (token + entry_size, token, + (grub_addr_t) end - (grub_addr_t) token); + *token = grub_cpu_to_be32(FDT_BEGIN_NODE); + token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */ + grub_strcpy((char *) (token + 1), name); + token += entry_size / sizeof(*token) - 1; + *token = grub_cpu_to_be32(FDT_END_NODE); + return ((grub_addr_t) token - (grub_addr_t) fdt + - grub_fdt_get_off_dt_struct(fdt)); +} + +/* Rearrange FDT blocks in the canonical order: first the memory reservation + block (just after the FDT header), then the structure block and finally the + strings block. No free space is left between the first and the second block, + while the space between the second and the third block is given by the + clearance argument. */ +static int rearrange_blocks (void *fdt, unsigned int clearance) +{ + grub_uint32_t off_mem_rsvmap = ALIGN_UP(sizeof(grub_fdt_header_t), 8); + grub_uint32_t off_dt_struct = off_mem_rsvmap + get_mem_rsvmap_size (fdt); + grub_uint32_t off_dt_strings = off_dt_struct + + grub_fdt_get_size_dt_struct (fdt) + + clearance; + grub_uint8_t *fdt_ptr = fdt; + grub_uint8_t *tmp_fdt; + + if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap) + && (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct)) + { + /* No need to allocate memory for a temporary FDT, just move the strings + block if needed. */ + if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings) + grub_memmove(fdt_ptr + off_dt_strings, + fdt_ptr + grub_fdt_get_off_dt_strings (fdt), + grub_fdt_get_size_dt_strings (fdt)); + return 0; + } + tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt)); + if (!tmp_fdt) + return -1; + grub_memcpy (tmp_fdt + off_mem_rsvmap, + fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt), + get_mem_rsvmap_size (fdt)); + grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap); + grub_memcpy (tmp_fdt + off_dt_struct, + fdt_ptr + grub_fdt_get_off_dt_struct (fdt), + grub_fdt_get_size_dt_struct (fdt)); + grub_fdt_set_off_dt_struct (fdt, off_dt_struct); + grub_memcpy (tmp_fdt + off_dt_strings, + fdt_ptr + grub_fdt_get_off_dt_strings (fdt), + grub_fdt_get_size_dt_strings (fdt)); + grub_fdt_set_off_dt_strings (fdt, off_dt_strings); + + /* Copy reordered blocks back to fdt. */ + memcpy (fdt_ptr + off_mem_rsvmap, tmp_fdt + off_mem_rsvmap, + grub_fdt_get_totalsize (fdt) - off_mem_rsvmap); + + grub_free(tmp_fdt); + return 0; +} + +static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset, + const char *name) +{ + grub_uint32_t *prop = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + + nodeoffset); + grub_uint32_t nameoff; + + do + { + if (grub_be_to_cpu32(*prop) == FDT_PROP) + { + nameoff = grub_be_to_cpu32(*(prop + 2)); + if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt)) + && !grub_strcmp (name, (char *) fdt + + grub_fdt_get_off_dt_strings (fdt) + nameoff)) + return prop; + prop += prop_entry_size(grub_be_to_cpu32(*prop + 1)) / sizeof (*prop); + } + else if (grub_be_to_cpu32(*prop) != FDT_NOP) + return NULL; + prop++; + } while ((grub_addr_t) prop < ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + + grub_fdt_get_size_dt_struct (fdt))); + return NULL; +} + +/* Check the FDT header for consistency and adjust the totalsize field to match + the size allocated for the FDT; if this function is called before the other + functions in this file and returns success, the other functions are + guaranteed not to access memory locations outside the allocated memory. */ +int grub_fdt_check_header (void *fdt, unsigned int size) +{ + if (((grub_addr_t) fdt & 0x7) || (grub_fdt_get_magic (fdt) != FDT_MAGIC) + || (grub_fdt_get_totalsize (fdt) > size) + || (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION) + || (grub_fdt_get_last_comp_version (fdt) > FDT_SUPPORTED_VERSION) + || (grub_fdt_get_off_dt_struct (fdt) & 0x00000003) + || (grub_fdt_get_size_dt_struct (fdt) & 0x00000003) + || (grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_size_dt_struct (fdt) + > grub_fdt_get_totalsize (fdt)) + || (grub_fdt_get_off_dt_strings (fdt) + grub_fdt_get_size_dt_strings (fdt) + > grub_fdt_get_totalsize (fdt)) + || (grub_fdt_get_off_mem_rsvmap (fdt) & 0x00000007) + || (grub_fdt_get_off_mem_rsvmap (fdt) + > grub_fdt_get_totalsize (fdt) - 2 * sizeof(grub_uint64_t))) + return -1; + return 0; +} + +/* Find a direct sub-node of a given parent node. */ +int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, + const char *name) +{ + grub_uint32_t *token, *end; + char *node_name; + + if (parentoffset & 0x3) + return -1; + token = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) + + parentoffset); + end = (void *) struct_end (fdt); + while (token < end) + { + switch (grub_be_to_cpu32(*token)) + { + case FDT_BEGIN_NODE: + node_name = (char *) (token + 1); + if (node_name + grub_strlen (name) >= (char *) end) + return -1; + if (!grub_strcmp (node_name, name)) + return (int) ((grub_addr_t) token + + ALIGN_UP(grub_strlen (name) + 1, 4) + - grub_fdt_get_off_dt_struct (fdt)); + token = get_next_node (fdt, node_name); + if (!token) + return -1; + break; + case FDT_END_NODE: + return -1; + case FDT_PROP: + /* Skip property token and following data (len, nameoff and property + value). */ + token += 3 + grub_be_to_cpu32 (*(token + 1)); + break; + case FDT_NOP: + token++; + break; + default: + return -1; + } + } + return -1; +} + +int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, + const char *name) +{ + unsigned int entry_size = node_entry_size(name); + + if ((parentoffset & 0x3) || (get_free_space (fdt) < entry_size)) + return -1; + + /* The new node entry will increase the size of the structure block: rearrange + blocks such that there is sufficient free space between the structure and + the strings block, then add the new node entry. */ + if (rearrange_blocks (fdt, entry_size) < 0) + return -1; + return add_subnode (fdt, parentoffset, name); +} + +int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name, + const void *val, grub_uint32_t len) +{ + grub_uint32_t *prop; + int prop_name_present = 0; + grub_uint32_t nameoff = 0; + + if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3)) + return -1; + prop = find_prop (fdt, nodeoffset, name); + if (prop) + { + grub_uint32_t prop_len = ALIGN_UP(grub_be_to_cpu32 (*(prop + 1)), + sizeof(grub_uint32_t)); + grub_uint32_t i; + + prop_name_present = 1; + for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++) + *(prop + 3 + i) = grub_cpu_to_be32 (FDT_NOP); + if (len > prop_len) + { + /* Length of new property value is greater than the space allocated + for the current value: a new entry needs to be created, so save the + nameoff field of the current entry and replace the current entry + with NOP tokens. */ + nameoff = grub_be_to_cpu32 (*(prop + 2)); + *prop = *(prop + 1) = *(prop + 2) = grub_cpu_to_be32 (FDT_NOP); + prop = NULL; + } + } + if (!prop || !prop_name_present) { + unsigned int needed_space = 0; + + if (!prop) + needed_space = prop_entry_size(len); + if (!prop_name_present) + needed_space += grub_strlen (name) + 1; + if (needed_space > get_free_space (fdt)) + return -1; + if (rearrange_blocks (fdt, !prop ? prop_entry_size(len) : 0) < 0) + return -1; + } + if (!prop_name_present) { + /* Append the property name at the end of the strings block. */ + nameoff = grub_fdt_get_size_dt_strings (fdt); + grub_strcpy ((char *) fdt + grub_fdt_get_off_dt_strings (fdt) + nameoff, + name); + grub_fdt_set_size_dt_strings (fdt, grub_fdt_get_size_dt_strings (fdt) + + grub_strlen (name) + 1); + } + if (!prop) { + prop = (void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct (fdt) + + nodeoffset); + grub_memmove (prop + prop_entry_size(len), prop, + grub_fdt_get_size_dt_struct (fdt) - nodeoffset); + *prop = grub_cpu_to_be32 (FDT_PROP); + *(prop + 1) = grub_cpu_to_be32 (len); + *(prop + 2) = grub_cpu_to_be32 (nameoff); + + /* Insert padding bytes at the end of the value; if they are not needed, + they will be overwritten by the follozing memcpy. */ + *(prop + prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0; + + grub_memcpy (prop + 3, val, len); + } + return 0; +} diff --git a/grub-core/lib/setjmp.S b/grub-core/lib/setjmp.S index 2e4974297..feb7b431c 100644 --- a/grub-core/lib/setjmp.S +++ b/grub-core/lib/setjmp.S @@ -11,6 +11,8 @@ #elif defined(__ia64__) #include "./ia64/setjmp.S" #include "./ia64/longjmp.S" +#elif defined(__arm__) +#include "./arm/setjmp.S" #else #error "Unknown target cpu type" #endif diff --git a/grub-core/lib/uboot/datetime.c b/grub-core/lib/uboot/datetime.c new file mode 100644 index 000000000..4be716928 --- /dev/null +++ b/grub-core/lib/uboot/datetime.c @@ -0,0 +1,41 @@ +/* + * 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 + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* No simple platform-independent RTC access exists in U-Boot. */ + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_INVALID_COMMAND, + "can\'t get datetime using U-Boot"); +} + +grub_err_t +grub_set_datetime (struct grub_datetime * datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_INVALID_COMMAND, + "can\'t set datetime using U-Boot"); +} diff --git a/grub-core/lib/uboot/halt.c b/grub-core/lib/uboot/halt.c new file mode 100644 index 000000000..9d5a1386d --- /dev/null +++ b/grub-core/lib/uboot/halt.c @@ -0,0 +1,31 @@ +/* + * 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 + +void +grub_halt (void) +{ + grub_machine_fini (); + + /* Just stop here */ + + while (1); +} diff --git a/grub-core/lib/uboot/reboot.c b/grub-core/lib/uboot/reboot.c new file mode 100644 index 000000000..e5c54d467 --- /dev/null +++ b/grub-core/lib/uboot/reboot.c @@ -0,0 +1,30 @@ +/* + * 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 + +void +grub_reboot (void) +{ + grub_machine_fini (); + + grub_uboot_reset (); + while (1); +} diff --git a/grub-core/loader/arm/linux.c b/grub-core/loader/arm/linux.c new file mode 100644 index 000000000..22450a09f --- /dev/null +++ b/grub-core/loader/arm/linux.c @@ -0,0 +1,401 @@ +/* linux.c - boot Linux */ +/* + * 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 +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static grub_addr_t initrd_start; +static grub_addr_t initrd_end; + +static grub_addr_t linux_addr; +static grub_size_t linux_size; + +static char *linux_args; + +static grub_uint32_t machine_type; +static void *fdt_addr; + +#define LINUX_ZIMAGE_OFFSET 0x24 +#define LINUX_ZIMAGE_MAGIC 0x016f2818 + +#define ARM_FDT_MACHINE_TYPE 0xFFFFFFFF + +#define LINUX_PHYS_OFFSET (0x00008000) +#define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000) +#define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000) + +/* + * linux_prepare_fdt(): + * Prepares a loaded FDT for being passed to Linux. + * Merges in command line parameters and sets up initrd addresses. + */ +static grub_err_t +linux_prepare_fdt (void) +{ + int node; + int retval; + int tmp_size; + void *tmp_fdt; + + tmp_size = grub_fdt_get_totalsize (fdt_addr) + 0x100 + grub_strlen (linux_args); + tmp_fdt = grub_malloc (tmp_size); + if (!tmp_fdt) + return grub_errno; + + grub_memcpy (tmp_fdt, fdt_addr, grub_fdt_get_totalsize (fdt_addr)); + grub_fdt_set_totalsize (tmp_fdt, tmp_size); + + /* Find or create '/chosen' node */ + node = grub_fdt_find_subnode (tmp_fdt, 0, "chosen"); + if (node < 0) + { + grub_printf ("No 'chosen' node in FDT - creating.\n"); + node = grub_fdt_add_subnode (tmp_fdt, 0, "chosen"); + if (node < 0) + goto failure; + } + + grub_printf ("linux_args: '%s'\n", linux_args); + + /* Generate and set command line */ + retval = grub_fdt_set_prop (tmp_fdt, node, "bootargs", linux_args, + grub_strlen (linux_args) + 1); + if (retval) + goto failure; + + if (initrd_start && initrd_end) + { + /* + * We're using physical addresses, so even if we have LPAE, we're + * restricted to a 32-bit address space. + */ + grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n", + initrd_start, initrd_end); + + retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-start", + initrd_start); + if (retval) + goto failure; + retval = grub_fdt_set_prop32 (tmp_fdt, node, "linux,initrd-end", + initrd_end); + if (retval) + goto failure; + } + + /* Copy updated FDT to its launch location */ + grub_memcpy (fdt_addr, tmp_fdt, tmp_size); + grub_free (tmp_fdt); + + grub_dprintf ("loader", "FDT updated for Linux boot\n"); + + return GRUB_ERR_NONE; + +failure: + grub_free (tmp_fdt); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unable to prepare FDT"); +} + +static grub_err_t +linux_boot (void) +{ + kernel_entry_t linuxmain; + grub_err_t err; + + if (!fdt_addr && machine_type == ARM_FDT_MACHINE_TYPE) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("device tree must be supplied")); + + grub_arch_sync_caches ((void *) linux_addr, linux_size); + + grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr); + + err = linux_prepare_fdt (); + if (err) + return err; + grub_dprintf ("loader", "FDT @ 0x%p\n", fdt_addr); + + grub_dprintf ("loader", "Jumping to Linux...\n"); + + /* Boot the kernel. + * Arguments to kernel: + * r0 - 0 + * r1 - machine type + * r2 - address of DTB + */ + linuxmain = (kernel_entry_t) linux_addr; + +#ifdef GRUB_MACHINE_EFI + err = grub_efi_prepare_platform(); + if (err != GRUB_ERR_NONE) + return err; +#endif + + linuxmain (0, machine_type, fdt_addr); + + return err; +} + +/* + * Only support zImage, so no relocations necessary + */ +static grub_err_t +linux_load (const char *filename, grub_file_t file) +{ + int size; + + size = grub_file_size (file); + if (size == 0) + return grub_error (GRUB_ERR_BAD_OS, "empty kernel"); + +#ifdef GRUB_MACHINE_EFI + linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_PHYS_OFFSET, size); + if (!linux_addr) + return grub_errno; +#else + linux_addr = LINUX_ADDRESS; +#endif + grub_dprintf ("loader", "Loading Linux to 0x%08x\n", + (grub_addr_t) linux_addr); + + if (grub_file_read (file, (void *) linux_addr, size) != size) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); + return grub_errno; + } + + if (*(grub_uint32_t *) (linux_addr + LINUX_ZIMAGE_OFFSET) + != LINUX_ZIMAGE_MAGIC) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("invalid zImage")); + } + + linux_size = size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +linux_unload (void) +{ + grub_dl_unref (my_mod); + + grub_free (linux_args); + linux_args = NULL; + + initrd_start = initrd_end = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + int size; + grub_err_t err; + grub_file_t file; + grub_dl_ref (my_mod); + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0]); + if (!file) + goto fail; + + err = linux_load (argv[0], file); + grub_file_close (file); + if (err) + goto fail; + + grub_loader_set (linux_boot, linux_unload, 0); + + size = grub_loader_cmdline_size (argc, argv); + linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (!linux_args) + { + grub_loader_unset(); + goto fail; + } + + /* Create kernel command line. */ + grub_memcpy (linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + linux_args + sizeof (LINUX_IMAGE) - 1, size); + + return GRUB_ERR_NONE; + +fail: + grub_dl_unref (my_mod); + return grub_errno; +} + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + int size; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0]); + if (!file) + return grub_errno; + + size = grub_file_size (file); + if (size == 0) + goto fail; + + if (initrd_start) + grub_free ((void *) initrd_start); +#ifdef GRUB_MACHINE_EFI + initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_INITRD_PHYS_OFFSET, size); + + if (!initrd_start) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed")); + goto fail; + } +#else + initrd_start = LINUX_INITRD_ADDRESS; +#endif + + grub_dprintf ("loader", "Loading initrd to 0x%08x\n", + (grub_addr_t) initrd_start); + + if (grub_file_read (file, (void *) initrd_start, size) != size) + { + initrd_start = 0; + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + argv[0]); + goto fail; + } + + initrd_end = initrd_start + size; + + return GRUB_ERR_NONE; + +fail: + grub_file_close (file); + + return grub_errno; +} + +static grub_err_t +load_dtb (grub_file_t dtb, int size) +{ + if ((grub_file_read (dtb, fdt_addr, size) != size) + || (grub_fdt_check_header (fdt_addr, size) != 0)) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree")); + + grub_fdt_set_totalsize (fdt_addr, size); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t dtb; + int size; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + dtb = grub_file_open (argv[0]); + if (!dtb) + goto out; + + size = grub_file_size (dtb); + if (size == 0) + { + grub_error (GRUB_ERR_BAD_OS, "empty file"); + goto out; + } + +#ifdef GRUB_MACHINE_EFI + fdt_addr = grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size); + if (!fdt_addr) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("memory allocation failed")); + goto out; + } +#else + fdt_addr = (void *) LINUX_FDT_ADDRESS; +#endif + + grub_dprintf ("loader", "Loading device tree to 0x%08x\n", + (grub_addr_t) fdt_addr); + load_dtb (dtb, size); + if (grub_errno != GRUB_ERR_NONE) + { + fdt_addr = NULL; + goto out; + } + + /* + * We've successfully loaded an FDT, so any machine type passed + * from firmware is now obsolete. + */ + machine_type = ARM_FDT_MACHINE_TYPE; + + out: + grub_file_close (dtb); + + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd, cmd_devicetree; + +GRUB_MOD_INIT (linux) +{ + cmd_linux = grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_devicetree = grub_register_command ("devicetree", grub_cmd_devicetree, + 0, N_("Load DTB file.")); + my_mod = mod; + fdt_addr = (void *) firmware_get_boot_data (); + machine_type = firmware_get_machine_type (); +} + +GRUB_MOD_FINI (linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_devicetree); +} diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c new file mode 100644 index 000000000..056052e40 --- /dev/null +++ b/grub-core/net/drivers/uboot/ubootnet.c @@ -0,0 +1,161 @@ +/* + * 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 +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +card_open (struct grub_net_card *dev) +{ + int status; + + status = grub_uboot_dev_open (dev->data); + if (status) + return grub_error (GRUB_ERR_IO, "Couldn't open network card."); + + return GRUB_ERR_NONE; +} + +static void +card_close (struct grub_net_card *dev) +{ + grub_uboot_dev_close (dev->data); +} + +static grub_err_t +send_card_buffer (struct grub_net_card *dev, struct grub_net_buff *pack) +{ + int status; + grub_size_t len; + + len = (pack->tail - pack->data); + if (len > dev->mtu) + len = dev->mtu; + + grub_memcpy (dev->txbuf, pack->data, len); + status = grub_uboot_dev_send (dev->data, dev->txbuf, len); + + if (status) + return grub_error (GRUB_ERR_IO, N_("couldn't send network packet")); + return GRUB_ERR_NONE; +} + +static struct grub_net_buff * +get_card_packet (struct grub_net_card *dev) +{ + int rc; + grub_uint64_t start_time; + struct grub_net_buff *nb; + int actual; + + nb = grub_netbuff_alloc (dev->mtu + 64 + 2); + if (!nb) + return NULL; + /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible + by 4. So that IP header is aligned on 4 bytes. */ + grub_netbuff_reserve (nb, 2); + + start_time = grub_get_time_ms (); + do + { + rc = grub_uboot_dev_recv (dev->data, nb->data, dev->mtu + 64, &actual); + grub_dprintf ("net", "rc=%d, actual=%d, time=%lld\n", rc, actual, + grub_get_time_ms () - start_time); + } + while ((actual <= 0 || rc < 0) && (grub_get_time_ms () - start_time < 200)); + if (actual > 0) + { + grub_netbuff_put (nb, actual); + return nb; + } + grub_netbuff_free (nb); + return NULL; +} + +static struct grub_net_card_driver ubootnet = + { + .name = "ubnet", + .open = card_open, + .close = card_close, + .send = send_card_buffer, + .recv = get_card_packet + }; + +GRUB_MOD_INIT (ubootnet) +{ + int devcount, i; + int nfound = 0; + + devcount = grub_uboot_dev_enum (); + + for (i = 0; i < devcount; i++) + { + struct device_info *devinfo = grub_uboot_dev_get (i); + struct grub_net_card *card; + + if (!(devinfo->type & DEV_TYP_NET)) + continue; + + card = grub_zalloc (sizeof (struct grub_net_card)); + if (!card) + { + grub_print_error (); + return; + } + + /* FIXME: Any way to check this? */ + card->mtu = 1500; + + grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6); + card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + + card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; + card->txbuf = grub_zalloc (card->txbufsize); + if (!card->txbuf) + { + grub_free (card); + grub_print_error (); + continue; + } + + card->data = devinfo; + card->flags = 0; + card->name = grub_xasprintf ("ubnet_%d", ++nfound); + card->idle_poll_delay_ms = 10; + + card->driver = &ubootnet; + grub_net_card_register (card); + } +} + +GRUB_MOD_FINI (ubootnet) +{ + struct grub_net_card *card, *next; + + FOR_NET_CARDS_SAFE (card, next) + if (card->driver && grub_strcmp (card->driver->name, "ubnet") == 0) + grub_net_card_unregister (card); +} diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c index 37e948255..bdb0e6618 100644 --- a/grub-core/term/terminfo.c +++ b/grub-core/term/terminfo.c @@ -745,7 +745,9 @@ grub_cmd_terminfo (grub_extcmd_context_t ctxt, int argc, char **args) static grub_extcmd_t cmd; -#if defined (GRUB_MACHINE_IEEE1275) || defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) || defined (GRUB_MACHINE_ARC) +#if defined (GRUB_MACHINE_IEEE1275) || defined (GRUB_MACHINE_MIPS_LOONGSON) \ + || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) || defined (GRUB_MACHINE_ARC) \ + || defined (GRUB_MACHINE_UBOOT) void grub_terminfo_init (void) #else GRUB_MOD_INIT(terminfo) diff --git a/grub-core/term/uboot/console.c b/grub-core/term/uboot/console.c new file mode 100644 index 000000000..51defee6e --- /dev/null +++ b/grub-core/term/uboot/console.c @@ -0,0 +1,141 @@ +/* console.c - console interface layer for U-Boot platforms */ +/* + * 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 void +put (struct grub_term_output *term __attribute__ ((unused)), const int c) +{ + grub_uboot_putc (c); +} + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + if (grub_uboot_tstc () > 0) + return grub_uboot_getc (); + + return -1; +} + +static void +uboot_console_setcursor (struct grub_term_output *term + __attribute__ ((unused)), int on + __attribute__ ((unused))) +{ + grub_terminfo_setcursor (term, on); +} + +static grub_err_t +uboot_console_init_input (struct grub_term_input *term) +{ + return grub_terminfo_input_init (term); +} + +extern struct grub_terminfo_output_state uboot_console_terminfo_output; + +static void +uboot_console_dimensions (void) +{ + /* Use a small console by default. */ + if (!uboot_console_terminfo_output.width) + uboot_console_terminfo_output.width = 80; + if (!uboot_console_terminfo_output.height) + uboot_console_terminfo_output.height = 24; +} + +static grub_err_t +uboot_console_init_output (struct grub_term_output *term) +{ + uboot_console_dimensions (); + + grub_terminfo_output_init (term); + + return 0; +} + +struct grub_terminfo_input_state uboot_console_terminfo_input = { + .readkey = readkey +}; + +struct grub_terminfo_output_state uboot_console_terminfo_output = { + .put = put, + .width = 80, + .height = 24 +}; + +static struct grub_term_input uboot_console_term_input = { + .name = "console", + .init = uboot_console_init_input, + .getkey = grub_terminfo_getkey, + .data = &uboot_console_terminfo_input +}; + +static struct grub_term_output uboot_console_term_output = { + .name = "console", + .init = uboot_console_init_output, + .putchar = grub_terminfo_putchar, + .getwh = grub_terminfo_getwh, + .getxy = grub_terminfo_getxy, + .gotoxy = grub_terminfo_gotoxy, + .cls = grub_terminfo_cls, + .setcolorstate = grub_terminfo_setcolorstate, + .setcursor = uboot_console_setcursor, + .flags = GRUB_TERM_CODE_TYPE_ASCII, + .data = &uboot_console_terminfo_output, +}; + +void +grub_console_init_early (void) +{ + grub_term_register_input ("console", &uboot_console_term_input); + grub_term_register_output ("console", &uboot_console_term_output); +} + + +/* + * grub_console_init_lately(): + * Initializes terminfo formatting by registering terminal type. + * Called after heap has been configured. + * + */ +void +grub_console_init_lately (void) +{ + const char *type; + + /* See if explicitly set by U-Boot environment */ + type = grub_uboot_env_get ("grub_term"); + if (!type) + type = "vt100"; + + grub_terminfo_init (); + grub_terminfo_output_register (&uboot_console_term_output, type); +} + +void +grub_console_fini (void) +{ +} diff --git a/include/grub/arm/efi/loader.h b/include/grub/arm/efi/loader.h new file mode 100644 index 000000000..4bab18e83 --- /dev/null +++ b/include/grub/arm/efi/loader.h @@ -0,0 +1,26 @@ +/* + * 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_LOADER_MACHINE_HEADER +#define GRUB_LOADER_MACHINE_HEADER 1 + +grub_err_t EXPORT_FUNC (grub_efi_prepare_platform) (void); +void * EXPORT_FUNC (grub_efi_allocate_loader_memory) (grub_uint32_t min_offset, + grub_uint32_t size); + +#endif /* ! GRUB_LOADER_MACHINE_HEADER */ diff --git a/include/grub/arm/efi/memory.h b/include/grub/arm/efi/memory.h new file mode 100644 index 000000000..c9a61bb77 --- /dev/null +++ b/include/grub/arm/efi/memory.h @@ -0,0 +1 @@ +#include diff --git a/include/grub/arm/linux.h b/include/grub/arm/linux.h new file mode 100644 index 000000000..1f62d76ac --- /dev/null +++ b/include/grub/arm/linux.h @@ -0,0 +1,59 @@ +/* linux.h - ARM linux specific definitions */ +/* + * 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_LINUX_CPU_HEADER +#define GRUB_LINUX_CPU_HEADER 1 + +#define LINUX_ZIMAGE_OFFSET 0x24 +#define LINUX_ZIMAGE_MAGIC 0x016f2818 + +#define ARM_FDT_MACHINE_TYPE 0xFFFFFFFF + +#if defined GRUB_MACHINE_UBOOT +# include +# define LINUX_ADDRESS (start_of_ram + 0x8000) +# define LINUX_INITRD_ADDRESS (start_of_ram + 0x02000000) +# define LINUX_FDT_ADDRESS (LINUX_INITRD_ADDRESS - 0x10000) +# define firmware_get_boot_data grub_uboot_get_boot_data +# define firmware_get_machine_type grub_uboot_get_machine_type +#elif defined GRUB_MACHINE_EFI +# include +# include +/* On UEFI platforms - load the images at the lowest available address not + less than *_PHYS_OFFSET from the first available memory location. */ +# define LINUX_PHYS_OFFSET (0x00008000) +# define LINUX_INITRD_PHYS_OFFSET (LINUX_PHYS_OFFSET + 0x02000000) +# define LINUX_FDT_PHYS_OFFSET (LINUX_INITRD_PHYS_OFFSET - 0x10000) +static inline grub_addr_t +firmware_get_boot_data (void) +{ + return 0; +} +static inline grub_uint32_t +firmware_get_machine_type (void) +{ + return ARM_FDT_MACHINE_TYPE; +} +#endif + +#define FDT_ADDITIONAL_ENTRIES_SIZE 0x300 + +typedef void (*kernel_entry_t) (int, unsigned long, void *); + +#endif /* ! GRUB_LINUX_CPU_HEADER */ diff --git a/include/grub/arm/reloc.h b/include/grub/arm/reloc.h new file mode 100644 index 000000000..50d070f01 --- /dev/null +++ b/include/grub/arm/reloc.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_ARM_RELOC_H +#define GRUB_ARM_RELOC_H 1 + +grub_err_t grub_arm_reloc_abs32 (grub_uint32_t *addr, Elf32_Addr sym_addr); +grub_err_t grub_arm_reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr); +grub_err_t grub_arm_reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr); +grub_err_t grub_arm_reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr); + +#endif diff --git a/include/grub/arm/setjmp.h b/include/grub/arm/setjmp.h new file mode 100644 index 000000000..1c1d0b54e --- /dev/null +++ b/include/grub/arm/setjmp.h @@ -0,0 +1,27 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2004,2006,2007,2009 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 grub_jmp_buf[10]; + +int grub_setjmp (grub_jmp_buf env) __attribute__ ((returns_twice)); +void grub_longjmp (grub_jmp_buf env, int val) __attribute__ ((noreturn)); + +#endif /* ! GRUB_SETJMP_CPU_HEADER */ diff --git a/include/grub/arm/system.h b/include/grub/arm/system.h new file mode 100644 index 000000000..e22060003 --- /dev/null +++ b/include/grub/arm/system.h @@ -0,0 +1,7 @@ +#ifndef GRUB_SYSTEM_CPU_HEADER +#define GRUB_SYSTEM_CPU_HEADER + +void grub_arm_disable_caches_mmu (void); + +#endif /* ! GRUB_SYSTEM_CPU_HEADER */ + diff --git a/include/grub/arm/time.h b/include/grub/arm/time.h new file mode 100644 index 000000000..4128506cb --- /dev/null +++ b/include/grub/arm/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/arm/types.h b/include/grub/arm/types.h new file mode 100644 index 000000000..4a806d05e --- /dev/null +++ b/include/grub/arm/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 4 + +/* The size of long. */ +#define GRUB_TARGET_SIZEOF_LONG 4 + +/* currently only support little-endian. */ +#undef GRUB_TARGET_WORDS_BIGENDIAN + +/* Unaligned accesses only supported if MMU enabled */ +//#define GRUB_HAVE_UNALIGNED_ACCESS 1 + +#endif /* ! GRUB_TYPES_CPU_HEADER */ diff --git a/include/grub/arm/uboot/kernel.h b/include/grub/arm/uboot/kernel.h new file mode 100644 index 000000000..c37a18d06 --- /dev/null +++ b/include/grub/arm/uboot/kernel.h @@ -0,0 +1,32 @@ +/* + * 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_KERNEL_MACHINE_HEADER +#define GRUB_KERNEL_MACHINE_HEADER 1 + +#ifndef ASM_FILE + +#include +#include + +#endif /* ! ASM_FILE */ + +#define GRUB_KERNEL_MACHINE_STACK_SIZE 0x40000 +#define GRUB_KERNEL_MACHINE_HEAP_SIZE (grub_size_t) (2 * 1024 * 1024) + +#endif /* ! GRUB_KERNEL_MACHINE_HEADER */ diff --git a/include/grub/disk.h b/include/grub/disk.h index c91583dfd..bf21473ca 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -46,7 +46,8 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_ARCDISK_ID, GRUB_DISK_DEVICE_HOSTDISK_ID, GRUB_DISK_DEVICE_PROCFS_ID, - GRUB_DISK_DEVICE_CBFSDISK_ID + GRUB_DISK_DEVICE_CBFSDISK_ID, + GRUB_DISK_DEVICE_UBOOTDISK_ID, }; struct grub_disk; diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index c3efa9b3d..7cacabd45 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -63,9 +63,10 @@ struct grub_pe32_coff_header grub_uint16_t characteristics; }; -#define GRUB_PE32_MACHINE_I386 0x14c -#define GRUB_PE32_MACHINE_IA64 0x200 -#define GRUB_PE32_MACHINE_X86_64 0x8664 +#define GRUB_PE32_MACHINE_I386 0x14c +#define GRUB_PE32_MACHINE_IA64 0x200 +#define GRUB_PE32_MACHINE_X86_64 0x8664 +#define GRUB_PE32_MACHINE_ARMTHUMB_MIXED 0x01c2 #define GRUB_PE32_RELOCS_STRIPPED 0x0001 #define GRUB_PE32_EXECUTABLE_IMAGE 0x0002 @@ -276,8 +277,10 @@ struct grub_pe32_fixup_block #define GRUB_PE32_REL_BASED_HIGHLOW 3 #define GRUB_PE32_REL_BASED_HIGHADJ 4 #define GRUB_PE32_REL_BASED_MIPS_JMPADDR 5 +#define GRUB_PE32_REL_BASED_ARM_MOV32A 5 #define GRUB_PE32_REL_BASED_SECTION 6 #define GRUB_PE32_REL_BASED_REL 7 +#define GRUB_PE32_REL_BASED_ARM_MOV32T 7 #define GRUB_PE32_REL_BASED_IA64_IMM64 9 #define GRUB_PE32_REL_BASED_DIR64 10 #define GRUB_PE32_REL_BASED_HIGH3ADJ 11 diff --git a/include/grub/fdt.h b/include/grub/fdt.h new file mode 100644 index 000000000..2ad0536b6 --- /dev/null +++ b/include/grub/fdt.h @@ -0,0 +1,99 @@ +/* + * 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_FDT_HEADER +#define GRUB_FDT_HEADER 1 + +#include + +#define FDT_MAGIC 0xD00DFEED + +typedef struct { + grub_uint32_t magic; + grub_uint32_t totalsize; + grub_uint32_t off_dt_struct; + grub_uint32_t off_dt_strings; + grub_uint32_t off_mem_rsvmap; + grub_uint32_t version; + grub_uint32_t last_comp_version; + grub_uint32_t boot_cpuid_phys; + grub_uint32_t size_dt_strings; + grub_uint32_t size_dt_struct; +} grub_fdt_header_t; + +#define grub_fdt_get_header(fdt, field) \ + grub_be_to_cpu32(((const grub_fdt_header_t *)(fdt))->field) +#define grub_fdt_set_header(fdt, field, value) \ + ((grub_fdt_header_t *)(fdt))->field = grub_cpu_to_be32(value) + +#define grub_fdt_get_magic(fdt) \ + grub_fdt_get_header(fdt, magic) +#define grub_fdt_set_magic(fdt, value) \ + grub_fdt_set_header(fdt, magic, value) +#define grub_fdt_get_totalsize(fdt) \ + grub_fdt_get_header(fdt, totalsize) +#define grub_fdt_set_totalsize(fdt, value) \ + grub_fdt_set_header(fdt, totalsize, value) +#define grub_fdt_get_off_dt_struct(fdt) \ + grub_fdt_get_header(fdt, off_dt_struct) +#define grub_fdt_set_off_dt_struct(fdt, value) \ + grub_fdt_set_header(fdt, off_dt_struct, value) +#define grub_fdt_get_off_dt_strings(fdt) \ + grub_fdt_get_header(fdt, off_dt_strings) +#define grub_fdt_set_off_dt_strings(fdt, value) \ + grub_fdt_set_header(fdt, off_dt_strings, value) +#define grub_fdt_get_off_mem_rsvmap(fdt) \ + grub_fdt_get_header(fdt, off_mem_rsvmap) +#define grub_fdt_set_off_mem_rsvmap(fdt, value) \ + grub_fdt_set_header(fdt, off_mem_rsvmap, value) +#define grub_fdt_get_version(fdt) \ + grub_fdt_get_header(fdt, version) +#define grub_fdt_set_version(fdt, value) \ + grub_fdt_set_header(fdt, version, value) +#define grub_fdt_get_last_comp_version(fdt) \ + grub_fdt_get_header(fdt, last_comp_version) +#define grub_fdt_set_last_comp_version(fdt, value) \ + grub_fdt_set_header(fdt, last_comp_version, value) +#define grub_fdt_get_boot_cpuid_phys(fdt) \ + grub_fdt_get_header(fdt, boot_cpuid_phys) +#define grub_fdt_set_boot_cpuid_phys(fdt, value) \ + grub_fdt_set_header(fdt, boot_cpuid_phys, value) +#define grub_fdt_get_size_dt_strings(fdt) \ + grub_fdt_get_header(fdt, size_dt_strings) +#define grub_fdt_set_size_dt_strings(fdt, value) \ + grub_fdt_set_header(fdt, size_dt_strings, value) +#define grub_fdt_get_size_dt_struct(fdt) \ + grub_fdt_get_header(fdt, size_dt_struct) +#define grub_fdt_set_size_dt_struct(fdt, value) \ + grub_fdt_set_header(fdt, size_dt_struct, value) + +int grub_fdt_check_header (void *fdt, unsigned int size); +int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, + const char *name); +int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, + const char *name); + +int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name, + const void *val, grub_uint32_t len); +#define grub_fdt_set_prop32(fdt, nodeoffset, name, val) \ +({ \ + grub_uint32_t _val = grub_cpu_to_be32(val); \ + grub_fdt_set_prop ((fdt), (nodeoffset), (name), &_val, 4); \ +}) + +#endif /* ! GRUB_FDT_HEADER */ diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 73ea41656..52105fcce 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -78,7 +78,7 @@ struct grub_module_info64 #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) \ || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_MIPS_QEMU_MIPS) \ || defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_ARC) \ - || defined (__sparc__) + || defined (__sparc__) || defined (GRUB_MACHINE_UBOOT) /* FIXME: stack is between 2 heap regions. Move it. */ #define GRUB_KERNEL_PRELOAD_SPACE_REUSABLE 1 #endif diff --git a/include/grub/libgcc.h b/include/grub/libgcc.h index ca0d5774e..d101db473 100644 --- a/include/grub/libgcc.h +++ b/include/grub/libgcc.h @@ -112,3 +112,14 @@ void EXPORT_FUNC (_savegpr_29) (void); void EXPORT_FUNC (_savegpr_30) (void); void EXPORT_FUNC (_savegpr_31) (void); #endif + +#if defined (__arm__) +void EXPORT_FUNC (__aeabi_idiv) (void); +void EXPORT_FUNC (__aeabi_idivmod) (void); +void EXPORT_FUNC (__aeabi_lasr) (void); +void EXPORT_FUNC (__aeabi_llsl) (void); +void EXPORT_FUNC (__aeabi_llsr) (void); +void EXPORT_FUNC (__aeabi_uidiv) (void); +void EXPORT_FUNC (__aeabi_uidivmod) (void); +void EXPORT_FUNC (__aeabi_ulcmp) (void); +#endif diff --git a/include/grub/offsets.h b/include/grub/offsets.h index c5ef79f7a..08e490c96 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -103,6 +103,7 @@ #define GRUB_KERNEL_I386_IEEE1275_MOD_GAP 0x0 #define GRUB_KERNEL_I386_COREBOOT_MOD_GAP 0x0 #define GRUB_KERNEL_SPARC64_IEEE1275_MOD_GAP 0x0 +#define GRUB_KERNEL_ARM_UBOOT_MOD_GAP 0x0 #define GRUB_KERNEL_POWERPC_IEEE1275_MOD_ALIGN 0x1000 #define GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN 0x1 @@ -111,6 +112,10 @@ #define GRUB_KERNEL_MIPS_ARC_MOD_ALIGN 0x1 #define GRUB_KERNEL_MIPS_QEMU_MIPS_MOD_ALIGN 0x1 +#define GRUB_KERNEL_ARM_UBOOT_MOD_ALIGN 0x8 +#define GRUB_KERNEL_ARM_UBOOT_TOTAL_MODULE_SIZE 0x4 +#define GRUB_KERNEL_ARM_UBOOT_LINK_ADDR 0x08000000 + /* Minimal gap between _end and the start of the modules. It's a hack for PowerMac to prevent "CLAIM failed" error. The real fix is to rewrite grub-mkimage to generate valid ELF files. */ diff --git a/include/grub/uboot/api_public.h b/include/grub/uboot/api_public.h new file mode 100644 index 000000000..0707f5cff --- /dev/null +++ b/include/grub/uboot/api_public.h @@ -0,0 +1,181 @@ +/* + * (C) Copyright 2007-2008 Semihalf + * + * Written by: Rafal Jaworowski + * + * This file is dual licensed; you can use it under the terms of + * either the GPL, or the BSD license, at your option. + * + * I. GPL: + * + * This file 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 2 of + * the License, or (at your option) any later version. + * + * This file 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Alternatively, + * + * II. BSD license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _API_PUBLIC_H_ +#define _API_PUBLIC_H_ + +#define API_EINVAL 1 /* invalid argument(s) */ +#define API_ENODEV 2 /* no device */ +#define API_ENOMEM 3 /* no memory */ +#define API_EBUSY 4 /* busy, occupied etc. */ +#define API_EIO 5 /* I/O error */ +#define API_ESYSC 6 /* syscall error */ + +typedef int (*scp_t) (int, int *, ...); + +#define API_SIG_VERSION 1 +#define API_SIG_MAGIC "UBootAPI" +#define API_SIG_MAGLEN 8 + +struct api_signature +{ + char magic[API_SIG_MAGLEN]; /* magic string */ + grub_uint16_t version; /* API version */ + grub_uint32_t checksum; /* checksum of this sig struct */ + scp_t syscall; /* entry point to the API */ +}; + +enum +{ + API_RSVD = 0, + API_GETC, + API_PUTC, + API_TSTC, + API_PUTS, + API_RESET, + API_GET_SYS_INFO, + API_UDELAY, + API_GET_TIMER, + API_DEV_ENUM, + API_DEV_OPEN, + API_DEV_CLOSE, + API_DEV_READ, + API_DEV_WRITE, + API_ENV_ENUM, + API_ENV_GET, + API_ENV_SET, + API_DISPLAY_GET_INFO, + API_DISPLAY_DRAW_BITMAP, + API_DISPLAY_CLEAR, + API_MAXCALL +}; + +#define MR_ATTR_FLASH 0x0001 +#define MR_ATTR_DRAM 0x0002 +#define MR_ATTR_SRAM 0x0003 +#define MR_ATTR_MASK 0x000f + +struct mem_region +{ + unsigned long start; + unsigned long size; + int flags; +}; + +struct sys_info +{ + unsigned long clk_bus; + unsigned long clk_cpu; + unsigned long bar; + struct mem_region *mr; + int mr_no; /* number of memory regions */ +}; + +#undef CONFIG_SYS_64BIT_LBA +#ifdef CONFIG_SYS_64BIT_LBA +typedef u_int64_t lbasize_t; +#else +typedef unsigned long lbasize_t; +#endif +typedef unsigned long lbastart_t; + +#define DEV_TYP_NONE 0x0000 +#define DEV_TYP_NET 0x0001 + +#define DEV_TYP_STOR 0x0002 +#define DT_STOR_IDE 0x0010 +#define DT_STOR_SCSI 0x0020 +#define DT_STOR_USB 0x0040 +#define DT_STOR_MMC 0x0080 +#define DT_STOR_SATA 0x0100 + +#define DEV_STA_CLOSED 0x0000 /* invalid, closed */ +#define DEV_STA_OPEN 0x0001 /* open i.e. active */ + +struct device_info +{ + int type; + void *cookie; + + union + { + struct + { + lbasize_t block_count; /* no of blocks */ + unsigned long block_size; /* size of one block */ + } storage; + + struct + { + unsigned char hwaddr[6]; + } net; + } info; +#define di_stor info.storage +#define di_net info.net + + int state; +}; + +#define DISPLAY_TYPE_LCD 0x0001 +#define DISPLAY_TYPE_VIDEO 0x0002 + +struct display_info +{ + int type; + /* screen size in pixels */ + int pixel_width; + int pixel_height; + /* screen size in rows and columns of text */ + int screen_rows; + int screen_cols; +}; + +#endif /* _API_PUBLIC_H_ */ diff --git a/include/grub/uboot/console.h b/include/grub/uboot/console.h new file mode 100644 index 000000000..993a53845 --- /dev/null +++ b/include/grub/uboot/console.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 GRUB_CONSOLE_MACHINE_HEADER +#define GRUB_CONSOLE_MACHINE_HEADER 1 + +/* Initialize the console system. */ +void grub_console_init_early (void); +void grub_console_init_lately (void); + +/* Exit the console system. */ +void grub_console_fini (void); + +#endif /* ! GRUB_CONSOLE_MACHINE_HEADER */ diff --git a/include/grub/uboot/disk.h b/include/grub/uboot/disk.h new file mode 100644 index 000000000..e380b4c89 --- /dev/null +++ b/include/grub/uboot/disk.h @@ -0,0 +1,43 @@ +/* + * 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_UBOOT_DISK_HEADER +#define GRUB_UBOOT_DISK_HEADER 1 + +#include +#include +#include + +void grub_ubootdisk_init (void); +void grub_ubootdisk_fini (void); + +enum disktype +{ cd, fd, hd }; + +struct ubootdisk_data +{ + void *cookie; + struct device_info *dev; + int opencount; + enum disktype type; + grub_uint32_t block_size; +}; + +grub_err_t grub_ubootdisk_register (struct device_info *newdev); + +#endif /* ! GRUB_UBOOT_DISK_HEADER */ diff --git a/include/grub/uboot/image.h b/include/grub/uboot/image.h new file mode 100644 index 000000000..3e3f0346e --- /dev/null +++ b/include/grub/uboot/image.h @@ -0,0 +1,175 @@ +/* + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program 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 2 of + * the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ******************************************************************** + * NOTE: This header file defines an interface to U-Boot. Including + * this (unmodified) header file in another file is considered normal + * use of U-Boot, and does *not* fall under the heading of "derived + * work". + ******************************************************************** + */ + +#ifndef __GRUB_UBOOT_IMAGE_H__ +#define __GRUB_UBOOT_IMAGE_H__ + +/* + * Operating System Codes + */ +#define GRUB_UBOOT_IH_OS_INVALID 0 /* Invalid OS */ +#define GRUB_UBOOT_IH_OS_OPENBSD 1 /* OpenBSD */ +#define GRUB_UBOOT_IH_OS_NETBSD 2 /* NetBSD */ +#define GRUB_UBOOT_IH_OS_FREEBSD 3 /* FreeBSD */ +#define GRUB_UBOOT_IH_OS_4_4BSD 4 /* 4.4BSD */ +#define GRUB_UBOOT_IH_OS_LINUX 5 /* Linux */ +#define GRUB_UBOOT_IH_OS_SVR4 6 /* SVR4 */ +#define GRUB_UBOOT_IH_OS_ESIX 7 /* Esix */ +#define GRUB_UBOOT_IH_OS_SOLARIS 8 /* Solaris */ +#define GRUB_UBOOT_IH_OS_IRIX 9 /* Irix */ +#define GRUB_UBOOT_IH_OS_SCO 10 /* SCO */ +#define GRUB_UBOOT_IH_OS_DELL 11 /* Dell */ +#define GRUB_UBOOT_IH_OS_NCR 12 /* NCR */ +#define GRUB_UBOOT_IH_OS_LYNXOS 13 /* LynxOS */ +#define GRUB_UBOOT_IH_OS_VXWORKS 14 /* VxWorks */ +#define GRUB_UBOOT_IH_OS_PSOS 15 /* pSOS */ +#define GRUB_UBOOT_IH_OS_QNX 16 /* QNX */ +#define GRUB_UBOOT_IH_OS_U_BOOT 17 /* Firmware */ +#define GRUB_UBOOT_IH_OS_RTEMS 18 /* RTEMS */ +#define GRUB_UBOOT_IH_OS_ARTOS 19 /* ARTOS */ +#define GRUB_UBOOT_IH_OS_UNITY 20 /* Unity OS */ +#define GRUB_UBOOT_IH_OS_INTEGRITY 21 /* INTEGRITY */ +#define GRUB_UBOOT_IH_OS_OSE 22 /* OSE */ + +/* + * CPU Architecture Codes (supported by Linux) + */ +#define GRUB_UBOOT_IH_ARCH_INVALID 0 /* Invalid CPU */ +#define GRUB_UBOOT_IH_ARCH_ALPHA 1 /* Alpha */ +#define GRUB_UBOOT_IH_ARCH_ARM 2 /* ARM */ +#define GRUB_UBOOT_IH_ARCH_I386 3 /* Intel x86 */ +#define GRUB_UBOOT_IH_ARCH_IA64 4 /* IA64 */ +#define GRUB_UBOOT_IH_ARCH_MIPS 5 /* MIPS */ +#define GRUB_UBOOT_IH_ARCH_MIPS64 6 /* MIPS 64 Bit */ +#define GRUB_UBOOT_IH_ARCH_PPC 7 /* PowerPC */ +#define GRUB_UBOOT_IH_ARCH_S390 8 /* IBM S390 */ +#define GRUB_UBOOT_IH_ARCH_SH 9 /* SuperH */ +#define GRUB_UBOOT_IH_ARCH_SPARC 10 /* Sparc */ +#define GRUB_UBOOT_IH_ARCH_SPARC64 11 /* Sparc 64 Bit */ +#define GRUB_UBOOT_IH_ARCH_M68K 12 /* M68K */ +#define GRUB_UBOOT_IH_ARCH_MICROBLAZE 14 /* MicroBlaze */ +#define GRUB_UBOOT_IH_ARCH_NIOS2 15 /* Nios-II */ +#define GRUB_UBOOT_IH_ARCH_BLACKFIN 16 /* Blackfin */ +#define GRUB_UBOOT_IH_ARCH_AVR32 17 /* AVR32 */ +#define GRUB_UBOOT_IH_ARCH_ST200 18 /* STMicroelectronics ST200 */ +#define GRUB_UBOOT_IH_ARCH_SANDBOX 19 /* Sandbox architecture (test only) */ +#define GRUB_UBOOT_IH_ARCH_NDS32 20 /* ANDES Technology - NDS32 */ +#define GRUB_UBOOT_IH_ARCH_OPENRISC 21 /* OpenRISC 1000 */ + +/* + * Image Types + * + * "Standalone Programs" are directly runnable in the environment + * provided by U-Boot; it is expected that (if they behave + * well) you can continue to work in U-Boot after return from + * the Standalone Program. + * "OS Kernel Images" are usually images of some Embedded OS which + * will take over control completely. Usually these programs + * will install their own set of exception handlers, device + * drivers, set up the MMU, etc. - this means, that you cannot + * expect to re-enter U-Boot except by resetting the CPU. + * "RAMDisk Images" are more or less just data blocks, and their + * parameters (address, size) are passed to an OS kernel that is + * being started. + * "Multi-File Images" contain several images, typically an OS + * (Linux) kernel image and one or more data images like + * RAMDisks. This construct is useful for instance when you want + * to boot over the network using BOOTP etc., where the boot + * server provides just a single image file, but you want to get + * for instance an OS kernel and a RAMDisk image. + * + * "Multi-File Images" start with a list of image sizes, each + * image size (in bytes) specified by an "uint32_t" in network + * byte order. This list is terminated by an "(uint32_t)0". + * Immediately after the terminating 0 follow the images, one by + * one, all aligned on "uint32_t" boundaries (size rounded up to + * a multiple of 4 bytes - except for the last file). + * + * "Firmware Images" are binary images containing firmware (like + * U-Boot or FPGA images) which usually will be programmed to + * flash memory. + * + * "Script files" are command sequences that will be executed by + * U-Boot's command interpreter; this feature is especially + * useful when you configure U-Boot to use a real shell (hush) + * as command interpreter (=> Shell Scripts). + */ + +#define GRUB_UBOOT_IH_TYPE_INVALID 0 /* Invalid Image */ +#define GRUB_UBOOT_IH_TYPE_STANDALONE 1 /* Standalone Program */ +#define GRUB_UBOOT_IH_TYPE_KERNEL 2 /* OS Kernel Image */ +#define GRUB_UBOOT_IH_TYPE_RAMDISK 3 /* RAMDisk Image */ +#define GRUB_UBOOT_IH_TYPE_MULTI 4 /* Multi-File Image */ +#define GRUB_UBOOT_IH_TYPE_FIRMWARE 5 /* Firmware Image */ +#define GRUB_UBOOT_IH_TYPE_SCRIPT 6 /* Script file */ +#define GRUB_UBOOT_IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ +#define GRUB_UBOOT_IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ +#define GRUB_UBOOT_IH_TYPE_KWBIMAGE 9 /* Kirkwood Boot Image */ +#define GRUB_UBOOT_IH_TYPE_IMXIMAGE 10 /* Freescale IMXBoot Image */ +#define GRUB_UBOOT_IH_TYPE_UBLIMAGE 11 /* Davinci UBL Image */ +#define GRUB_UBOOT_IH_TYPE_OMAPIMAGE 12 /* TI OMAP Config Header Image */ +#define GRUB_UBOOT_IH_TYPE_AISIMAGE 13 /* TI Davinci AIS Image */ +#define GRUB_UBOOT_IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image, can run from any load address */ +#define GRUB_UBOOT_IH_TYPE_PBLIMAGE 15 /* Freescale PBL Boot Image */ + +/* + * Compression Types + */ +#define GRUB_UBOOT_IH_COMP_NONE 0 /* No Compression Used */ +#define GRUB_UBOOT_IH_COMP_GZIP 1 /* gzip Compression Used */ +#define GRUB_UBOOT_IH_COMP_BZIP2 2 /* bzip2 Compression Used */ +#define GRUB_UBOOT_IH_COMP_LZMA 3 /* lzma Compression Used */ +#define GRUB_UBOOT_IH_COMP_LZO 4 /* lzo Compression Used */ + +#define GRUB_UBOOT_IH_MAGIC 0x27051956 /* Image Magic Number */ +#define GRUB_UBOOT_IH_NMLEN 32 /* Image Name Length */ + +/* + * Legacy format image header, + * all data in network byte order (aka natural aka bigendian). + */ +struct grub_uboot_image_header { + grub_uint32_t ih_magic; /* Image Header Magic Number */ + grub_uint32_t ih_hcrc; /* Image Header CRC Checksum */ + grub_uint32_t ih_time; /* Image Creation Timestamp */ + grub_uint32_t ih_size; /* Image Data Size */ + grub_uint32_t ih_load; /* Data Load Address */ + grub_uint32_t ih_ep; /* Entry Point Address */ + grub_uint32_t ih_dcrc; /* Image Data CRC Checksum */ + grub_uint8_t ih_os; /* Operating System */ + grub_uint8_t ih_arch; /* CPU architecture */ + grub_uint8_t ih_type; /* Image Type */ + grub_uint8_t ih_comp; /* Compression Type */ + grub_uint8_t ih_name[GRUB_UBOOT_IH_NMLEN]; /* Image Name */ +}; + +#endif /* __IMAGE_H__ */ diff --git a/include/grub/uboot/uboot.h b/include/grub/uboot/uboot.h new file mode 100644 index 000000000..c122de6ab --- /dev/null +++ b/include/grub/uboot/uboot.h @@ -0,0 +1,86 @@ +/* uboot.h - declare variables and functions for U-Boot 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 . + */ + +#ifndef GRUB_UBOOT_UBOOT_HEADER +#define GRUB_UBOOT_UBOOT_HEADER 1 + +#include +#include + +/* Functions. */ +void grub_uboot_mm_init (void); +void grub_uboot_init (void); +void grub_uboot_fini (void); + +void grub_uboot_return (int) __attribute__ ((noreturn)); + +grub_addr_t grub_uboot_get_real_bss_start (void); + +grub_err_t grub_uboot_probe_hardware (void); + +extern grub_addr_t EXPORT_VAR (start_of_ram); + +grub_uint32_t EXPORT_FUNC (grub_uboot_get_machine_type) (void); +grub_addr_t EXPORT_FUNC (grub_uboot_get_boot_data) (void); + + +/* + * The U-Boot API operates through a "syscall" interface, consisting of an + * entry point address and a set of syscall numbers. The location of this + * entry point is described in a structure allocated on the U-Boot heap. + * We scan through a defined region around the hint address passed to us + * from U-Boot. + */ + +#define UBOOT_API_SEARCH_LEN (3 * 1024 * 1024) +int grub_uboot_api_init (void); + +/* + * All functions below are wrappers around the uboot_syscall() function, + * implemented in grub-core/kern/uboot/uboot.c +*/ + +int grub_uboot_getc (void); +int grub_uboot_tstc (void); +void grub_uboot_putc (int c); +void grub_uboot_puts (const char *s); + +void EXPORT_FUNC (grub_uboot_reset) (void); + +struct sys_info *grub_uboot_get_sys_info (void); + +void grub_uboot_udelay (grub_uint32_t usec); +grub_uint32_t grub_uboot_get_timer (grub_uint32_t base); + +int EXPORT_FUNC (grub_uboot_dev_enum) (void); +struct device_info * EXPORT_FUNC (grub_uboot_dev_get) (int index); +int EXPORT_FUNC (grub_uboot_dev_open) (struct device_info *dev); +int EXPORT_FUNC (grub_uboot_dev_close) (struct device_info *dev); +int grub_uboot_dev_write (struct device_info *dev, void *buf, int *len); +int grub_uboot_dev_read (struct device_info *dev, void *buf, grub_size_t blocks, + grub_uint32_t start, grub_size_t * real_blocks); +int EXPORT_FUNC (grub_uboot_dev_recv) (struct device_info *dev, void *buf, + int size, int *real_size); +int EXPORT_FUNC (grub_uboot_dev_send) (struct device_info *dev, void *buf, + int size); + +char *grub_uboot_env_get (const char *name); +void grub_uboot_env_set (const char *name, const char *value); + +#endif /* ! GRUB_UBOOT_UBOOT_HEADER */ diff --git a/util/grub-install.in b/util/grub-install.in index 3a33797cd..1816bb1f2 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -272,6 +272,8 @@ if [ x$source_directory = x ]; then target=i386-pc fi ;; + x"arm"*) + target="arm-uboot";; *) gettext "Unable to determine your platform. Use --target." ; echo ;; @@ -291,7 +293,7 @@ if [ "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" = "i386-pc" ] ; then if [ x$disk_module = xunspecified ]; then disk_module=biosdisk fi -elif [ "${grub_modinfo_platform}" = "ieee1275" ] || [ "${grub_modinfo_platform}" = "efi" ] || [ "${grub_modinfo_platform}" = "arc" ] ; then +elif [ "${grub_modinfo_platform}" = "ieee1275" ] || [ "${grub_modinfo_platform}" = "efi" ] || [ "${grub_modinfo_platform}" = "arc" ] || [ "${grub_modinfo_platform}" = "uboot" ] ; then disk_module= else disk_module=native @@ -424,6 +426,8 @@ if [ x"$grub_modinfo_platform" = xefi ]; then # expansion. ia64) efi_file=BOOTIA64.EFI ;; + arm) + efi_file=BOOTARM.EFI ;; esac else # It is convenient for each architecture to have a different @@ -438,6 +442,8 @@ if [ x"$grub_modinfo_platform" = xefi ]; then # expansion. ia64) efi_file=grubia64.efi ;; + arm) + efi_file=grubarm.efi ;; *) efi_file=grub.efi ;; esac diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index daecf24ab..c5a0c7a9d 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #define _GNU_SOURCE 1 @@ -71,7 +73,7 @@ struct image_target_desc IMAGE_I386_IEEE1275, IMAGE_LOONGSON_ELF, IMAGE_QEMU, IMAGE_PPC, IMAGE_YEELOONG_FLASH, IMAGE_FULOONG2F_FLASH, IMAGE_I386_PC_PXE, IMAGE_MIPS_ARC, - IMAGE_QEMU_MIPS_FLASH + IMAGE_QEMU_MIPS_FLASH, IMAGE_UBOOT } id; enum { @@ -489,6 +491,46 @@ struct image_target_desc image_targets[] = .link_align = GRUB_KERNEL_MIPS_QEMU_MIPS_LINK_ALIGN, .default_compression = COMPRESSION_NONE }, + { + .dirname = "arm-uboot", + .names = { "arm-uboot", NULL }, + .voidp_sizeof = 4, + .bigendian = 0, + .id = IMAGE_UBOOT, + .flags = PLATFORM_FLAGS_NONE, + .total_module_size = GRUB_KERNEL_ARM_UBOOT_TOTAL_MODULE_SIZE, + .decompressor_compressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_size = TARGET_NO_FIELD, + .decompressor_uncompressed_addr = TARGET_NO_FIELD, + .section_align = GRUB_KERNEL_ARM_UBOOT_MOD_ALIGN, + .vaddr_offset = 0, + .link_addr = GRUB_KERNEL_ARM_UBOOT_LINK_ADDR, + .elf_target = EM_ARM, + .mod_gap = GRUB_KERNEL_ARM_UBOOT_MOD_GAP, + .mod_align = GRUB_KERNEL_ARM_UBOOT_MOD_ALIGN, + .link_align = 4 + }, + { + .dirname = "arm-efi", + .names = { "arm-efi", NULL }, + .voidp_sizeof = 4, + .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 = ALIGN_UP (GRUB_PE32_MSDOS_STUB_SIZE + + GRUB_PE32_SIGNATURE_SIZE + + sizeof (struct grub_pe32_coff_header) + + sizeof (struct grub_pe32_optional_header) + + 4 * sizeof (struct grub_pe32_section_table), + GRUB_PE32_SECTION_ALIGNMENT), + .pe_target = GRUB_PE32_MACHINE_ARMTHUMB_MIXED, + .elf_target = EM_ARM, + }, }; #define grub_target_to_host32(x) (grub_target_to_host32_real (image_target, (x))) @@ -1057,6 +1099,7 @@ generate_image (const char *dir, const char *prefix, case IMAGE_SPARC64_CDCORE: case IMAGE_I386_IEEE1275: case IMAGE_PPC: + case IMAGE_UBOOT: break; } @@ -1496,6 +1539,42 @@ generate_image (const char *dir, const char *prefix, core_size = rom_size; } break; + + case IMAGE_UBOOT: + { + struct grub_uboot_image_header *hdr; + GRUB_PROPERLY_ALIGNED_ARRAY (crc32_context, GRUB_MD_CRC32->contextsize); + + hdr = xmalloc (core_size + sizeof (struct grub_uboot_image_header)); + memcpy (hdr + 1, core_img, core_size); + + memset (hdr, 0, sizeof (*hdr)); + hdr->ih_magic = grub_cpu_to_be32_compile_time (GRUB_UBOOT_IH_MAGIC); + hdr->ih_time = grub_cpu_to_be32 (time (0)); + hdr->ih_size = grub_cpu_to_be32 (core_size); + hdr->ih_load = grub_cpu_to_be32 (image_target->link_addr); + hdr->ih_ep = grub_cpu_to_be32 (image_target->link_addr); + hdr->ih_type = GRUB_UBOOT_IH_TYPE_KERNEL; + hdr->ih_os = GRUB_UBOOT_IH_OS_LINUX; + hdr->ih_arch = GRUB_UBOOT_IH_ARCH_ARM; + hdr->ih_comp = GRUB_UBOOT_IH_COMP_NONE; + + GRUB_MD_CRC32->init(crc32_context); + GRUB_MD_CRC32->write(crc32_context, hdr + 1, core_size); + GRUB_MD_CRC32->final(crc32_context); + hdr->ih_dcrc = grub_get_unaligned32 (GRUB_MD_CRC32->read (crc32_context)); + + GRUB_MD_CRC32->init(crc32_context); + GRUB_MD_CRC32->write(crc32_context, hdr, sizeof (*hdr)); + GRUB_MD_CRC32->final(crc32_context); + hdr->ih_hcrc = grub_get_unaligned32 (GRUB_MD_CRC32->read (crc32_context)); + + free (core_img); + core_img = (char *) hdr; + core_size += sizeof (struct grub_uboot_image_header); + } + break; + case IMAGE_MIPS_ARC: { char *ecoff_img; diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index b6b263d46..0f4464084 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -58,6 +58,8 @@ #error "I'm confused" #endif +static Elf_Addr SUFFIX (entry_point); + /* Relocate symbols; note that this function overwrites the symbol table. Return the address of a start symbol. */ static Elf_Addr @@ -408,6 +410,55 @@ SUFFIX (relocate_addresses) (Elf_Ehdr *e, Elf_Shdr *sections, } break; #endif +#if defined(MKIMAGE_ELF32) + case EM_ARM: + { + sym_addr += addend; + sym_addr -= SUFFIX (entry_point); + switch (ELF_R_TYPE (info)) + { + case R_ARM_ABS32: + { + grub_util_info (" ABS32:\toffset=%d\t(0x%08x)", + (int) sym_addr, (int) sym_addr); + /* Data will be naturally aligned */ + sym_addr += 0x400; + *target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr); + } + break; + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + { + grub_err_t err; + grub_util_info (" THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)", (unsigned long) target, sym_addr); + sym_addr -= offset; + /* Thumb instructions can be 16-bit aligned */ + err = grub_arm_reloc_thm_call ((grub_uint16_t *) target, + sym_addr); + if (err) + grub_util_error ("%s", grub_errmsg); + } + break; + case R_ARM_THM_JUMP19: + { + grub_err_t err; + grub_util_info (" THM_JUMP19:\toffset=%d\t(0x%08x)", + sym_addr, sym_addr); + sym_addr -= offset; + + /* Thumb instructions can be 16-bit aligned */ + err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); + if (err) + grub_util_error ("%s", grub_errmsg); + } + break; + default: + grub_util_error (_("relocation 0x%x is not implemented yet!"), ELF_R_TYPE (info)); + break; + } + break; + } +#endif /* MKIMAGE_ELF32 */ default: grub_util_error ("unknown architecture type %d", image_target->elf_target); @@ -635,6 +686,41 @@ SUFFIX (make_reloc_section) (Elf_Ehdr *e, void **out, break; } break; +#if defined(MKIMAGE_ELF32) + case EM_ARM: + switch (ELF_R_TYPE (info)) + { + /* Relative relocations do not require fixup entries. */ + case R_ARM_JUMP24: + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP19: + case R_ARM_THM_JUMP24: + { + Elf_Addr addr; + + addr = section_address + offset; + grub_util_info (" %s: not adding fixup: 0x%08x : 0x%08x", __FUNCTION__, (unsigned int) addr, (unsigned int) current_address); + } + break; + /* Create fixup entry for PE/COFF loader */ + case R_ARM_ABS32: + { + Elf_Addr addr; + + addr = section_address + offset; + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_HIGHLOW, + addr, 0, current_address, + image_target); + } + break; + default: + grub_util_error (_("fixup for relocation 0x%x not implemented"), ELF_R_TYPE (info)); + break; + } + break; +#endif /* defined(MKIMAGE_ELF32) */ default: grub_util_error ("unknown machine type 0x%x", image_target->elf_target); } @@ -945,6 +1031,8 @@ SUFFIX (load_image) (const char *kernel_path, grub_size_t *exec_size, if (*start == 0) grub_util_error ("start symbol is not defined"); + SUFFIX (entry_point) = (Elf_Addr) *start; + /* Resolve addresses in the virtual address space. */ SUFFIX (relocate_addresses) (e, sections, section_addresses, section_entsize,