diff --git a/.bzrignore b/.bzrignore index c6797ad8f..a0cd25dd4 100644 --- a/.bzrignore +++ b/.bzrignore @@ -174,3 +174,5 @@ po/*.gmo po/LINGUAS include/grub/gcrypt/gcrypt.h include/grub/gcrypt/g10lib.h +grub-core/lib/dtc-grub +grub-core/Makefile.libfdt.def diff --git a/Makefile.util.def b/Makefile.util.def index 513dc3858..3c74e853b 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -150,6 +150,8 @@ program = { common = util/resolve.c; common = grub-core/kern/emu/argp_common.c; + arm = grub-core/kern/arm/dl.c; + extra_dist = util/grub-mkimagexx.c; ldadd = libgrubmods.a; @@ -470,6 +472,7 @@ script = { enable = mips_loongson; enable = ia64_efi; enable = powerpc_ieee1275; + enable = arm_uboot; }; script = { diff --git a/autogen.sh b/autogen.sh index 7a4b5c8be..6df462e0a 100755 --- a/autogen.sh +++ b/autogen.sh @@ -30,6 +30,9 @@ for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c mpih-mul2.c mpih-mul ln -s generic/"$x" grub-core/lib/libgcrypt-grub/mpi/"$x" done +echo "Importing libfdt..." +python util/import_libfdt.py grub-core/lib/dtc/ grub-core + echo "Creating Makefile.tpl..." python gentpl.py | sed -e '/^$/{N;/^\n$/D;}' > Makefile.tpl @@ -43,7 +46,7 @@ if [ "x${GRUB_CONTRIB}" != x ]; then fi UTIL_DEFS='Makefile.util.def Makefile.utilgcry.def' -CORE_DEFS='grub-core/Makefile.core.def grub-core/Makefile.gcry.def' +CORE_DEFS='grub-core/Makefile.core.def grub-core/Makefile.gcry.def grub-core/Makefile.libfdt.def' for extra in contrib/*/Makefile.util.def; do if test -e "$extra"; then diff --git a/conf/Makefile.common b/conf/Makefile.common index c185a553d..d9b34714a 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -37,6 +37,13 @@ if COND_sparc64_ieee1275 CFLAGS_PLATFORM += -mno-app-regs LDFLAGS_PLATFORM = -Wl,-melf64_sparc -mno-relax endif +if COND_arm +# Image entry point always in ARM (A32) state - ensure proper functionality if +# the rest is built for the Thumb (T32) state. + CFLAGS_PLATFORM += -mthumb-interwork -mno-unaligned-access -mlong-calls + CCASFLAGS_PLATFORM = -Wa,-mimplicit-it=thumb + LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache +endif # Other options @@ -110,6 +117,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 2e365008f..0a7a8befe 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 @@ -27,6 +28,7 @@ EXTRA_DIST += grub-core/genemuinit.sh EXTRA_DIST += grub-core/genemuinitheader.sh EXTRA_DIST += grub-core/lib/libgcrypt/cipher +EXTRA_DIST += grub-core/lib/dtc EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/lib -name '*.h') EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/gnulib -name '*.h') diff --git a/configure.ac b/configure.ac index a39a025fe..ed8de8ab4 100644 --- a/configure.ac +++ b/configure.ac @@ -94,6 +94,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). @@ -114,6 +117,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 @@ -148,6 +152,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 @@ -179,6 +185,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" ;; @@ -187,6 +194,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 @@ -1150,6 +1158,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 bd366a67d..31a0420f9 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -3389,6 +3389,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 * export:: Export an environment variable @@ -3698,6 +3699,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 b159795a1..93089bfd9 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"] = ["mips_loongson", "mips_arc", "mips_qemu_mips" ] + GROUPS["ieee1275"]; +GROUPS["terminfoinkernel"] = ["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 6f156e75d..f1190a6c5 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -211,6 +211,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 4b4c02473..f58888a6f 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -45,6 +45,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'; @@ -66,6 +69,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; @@ -77,6 +82,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; @@ -112,6 +119,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; @@ -143,6 +156,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; @@ -192,6 +208,10 @@ kernel = { sparc64_ieee1275 = kern/sparc64/dl.c; sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c; + arm = kern/arm/dl.c; + arm = kern/arm/cache.S; + arm = kern/arm/misc.S; + emu = disk/host.c; emu = gnulib/progname.c; emu = gnulib/error.c; @@ -688,6 +708,7 @@ module = { efi = lib/efi/halt.c; ieee1275 = lib/ieee1275/halt.c; emu = lib/emu/halt.c; + uboot = lib/uboot/halt.c; }; module = { @@ -696,11 +717,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; }; @@ -1358,6 +1381,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; @@ -1377,6 +1401,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 = { @@ -1456,9 +1481,12 @@ 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; common = loader/linux.c; common = lib/cmdline.c; enable = noemu; + + fdt_cppflags = '$(CPPFLAGS_LIBFDT)'; }; module = { @@ -1507,6 +1535,7 @@ module = { enable = x86; enable = ia64_efi; + enable = arm_efi; enable = mips; }; diff --git a/grub-core/disk/uboot/ubootdisk.c b/grub-core/disk/uboot/ubootdisk.c new file mode 100644 index 000000000..92ce1e780 --- /dev/null +++ b/grub-core/disk/uboot/ubootdisk.c @@ -0,0 +1,346 @@ +/* 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 + +static struct ubootdisk_data *fd_devices; +static struct ubootdisk_data *hd_devices; +static struct ubootdisk_data *cd_devices; + +/* + * 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, int handle) +{ + struct ubootdisk_data *d; + enum disktype type; + +#define STOR_TYPE(x) ((x) & 0x0ff0) + switch (STOR_TYPE (newdev->type)) + { + case DT_STOR_IDE: + case DT_STOR_SATA: + /* hd */ + type = hd; + break; + case DT_STOR_MMC: + case DT_STOR_USB: + /* fd */ + type = fd; + break; + default: + return GRUB_ERR_BAD_DEVICE; + break; + } + + d = (struct ubootdisk_data *) grub_malloc (sizeof (struct ubootdisk_data)); + if (!d) + return GRUB_ERR_OUT_OF_MEMORY; + d->handle = handle; + d->cookie = newdev->cookie; + d->opencount = 0; + + switch (type) + { + case cd: + grub_dprintf ("ubootdisk", "registering cd device\n"); + d->next = cd_devices; + cd_devices = d; + + break; + case fd: + grub_dprintf ("ubootdisk", "registering fd device\n"); + d->next = fd_devices; + fd_devices = d; + + break; + case hd: + grub_dprintf ("ubootdisk", "registering hd device\n"); + d->next = hd_devices; + hd_devices = d; + + break; + default: + grub_free (d); + return GRUB_ERR_BAD_DEVICE; + } + + return 0; +} + +/* + * uboot_disk_iterate(): + * Itarator over enumerated disk devices. + */ +static int +uboot_disk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct ubootdisk_data *d; + char buf[16]; + int count; + + switch (pull) + { + case GRUB_DISK_PULL_NONE: + /* "hd" - built-in mass-storage */ + for (d = hd_devices, count = 0; d; d = d->next, 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; + case GRUB_DISK_PULL_REMOVABLE: + /* "floppy" - removable mass storage */ + for (d = fd_devices, count = 0; d; d = d->next, count++) + { + grub_snprintf (buf, sizeof (buf) - 1, "fd%d", count); + grub_dprintf ("ubootdisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + + /* "cdrom" - removeable read-only storage */ + for (d = cd_devices, count = 0; d; d = d->next, count++) + { + grub_snprintf (buf, sizeof (buf) - 1, "cd%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_device (struct ubootdisk_data *devices, int num) +{ + struct ubootdisk_data *d; + + for (d = devices; d && num; d = d->next, num--) + ; + + if (num == 0) + return d; + + 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 'f': + d = get_device (fd_devices, num); + break; + case 'c': + d = get_device (cd_devices, num); + break; + case 'h': + d = get_device (hd_devices, 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 = uboot_dev_open (d->handle); + 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; + + /* Device has previously been enumerated, so this should never fail */ + if ((devinfo = uboot_dev_get (d->handle)) == NULL) + goto fail; + + 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); + + 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 = uboot_dev_close (d->handle); + 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; + lbasize_t real_size; + int retval; + + d = disk->data; + + retval = uboot_dev_read (d->handle, 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))) +{ + grub_dprintf ("ubootdisk", "attempt to write\n"); + return GRUB_ERR_NOT_IMPLEMENTED_YET; +} + +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..6d7ed8537 --- /dev/null +++ b/grub-core/kern/arm/cache.S @@ -0,0 +1,251 @@ +/* + * 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 "cache.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 + +/* + * Simple cache maintenance functions + */ + +@ r0 - *beg (inclusive) +@ r1 - *end (exclusive) +clean_dcache_range: + @ Clean data cache range for range to point-of-unification + ldr r2, dlinesz +1: cmp r0, r1 + bge 2f +#ifdef DEBUG + push {r0-r2, lr} + mov r1, r2 + mov r2, r0 + ldr r0, =dcstr + bl EXT_C(grub_printf) + pop {r0-r2, lr} +#endif + mcr p15, 0, r0, c7, c11, 1 @ DCCMVAU + 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, ilinesz +1: cmp r0, r1 + bge 2f +#ifdef DEBUG + push {r0-r2, lr} + mov r1, r2 + mov r2, r0 + ldr r0, =icstr + bl EXT_C(grub_printf) + pop {r0-r2, lr} +#endif + 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 __wrap___clear_cache(char *beg, char *end); +FUNCTION(__wrap___clear_cache) + dmb + dsb + push {r4-r6, lr} + ldr r2, probed @ If first call, probe cache sizes + cmp r2, #0 + bleq probe_caches @ This call corrupts r3 + mov r4, r0 + mov r5, r1 + bl clean_dcache_range + mov r0, r4 + mov r1, r5 + bl invalidate_icache_range + pop {r4-r6, pc} + +probe_caches: + push {r4-r6, lr} + mrc p15, 0, r4, c0, c0, 1 @ Read Cache Type Register + mov r5, #1 + ubfx r6, r4, #16, #4 @ Extract min D-cache num word log2 + add r6, r6, #2 @ words->bytes + lsl r6, r5, r6 @ Convert to num bytes + ldr r3, =dlinesz + str r6, [r3] + and r6, r4, #0xf @ Extract min I-cache num word log2 + add r6, r6, #2 @ words->bytes + lsl r6, r5, r6 @ Convert to num bytes + ldr r3, =ilinesz + str r6, [r3] + ldr r3, =probed @ Flag cache probing done + str r5, [r3] + pop {r4-r6, pc} + +#ifdef DEBUG +dcstr: .asciz "cleaning %d bytes of D cache @ 0x%08x\n" +icstr: .asciz "invalidating %d bytes of I cache @ 0x%08x\n" +#endif + + .align 3 +probed: .long 0 +dlinesz: + .long 0 +ilinesz: + .long 0 + +@void grub_arch_sync_caches (void *address, grub_size_t len) +FUNCTION(grub_arch_sync_caches) + add r1, r0, r1 + b __wrap___clear_cache + + @ 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 + ubfx r1, r0, #24, #3 @ Extract LoC + + 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 CSSIDR 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 CSSIDR + ubfx r3, r8, #13, #14 @ Number of sets -1 + ubfx r4, r8, #3, #9 @ Number of ways -1 + 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 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} + +FUNCTION(grub_arm_disable_caches_mmu) + 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/dl.c b/grub-core/kern/arm/dl.c new file mode 100644 index 000000000..39a34ca5b --- /dev/null +++ b/grub-core/kern/arm/dl.c @@ -0,0 +1,490 @@ +/* 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 + +#ifdef GRUB_UTIL +# include +#else +# if !defined(__thumb2__) +# error "Relocations not implemented for A32 ("ARM") instruction set yet!" +# endif + +grub_err_t reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr); +grub_err_t reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr); +grub_err_t reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr); + +#ifdef DL_DEBUG +static const char *symstrtab; + +/* + * This is a bit of a hack, setting the symstrtab pointer to the last STRTAB + * section in the module (which is where symbol names are in the objects I've + * inspected manually). + */ +static void +set_symstrtab (Elf_Ehdr * e) +{ + int i; + Elf_Shdr *s; + + symstrtab = NULL; + + 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_STRTAB) + symstrtab = (void *) ((grub_addr_t) e + s->sh_offset); +} + +static const char * +get_symbolname (Elf_Sym * sym) +{ + const char *symbolname = symstrtab + sym->st_name; + + return (*symbolname ? symbolname : NULL); +} +#endif /* DL_DEBUG */ + +/* + * R_ARM_ABS32 + * + * Simple relocation of 32-bit value (in literal pool) + */ +static grub_err_t +reloc_abs32 (Elf_Word *target, Elf_Addr sym_addr) +{ + Elf_Addr tmp; + + tmp = *target; + tmp += sym_addr; + *target = tmp; +#if 0 //def GRUB_UTIL + grub_util_info (" %s: reloc_abs32 0x%08x => 0x%08x", __FUNCTION__, + (unsigned int) sym_addr, (unsigned int) tmp); +#endif + + return GRUB_ERR_NONE; +} +#endif /* ndef GRUB_UTIL */ + + +/******************************************************************** + * 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 +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 = (*target << 16) | (*(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)) + { +#ifndef GRUB_UTIL + return grub_error + (GRUB_ERR_BUG, N_("Relocation targeting wrong execution state")); +#else + grub_util_error ("Relocation targeting wrong execution state"); +#endif + } + + 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); +#ifdef GRUB_UTIL + grub_util_info (" sym_addr = 0x%08x", sym_addr); +#endif +#ifdef GRUB_UTIL + offset += sym_addr; +#else + offset += sym_addr - (grub_uint32_t) target; +#endif +#ifdef DEBUG + grub_printf(" %s: target=0x%08x, sym_addr=0x%08x, offset=%d\n", + is_blx ? "BLX" : "BL", (unsigned int) target, sym_addr, offset); +#endif + + if ((offset < offset_low) || (offset > offset_high)) + { +#ifdef GRUB_UTIL + grub_util_error ("Relocation out of range"); +#else + return grub_error + (GRUB_ERR_OUT_OF_RANGE, N_("THM_CALL Relocation out of range.")); +#endif + } + +#ifdef GRUB_UTIL + grub_util_info (" relative destination = 0x%08x", + (unsigned int)target + offset); +#endif + + /* 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 = (insword >> 16) & 0xffff; + *(target + 1) = insword & 0xffff; + +#ifdef GRUB_UTIL +#pragma GCC diagnostic ignored "-Wcast-align" + grub_util_info (" *target = 0x%08x", *((unsigned int *)target)); +#endif + + return GRUB_ERR_NONE; +} + +/* + * R_ARM_THM_JUMP19 + * + * Relocate conditional Thumb (T32) B.W + */ +grub_err_t +reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr) +{ + grub_int32_t offset; + grub_uint32_t insword, insmask; + + /* Extract instruction word in alignment-safe manner */ + insword = (*addr) << 16 | *(addr + 1); + insmask = 0xfbc0d800; + + /* Extract and sign extend offset */ + offset = ((insword >> 26) & 1) << 18 + | ((insword >> 11) & 1) << 17 + | ((insword >> 13) & 1) << 16 + | ((insword >> 16) & 0x3f) << 11 + | (insword & 0x7ff); + offset <<= 1; + if (offset & (1 << 19)) + offset -= (1 << 20); + + /* Adjust and re-truncate offset */ +#ifdef GRUB_UTIL + offset += sym_addr; +#else + offset += sym_addr - (grub_uint32_t) addr; +#endif + if ((offset > 1048574) || (offset < -1048576)) + { + return grub_error + (GRUB_ERR_OUT_OF_RANGE, N_("THM_JUMP19 Relocation out of range.")); + } + + offset >>= 1; + offset &= 0x7ffff; + + /* Reassemble instruction word and write back */ + insword &= insmask; + insword |= ((offset >> 18) & 1) << 26 + | ((offset >> 17) & 1) << 11 + | ((offset >> 16) & 1) << 13 + | ((offset >> 11) & 0x3f) << 16 + | (offset & 0x7ff); + *addr = insword >> 16; + *(addr + 1) = 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 +reloc_jump24 (grub_uint32_t *addr, Elf32_Addr sym_addr) +{ + grub_uint32_t insword; + grub_int32_t offset; + + insword = *addr; + + offset = (insword & 0x00ffffff) << 2; + if (offset & 0x02000000) + offset -= 0x04000000; +#ifdef GRUB_UTIL + offset += sym_addr; +#else + offset += sym_addr - (grub_uint32_t) addr; +#endif + + insword &= 0xff000000; + insword |= (offset >> 2) & 0x00ffffff; + + *addr = insword; + + return GRUB_ERR_NONE; +} + + + +/************************************************* + * Runtime dynamic linker with helper functions. * + *************************************************/ +#ifndef GRUB_UTIL +/* + * find_segment(): finds a module segment matching sh_info + */ +static grub_dl_segment_t +find_segment (grub_dl_segment_t seg, Elf32_Word sh_info) +{ + for (; seg; seg = seg->next) + if (seg->section == sh_info) + return seg; + + return NULL; +} + + +/* + * do_relocations(): + * Iterate over all relocations in section, calling appropriate functions + * for patching. + */ +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. */ + seg = find_segment (mod->segment, relhdr->sh_info); + 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 = (Elf_Word *) ((grub_addr_t) seg->addr + rel[i].r_offset); + + sym_addr = sym[relsym].st_value; + +#ifdef DL_DEBUG + + grub_printf ("%s: 0x%08x -> %s @ 0x%08x\n", __FUNCTION__, + (grub_addr_t) sym_addr, get_symbolname (sym), sym->st_value); +#endif + + switch (reltype) + { + case R_ARM_ABS32: + { + /* Data will be naturally aligned */ + retval = reloc_abs32 (target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + case R_ARM_JUMP24: + { + retval = 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 = 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 = 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")); + +#ifdef DL_DEBUG + set_symstrtab (e); +#endif + +#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_printf ("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; +} +#endif /* ndef GRUB_UTIL */ 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..0ed33cc42 --- /dev/null +++ b/grub-core/kern/arm/uboot/startup.S @@ -0,0 +1,176 @@ +/* + * 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). + * : + * Any part of the module blob that extends beyond _end. + * : + * 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) + + @ Put kernel parameters aside until we can store them (further down) + mov r4, r1 @ machine type + mov r5, r2 @ boot data + + @ Modules have been stored as a blob in BSS, + @ they need to be manually relocated to _end or + @ (__bss_start + grub_total_module_size), whichever greater. + bl uboot_get_real_bss_start @ r0 = src + ldr r1, =EXT_C(_end) @ dst = End of BSS + ldr r2, grub_total_module_size @ blob size + add r3, r0, r2 @ blob end + cmp r1, r3 @ _end < blob end? + movlt r1, r3 @ dst = blob end + blob size + +1: ldr r3, [r0], #4 @ r3 = *src++ + str r3, [r1], #4 @ *dst++ = r3 + subs r2, #4 @ remaining -= 4 + bne 1b @ while remaining != 0 + + @ Set up a new stack, beyond the end of copied modules. + ldr r3, =GRUB_KERNEL_MACHINE_STACK_SIZE + add r3, r1, r3 @ Place stack beyond end of modules + and sp, r3, #~0x7 @ Ensure 8-byte alignment + + @ Since we _are_ the C run-time, we need to manually zero the BSS + @ region before continuing + bl uboot_get_real_bss_start @ zero from here + ldr r1, =EXT_C(_end) @ to here + mov r2, #0 +1: str r2, [r0], #4 + cmp r0, r1 + bne 1b + + @ Global variables now accessible - store kernel parameters in memory + ldr r12, =EXT_C(uboot_machine_type) + str r4, [r12] + ldr r12, =EXT_C(uboot_boot_data) + str r5, [r12] + + b EXT_C(grub_main) + + /* + * __bss_start does not actually point to the start of the runtime + * BSS, but rather to the next byte following the preceding data. + */ +FUNCTION (uboot_get_real_bss_start) + ldr r0, =EXT_C(__bss_start) @ src + tst r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + beq 1f + mvn r1, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + and r0, r0, r1 + add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN) +1: bx lr + + /* + * 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(uboot_syscall) + ldr ip, =transition_space + stm ip, {r8, lr} + ldr ip, =gd_backup + ldr r8, [ip] + ldr ip, =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(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(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(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..afa1e1069 --- /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 + +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 = uboot_get_sys_info (); + + grub_mm_init_region ((void *) (grub_modules_get_end () + + GRUB_KERNEL_MACHINE_STACK_SIZE), + 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 = uboot_dev_enum (); + grub_dprintf ("init", "%d devices found\n", devcount); + + for (i = 0; i < devcount; i++) + { + struct device_info *devinfo = 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, i); + } + else if (devinfo->type & DEV_TYP_NET) + { + 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 = 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..b9944a452 --- /dev/null +++ b/grub-core/kern/uboot/init.c @@ -0,0 +1,171 @@ +/* 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 + +extern char __bss_start[]; +extern char _end[]; +extern grub_size_t grub_total_module_size; +extern int (*uboot_syscall_ptr) (int, int *, ...); + +grub_addr_t grub_modbase; + +grub_uint32_t uboot_machine_type; +grub_addr_t uboot_boot_data; + +static unsigned long timer_start; + +void +grub_exit (void) +{ + uboot_return (0); +} + +grub_uint32_t +uboot_get_machine_type (void) +{ + return uboot_machine_type; +} + +grub_addr_t +uboot_get_boot_data (void) +{ + return uboot_boot_data; +} + +static grub_uint64_t +uboot_timer_ms (void) +{ + return (grub_uint64_t) uboot_get_timer (timer_start); +} + +void +grub_machine_init (void) +{ + grub_addr_t end, real_bss_start; + int ver; + + /* First of all - establish connection with U-Boot */ + ver = 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 */ + uboot_puts ("invalid U-Boot API version\n"); + } + + /* + * Modules were relocated to _end, or __bss_start + grub_total_module_size, + * whichever greater. (And __bss_start may not point to actual BSS start...) + */ + real_bss_start = uboot_get_real_bss_start (); + end = real_bss_start + grub_total_module_size; + if (end < (grub_addr_t) _end) + end = (grub_addr_t) _end; + grub_modbase = end; + + /* 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: 0x%08x, real_bss_start: 0x%08x\n", + (grub_addr_t) __bss_start, real_bss_start); + grub_dprintf ("init", "end: 0x%08x, _end: 0x%08x\n", + (grub_addr_t) end, (grub_addr_t) _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 = 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 = uboot_env_get ("grub_bootdev"); + if (tmp) + { + *device = grub_malloc (grub_strlen (tmp) + 1); + if (*device == NULL) + return; + grub_strncpy (*device, tmp, grub_strlen (tmp) + 1); + } + else + *device = NULL; + + tmp = uboot_env_get ("grub_bootpath"); + if (tmp) + { + *path = grub_malloc (grub_strlen (tmp) + 1); + if (*path == NULL) + return; + grub_strncpy (*path, tmp, grub_strlen (tmp) + 1); + } + 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..1e78d8ad9 --- /dev/null +++ b/grub-core/kern/uboot/uboot.c @@ -0,0 +1,363 @@ +/* + * 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 + +/* + * 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 (*uboot_syscall_ptr) (int, int *, ...); +extern int uboot_syscall (int, int *, ...); +extern grub_addr_t uboot_search_hint; + +static struct sys_info uboot_sys_info; +static struct mem_region uboot_mem_info[5]; +static struct device_info uboot_devices[6]; +static int num_devices; + +int +uboot_api_init (void) +{ + struct api_signature *start, *end; + struct api_signature *p; + + if (uboot_search_hint) + { + /* Extended search range to work around Trim Slice U-Boot issue */ + start = (struct api_signature *) ((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) + { + uboot_syscall_ptr = p->syscall; + return p->version; + } + } + + return 0; +} + +/* All functions below are wrappers around the uboot_syscall() function */ + +/* + * int API_getc(int *c) + */ +int +uboot_getc (void) +{ + int c; + if (!uboot_syscall (API_GETC, NULL, &c)) + return -1; + + return c; +} + +/* + * int API_tstc(int *c) + */ +int +uboot_tstc (void) +{ + int c; + if (!uboot_syscall (API_TSTC, NULL, &c)) + return -1; + + return c; +} + +/* + * int API_putc(char *ch) + */ +void +uboot_putc (int c) +{ + uboot_syscall (API_PUTC, NULL, &c); +} + +/* + * int API_puts(const char *s) + */ +void +uboot_puts (const char *s) +{ + uboot_syscall (API_PUTS, NULL, s); +} + +/* + * int API_reset(void) + */ +void +uboot_reset (void) +{ + uboot_syscall (API_RESET, NULL, 0); +} + +/* + * int API_get_sys_info(struct sys_info *si) + * + * fill out the sys_info struct containing selected parameters about the + * machine + */ +struct sys_info * +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 (uboot_syscall (API_GET_SYS_INFO, &retval, &uboot_sys_info)) + if (retval == 0) + return &uboot_sys_info; + + return NULL; +} + +/* + * int API_udelay(unsigned long *udelay) + */ +void +uboot_udelay (grub_uint32_t usec) +{ + uboot_syscall (API_UDELAY, NULL, &usec); +} + +/* + * int API_get_timer(unsigned long *current, unsigned long *base) + */ +grub_uint32_t +uboot_get_timer (grub_uint32_t base) +{ + grub_uint32_t current; + + if (!uboot_syscall (API_GET_TIMER, NULL, ¤t, &base)) + return 0; + + return current; +} + +/* + * int API_dev_enum(struct device_info *) + * + */ +int +uboot_dev_enum (void) +{ + int max; + + grub_memset (&uboot_devices, 0, sizeof (uboot_devices)); + max = sizeof (uboot_devices) / sizeof (struct device_info); + + /* + * 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 prevoiusly enumerated device cookie "seeded" into the target + * structure. + */ + if (!uboot_syscall (API_DEV_ENUM, NULL, &uboot_devices) + || uboot_devices[0].cookie == NULL) + return 0; + + for (num_devices = 1; num_devices < max; num_devices++) + { + uboot_devices[num_devices].cookie = + uboot_devices[num_devices - 1].cookie; + if (!uboot_syscall (API_DEV_ENUM, NULL, &uboot_devices[num_devices])) + return 0; + + /* When no more devices to enumerate, target cookie set to NULL */ + if (uboot_devices[num_devices].cookie == NULL) + break; + } + + return num_devices; +} + +#define VALID_DEV(x) (((x) < num_devices) && ((x) >= 0)) +#define OPEN_DEV(x) (VALID_DEV(x) && (uboot_devices[(x)].state == DEV_STA_OPEN)) + +struct device_info * +uboot_dev_get (int handle) +{ + if (VALID_DEV (handle)) + return &uboot_devices[handle]; + + return NULL; +} + + +/* + * int API_dev_open(struct device_info *) + */ +int +uboot_dev_open (int handle) +{ + struct device_info *dev; + int retval; + + if (!VALID_DEV (handle)) + return -1; + + dev = &uboot_devices[handle]; + + if (!uboot_syscall (API_DEV_OPEN, &retval, dev)) + return -1; + + return retval; +} + +/* + * int API_dev_close(struct device_info *) + */ +int +uboot_dev_close (int handle) +{ + struct device_info *dev; + int retval; + + if (!VALID_DEV (handle)) + return -1; + + dev = &uboot_devices[handle]; + + if (!uboot_syscall (API_DEV_CLOSE, &retval, dev)) + return -1; + + return retval; +} + + +/* + * int API_dev_read(struct device_info *di, void *buf, size_t *len, + * unsigned long *start, size_t *act_len) + */ +int +uboot_dev_read (int handle, void *buf, lbasize_t blocks, + lbastart_t start, lbasize_t * real_blocks) +{ + struct device_info *dev; + int retval; + + if (!OPEN_DEV (handle)) + return -1; + + dev = &uboot_devices[handle]; + + if (!uboot_syscall (API_DEV_READ, &retval, dev, buf, + &blocks, &start, real_blocks)) + return -1; + + return retval; +} + +/* + * int API_dev_read(struct device_info *di, void *buf, + * size_t *len, size_t *act_len) + */ +int +uboot_dev_recv (int handle, void *buf, int size, int *real_size) +{ + struct device_info *dev; + int retval; + + if (!OPEN_DEV (handle)) + return -1; + + dev = &uboot_devices[handle]; + if (!uboot_syscall (API_DEV_READ, &retval, dev, buf, &size, real_size)) + return -1; + + return retval; + +} + +/* + * Notice: this is for sending network packets only, as U-Boot does not + * support writing to storage at the moment (12.2007) + * + * int API_dev_write(struct device_info *di, void *buf, int *len) + */ +int +uboot_dev_send (int handle, void *buf, int size) +{ + struct device_info *dev; + int retval; + + if (!OPEN_DEV (handle)) + return -1; + + dev = &uboot_devices[handle]; + if (!uboot_syscall (API_DEV_WRITE, &retval, dev, buf, &size)) + return -1; + + return retval; +} + +/* + * int API_env_get(const char *name, char **value) + */ +char * +uboot_env_get (const char *name) +{ + char *value; + + if (!uboot_syscall (API_ENV_GET, NULL, name, &value)) + return NULL; + + return value; +} + +/* + * int API_env_set(const char *name, const char *value) + */ +void +uboot_env_set (const char *name, const char *value) +{ + 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..7038a229c --- /dev/null +++ b/grub-core/lib/arm/setjmp.S @@ -0,0 +1,55 @@ +/* + * 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 "setjmp.S" + .syntax unified +#if !defined (__thumb2__) + .arm +#define ARM(x...) x +#define THUMB(x...) +#else + .thumb +#define THUMB(x...) x +#define ARM(x...) +#endif + + .text + +/* + * int grub_setjmp (grub_jmp_buf env) + */ +FUNCTION(grub_setjmp) + THUMB( mov ip, sp ) + THUMB( stm r0, { r4-r11, ip, lr } ) + ARM( stm r0, { r4-r11, sp, lr } ) + mov r0, #0 + bx lr + +/* + * int grub_longjmp (grub_jmp_buf env, int val) + */ +FUNCTION(grub_longjmp) + THUMB( ldm r0, { r4-r11, ip, lr } ) + THUMB( mov sp, ip ) + ARM( ldm r0, { r4-r11, sp, lr } ) + movs r0, r1 + moveq r0, #1 + bx lr diff --git a/grub-core/lib/dtc/libfdt-grub.diff b/grub-core/lib/dtc/libfdt-grub.diff new file mode 100644 index 000000000..173619d0d --- /dev/null +++ b/grub-core/lib/dtc/libfdt-grub.diff @@ -0,0 +1,45 @@ +diff -purN libfdt.orig/fdt_rw.c libfdt/fdt_rw.c +--- libfdt.orig/fdt_rw.c 2011-05-08 20:45:39.000000000 +0100 ++++ libfdt/fdt_rw.c 2012-10-19 15:33:11.085523185 +0100 +@@ -88,9 +88,9 @@ static int _fdt_rw_check_header(void *fd + + #define FDT_RW_CHECK_HEADER(fdt) \ + { \ +- int err; \ +- if ((err = _fdt_rw_check_header(fdt)) != 0) \ +- return err; \ ++ int macro_err; \ ++ if ((macro_err = _fdt_rw_check_header(fdt)) != 0) \ ++ return macro_err; \ + } + + static inline int +diff -purN libfdt.orig/libfdt_env.h libfdt/libfdt_env.h +--- libfdt.orig/libfdt_env.h 2011-05-08 20:45:39.000000000 +0100 ++++ libfdt/libfdt_env.h 2012-10-19 16:13:19.051344173 +0100 +@@ -7,6 +7,9 @@ + #include + #include + #include ++#pragma GCC diagnostic ignored "-Wcast-align" ++#pragma GCC diagnostic ignored "-Wsign-compare" ++typedef grub_addr_t uintptr_t; + + #define _B(n) ((unsigned long long)((uint8_t *)&x)[n]) + static inline uint32_t +diff -purN libfdt.orig/libfdt_internal.h libfdt/libfdt_internal.h +--- libfdt.orig/libfdt_internal.h 2011-05-08 20:45:39.000000000 +0100 ++++ libfdt/libfdt_internal.h 2012-10-19 15:33:11.105524731 +0100 +@@ -60,9 +60,9 @@ + + #define FDT_CHECK_HEADER(fdt) \ + { \ +- int err; \ +- if ((err = fdt_check_header(fdt)) != 0) \ +- return err; \ ++ int macro_err; \ ++ if ((macro_err = fdt_check_header(fdt)) != 0) \ ++ return macro_err; \ + } + + int _fdt_check_node_offset (const void *fdt, int offset); diff --git a/grub-core/lib/dtc/libfdt/Makefile.libfdt b/grub-core/lib/dtc/libfdt/Makefile.libfdt new file mode 100644 index 000000000..d55a6f852 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/Makefile.libfdt @@ -0,0 +1,10 @@ +# Makefile.libfdt +# +# This is not a complete Makefile of itself. Instead, it is designed to +# be easily embeddable into other systems of Makefiles. +# +LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1 +LIBFDT_INCLUDES = fdt.h libfdt.h +LIBFDT_VERSION = version.lds +LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c +LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) diff --git a/grub-core/lib/dtc/libfdt/TODO b/grub-core/lib/dtc/libfdt/TODO new file mode 100644 index 000000000..288437e39 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/TODO @@ -0,0 +1,3 @@ +- Tree traversal functions +- Graft function +- Complete libfdt.h documenting comments diff --git a/grub-core/lib/dtc/libfdt/fdt.c b/grub-core/lib/dtc/libfdt/fdt.c new file mode 100644 index 000000000..e8627349e --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt.c @@ -0,0 +1,241 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int +fdt_check_header (const void *fdt) +{ + if (fdt_magic (fdt) == FDT_MAGIC) + { + /* Complete tree */ + if (fdt_version (fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version (fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } + else if (fdt_magic (fdt) == FDT_SW_MAGIC) + { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct (fdt) == 0) + return -FDT_ERR_BADSTATE; + } + else + { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +const void * +fdt_offset_ptr (const void *fdt, int offset, unsigned int len) +{ + const char *p; + + if (fdt_version (fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct (fdt))) + return NULL; + + p = _fdt_offset_ptr (fdt, offset); + + if (p + len < p) + return NULL; + return p; +} + +uint32_t +fdt_next_tag (const void *fdt, int startoffset, int *nextoffset) +{ + const uint32_t *tagp, *lenp; + uint32_t tag; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr (fdt, offset, FDT_TAGSIZE); + if (!tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu (*tagp); + offset += FDT_TAGSIZE; + + *nextoffset = -FDT_ERR_BADSTRUCTURE; + switch (tag) + { + case FDT_BEGIN_NODE: + /* skip name */ + do + { + p = fdt_offset_ptr (fdt, offset++, 1); + } + while (p && (*p != '\0')); + if (!p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr (fdt, offset, sizeof (*lenp)); + if (!lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof (struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu (*lenp); + break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; + } + + if (!fdt_offset_ptr (fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + + *nextoffset = FDT_TAGALIGN (offset); + return tag; +} + +int +_fdt_check_node_offset (const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag (fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int +_fdt_check_prop_offset (const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag (fdt, offset, &offset) != FDT_PROP)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int +fdt_next_node (const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = _fdt_check_node_offset (fdt, offset)) < 0) + return nextoffset; + + do + { + offset = nextoffset; + tag = fdt_next_tag (fdt, offset, &nextoffset); + + switch (tag) + { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth && ((--(*depth)) < 0)) + return nextoffset; + break; + + case FDT_END: + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; + } + } + while (tag != FDT_BEGIN_NODE); + + return offset; +} + +const char * +_fdt_find_string (const char *strtab, int tabsize, const char *s) +{ + int len = strlen (s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp (p, s, len) == 0) + return p; + return NULL; +} + +int +fdt_move (const void *fdt, void *buf, int bufsize) +{ + FDT_CHECK_HEADER (fdt); + + if (fdt_totalsize (fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove (buf, fdt, fdt_totalsize (fdt)); + return 0; +} diff --git a/grub-core/lib/dtc/libfdt/fdt.h b/grub-core/lib/dtc/libfdt/fdt.h new file mode 100644 index 000000000..25bd3082a --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt.h @@ -0,0 +1,64 @@ +#ifndef _FDT_H +#define _FDT_H + +#ifndef __ASSEMBLY__ + +struct fdt_header +{ + uint32_t magic; /* magic word FDT_MAGIC */ + uint32_t totalsize; /* total size of DT block */ + uint32_t off_dt_struct; /* offset to structure */ + uint32_t off_dt_strings; /* offset to strings */ + uint32_t off_mem_rsvmap; /* offset to memory reserve map */ + uint32_t version; /* format version */ + uint32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + uint32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + uint32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + uint32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry +{ + uint64_t address; + uint64_t size; +}; + +struct fdt_node_header +{ + uint32_t tag; + char name[0]; +}; + +struct fdt_property +{ + uint32_t tag; + uint32_t len; + uint32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(uint32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(uint32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(uint32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(uint32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(uint32_t)) + +#endif /* _FDT_H */ diff --git a/grub-core/lib/dtc/libfdt/fdt_ro.c b/grub-core/lib/dtc/libfdt/fdt_ro.c new file mode 100644 index 000000000..7014e302d --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt_ro.c @@ -0,0 +1,608 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int +_fdt_nodename_eq (const void *fdt, int offset, const char *s, int len) +{ + const char *p = fdt_offset_ptr (fdt, offset + FDT_TAGSIZE, len + 1); + + if (!p) + /* short match */ + return 0; + + if (memcmp (p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr (s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char * +fdt_string (const void *fdt, int stroffset) +{ + return (const char *) fdt + fdt_off_dt_strings (fdt) + stroffset; +} + +static int +_fdt_string_eq (const void *fdt, int stroffset, const char *s, int len) +{ + const char *p = fdt_string (fdt, stroffset); + + return (strlen (p) == len) && (memcmp (p, s, len) == 0); +} + +int +fdt_get_mem_rsv (const void *fdt, int n, uint64_t * address, uint64_t * size) +{ + FDT_CHECK_HEADER (fdt); + *address = fdt64_to_cpu (_fdt_mem_rsv (fdt, n)->address); + *size = fdt64_to_cpu (_fdt_mem_rsv (fdt, n)->size); + return 0; +} + +int +fdt_num_mem_rsv (const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu (_fdt_mem_rsv (fdt, i)->size) != 0) + i++; + return i; +} + +static int +_nextprop (const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + do + { + tag = fdt_next_tag (fdt, offset, &nextoffset); + + switch (tag) + { + case FDT_END: + if (nextoffset >= 0) + return -FDT_ERR_BADSTRUCTURE; + else + return nextoffset; + + case FDT_PROP: + return offset; + } + offset = nextoffset; + } + while (tag == FDT_NOP); + + return -FDT_ERR_NOTFOUND; +} + +int +fdt_subnode_offset_namelen (const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_CHECK_HEADER (fdt); + + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node (fdt, offset, &depth)) + if ((depth == 1) && _fdt_nodename_eq (fdt, offset, name, namelen)) + return offset; + + if (depth < 0) + return -FDT_ERR_NOTFOUND; + return offset; /* error */ +} + +int +fdt_subnode_offset (const void *fdt, int parentoffset, const char *name) +{ + return fdt_subnode_offset_namelen (fdt, parentoffset, name, strlen (name)); +} + +int +fdt_path_offset (const void *fdt, const char *path) +{ + const char *end = path + strlen (path); + const char *p = path; + int offset = 0; + + FDT_CHECK_HEADER (fdt); + + /* see if we have an alias */ + if (*path != '/') + { + const char *q = strchr (path, '/'); + + if (!q) + q = end; + + p = fdt_get_alias_namelen (fdt, p, q - p); + if (!p) + return -FDT_ERR_BADPATH; + offset = fdt_path_offset (fdt, p); + + p = q; + } + + while (*p) + { + const char *q; + + while (*p == '/') + p++; + if (!*p) + return offset; + q = strchr (p, '/'); + if (!q) + q = end; + + offset = fdt_subnode_offset_namelen (fdt, offset, p, q - p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +const char * +fdt_get_name (const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = _fdt_offset_ptr (fdt, nodeoffset); + int err; + + if (((err = fdt_check_header (fdt)) != 0) + || ((err = _fdt_check_node_offset (fdt, nodeoffset)) < 0)) + goto fail; + + if (len) + *len = strlen (nh->name); + + return nh->name; + +fail: + if (len) + *len = err; + return NULL; +} + +int +fdt_first_property_offset (const void *fdt, int nodeoffset) +{ + int offset; + + if ((offset = _fdt_check_node_offset (fdt, nodeoffset)) < 0) + return offset; + + return _nextprop (fdt, offset); +} + +int +fdt_next_property_offset (const void *fdt, int offset) +{ + if ((offset = _fdt_check_prop_offset (fdt, offset)) < 0) + return offset; + + return _nextprop (fdt, offset); +} + +const struct fdt_property * +fdt_get_property_by_offset (const void *fdt, int offset, int *lenp) +{ + int err; + const struct fdt_property *prop; + + if ((err = _fdt_check_prop_offset (fdt, offset)) < 0) + { + if (lenp) + *lenp = err; + return NULL; + } + + prop = _fdt_offset_ptr (fdt, offset); + + if (lenp) + *lenp = fdt32_to_cpu (prop->len); + + return prop; +} + +const struct fdt_property * +fdt_get_property_namelen (const void *fdt, + int offset, + const char *name, int namelen, int *lenp) +{ + for (offset = fdt_first_property_offset (fdt, offset); + (offset >= 0); (offset = fdt_next_property_offset (fdt, offset))) + { + const struct fdt_property *prop; + + if (!(prop = fdt_get_property_by_offset (fdt, offset, lenp))) + { + offset = -FDT_ERR_INTERNAL; + break; + } + if (_fdt_string_eq (fdt, fdt32_to_cpu (prop->nameoff), name, namelen)) + return prop; + } + + if (lenp) + *lenp = offset; + return NULL; +} + +const struct fdt_property * +fdt_get_property (const void *fdt, + int nodeoffset, const char *name, int *lenp) +{ + return fdt_get_property_namelen (fdt, nodeoffset, name, + strlen (name), lenp); +} + +const void * +fdt_getprop_namelen (const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_namelen (fdt, nodeoffset, name, namelen, lenp); + if (!prop) + return NULL; + + return prop->data; +} + +const void * +fdt_getprop_by_offset (const void *fdt, int offset, + const char **namep, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset (fdt, offset, lenp); + if (!prop) + return NULL; + if (namep) + *namep = fdt_string (fdt, fdt32_to_cpu (prop->nameoff)); + return prop->data; +} + +const void * +fdt_getprop (const void *fdt, int nodeoffset, const char *name, int *lenp) +{ + return fdt_getprop_namelen (fdt, nodeoffset, name, strlen (name), lenp); +} + +uint32_t +fdt_get_phandle (const void *fdt, int nodeoffset) +{ + const uint32_t *php; + int len; + + /* FIXME: This is a bit sub-optimal, since we potentially scan + * over all the properties twice. */ + php = fdt_getprop (fdt, nodeoffset, "phandle", &len); + if (!php || (len != sizeof (*php))) + { + php = fdt_getprop (fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof (*php))) + return 0; + } + + return fdt32_to_cpu (*php); +} + +const char * +fdt_get_alias_namelen (const void *fdt, const char *name, int namelen) +{ + int aliasoffset; + + aliasoffset = fdt_path_offset (fdt, "/aliases"); + if (aliasoffset < 0) + return NULL; + + return fdt_getprop_namelen (fdt, aliasoffset, name, namelen, NULL); +} + +const char * +fdt_get_alias (const void *fdt, const char *name) +{ + return fdt_get_alias_namelen (fdt, name, strlen (name)); +} + +int +fdt_get_path (const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_CHECK_HEADER (fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node (fdt, offset, &depth)) + { + while (pdepth > depth) + { + do + { + p--; + } + while (buf[p - 1] != '/'); + pdepth--; + } + + if (pdepth >= depth) + { + name = fdt_get_name (fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) + { + memcpy (buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + } + + if (offset == nodeoffset) + { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return 0; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int +fdt_supernode_atdepth_offset (const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_CHECK_HEADER (fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node (fdt, offset, &depth)) + { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) + { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int +fdt_node_depth (const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset (fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int +fdt_parent_offset (const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth (fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset (fdt, nodeoffset, nodedepth - 1, NULL); +} + +int +fdt_node_offset_by_prop_value (const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_CHECK_HEADER (fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node (fdt, startoffset, NULL); + offset >= 0; offset = fdt_next_node (fdt, offset, NULL)) + { + val = fdt_getprop (fdt, offset, propname, &len); + if (val && (len == proplen) && (memcmp (val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int +fdt_node_offset_by_phandle (const void *fdt, uint32_t phandle) +{ + int offset; + + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + + FDT_CHECK_HEADER (fdt); + + /* FIXME: The algorithm here is pretty horrible: we + * potentially scan each property of a node in + * fdt_get_phandle(), then if that didn't find what + * we want, we scan over them again making our way to the next + * node. Still it's the easiest to implement approach; + * performance can come later. */ + for (offset = fdt_next_node (fdt, -1, NULL); + offset >= 0; offset = fdt_next_node (fdt, offset, NULL)) + { + if (fdt_get_phandle (fdt, offset) == phandle) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +static int +_fdt_stringlist_contains (const char *strlist, int listlen, const char *str) +{ + int len = strlen (str); + const char *p; + + while (listlen >= len) + { + if (memcmp (str, strlist, len + 1) == 0) + return 1; + p = memchr (strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p - strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int +fdt_node_check_compatible (const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop (fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + if (_fdt_stringlist_contains (prop, len, compatible)) + return 0; + else + return 1; +} + +int +fdt_node_offset_by_compatible (const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_CHECK_HEADER (fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node (fdt, startoffset, NULL); + offset >= 0; offset = fdt_next_node (fdt, offset, NULL)) + { + err = fdt_node_check_compatible (fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/grub-core/lib/dtc/libfdt/fdt_rw.c b/grub-core/lib/dtc/libfdt/fdt_rw.c new file mode 100644 index 000000000..58bc4adf0 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt_rw.c @@ -0,0 +1,490 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int +_fdt_blocks_misordered (const void *fdt, int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap (fdt) < + FDT_ALIGN (sizeof (struct fdt_header), 8)) + || (fdt_off_dt_struct (fdt) < (fdt_off_mem_rsvmap (fdt) + mem_rsv_size)) + || (fdt_off_dt_strings (fdt) < (fdt_off_dt_struct (fdt) + struct_size)) + || (fdt_totalsize (fdt) < + (fdt_off_dt_strings (fdt) + fdt_size_dt_strings (fdt))); +} + +static int +_fdt_rw_check_header (void *fdt) +{ + FDT_CHECK_HEADER (fdt); + + if (fdt_version (fdt) < 17) + return -FDT_ERR_BADVERSION; + if (_fdt_blocks_misordered (fdt, sizeof (struct fdt_reserve_entry), + fdt_size_dt_struct (fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version (fdt) > 17) + fdt_set_version (fdt, 17); + + return 0; +} + +#define FDT_RW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_rw_check_header(fdt)) != 0) \ + return err; \ + } + +static inline int +_fdt_data_size (void *fdt) +{ + return fdt_off_dt_strings (fdt) + fdt_size_dt_strings (fdt); +} + +static int +_fdt_splice (void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *) fdt + _fdt_data_size (fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *) fdt + fdt_totalsize (fdt))) + return -FDT_ERR_NOSPACE; + memmove (p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int +_fdt_splice_mem_rsv (void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof (*p); + int err; + err = _fdt_splice (fdt, p, oldn * sizeof (*p), newn * sizeof (*p)); + if (err) + return err; + fdt_set_off_dt_struct (fdt, fdt_off_dt_struct (fdt) + delta); + fdt_set_off_dt_strings (fdt, fdt_off_dt_strings (fdt) + delta); + return 0; +} + +static int +_fdt_splice_struct (void *fdt, void *p, int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _fdt_splice (fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct (fdt, fdt_size_dt_struct (fdt) + delta); + fdt_set_off_dt_strings (fdt, fdt_off_dt_strings (fdt) + delta); + return 0; +} + +static int +_fdt_splice_string (void *fdt, int newlen) +{ + void *p = (char *) fdt + + fdt_off_dt_strings (fdt) + fdt_size_dt_strings (fdt); + int err; + + if ((err = _fdt_splice (fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings (fdt, fdt_size_dt_strings (fdt) + newlen); + return 0; +} + +static int +_fdt_find_add_string (void *fdt, const char *s) +{ + char *strtab = (char *) fdt + fdt_off_dt_strings (fdt); + const char *p; + char *new; + int len = strlen (s) + 1; + int err; + + p = _fdt_find_string (strtab, fdt_size_dt_strings (fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings (fdt); + err = _fdt_splice_string (fdt, len); + if (err) + return err; + + memcpy (new, s, len); + return (new - strtab); +} + +int +fdt_add_mem_rsv (void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_CHECK_HEADER (fdt); + + re = _fdt_mem_rsv_w (fdt, fdt_num_mem_rsv (fdt)); + err = _fdt_splice_mem_rsv (fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64 (address); + re->size = cpu_to_fdt64 (size); + return 0; +} + +int +fdt_del_mem_rsv (void *fdt, int n) +{ + struct fdt_reserve_entry *re = _fdt_mem_rsv_w (fdt, n); + int err; + + FDT_RW_CHECK_HEADER (fdt); + + if (n >= fdt_num_mem_rsv (fdt)) + return -FDT_ERR_NOTFOUND; + + err = _fdt_splice_mem_rsv (fdt, re, 1, 0); + if (err) + return err; + return 0; +} + +static int +_fdt_resize_property (void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w (fdt, nodeoffset, name, &oldlen); + if (!(*prop)) + return oldlen; + + if ((err = _fdt_splice_struct (fdt, (*prop)->data, FDT_TAGALIGN (oldlen), + FDT_TAGALIGN (len)))) + return err; + + (*prop)->len = cpu_to_fdt32 (len); + return 0; +} + +static int +_fdt_add_property (void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + + if ((nextoffset = _fdt_check_node_offset (fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = _fdt_find_add_string (fdt, name); + if (namestroff < 0) + return namestroff; + + *prop = _fdt_offset_ptr_w (fdt, nextoffset); + proplen = sizeof (**prop) + FDT_TAGALIGN (len); + + err = _fdt_splice_struct (fdt, *prop, 0, proplen); + if (err) + return err; + + (*prop)->tag = cpu_to_fdt32 (FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32 (namestroff); + (*prop)->len = cpu_to_fdt32 (len); + return 0; +} + +int +fdt_set_name (void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_CHECK_HEADER (fdt); + + namep = (char *) (uintptr_t) fdt_get_name (fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen (name); + + err = _fdt_splice_struct (fdt, namep, FDT_TAGALIGN (oldlen + 1), + FDT_TAGALIGN (newlen + 1)); + if (err) + return err; + + memcpy (namep, name, newlen + 1); + return 0; +} + +int +fdt_setprop (void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + FDT_RW_CHECK_HEADER (fdt); + + err = _fdt_resize_property (fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = _fdt_add_property (fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + memcpy (prop->data, val, len); + return 0; +} + +int +fdt_delprop (void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_CHECK_HEADER (fdt); + + prop = fdt_get_property_w (fdt, nodeoffset, name, &len); + if (!prop) + return len; + + proplen = sizeof (*prop) + FDT_TAGALIGN (len); + return _fdt_splice_struct (fdt, prop, proplen, 0); +} + +int +fdt_add_subnode_namelen (void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + uint32_t *endtag; + + FDT_RW_CHECK_HEADER (fdt); + + offset = fdt_subnode_offset_namelen (fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag (fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do + { + offset = nextoffset; + tag = fdt_next_tag (fdt, offset, &nextoffset); + } + while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = _fdt_offset_ptr_w (fdt, offset); + nodelen = sizeof (*nh) + FDT_TAGALIGN (namelen + 1) + FDT_TAGSIZE; + + err = _fdt_splice_struct (fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32 (FDT_BEGIN_NODE); + memset (nh->name, 0, FDT_TAGALIGN (namelen + 1)); + memcpy (nh->name, name, namelen); + endtag = (uint32_t *) ((char *) nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32 (FDT_END_NODE); + + return offset; +} + +int +fdt_add_subnode (void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen (fdt, parentoffset, name, strlen (name)); +} + +int +fdt_del_node (void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_CHECK_HEADER (fdt); + + endoffset = _fdt_node_end_offset (fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return _fdt_splice_struct (fdt, _fdt_offset_ptr_w (fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void +_fdt_packblocks (const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN (sizeof (struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove (new + mem_rsv_off, old + fdt_off_mem_rsvmap (old), mem_rsv_size); + fdt_set_off_mem_rsvmap (new, mem_rsv_off); + + memmove (new + struct_off, old + fdt_off_dt_struct (old), struct_size); + fdt_set_off_dt_struct (new, struct_off); + fdt_set_size_dt_struct (new, struct_size); + + memmove (new + strings_off, old + fdt_off_dt_strings (old), + fdt_size_dt_strings (old)); + fdt_set_off_dt_strings (new, strings_off); + fdt_set_size_dt_strings (new, fdt_size_dt_strings (old)); +} + +int +fdt_open_into (const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize (fdt); + char *tmp; + + FDT_CHECK_HEADER (fdt); + + mem_rsv_size = (fdt_num_mem_rsv (fdt) + 1) + * sizeof (struct fdt_reserve_entry); + + if (fdt_version (fdt) >= 17) + { + struct_size = fdt_size_dt_struct (fdt); + } + else + { + struct_size = 0; + while (fdt_next_tag (fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } + + if (!_fdt_blocks_misordered (fdt, mem_rsv_size, struct_size)) + { + /* no further work necessary */ + err = fdt_move (fdt, buf, bufsize); + if (err) + return err; + fdt_set_version (buf, 17); + fdt_set_size_dt_struct (buf, struct_size); + fdt_set_totalsize (buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN (sizeof (struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings (fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) + { + /* Try right after the old tree instead */ + tmp = (char *) (uintptr_t) fdtend; + if ((tmp + newsize) > ((char *) buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + _fdt_packblocks (fdt, tmp, mem_rsv_size, struct_size); + memmove (buf, tmp, newsize); + + fdt_set_magic (buf, FDT_MAGIC); + fdt_set_totalsize (buf, bufsize); + fdt_set_version (buf, 17); + fdt_set_last_comp_version (buf, 16); + fdt_set_boot_cpuid_phys (buf, fdt_boot_cpuid_phys (fdt)); + + return 0; +} + +int +fdt_pack (void *fdt) +{ + int mem_rsv_size; + + FDT_RW_CHECK_HEADER (fdt); + + mem_rsv_size = (fdt_num_mem_rsv (fdt) + 1) + * sizeof (struct fdt_reserve_entry); + _fdt_packblocks (fdt, fdt, mem_rsv_size, fdt_size_dt_struct (fdt)); + fdt_set_totalsize (fdt, _fdt_data_size (fdt)); + + return 0; +} diff --git a/grub-core/lib/dtc/libfdt/fdt_strerror.c b/grub-core/lib/dtc/libfdt/fdt_strerror.c new file mode 100644 index 000000000..2d8606bec --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt_strerror.c @@ -0,0 +1,100 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +struct fdt_errtabent +{ + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT (FDT_ERR_NOTFOUND), + FDT_ERRTABENT (FDT_ERR_EXISTS), + FDT_ERRTABENT (FDT_ERR_NOSPACE), + + FDT_ERRTABENT (FDT_ERR_BADOFFSET), + FDT_ERRTABENT (FDT_ERR_BADPATH), + FDT_ERRTABENT (FDT_ERR_BADSTATE), + + FDT_ERRTABENT (FDT_ERR_TRUNCATED), + FDT_ERRTABENT (FDT_ERR_BADMAGIC), + FDT_ERRTABENT (FDT_ERR_BADVERSION), + FDT_ERRTABENT (FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT (FDT_ERR_BADLAYOUT), +}; + +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char * +fdt_strerror (int errval) +{ + if (errval > 0) + return ""; + else if (errval == 0) + return ""; + else if (errval > -FDT_ERRTABSIZE) + { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return ""; +} diff --git a/grub-core/lib/dtc/libfdt/fdt_sw.c b/grub-core/lib/dtc/libfdt/fdt_sw.c new file mode 100644 index 000000000..86d1d7353 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt_sw.c @@ -0,0 +1,267 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +static int +_fdt_sw_check_header (void *fdt) +{ + if (fdt_magic (fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + /* FIXME: should check more details about the header state */ + return 0; +} + +#define FDT_SW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_sw_check_header(fdt)) != 0) \ + return err; \ + } + +static void * +_fdt_grab_space (void *fdt, size_t len) +{ + int offset = fdt_size_dt_struct (fdt); + int spaceleft; + + spaceleft = fdt_totalsize (fdt) - fdt_off_dt_struct (fdt) + - fdt_size_dt_strings (fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct (fdt, offset + len); + return _fdt_offset_ptr_w (fdt, offset); +} + +int +fdt_create (void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof (struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset (buf, 0, bufsize); + + fdt_set_magic (fdt, FDT_SW_MAGIC); + fdt_set_version (fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version (fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize (fdt, bufsize); + + fdt_set_off_mem_rsvmap (fdt, FDT_ALIGN (sizeof (struct fdt_header), + sizeof (struct fdt_reserve_entry))); + fdt_set_off_dt_struct (fdt, fdt_off_mem_rsvmap (fdt)); + fdt_set_off_dt_strings (fdt, bufsize); + + return 0; +} + +int +fdt_add_reservemap_entry (void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_CHECK_HEADER (fdt); + + if (fdt_size_dt_struct (fdt)) + return -FDT_ERR_BADSTATE; + + offset = fdt_off_dt_struct (fdt); + if ((offset + sizeof (*re)) > fdt_totalsize (fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *) ((char *) fdt + offset); + re->address = cpu_to_fdt64 (addr); + re->size = cpu_to_fdt64 (size); + + fdt_set_off_dt_struct (fdt, offset + sizeof (*re)); + + return 0; +} + +int +fdt_finish_reservemap (void *fdt) +{ + return fdt_add_reservemap_entry (fdt, 0, 0); +} + +int +fdt_begin_node (void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen = strlen (name) + 1; + + FDT_SW_CHECK_HEADER (fdt); + + nh = _fdt_grab_space (fdt, sizeof (*nh) + FDT_TAGALIGN (namelen)); + if (!nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32 (FDT_BEGIN_NODE); + memcpy (nh->name, name, namelen); + return 0; +} + +int +fdt_end_node (void *fdt) +{ + uint32_t *en; + + FDT_SW_CHECK_HEADER (fdt); + + en = _fdt_grab_space (fdt, FDT_TAGSIZE); + if (!en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32 (FDT_END_NODE); + return 0; +} + +static int +_fdt_find_add_string (void *fdt, const char *s) +{ + char *strtab = (char *) fdt + fdt_totalsize (fdt); + const char *p; + int strtabsize = fdt_size_dt_strings (fdt); + int len = strlen (s) + 1; + int struct_top, offset; + + p = _fdt_find_string (strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct (fdt) + fdt_size_dt_struct (fdt); + if (fdt_totalsize (fdt) + offset < struct_top) + return 0; /* no more room */ + + memcpy (strtab + offset, s, len); + fdt_set_size_dt_strings (fdt, strtabsize + len); + return offset; +} + +int +fdt_property (void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int nameoff; + + FDT_SW_CHECK_HEADER (fdt); + + nameoff = _fdt_find_add_string (fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = _fdt_grab_space (fdt, sizeof (*prop) + FDT_TAGALIGN (len)); + if (!prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32 (FDT_PROP); + prop->nameoff = cpu_to_fdt32 (nameoff); + prop->len = cpu_to_fdt32 (len); + memcpy (prop->data, val, len); + return 0; +} + +int +fdt_finish (void *fdt) +{ + char *p = (char *) fdt; + uint32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_CHECK_HEADER (fdt); + + /* Add terminator */ + end = _fdt_grab_space (fdt, sizeof (*end)); + if (!end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32 (FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize (fdt) - fdt_size_dt_strings (fdt); + newstroffset = fdt_off_dt_struct (fdt) + fdt_size_dt_struct (fdt); + memmove (p + newstroffset, p + oldstroffset, fdt_size_dt_strings (fdt)); + fdt_set_off_dt_strings (fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag (fdt, offset, &nextoffset)) != FDT_END) + { + if (tag == FDT_PROP) + { + struct fdt_property *prop = _fdt_offset_ptr_w (fdt, offset); + int nameoff; + + nameoff = fdt32_to_cpu (prop->nameoff); + nameoff += fdt_size_dt_strings (fdt); + prop->nameoff = cpu_to_fdt32 (nameoff); + } + offset = nextoffset; + } + if (nextoffset < 0) + return nextoffset; + + /* Finally, adjust the header */ + fdt_set_totalsize (fdt, newstroffset + fdt_size_dt_strings (fdt)); + fdt_set_magic (fdt, FDT_MAGIC); + return 0; +} diff --git a/grub-core/lib/dtc/libfdt/fdt_wip.c b/grub-core/lib/dtc/libfdt/fdt_wip.c new file mode 100644 index 000000000..09297d9a6 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/fdt_wip.c @@ -0,0 +1,123 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int +fdt_setprop_inplace (void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_w (fdt, nodeoffset, name, &proplen); + if (!propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + memcpy (propval, val, len); + return 0; +} + +static void +_fdt_nop_region (void *start, int len) +{ + uint32_t *p; + + for (p = start; (char *) p < ((char *) start + len); p++) + *p = cpu_to_fdt32 (FDT_NOP); +} + +int +fdt_nop_property (void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w (fdt, nodeoffset, name, &len); + if (!prop) + return len; + + _fdt_nop_region (prop, len + sizeof (*prop)); + + return 0; +} + +int +_fdt_node_end_offset (void *fdt, int offset) +{ + int depth = 0; + + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node (fdt, offset, &depth); + + return offset; +} + +int +fdt_nop_node (void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = _fdt_node_end_offset (fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + _fdt_nop_region (fdt_offset_ptr_w (fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/grub-core/lib/dtc/libfdt/libfdt.h b/grub-core/lib/dtc/libfdt/libfdt.h new file mode 100644 index 000000000..6b9bfb5dc --- /dev/null +++ b/grub-core/lib/dtc/libfdt/libfdt.h @@ -0,0 +1,1239 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +#include +#include + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attemped to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle + * value. phandle values of 0 and -1 are not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: Structure block of the given device tree + * ends without an FDT_END tag. */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +#define FDT_ERR_MAX 13 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +const void *fdt_offset_ptr (const void *fdt, int offset, + unsigned int checklen); +static inline void * +fdt_offset_ptr_w (void *fdt, int offset, int checklen) +{ + return (void *) (uintptr_t) fdt_offset_ptr (fdt, offset, checklen); +} + +uint32_t fdt_next_tag (const void *fdt, int offset, int *nextoffset); + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node (const void *fdt, int offset, int *depth); + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ + +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define __fdt_set_hdr(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header*)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +__fdt_set_hdr (magic); +__fdt_set_hdr (totalsize); +__fdt_set_hdr (off_dt_struct); +__fdt_set_hdr (off_dt_strings); +__fdt_set_hdr (off_mem_rsvmap); +__fdt_set_hdr (version); +__fdt_set_hdr (last_comp_version); +__fdt_set_hdr (boot_cpuid_phys); +__fdt_set_hdr (size_dt_strings); +__fdt_set_hdr (size_dt_struct); +#undef __fdt_set_hdr + +/** + * fdt_check_header - sanity check a device tree or possible device tree + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings, as above + */ +int fdt_check_header (const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move (const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds + */ +const char *fdt_string (const void *fdt, int stroffset); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv (const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv (const void *fdt, int n, uint64_t * address, + uint64_t * size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +int fdt_subnode_offset_namelen (const void *fdt, int parentoffset, + const char *name, int namelen); +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset (const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset (const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name (const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset (const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset (const void *fdt, int offset); + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset (const void *fdt, + int offset, int *lenp); + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property_namelen(), but only examine the first + * namelen characters of name for matching the property name. + */ +const struct fdt_property *fdt_get_property_namelen (const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property (const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property * +fdt_get_property_w (void *fdt, int nodeoffset, const char *name, int *lenp) +{ + return (struct fdt_property *) (uintptr_t) + fdt_get_property (fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @ffset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop_by_offset (const void *fdt, int offset, + const char **namep, int *lenp); + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + */ +const void *fdt_getprop_namelen (const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop (const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void * +fdt_getprop_w (void *fdt, int nodeoffset, const char *name, int *lenp) +{ + return (void *) (uintptr_t) fdt_getprop (fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle (const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first namelen + * characters of name for matching the alias name. + */ +const char *fdt_get_alias_namelen (const void *fdt, + const char *name, int namelen); + +/** + * fdt_get_alias - retreive the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named 'name' in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', of it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias (const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path (const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag +* -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset (const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth (const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset (const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value (const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle (const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible (const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible (const void *fdt, int startoffset, + const char *compatible); + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_inplace (void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: cell (32-bit integer) value to replace the property with + * + * fdt_setprop_inplace_cell() replaces the value of a given property + * with the 32-bit integer cell value in val, converting val to + * big-endian if necessary. This function cannot change the size of a + * property, and so will only work if the property already exists and + * has length 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int +fdt_setprop_inplace_cell (void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + val = cpu_to_fdt32 (val); + return fdt_setprop_inplace (fdt, nodeoffset, name, &val, sizeof (val)); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property (void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node (void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +int fdt_create (void *buf, int bufsize); +int fdt_add_reservemap_entry (void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap (void *fdt); +int fdt_begin_node (void *fdt, const char *name); +int fdt_property (void *fdt, const char *name, const void *val, int len); +static inline int +fdt_property_cell (void *fdt, const char *name, uint32_t val) +{ + val = cpu_to_fdt32 (val); + return fdt_property (fdt, name, &val, sizeof (val)); +} + +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node (void *fdt); +int fdt_finish (void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_open_into (const void *fdt, void *buf, int bufsize); +int fdt_pack (void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv (void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv (void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name (void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop (void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_cell - set a property to a single cell value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_cell() sets the value of the named property in the + * given node to the given cell value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int +fdt_setprop_cell (void *fdt, int nodeoffset, const char *name, uint32_t val) +{ + val = cpu_to_fdt32 (val); + return fdt_setprop (fdt, nodeoffset, name, &val, sizeof (val)); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop (void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +int fdt_add_subnode_namelen (void *fdt, int parentoffset, + const char *name, int namelen); + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode (void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node (void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror (int errval); + +#endif /* _LIBFDT_H */ diff --git a/grub-core/lib/dtc/libfdt/libfdt_env.h b/grub-core/lib/dtc/libfdt/libfdt_env.h new file mode 100644 index 000000000..bf66ffd49 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/libfdt_env.h @@ -0,0 +1,27 @@ +#ifndef _LIBFDT_ENV_H +#define _LIBFDT_ENV_H + +#include +#include +#include + +#define _B(n) ((unsigned long long)((uint8_t *)&x)[n]) +static inline uint32_t +fdt32_to_cpu (uint32_t x) +{ + return (_B (0) << 24) | (_B (1) << 16) | (_B (2) << 8) | _B (3); +} + +#define cpu_to_fdt32(x) fdt32_to_cpu(x) + +static inline uint64_t +fdt64_to_cpu (uint64_t x) +{ + return (_B (0) << 56) | (_B (1) << 48) | (_B (2) << 40) | (_B (3) << 32) + | (_B (4) << 24) | (_B (5) << 16) | (_B (6) << 8) | _B (7); +} + +#define cpu_to_fdt64(x) fdt64_to_cpu(x) +#undef _B + +#endif /* _LIBFDT_ENV_H */ diff --git a/grub-core/lib/dtc/libfdt/libfdt_internal.h b/grub-core/lib/dtc/libfdt/libfdt_internal.h new file mode 100644 index 000000000..b197032c8 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/libfdt_internal.h @@ -0,0 +1,100 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ +#include + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +#define FDT_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = fdt_check_header(fdt)) != 0) \ + return err; \ + } + +int _fdt_check_node_offset (const void *fdt, int offset); +int _fdt_check_prop_offset (const void *fdt, int offset); +const char *_fdt_find_string (const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset (void *fdt, int nodeoffset); + +static inline const void * +_fdt_offset_ptr (const void *fdt, int offset) +{ + return (const char *) fdt + fdt_off_dt_struct (fdt) + offset; +} + +static inline void * +_fdt_offset_ptr_w (void *fdt, int offset) +{ + return (void *) (uintptr_t) _fdt_offset_ptr (fdt, offset); +} + +static inline const struct fdt_reserve_entry * +_fdt_mem_rsv (const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *) fdt + fdt_off_mem_rsvmap (fdt)); + + return rsv_table + n; +} + +static inline struct fdt_reserve_entry * +_fdt_mem_rsv_w (void *fdt, int n) +{ + return (void *) (uintptr_t) _fdt_mem_rsv (fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +#endif /* _LIBFDT_INTERNAL_H */ diff --git a/grub-core/lib/dtc/libfdt/version.lds b/grub-core/lib/dtc/libfdt/version.lds new file mode 100644 index 000000000..3c3994e27 --- /dev/null +++ b/grub-core/lib/dtc/libfdt/version.lds @@ -0,0 +1,54 @@ +LIBFDT_1.2 { + global: + fdt_next_node; + fdt_check_header; + fdt_move; + fdt_string; + fdt_num_mem_rsv; + fdt_get_mem_rsv; + fdt_subnode_offset_namelen; + fdt_subnode_offset; + fdt_path_offset; + fdt_get_name; + fdt_get_property_namelen; + fdt_get_property; + fdt_getprop_namelen; + fdt_getprop; + fdt_get_phandle; + fdt_get_alias_namelen; + fdt_get_alias; + fdt_get_path; + fdt_supernode_atdepth_offset; + fdt_node_depth; + fdt_parent_offset; + fdt_node_offset_by_prop_value; + fdt_node_offset_by_phandle; + fdt_node_check_compatible; + fdt_node_offset_by_compatible; + fdt_setprop_inplace; + fdt_nop_property; + fdt_nop_node; + fdt_create; + fdt_add_reservemap_entry; + fdt_finish_reservemap; + fdt_begin_node; + fdt_property; + fdt_end_node; + fdt_finish; + fdt_open_into; + fdt_pack; + fdt_add_mem_rsv; + fdt_del_mem_rsv; + fdt_set_name; + fdt_setprop; + fdt_delprop; + fdt_add_subnode_namelen; + fdt_add_subnode; + fdt_del_node; + fdt_strerror; + fdt_offset_ptr; + fdt_next_tag; + + local: + *; +}; 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/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..3a9004418 --- /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 (); + + 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..40b5b5e01 --- /dev/null +++ b/grub-core/loader/arm/linux.c @@ -0,0 +1,405 @@ +/* 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_size_t initrd_end; + +static grub_addr_t linux_addr; +static grub_size_t linux_size; + +static char *linux_args; + +static grub_addr_t firmware_boot_data; +static grub_addr_t boot_data; +static grub_uint32_t machine_type; + +/* + * 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 = fdt_totalsize ((void *) boot_data) + FDT_ADDITIONAL_ENTRIES_SIZE; + tmp_fdt = grub_malloc (tmp_size); + if (!tmp_fdt) + return GRUB_ERR_OUT_OF_MEMORY; + + fdt_open_into ((void *) boot_data, tmp_fdt, tmp_size); + + /* Find or create '/chosen' node */ + node = fdt_subnode_offset (tmp_fdt, 0, "chosen"); + if (node < 0) + { + grub_printf ("No 'chosen' node in FDT - creating.\n"); + node = 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 = fdt_setprop (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_uint32_t fdt_initrd_start = cpu_to_fdt32 (initrd_start); + grub_uint32_t fdt_initrd_end = cpu_to_fdt32 (initrd_end); + + grub_dprintf ("loader", "Initrd @ 0x%08x-0x%08x\n", + initrd_start, initrd_end); + + retval = fdt_setprop (tmp_fdt, node, "linux,initrd-start", + &fdt_initrd_start, sizeof (fdt_initrd_start)); + if (retval) + goto failure; + retval = fdt_setprop (tmp_fdt, node, "linux,initrd-end", + &fdt_initrd_end, sizeof (fdt_initrd_end)); + if (retval) + goto failure; + } + + /* Copy updated FDT to its launch location */ + fdt_move (tmp_fdt, (void *) boot_data, fdt_totalsize (tmp_fdt)); + grub_free (tmp_fdt); + fdt_pack ((void *) boot_data); + + grub_dprintf ("loader", "FDT updated for Linux boot\n"); + + return GRUB_ERR_NONE; + +failure: + grub_free (tmp_fdt); + return GRUB_ERR_BAD_ARGUMENT; +} + +static grub_err_t +linux_boot (void) +{ + kernel_entry_t linuxmain; + grub_err_t err = GRUB_ERR_NONE; + + grub_arch_sync_caches ((void *) linux_addr, linux_size); + + grub_dprintf ("loader", "Kernel at: 0x%x\n", linux_addr); + + if (!boot_data) + { + if (firmware_boot_data) + { + grub_printf ("Using firmware-supplied boot data @ 0x%08x\n", + firmware_boot_data); + boot_data = firmware_boot_data; + } + else + { + return GRUB_ERR_FILE_NOT_FOUND; + } + } + + grub_dprintf ("loader", "Boot data at: 0x%x\n", boot_data); + + if (fdt32_to_cpu (*(grub_uint32_t *) (boot_data)) == FDT_MAGIC) + { + grub_dprintf ("loader", "FDT @ 0x%08x\n", (grub_addr_t) boot_data); + if (linux_prepare_fdt () != GRUB_ERR_NONE) + { + grub_dprintf ("loader", "linux_prepare_fdt() failed\n"); + return GRUB_ERR_FILE_NOT_FOUND; + } + } + + grub_dprintf ("loader", "Jumping to Linux...\n"); + + /* Boot the kernel. + * Arguments to kernel: + * r0 - 0 + * r1 - machine type (possibly passed from firmware) + * r2 - address of DTB or ATAG list + */ + 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, (void *) boot_data); + + return err; +} + +/* + * Only support zImage, so no relocations necessary + */ +static grub_err_t +linux_load (const char *filename) +{ + grub_file_t file; + int size; + + file = grub_file_open (filename); + if (!file) + return GRUB_ERR_FILE_NOT_FOUND; + + size = grub_file_size (file); + if (size == 0) + return GRUB_ERR_FILE_READ_ERROR; + +#ifdef GRUB_MACHINE_EFI + linux_addr = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_PHYS_OFFSET, size); +#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) + { + grub_printf ("Kernel read failed!\n"); + return GRUB_ERR_FILE_READ_ERROR; + } + + 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, retval; + 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; + + retval = linux_load (argv[0]); + grub_file_close (file); + if (retval != GRUB_ERR_NONE) + goto fail; + + grub_loader_set (linux_boot, linux_unload, 1); + + size = grub_loader_cmdline_size (argc, argv); + linux_args = grub_malloc (size + sizeof (LINUX_IMAGE)); + if (!linux_args) + 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; + +#ifdef GRUB_MACHINE_EFI + initrd_start = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_INITRD_PHYS_OFFSET, size); +#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) + goto fail; + + initrd_end = initrd_start + size; + + return GRUB_ERR_NONE; + +fail: + grub_file_close (file); + + return grub_errno; +} + +static void * +load_dtb (grub_file_t dtb, int size) +{ + void *fdt; + + fdt = grub_malloc (size); + if (!fdt) + return NULL; + + if (grub_file_read (dtb, fdt, size) != size) + { + grub_free (fdt); + return NULL; + } + + if (fdt_check_header (fdt) != 0) + { + grub_free (fdt); + return NULL; + } + + return fdt; +} + +static grub_err_t +grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t dtb; + void *blob; + int size; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + dtb = grub_file_open (argv[0]); + if (!dtb) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("failed to open file")); + + size = grub_file_size (dtb); + if (size == 0) + goto out; + + blob = load_dtb (dtb, size); + if (!blob) + return GRUB_ERR_FILE_NOT_FOUND; + +#ifdef GRUB_MACHINE_EFI + boot_data = (grub_addr_t) grub_efi_allocate_loader_memory (LINUX_FDT_PHYS_OFFSET, size); +#else + boot_data = LINUX_FDT_ADDRESS; +#endif + grub_dprintf ("loader", "Loading device tree to 0x%08x\n", + (grub_addr_t) boot_data); + fdt_move (blob, (void *) boot_data, fdt_totalsize (blob)); + grub_free (blob); + + /* + * We've successfully loaded an FDT, so any machine type passed + * from firmware is now obsolete. + */ + machine_type = ARM_FDT_MACHINE_TYPE; + + return GRUB_ERR_NONE; + + 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; + firmware_boot_data = firmware_get_boot_data (); + + boot_data = (grub_addr_t) NULL; + 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/term/terminfo.c b/grub-core/term/terminfo.c index a46bb4b27..efb17e210 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..e351e6193 --- /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) +{ + uboot_putc (c); +} + +static int +readkey (struct grub_term_input *term __attribute__ ((unused))) +{ + if (uboot_tstc () > 0) + return 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 = 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..33e6c4b84 --- /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 uboot_get_boot_data +# define firmware_get_machine_type 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/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 d19b1ac96..70ff60997 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -44,6 +44,7 @@ enum grub_disk_dev_id GRUB_DISK_DEVICE_ARCDISK_ID, GRUB_DISK_DEVICE_HOSTDISK_ID, GRUB_DISK_DEVICE_PROCFS_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/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..71674a940 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 (__wrap___clear_cache) (void *, void *); +#endif diff --git a/include/grub/offsets.h b/include/grub/offsets.h index bce755d98..1d5152ce9 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/symbol.h b/include/grub/symbol.h index 390eb62f2..e2119bf35 100644 --- a/include/grub/symbol.h +++ b/include/grub/symbol.h @@ -29,7 +29,11 @@ #if HAVE_ASM_USCORE #ifdef ASM_FILE -# define EXT_C(sym) _ ## sym +# ifndef (__arm__) +# define EXT_C(sym) _ ## sym +# else +# define EXT_C(sym) % ## sym +# endif #else # define EXT_C(sym) "_" sym #endif diff --git a/include/grub/uboot/api_public.h b/include/grub/uboot/api_public.h new file mode 100644 index 000000000..35910ece6 --- /dev/null +++ b/include/grub/uboot/api_public.h @@ -0,0 +1,184 @@ +/* + * (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 *, ...); + +typedef grub_uint16_t uint16_t; +typedef grub_uint32_t uint32_t; + +#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 */ + uint16_t version; /* API version */ + 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..b93665fea --- /dev/null +++ b/include/grub/uboot/disk.h @@ -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 . + */ + +#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 +{ + struct ubootdisk_data *next; + void *cookie; + int handle; + int opencount; + enum disktype type; + grub_uint32_t block_size; +}; + +grub_err_t grub_ubootdisk_register (struct device_info *newdev, int handle); + +#endif /* ! GRUB_UBOOT_DISK_HEADER */ diff --git a/include/grub/uboot/uboot.h b/include/grub/uboot/uboot.h new file mode 100644 index 000000000..642bbb6fa --- /dev/null +++ b/include/grub/uboot/uboot.h @@ -0,0 +1,150 @@ +/* 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 uboot_return (int) __attribute__ ((noreturn)); + +grub_addr_t 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 (uboot_get_machine_type) (void); +grub_addr_t EXPORT_FUNC (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. + */ +#include + +#define UBOOT_API_SEARCH_LEN (3 * 1024 * 1024) +int uboot_api_init (void); + +/* All functions below are wrappers around the uboot_syscall() function */ + +/* + * int API_getc(int *c) + */ +int uboot_getc (void); + +/* + * int API_tstc(int *c) + */ +int uboot_tstc (void); + +/* + * int API_putc(char *ch) + */ +void uboot_putc (int c); + +/* + * int API_puts(const char *s) + */ +void uboot_puts (const char *s); + +/* + * int API_reset(void) + */ +void EXPORT_FUNC (uboot_reset) (void); + +/* + * int API_get_sys_info(struct sys_info *si) + * + * fill out the sys_info struct containing selected parameters about the + * machine + */ +struct sys_info *uboot_get_sys_info (void); + +/* + * int API_udelay(unsigned long *udelay) + */ +void uboot_udelay (grub_uint32_t usec); + +/* + * int API_get_timer(unsigned long *current, unsigned long *base) + */ +grub_uint32_t uboot_get_timer (grub_uint32_t base); + +/* + * int API_dev_enum(struct device_info *) + */ +int uboot_dev_enum (void); + +struct device_info *uboot_dev_get (int handle); + +/* + * int API_dev_open(struct device_info *) + */ +int uboot_dev_open (int handle); + +/* + * int API_dev_close(struct device_info *) + */ +int uboot_dev_close (int handle); + +/* + * Notice: this is for sending network packets only, as U-Boot does not + * support writing to storage at the moment (12.2007) + * + * int API_dev_write(struct device_info *di, void *buf, int *len) + */ +int uboot_dev_write (int handle, void *buf, int *len); + +/* + * int API_dev_read( + * struct device_info *di, + * void *buf, + * size_t *len, + * unsigned long *start + * size_t *act_len + * ) + */ +int uboot_dev_read (int handle, void *buf, lbasize_t blocks, + lbastart_t start, lbasize_t * real_blocks); + +int uboot_dev_recv (int handle, void *buf, int size, int *real_size); +int uboot_dev_send (int handle, void *buf, int size); + +/* + * int API_env_get(const char *name, char **value) + */ +char *uboot_env_get (const char *name); + +/* + * int API_env_set(const char *name, const char *value) + */ +void 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 016b16147..65f1e9013 100644 --- a/util/grub-install.in +++ b/util/grub-install.in @@ -316,6 +316,8 @@ if [ x$source_dir = x ]; then target=i386-pc fi ;; + x"arm"*) + target="arm-uboot";; *) gettext "Unable to determine your platform. Use --target." ; echo ;; @@ -335,7 +337,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 @@ -464,6 +466,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 @@ -478,6 +482,8 @@ if [ x"$grub_modinfo_platform" = xefi ]; then # expansion. ia64) efi_file=grubia64.efi ;; + arm) + efi_file=grubarm.efi ;; *) efi_file=grub.efi ;; esac @@ -827,6 +833,14 @@ elif [ x"$grub_modinfo_platform" = xefi ]; then -L "$bootloader_id" -l "\\EFI\\$efi_distributor\\$efi_file" fi fi +elif [ x"${grub_modinfo_target_cpu}-${grub_modinfo_platform}" = xarm-uboot ]; then + grub_imgname="${grubdir}/${grub_modinfo_target_cpu}-$grub_modinfo_platform/core.${imgext}" + raw_imgname="${uboot_imgname}.raw" + mv "$grub_imgname" "$raw_imgname" + mkimage -T kernel -A ARM -O Linux -a 0x08000000 -e 0x08000000 -C none -d "$raw_imgname" "$grub_imgname" + if [ $? -eq 0 ]; then + rm -f "$raw_imgname" + fi else gettext "WARNING: no platform-specific install was performed" 1>&2 echo 1>&2 diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index ecea5d4f1..682e27e8f 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -69,7 +69,7 @@ struct image_target_desc IMAGE_SPARC64_AOUT, IMAGE_SPARC64_RAW, 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 { @@ -455,6 +455,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))) @@ -1022,6 +1062,7 @@ generate_image (const char *dir, const char *prefix, case IMAGE_SPARC64_RAW: case IMAGE_I386_IEEE1275: case IMAGE_PPC: + case IMAGE_UBOOT: break; } @@ -1684,6 +1725,9 @@ generate_image (const char *dir, const char *prefix, core_size = program_size + header_size + footer_size; } break; + case IMAGE_UBOOT: + /* Raw image, header added by grub-install */ + break; } grub_util_write_image (core_img, core_size, out, outname); diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index 476d05eeb..812db900a 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -58,6 +58,11 @@ #error "I'm confused" #endif +static Elf_Addr SUFFIX (entry_point); + +grub_err_t reloc_thm_call (grub_uint16_t *addr, Elf32_Addr sym_addr); +grub_err_t reloc_thm_jump19 (grub_uint16_t *addr, Elf32_Addr sym_addr); + /* Relocate symbols; note that this function overwrites the symbol table. Return the address of a start symbol. */ static Elf_Addr @@ -528,6 +533,48 @@ 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 -= offset; + 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_util_info (" THM_JUMP24:\ttarget=0x%08x\toffset=(0x%08x)", (unsigned int) target, sym_addr); + sym_addr -= offset; + /* Thumb instructions can be 16-bit aligned */ + reloc_thm_call ((grub_uint16_t *) target, sym_addr); + } + break; + case R_ARM_THM_JUMP19: + { + grub_util_info (" THM_JUMP19:\toffset=%d\t(0x%08x)", + sym_addr, sym_addr); + sym_addr -= offset; + /* Thumb instructions can be 16-bit aligned */ + reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr); + } + 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); @@ -755,6 +802,46 @@ 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; +#if 0 + grub_util_info (" %s: add_fixup: 0x%08x : 0x%08x", + __FUNCTION__, (unsigned int) addr, + (unsigned int) current_address); +#endif + current_address + = SUFFIX (add_fixup_entry) (&lst, + GRUB_PE32_REL_BASED_HIGHLOW, + addr, 0, current_address, + image_target); + } + break; + default: + grub_util_error (_("relocation 0x%x is not implemented yet2"), ELF_R_TYPE (info)); + break; + } + break; +#endif /* defined(MKIMAGE_ELF32) */ default: grub_util_error ("unknown machine type 0x%x", image_target->elf_target); } @@ -1065,6 +1152,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, diff --git a/util/import_libfdt.py b/util/import_libfdt.py new file mode 100644 index 000000000..752a42363 --- /dev/null +++ b/util/import_libfdt.py @@ -0,0 +1,103 @@ +#* +#* 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 . +#* + +import re +import sys +import os +import codecs +import datetime + +if len (sys.argv) < 3: + print ("Usage: %s SOURCE DESTINATION" % sys.argv[0]) + exit (0) +dtcdir = sys.argv[1] +indir = os.path.join (dtcdir, "libfdt/") +outdir = os.path.join (sys.argv[2], "lib/dtc-grub/libfdt/") +try: + os.makedirs (outdir) +except: + print ("WARNING: %s already exists" % outdir) + +conf = codecs.open (os.path.join ("grub-core/", "Makefile.libfdt.def"), "w", "utf-8") +conf.write ("AutoGen definitions Makefile.tpl;\n\n") +conf.write ("module = {\n") +conf.write (" name = fdt;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt.c;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt_ro.c;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt_rw.c;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt_strerror.c;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt_sw.c;\n") +conf.write (" common = lib/dtc-grub/libfdt/fdt_wip.c;\n") +conf.write (" cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_LIBFDT)';\n") +conf.write ("\n") +conf.write (" enable = fdt;\n") +conf.write ("};\n") + +conf.close (); + + +libfdt_files = sorted (os.listdir (indir)) +chlog = "" + +for libfdt_file in libfdt_files: + infile = os.path.join (indir, (libfdt_file)) + outfile = os.path.join (outdir, (libfdt_file)) + + if not re.match (".*\.[ch]$", libfdt_file): + chlog = "%s * %s: Removed\n" % (chlog, libfdt_file) + continue + +# print ("file: %s, infile: %s, outfile: %s" % (libfdt_file, infile, outfile)) + + f = codecs.open (infile, "r", "utf-8") + fw = codecs.open (outfile, "w", "utf-8") + + lineno = 1 + + fw.write ("/* This file was automatically imported with \n") + fw.write (" import_libfdt.py. Please don't modify it */\n") + fw.write ("#include \n") + + # libfdt is dual-licensed: BSD or GPLv2+ + if re.match (".*\.c$", libfdt_file): + fw.write ("GRUB_MOD_LICENSE (\"GPLv2+\");\n") + + lines = f.readlines() + + for line in lines: + fw.write (line) + + f.close () + fw.close () + +patchfile = os.path.join (dtcdir, "libfdt-grub.diff") +#print "Patchfile: %s\n" % patchfile +ret = os.system("patch -d %s -p1 < %s" % (outdir, patchfile)) +if ret: + chlog = "%s * Applied Grub build patch\n" % chlog + + +dt = datetime.date.today () +fw = codecs.open (os.path.join (outdir, "ImportLog"), "w", "utf-8") +fw.write ("%04d-%02d-%02d Automatic import tool\n" % \ + (dt.year,dt.month, dt.day)) +fw.write ("\n") +fw.write (" Imported libfdt to GRUB\n") +fw.write ("\n") +fw.write (chlog) +fw.close ()