Index: release/amd64/make-memstick.sh =================================================================== --- release/amd64/make-memstick.sh +++ release/amd64/make-memstick.sh @@ -12,6 +12,9 @@ set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH @@ -36,11 +39,16 @@ rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make a 33292 KB ESP in a file. 33292 KB is the minimum size of a +# FAT32 filesystem +make_esp_file /tmp/efiboot.img 33292 ${1}/boot/loader.efi + mkimg -s mbr \ -b ${1}/boot/mbr \ - -p efi:=${1}/boot/boot1.efifat \ + -p efi:=/tmp/efiboot.img \ -p freebsd:-"mkimg -s bsd -b ${1}/boot/boot -p freebsd-ufs:=${2}.part" \ -a 2 \ -o ${2} +rm /tmp/efiboot.img rm ${2}.part Index: release/amd64/mkisoimages.sh =================================================================== --- release/amd64/mkisoimages.sh +++ release/amd64/mkisoimages.sh @@ -25,6 +25,9 @@ set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + if [ -z $ETDUMP ]; then ETDUMP=etdump fi @@ -43,18 +46,11 @@ bootable="-o bootimage=i386;$BASEBITSDIR/boot/cdboot -o no-emul-boot" # Make EFI system partition (should be done with makefs in the future) - dd if=/dev/zero of=efiboot.img bs=4k count=200 - device=`mdconfig -a -t vnode -f efiboot.img` - newfs_msdos -F 12 -m 0xf8 /dev/$device - mkdir efi - mount -t msdosfs /dev/$device efi - mkdir -p efi/efi/boot - cp "$BASEBITSDIR/boot/loader.efi" efi/efi/boot/bootx64.efi - umount efi - rmdir efi - mdconfig -d -u $device - bootable="$bootable -o bootimage=i386;efiboot.img -o no-emul-boot -o platformid=efi" - + # The ISO file is a special case, in that it only has a maximum of + # 800 KB available for the boot code. So make an 800 KB ESP + make_esp_file /tmp/efiboot.img 800 ${BASEBITSDIR}/boot/loader.efi + bootable="$bootable -o bootimage=i386;/tmp/efiboot.img -o no-emul-boot -o platformid=efi" + shift else BASEBITSDIR="$3" @@ -73,7 +69,7 @@ echo "/dev/iso9660/$LABEL / cd9660 ro 0 0" > "$BASEBITSDIR/etc/fstab" $MAKEFS -t cd9660 $bootable -o rockridge -o label="$LABEL" -o publisher="$publisher" "$NAME" "$@" rm -f "$BASEBITSDIR/etc/fstab" -rm -f efiboot.img +rm -f /tmp/efiboot.img if [ "$bootable" != "" ]; then # Look for the EFI System Partition image we dropped in the ISO image. Index: release/arm64/make-memstick.sh =================================================================== --- release/arm64/make-memstick.sh +++ release/arm64/make-memstick.sh @@ -15,6 +15,9 @@ PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + if [ $# -ne 2 ]; then echo "make-memstick.sh /path/to/directory /path/to/image/file" exit 1 @@ -36,9 +39,14 @@ rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make a 33292 KB ESP in a file. 33292 KB is the minimum size of a +# FAT32 filesystem +make_esp_file /tmp/efiboot.img 33292 ${1}/boot/loader.efi + mkimg -s gpt \ - -p efi:=${1}/boot/boot1.efifat \ + -p efi:=/tmp/efiboot.img \ -p freebsd:=${2}.part \ -o ${2} +rm /tmp/efiboot.img rm ${2}.part Index: release/i386/make-memstick.sh =================================================================== --- release/i386/make-memstick.sh +++ release/i386/make-memstick.sh @@ -12,6 +12,9 @@ set -e +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + PATH=/bin:/usr/bin:/sbin:/usr/sbin export PATH @@ -36,9 +39,15 @@ rm ${1}/etc/fstab rm ${1}/etc/rc.conf.local +# Make a 33292 KB ESP in a file. 33292 KB is the minimum size of a +# FAT32 filesystem +make_esp_file /tmp/efiboot.img 33292 ${1}/boot/loader.efi + mkimg -s mbr \ -b ${1}/boot/mbr \ + -p efi:=/tmp/efiboot.img -p freebsd:-"mkimg -s bsd -b ${1}/boot/boot -p freebsd-ufs:=${2}.part" \ -o ${2} +rm /tmp/efiboot.img rm ${2}.part Index: release/tools/vmimage.subr =================================================================== --- release/tools/vmimage.subr +++ release/tools/vmimage.subr @@ -6,6 +6,9 @@ # Common functions for virtual machine image build scripts. # +scriptdir=$(dirname $(realpath $0)) +. ${scriptdir}/../../tools/boot/install-boot.sh + export PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" trap "cleanup" INT QUIT TRAP ABRT TERM @@ -29,10 +32,8 @@ -o ${VMIMAGE} ;; arm64:aarch64) - mkimg -s mbr -f ${VMFORMAT} \ - -p efi:=${BOOTFILES}/efi/boot1/boot1.efifat \ - -p freebsd:=${VMBASE} \ - -o ${VMIMAGE} + # Create an ESP (EFI System Partition) of 33292 KB (minimum FAT32 size) + make_esp_file ${BOOTFILES}/efi/efiboot.img 33292 ${BOOTFILES}/efi/loader_lua/loader_lua.efi ;; powerpc:powerpc*) mkimg -s apm -f ${VMFORMAT} \ Index: share/man/man8/uefi.8 =================================================================== --- share/man/man8/uefi.8 +++ share/man/man8/uefi.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 25, 2018 +.Dd November 28, 2018 .Dt UEFI 8 .Os .Sh NAME @@ -77,20 +77,16 @@ boot configuration for .Fx installs -.Pa boot1.efi +.Pa loader.efi in the default path. .It -.Pa boot1.efi +.Pa loader.efi reads boot configuration from .Pa /boot.config or .Pa /boot/config . -Unlike other first-stage boot loaders, -.Pa boot1.efi -passes the configuration to the next stage boot loader and does not -itself act on the contents of the file. .It -.Pa boot1.efi +.Pa loader.efi searches partitions of type .Li freebsd-ufs and @@ -98,7 +94,7 @@ for .Pa loader.efi . The search begins with partitions on the device from which -.Pa boot1.efi +.Pa loader.efi was loaded, and continues with other available partitions. If both .Li freebsd-ufs @@ -107,9 +103,6 @@ partitions exist on the same device the .Li freebsd-zfs partition is preferred. -.Pa boot1.efi -then loads and executes -.Pa loader.efi . .It .Pa loader.efi loads and boots the kernel, as described in @@ -122,20 +115,8 @@ .Nm . .Sh FILES .Bl -tag -width /boot/loader -compact -.It Pa /boot/boot1.efi -First stage .Nm bootstrap -.It Pa /boot/boot1.efifat -.Xr msdosfs 5 -FAT file system image containing -.Pa boot1.efi -for use by -.Xr bsdinstall 8 -and the -.Ar bootcode -argument to -.Xr gpart 8 . .It Pa /boot/loader.efi Final stage bootstrap .It Pa /boot/kernel/kernel Index: stand/efi/Makefile =================================================================== --- stand/efi/Makefile +++ stand/efi/Makefile @@ -9,7 +9,7 @@ .if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500 SUBDIR.${MK_FDT}+= fdt -SUBDIR.yes+= libefi boot1 +SUBDIR.yes+= libefi SUBDIR.${MK_FORTH}+= loader_4th SUBDIR.${MK_LOADER_LUA}+= loader_lua SUBDIR.yes+= loader_simp Index: stand/efi/boot1/Makefile =================================================================== --- stand/efi/boot1/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -# $FreeBSD$ - -.include - -PROG= boot1.sym -INTERNALPROG= -WARNS?= 6 - -CFLAGS+= -DEFI_BOOT1 -# We implement a slightly non-standard %S in that it always takes a -# CHAR16 that's common in UEFI-land instead of a wchar_t. This only -# seems to matter on arm64 where wchar_t defaults to an int instead -# of a short. There's no good cast to use here so just ignore the -# warnings for now. -CWARNFLAGS.boot1.c+= -Wno-format - -# Disable warnings that are currently incompatible with the zfs boot code -CWARNFLAGS.zfs_module.c += -Wno-array-bounds -CWARNFLAGS.zfs_module.c += -Wno-cast-align -CWARNFLAGS.zfs_module.c += -Wno-cast-qual -CWARNFLAGS.zfs_module.c += -Wno-missing-prototypes -CWARNFLAGS.zfs_module.c += -Wno-sign-compare -CWARNFLAGS.zfs_module.c += -Wno-unused-parameter -CWARNFLAGS.zfs_module.c += -Wno-unused-function - -# architecture-specific loader code -SRCS= boot1.c self_reloc.c start.S ufs_module.c -.if ${MK_ZFS} != "no" -SRCS+= zfs_module.c -CFLAGS.zfs_module.c+= -I${ZFSSRC} -CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/boot/zfs -CFLAGS.zfs_module.c+= -I${SYSDIR}/crypto/skein -CFLAGS+= -DEFI_ZFS_BOOT -.endif - -.if ${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} > 40201 -CWARNFLAGS.self_reloc.c+= -Wno-error=maybe-uninitialized -.endif - -CFLAGS+= -I${EFIINC} -CFLAGS+= -I${EFIINCMD} -CFLAGS+= -I${SYSDIR}/contrib/dev/acpica/include -CFLAGS+= -DEFI_UFS_BOOT -.ifdef(EFI_DEBUG) -CFLAGS+= -DEFI_DEBUG -.endif - -# Always add MI sources and REGULAR efi loader bits -.PATH: ${EFISRC}/loader/arch/${MACHINE} -.PATH: ${EFISRC}/loader -.PATH: ${LDRSRC} -CFLAGS+= -I${LDRSRC} - -FILES= boot1.efi boot1.efifat -FILESMODE_boot1.efi= ${BINMODE} - -LDSCRIPT= ${EFISRC}/loader/arch/${MACHINE}/ldscript.${MACHINE} -LDFLAGS+= -Wl,-T${LDSCRIPT},-Bsymbolic,-znotext -shared - -.if ${MACHINE_CPUARCH} == "aarch64" -CFLAGS+= -mgeneral-regs-only -.endif -.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" -CFLAGS+= -fPIC -LDFLAGS+= -Wl,-znocombreloc -.endif - -LIBEFI= ${BOOTOBJ}/efi/libefi/libefi.a - -# -# Add libstand for the runtime functions used by the compiler - for example -# __aeabi_* (arm) or __divdi3 (i386). -# as well as required string and memory functions for all platforms. -# -DPADD+= ${LIBEFI} ${LIBSA} -LDADD+= ${LIBEFI} ${LIBSA} - -DPADD+= ${LDSCRIPT} - -boot1.efi: ${PROG} - if ${NM} ${.ALLSRC} | grep ' U '; then \ - echo "Undefined symbols in ${.ALLSRC}"; \ - exit 1; \ - fi - SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} \ - ${OBJCOPY} -j .peheader -j .text -j .sdata -j .data \ - -j .dynamic -j .dynsym -j .rel.dyn \ - -j .rela.dyn -j .reloc -j .eh_frame \ - --output-target=${EFI_TARGET} ${.ALLSRC} ${.TARGET} - -# The following inserts our objects into a template FAT file system -# created by generate-fat.sh - -.include "Makefile.fat" - -boot1.efifat: boot1.efi - @set -- `ls -l ${.ALLSRC}`; \ - x=$$(($$5-${BOOT1_MAXSIZE})); \ - if [ $$x -ge 0 ]; then \ - echo "boot1 $$x bytes too large; regenerate FAT templates?" >&2 ;\ - exit 1; \ - fi - echo ${.OBJDIR} - xz -d -c ${BOOTSRC}/efi/boot1/fat-${MACHINE}.tmpl.xz > ${.TARGET} - ${DD} if=${.ALLSRC} of=${.TARGET} seek=${BOOT1_OFFSET} conv=notrunc - -CLEANFILES+= boot1.efi boot1.efifat - -.include Index: stand/efi/boot1/Makefile.depend =================================================================== --- stand/efi/boot1/Makefile.depend +++ /dev/null @@ -1,17 +0,0 @@ -# $FreeBSD$ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - include \ - include/xlocale \ - lib/libmd \ - stand/efi/libefi \ - stand/libsa \ - stand/zfs \ - - -.include - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif Index: stand/efi/boot1/Makefile.fat =================================================================== --- stand/efi/boot1/Makefile.fat +++ /dev/null @@ -1,4 +0,0 @@ -# This file autogenerated by generate-fat.sh - DO NOT EDIT -# $FreeBSD$ -BOOT1_OFFSET=0x2d -BOOT1_MAXSIZE=393216 Index: stand/efi/boot1/boot1.c =================================================================== --- stand/efi/boot1/boot1.c +++ /dev/null @@ -1,581 +0,0 @@ -/*- - * Copyright (c) 1998 Robert Nordier - * All rights reserved. - * Copyright (c) 2001 Robert Drehmel - * All rights reserved. - * Copyright (c) 2014 Nathan Whitehorn - * All rights reserved. - * Copyright (c) 2015 Eric McCorkle - * All rights reserved. - * - * Redistribution and use in source and binary forms are freely - * permitted provided that the above copyright notice and this - * paragraph and the following disclaimer are duplicated in all - * such forms. - * - * This software is provided "AS IS" and without any express or - * implied warranties, including, without limitation, the implied - * warranties of merchantability and fitness for a particular - * purpose. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include - -#include -#include -#include - -#include "boot_module.h" -#include "paths.h" - -static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); - -static const boot_module_t *boot_modules[] = -{ -#ifdef EFI_ZFS_BOOT - &zfs_module, -#endif -#ifdef EFI_UFS_BOOT - &ufs_module -#endif -}; - -#define NUM_BOOT_MODULES nitems(boot_modules) -/* The initial number of handles used to query EFI for partitions. */ -#define NUM_HANDLES_INIT 24 - -static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; -static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; -static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; -static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; - -/* - * Provide Malloc / Free backed by EFIs AllocatePool / FreePool which ensures - * memory is correctly aligned avoiding EFI_INVALID_PARAMETER returns from - * EFI methods. - */ -void * -Malloc(size_t len, const char *file __unused, int line __unused) -{ - void *out; - - if (BS->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) - return (out); - - return (NULL); -} - -void -Free(void *buf, const char *file __unused, int line __unused) -{ - if (buf != NULL) - (void)BS->FreePool(buf); -} - -/* - * nodes_match returns TRUE if the imgpath isn't NULL and the nodes match, - * FALSE otherwise. - */ -static BOOLEAN -nodes_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) -{ - size_t len; - - if (imgpath == NULL || imgpath->Type != devpath->Type || - imgpath->SubType != devpath->SubType) - return (FALSE); - - len = DevicePathNodeLength(imgpath); - if (len != DevicePathNodeLength(devpath)) - return (FALSE); - - return (memcmp(imgpath, devpath, (size_t)len) == 0); -} - -/* - * device_paths_match returns TRUE if the imgpath isn't NULL and all nodes - * in imgpath and devpath match up to their respective occurrences of a - * media node, FALSE otherwise. - */ -static BOOLEAN -device_paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) -{ - - if (imgpath == NULL) - return (FALSE); - - while (!IsDevicePathEnd(imgpath) && !IsDevicePathEnd(devpath)) { - if (IsDevicePathType(imgpath, MEDIA_DEVICE_PATH) && - IsDevicePathType(devpath, MEDIA_DEVICE_PATH)) - return (TRUE); - - if (!nodes_match(imgpath, devpath)) - return (FALSE); - - imgpath = NextDevicePathNode(imgpath); - devpath = NextDevicePathNode(devpath); - } - - return (FALSE); -} - -/* - * devpath_last returns the last non-path end node in devpath. - */ -static EFI_DEVICE_PATH * -devpath_last(EFI_DEVICE_PATH *devpath) -{ - - while (!IsDevicePathEnd(NextDevicePathNode(devpath))) - devpath = NextDevicePathNode(devpath); - - return (devpath); -} - -/* - * load_loader attempts to load the loader image data. - * - * It tries each module and its respective devices, identified by mod->probe, - * in order until a successful load occurs at which point it returns EFI_SUCCESS - * and EFI_NOT_FOUND otherwise. - * - * Only devices which have preferred matching the preferred parameter are tried. - */ -static EFI_STATUS -load_loader(const boot_module_t **modp, dev_info_t **devinfop, void **bufp, - size_t *bufsize, BOOLEAN preferred) -{ - UINTN i; - dev_info_t *dev; - const boot_module_t *mod; - - for (i = 0; i < NUM_BOOT_MODULES; i++) { - mod = boot_modules[i]; - for (dev = mod->devices(); dev != NULL; dev = dev->next) { - if (dev->preferred != preferred) - continue; - - if (mod->load(PATH_LOADER_EFI, dev, bufp, bufsize) == - EFI_SUCCESS) { - *devinfop = dev; - *modp = mod; - return (EFI_SUCCESS); - } - } - } - - return (EFI_NOT_FOUND); -} - -/* - * try_boot only returns if it fails to load the loader. If it succeeds - * it simply boots, otherwise it returns the status of last EFI call. - */ -static EFI_STATUS -try_boot(void) -{ - size_t bufsize, loadersize, cmdsize; - void *buf, *loaderbuf; - char *cmd; - dev_info_t *dev; - const boot_module_t *mod; - EFI_HANDLE loaderhandle; - EFI_LOADED_IMAGE *loaded_image; - EFI_STATUS status; - - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, TRUE); - if (status != EFI_SUCCESS) { - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, - FALSE); - if (status != EFI_SUCCESS) { - printf("Failed to load '%s'\n", PATH_LOADER_EFI); - return (status); - } - } - - /* - * Read in and parse the command line from /boot.config or /boot/config, - * if present. We'll pass it the next stage via a simple ASCII - * string. loader.efi has a hack for ASCII strings, so we'll use that to - * keep the size down here. We only try to read the alternate file if - * we get EFI_NOT_FOUND because all other errors mean that the boot_module - * had troubles with the filesystem. We could return early, but we'll let - * loading the actual kernel sort all that out. Since these files are - * optional, we don't report errors in trying to read them. - */ - cmd = NULL; - cmdsize = 0; - status = mod->load(PATH_DOTCONFIG, dev, &buf, &bufsize); - if (status == EFI_NOT_FOUND) - status = mod->load(PATH_CONFIG, dev, &buf, &bufsize); - if (status == EFI_SUCCESS) { - cmdsize = bufsize + 1; - cmd = malloc(cmdsize); - if (cmd == NULL) - goto errout; - memcpy(cmd, buf, bufsize); - cmd[bufsize] = '\0'; - free(buf); - buf = NULL; - } - - if ((status = BS->LoadImage(TRUE, IH, devpath_last(dev->devpath), - loaderbuf, loadersize, &loaderhandle)) != EFI_SUCCESS) { - printf("Failed to load image provided by %s, size: %zu, (%lu)\n", - mod->name, loadersize, EFI_ERROR_CODE(status)); - goto errout; - } - - if ((status = BS->HandleProtocol(loaderhandle, &LoadedImageGUID, - (VOID**)&loaded_image)) != EFI_SUCCESS) { - printf("Failed to query LoadedImage provided by %s (%lu)\n", - mod->name, EFI_ERROR_CODE(status)); - goto errout; - } - - if (cmd != NULL) - printf(" command args: %s\n", cmd); - - loaded_image->DeviceHandle = dev->devhandle; - loaded_image->LoadOptionsSize = cmdsize; - loaded_image->LoadOptions = cmd; - - DPRINTF("Starting '%s' in 5 seconds...", PATH_LOADER_EFI); - DSTALL(1000000); - DPRINTF("."); - DSTALL(1000000); - DPRINTF("."); - DSTALL(1000000); - DPRINTF("."); - DSTALL(1000000); - DPRINTF("."); - DSTALL(1000000); - DPRINTF(".\n"); - - if ((status = BS->StartImage(loaderhandle, NULL, NULL)) != - EFI_SUCCESS) { - printf("Failed to start image provided by %s (%lu)\n", - mod->name, EFI_ERROR_CODE(status)); - loaded_image->LoadOptionsSize = 0; - loaded_image->LoadOptions = NULL; - } - -errout: - if (cmd != NULL) - free(cmd); - if (buf != NULL) - free(buf); - if (loaderbuf != NULL) - free(loaderbuf); - - return (status); -} - -/* - * probe_handle determines if the passed handle represents a logical partition - * if it does it uses each module in order to probe it and if successful it - * returns EFI_SUCCESS. - */ -static EFI_STATUS -probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN *preferred) -{ - dev_info_t *devinfo; - EFI_BLOCK_IO *blkio; - EFI_DEVICE_PATH *devpath; - EFI_STATUS status; - UINTN i; - - /* Figure out if we're dealing with an actual partition. */ - status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); - if (status == EFI_UNSUPPORTED) - return (status); - - if (status != EFI_SUCCESS) { - DPRINTF("\nFailed to query DevicePath (%lu)\n", - EFI_ERROR_CODE(status)); - return (status); - } -#ifdef EFI_DEBUG - { - CHAR16 *text = efi_devpath_name(devpath); - DPRINTF("probing: %S\n", text); - efi_free_devpath_name(text); - } -#endif - status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); - if (status == EFI_UNSUPPORTED) - return (status); - - if (status != EFI_SUCCESS) { - DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", - EFI_ERROR_CODE(status)); - return (status); - } - - if (!blkio->Media->LogicalPartition) - return (EFI_UNSUPPORTED); - - *preferred = device_paths_match(imgpath, devpath); - - /* Run through each module, see if it can load this partition */ - for (i = 0; i < NUM_BOOT_MODULES; i++) { - devinfo = malloc(sizeof(*devinfo)); - if (devinfo == NULL) { - DPRINTF("\nFailed to allocate devinfo\n"); - continue; - } - devinfo->dev = blkio; - devinfo->devpath = devpath; - devinfo->devhandle = h; - devinfo->devdata = NULL; - devinfo->preferred = *preferred; - devinfo->next = NULL; - - status = boot_modules[i]->probe(devinfo); - if (status == EFI_SUCCESS) - return (EFI_SUCCESS); - free(devinfo); - } - - return (EFI_UNSUPPORTED); -} - -/* - * probe_handle_status calls probe_handle and outputs the returned status - * of the call. - */ -static void -probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) -{ - EFI_STATUS status; - BOOLEAN preferred; - - preferred = FALSE; - status = probe_handle(h, imgpath, &preferred); - - DPRINTF("probe: "); - switch (status) { - case EFI_UNSUPPORTED: - printf("."); - DPRINTF(" not supported\n"); - break; - case EFI_SUCCESS: - if (preferred) { - printf("%c", '*'); - DPRINTF(" supported (preferred)\n"); - } else { - printf("%c", '+'); - DPRINTF(" supported\n"); - } - break; - default: - printf("x"); - DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status)); - break; - } - DSTALL(500000); -} - -EFI_STATUS -efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) -{ - EFI_HANDLE *handles; - EFI_LOADED_IMAGE *img; - EFI_DEVICE_PATH *imgpath; - EFI_STATUS status; - EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; - SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; - UINTN i, hsize, nhandles; - CHAR16 *text; - UINT16 boot_current; - size_t sz; - UINT16 boot_order[100]; - - /* Basic initialization*/ - ST = Xsystab; - IH = Ximage; - BS = ST->BootServices; - RS = ST->RuntimeServices; - - /* Set up the console, so printf works. */ - status = BS->LocateProtocol(&ConsoleControlGUID, NULL, - (VOID **)&ConsoleControl); - if (status == EFI_SUCCESS) - (void)ConsoleControl->SetMode(ConsoleControl, - EfiConsoleControlScreenText); - /* - * Reset the console enable the cursor. Later we'll choose a better - * console size through GOP/UGA. - */ - conout = ST->ConOut; - conout->Reset(conout, TRUE); - /* Explicitly set conout to mode 0, 80x25 */ - conout->SetMode(conout, 0); - conout->EnableCursor(conout, TRUE); - conout->ClearScreen(conout); - - printf("\n>> FreeBSD EFI boot block\n"); - printf(" Loader path: %s\n\n", PATH_LOADER_EFI); - printf(" Initializing modules:"); - for (i = 0; i < NUM_BOOT_MODULES; i++) { - printf(" %s", boot_modules[i]->name); - if (boot_modules[i]->init != NULL) - boot_modules[i]->init(); - } - putchar('\n'); - - /* Determine the devpath of our image so we can prefer it. */ - status = BS->HandleProtocol(IH, &LoadedImageGUID, (VOID**)&img); - imgpath = NULL; - if (status == EFI_SUCCESS) { - text = efi_devpath_name(img->FilePath); - if (text != NULL) { - printf(" Load Path: %S\n", text); - efi_setenv_freebsd_wcs("Boot1Path", text); - efi_free_devpath_name(text); - } - - status = BS->HandleProtocol(img->DeviceHandle, &DevicePathGUID, - (void **)&imgpath); - if (status != EFI_SUCCESS) { - DPRINTF("Failed to get image DevicePath (%lu)\n", - EFI_ERROR_CODE(status)); - } else { - text = efi_devpath_name(imgpath); - if (text != NULL) { - printf(" Load Device: %S\n", text); - efi_setenv_freebsd_wcs("Boot1Dev", text); - efi_free_devpath_name(text); - } - } - } - - boot_current = 0; - sz = sizeof(boot_current); - if (efi_global_getenv("BootCurrent", &boot_current, &sz) == EFI_SUCCESS) { - printf(" BootCurrent: %04x\n", boot_current); - - sz = sizeof(boot_order); - if (efi_global_getenv("BootOrder", &boot_order, &sz) == EFI_SUCCESS) { - printf(" BootOrder:"); - for (i = 0; i < sz / sizeof(boot_order[0]); i++) - printf(" %04x%s", boot_order[i], - boot_order[i] == boot_current ? "[*]" : ""); - printf("\n"); - } - } - -#ifdef TEST_FAILURE - /* - * For testing failover scenarios, it's nice to be able to fail fast. - * Define TEST_FAILURE to create a boot1.efi that always fails after - * reporting the boot manager protocol details. - */ - BS->Exit(IH, EFI_OUT_OF_RESOURCES, 0, NULL); -#endif - - /* Get all the device handles */ - hsize = (UINTN)NUM_HANDLES_INIT * sizeof(EFI_HANDLE); - handles = malloc(hsize); - if (handles == NULL) - printf("Failed to allocate %d handles\n", NUM_HANDLES_INIT); - - status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, - &hsize, handles); - switch (status) { - case EFI_SUCCESS: - break; - case EFI_BUFFER_TOO_SMALL: - free(handles); - handles = malloc(hsize); - if (handles == NULL) - efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", - NUM_HANDLES_INIT); - status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, - NULL, &hsize, handles); - if (status != EFI_SUCCESS) - efi_panic(status, "Failed to get device handles\n"); - break; - default: - efi_panic(status, "Failed to get device handles\n"); - break; - } - - /* Scan all partitions, probing with all modules. */ - nhandles = hsize / sizeof(*handles); - printf(" Probing %zu block devices...", nhandles); - DPRINTF("\n"); - - for (i = 0; i < nhandles; i++) - probe_handle_status(handles[i], imgpath); - printf(" done\n"); - - /* Status summary. */ - for (i = 0; i < NUM_BOOT_MODULES; i++) { - printf(" "); - boot_modules[i]->status(); - } - - try_boot(); - - /* If we get here, we're out of luck... */ - efi_panic(EFI_LOAD_ERROR, "No bootable partitions found!"); -} - -/* - * add_device adds a device to the passed devinfo list. - */ -void -add_device(dev_info_t **devinfop, dev_info_t *devinfo) -{ - dev_info_t *dev; - - if (*devinfop == NULL) { - *devinfop = devinfo; - return; - } - - for (dev = *devinfop; dev->next != NULL; dev = dev->next) - ; - - dev->next = devinfo; -} - -/* - * OK. We totally give up. Exit back to EFI with a sensible status so - * it can try the next option on the list. - */ -static void -efi_panic(EFI_STATUS s, const char *fmt, ...) -{ - va_list ap; - - printf("panic: "); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - printf("\n"); - - BS->Exit(IH, s, 0, NULL); -} - -void -putchar(int c) -{ - CHAR16 buf[2]; - - if (c == '\n') { - buf[0] = '\r'; - buf[1] = 0; - ST->ConOut->OutputString(ST->ConOut, buf); - } - buf[0] = c; - buf[1] = 0; - ST->ConOut->OutputString(ST->ConOut, buf); -} Index: stand/efi/boot1/boot_module.h =================================================================== --- stand/efi/boot1/boot_module.h +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * Copyright (c) 2015 Eric McCorkle - * All rights reserved. - * - * 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. - * - * $FreeBSD$ - */ - -#ifndef _BOOT_MODULE_H_ -#define _BOOT_MODULE_H_ - -#include - -#include -#include -#include - -#ifdef EFI_DEBUG -#define DPRINTF(fmt, args...) printf(fmt, ##args) -#define DSTALL(d) BS->Stall(d) -#else -#define DPRINTF(fmt, ...) {} -#define DSTALL(d) {} -#endif - -/* EFI device info */ -typedef struct dev_info -{ - EFI_BLOCK_IO *dev; - EFI_DEVICE_PATH *devpath; - EFI_HANDLE *devhandle; - void *devdata; - uint64_t partoff; - int preferred; - struct dev_info *next; -} dev_info_t; - -/* - * A boot loader module. - * - * This is a standard interface for filesystem modules in the EFI system. - */ -typedef struct boot_module_t -{ - const char *name; - - /* init is the optional initialiser for the module. */ - void (*init)(void); - - /* - * probe checks to see if the module can handle dev. - * - * Return codes: - * EFI_SUCCESS = The module can handle the device. - * EFI_NOT_FOUND = The module can not handle the device. - * Other = The module encountered an error. - */ - EFI_STATUS (*probe)(dev_info_t* dev); - - /* - * load should select the best out of a set of devices that probe - * indicated were loadable and load the specified file. - * - * Return codes: - * EFI_SUCCESS = The module can handle the device. - * EFI_NOT_FOUND = The module can not handle the device. - * Other = The module encountered an error. - */ - EFI_STATUS (*load)(const char *filepath, dev_info_t *devinfo, - void **buf, size_t *bufsize); - - /* status outputs information about the probed devices. */ - void (*status)(void); - - /* valid devices as found by probe. */ - dev_info_t *(*devices)(void); -} boot_module_t; - -/* Standard boot modules. */ -#ifdef EFI_UFS_BOOT -extern const boot_module_t ufs_module; -#endif -#ifdef EFI_ZFS_BOOT -extern const boot_module_t zfs_module; -#endif - -/* Functions available to modules. */ -extern void add_device(dev_info_t **devinfop, dev_info_t *devinfo); -extern int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); -#endif Index: stand/efi/boot1/generate-fat.sh =================================================================== --- stand/efi/boot1/generate-fat.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh - -# This script generates the dummy FAT filesystem used for the EFI boot -# blocks. It uses newfs_msdos to generate a template filesystem with the -# relevant interesting files. These are then found by grep, and the offsets -# written to a Makefile snippet. -# -# Because it requires root, and because it is overkill, we do not -# do this as part of the normal build. If makefs(8) grows workable FAT -# support, this should be revisited. - -# $FreeBSD$ - -FAT_SIZE=1600 #Size in 512-byte blocks of the produced image - -BOOT1_OFFSET=2d -BOOT1_SIZE=384k - -if [ $(id -u) != 0 ]; then - echo "${0##*/}: must run as root" >&2 - exit 1 -fi - -# Record maximum boot1 size in bytes -case $BOOT1_SIZE in -*k) - BOOT1_MAXSIZE=$(expr ${BOOT1_SIZE%k} '*' 1024) - ;; -*) - BOOT1_MAXSIZE=$BOOT1_SIZE - ;; -esac - -echo '# This file autogenerated by generate-fat.sh - DO NOT EDIT' > Makefile.fat -echo "# \$FreeBSD\$" >> Makefile.fat -echo "BOOT1_OFFSET=0x$BOOT1_OFFSET" >> Makefile.fat -echo "BOOT1_MAXSIZE=$BOOT1_MAXSIZE" >> Makefile.fat - -while read ARCH FILENAME; do - # Generate 800K FAT image - OUTPUT_FILE=fat-${ARCH}.tmpl - - dd if=/dev/zero of=$OUTPUT_FILE bs=512 count=$FAT_SIZE - DEVICE=`mdconfig -a -f $OUTPUT_FILE` - newfs_msdos -F 12 -L EFISYS $DEVICE - mkdir stub - mount -t msdosfs /dev/$DEVICE stub - - # Create and bless a directory for the boot loader - mkdir -p stub/efi/boot - - # Make a dummy file for boot1 - echo 'Boot1 START' | dd of=stub/efi/boot/$FILENAME cbs=$BOOT1_SIZE count=1 conv=block - # Provide a fallback startup.nsh - echo $FILENAME > stub/efi/boot/startup.nsh - - umount stub - mdconfig -d -u $DEVICE - rmdir stub - - # Locate the offset of the fake file - OFFSET=$(hd $OUTPUT_FILE | grep 'Boot1 START' | cut -f 1 -d ' ') - - # Convert to number of blocks - OFFSET=$(echo 0x$OFFSET | awk '{printf("%x\n",$1/512);}') - - # Validate the offset - if [ $OFFSET != $BOOT1_OFFSET ]; then - echo "Incorrect offset $OFFSET != $BOOT1_OFFSET" >&2 - exit 1 - fi - - xz -f $OUTPUT_FILE -done < -#include -#include -#include -#include -#include - -#include "boot_module.h" - -#define BSD_LABEL_BUFFER 8192 -#define BSD_LABEL_OFFSET DEV_BSIZE - -static dev_info_t *devinfo; -static dev_info_t *devices; - -static int -dskread(void *buf, uint64_t lba, int nblk) -{ - int size; - EFI_STATUS status; - - lba += devinfo->partoff; - lba = lba / (devinfo->dev->Media->BlockSize / DEV_BSIZE); - size = nblk * DEV_BSIZE; - - status = devinfo->dev->ReadBlocks(devinfo->dev, - devinfo->dev->Media->MediaId, lba, size, buf); - - if (status != EFI_SUCCESS) { - DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, " - "status: %lu\n", devinfo->dev, - devinfo->dev->Media->MediaId, (uintmax_t)lba, size, - EFI_ERROR_CODE(status)); - return (-1); - } - - return (0); -} - -#include "ufsread.c" - -static struct dmadat __dmadat; - -static int -init_dev(dev_info_t* dev) -{ - char buffer[BSD_LABEL_BUFFER]; - struct disklabel *dl; - uint64_t bs; - int ok; - - devinfo = dev; - dmadat = &__dmadat; - - /* - * First try offset 0. This is the typical GPT case where we have no - * further partitioning (as well as the degenerate MBR case where - * the bsdlabel has a 0 offset). - */ - devinfo->partoff = 0; - ok = fsread(0, NULL, 0); - if (ok >= 0) - return (ok); - - /* - * Next, we look for a bsdlabel. This is technically located in sector - * 1. For 4k sectors, this offset is 4096, for 512b sectors it's - * 512. However, we have to fall back to 512 here because we create - * images that assume 512 byte blocks, but these can be put on devices - * who have 4k (or other) block sizes. If there's a crazy block size, we - * skip the 'at one sector' and go stright to checking at 512 bytes. - * There are other offsets that are historic, but we don't probe those - * since they were never used for MBR disks on FreeBSD on systems that - * could boot UEFI. UEFI is little endian only, as are BSD labels. We - * will retry fsread(0) only if there's a label found with a non-zero - * offset. - */ - if (dskread(buffer, 0, BSD_LABEL_BUFFER / DEV_BSIZE) != 0) - return (-1); - dl = NULL; - bs = devinfo->dev->Media->BlockSize; - if (bs != 0 && bs <= BSD_LABEL_BUFFER / 2) - dl = (struct disklabel *)&buffer[bs]; - if (dl == NULL || dl->d_magic != BSD_MAGIC || dl->d_magic2 != BSD_MAGIC) - dl = (struct disklabel *)&buffer[BSD_LABEL_OFFSET]; - if (dl->d_magic != BSD_MAGIC || dl->d_magic2 != BSD_MAGIC || - dl->d_partitions[0].p_offset == 0) - return (-1); - devinfo->partoff = dl->d_partitions[0].p_offset; - return (fsread(0, NULL, 0)); -} - -static EFI_STATUS -probe(dev_info_t* dev) -{ - - if (init_dev(dev) < 0) - return (EFI_UNSUPPORTED); - - add_device(&devices, dev); - - return (EFI_SUCCESS); -} - -static EFI_STATUS -load(const char *filepath, dev_info_t *dev, void **bufp, size_t *bufsize) -{ - ufs_ino_t ino; - EFI_STATUS status; - size_t size; - ssize_t read; - void *buf; - -#ifdef EFI_DEBUG - { - CHAR16 *text = efi_devpath_name(dev->devpath); - DPRINTF("Loading '%s' from %S\n", filepath, text); - efi_free_devpath_name(text); - } -#endif - if (init_dev(dev) < 0) { - DPRINTF("Failed to init device\n"); - return (EFI_UNSUPPORTED); - } - - if ((ino = lookup(filepath)) == 0) { - DPRINTF("Failed to lookup '%s' (file not found?)\n", filepath); - return (EFI_NOT_FOUND); - } - - if (fsread_size(ino, NULL, 0, &size) < 0 || size <= 0) { - printf("Failed to read size of '%s' ino: %d\n", filepath, ino); - return (EFI_INVALID_PARAMETER); - } - - if ((status = BS->AllocatePool(EfiLoaderData, size, &buf)) != - EFI_SUCCESS) { - printf("Failed to allocate read buffer %zu for '%s' (%lu)\n", - size, filepath, EFI_ERROR_CODE(status)); - return (status); - } - - read = fsread(ino, buf, size); - if ((size_t)read != size) { - printf("Failed to read '%s' (%zd != %zu)\n", filepath, read, - size); - (void)BS->FreePool(buf); - return (EFI_INVALID_PARAMETER); - } - - DPRINTF("Load complete\n"); - - *bufp = buf; - *bufsize = size; - - return (EFI_SUCCESS); -} - -static void -status(void) -{ - int i; - dev_info_t *dev; - - for (dev = devices, i = 0; dev != NULL; dev = dev->next, i++) - ; - - printf("%s found ", ufs_module.name); - switch (i) { - case 0: - printf("no partitions\n"); - break; - case 1: - printf("%d partition\n", i); - break; - default: - printf("%d partitions\n", i); - } -} - -static dev_info_t * -_devices(void) -{ - - return (devices); -} - -const boot_module_t ufs_module = -{ - .name = "UFS", - .probe = probe, - .load = load, - .status = status, - .devices = _devices -}; Index: stand/efi/boot1/zfs_module.c =================================================================== --- stand/efi/boot1/zfs_module.c +++ /dev/null @@ -1,248 +0,0 @@ -/*- - * Copyright (c) 2015 Eric McCorkle - * All rights reserved. - * - * 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. - * - * $FreeBSD$ - */ -#include -#include -#include -#include -#include -#include -#include - -#include "boot_module.h" - -#include "libzfs.h" -#include "zfsimpl.c" - -static dev_info_t *devices; - -uint64_t -ldi_get_size(void *priv) -{ - dev_info_t *devinfo = priv; - - return (devinfo->dev->Media->BlockSize * - (devinfo->dev->Media->LastBlock + 1)); -} - -static int -vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) -{ - dev_info_t *devinfo; - uint64_t lba; - size_t size, remainder, rb_size, blksz; - char *bouncebuf = NULL, *rb_buf; - EFI_STATUS status; - - devinfo = (dev_info_t *)priv; - lba = off / devinfo->dev->Media->BlockSize; - remainder = off % devinfo->dev->Media->BlockSize; - - rb_buf = buf; - rb_size = bytes; - - /* - * If we have remainder from off, we need to add remainder part. - * Since buffer must be multiple of the BlockSize, round it all up. - */ - size = roundup2(bytes + remainder, devinfo->dev->Media->BlockSize); - blksz = size; - if (remainder != 0 || size != bytes) { - rb_size = devinfo->dev->Media->BlockSize; - bouncebuf = malloc(rb_size); - if (bouncebuf == NULL) { - printf("vdev_read: out of memory\n"); - return (-1); - } - rb_buf = bouncebuf; - blksz = rb_size - remainder; - } - - while (bytes > 0) { - status = devinfo->dev->ReadBlocks(devinfo->dev, - devinfo->dev->Media->MediaId, lba, rb_size, rb_buf); - if (EFI_ERROR(status)) - goto error; - if (bytes < blksz) - blksz = bytes; - if (bouncebuf != NULL) - memcpy(buf, rb_buf + remainder, blksz); - buf = (void *)((uintptr_t)buf + blksz); - bytes -= blksz; - lba++; - remainder = 0; - blksz = rb_size; - } - - free(bouncebuf); - return (0); - -error: - free(bouncebuf); - DPRINTF("vdev_read: failed dev: %p, id: %u, lba: %ju, size: %zu," - " rb_size: %zu, status: %lu\n", devinfo->dev, - devinfo->dev->Media->MediaId, (uintmax_t)lba, bytes, rb_size, - EFI_ERROR_CODE(status)); - return (-1); -} - -static EFI_STATUS -probe(dev_info_t *dev) -{ - spa_t *spa; - dev_info_t *tdev; - EFI_STATUS status; - - /* ZFS consumes the dev on success so we need a copy. */ - if ((status = BS->AllocatePool(EfiLoaderData, sizeof(*dev), - (void**)&tdev)) != EFI_SUCCESS) { - DPRINTF("Failed to allocate tdev (%lu)\n", - EFI_ERROR_CODE(status)); - return (status); - } - memcpy(tdev, dev, sizeof(*dev)); - - if (vdev_probe(vdev_read, tdev, &spa) != 0) { - (void)BS->FreePool(tdev); - return (EFI_UNSUPPORTED); - } - - dev->devdata = spa; - add_device(&devices, dev); - - return (EFI_SUCCESS); -} - -static EFI_STATUS -load(const char *filepath, dev_info_t *devinfo, void **bufp, size_t *bufsize) -{ - spa_t *spa; - struct zfsmount zfsmount; - dnode_phys_t dn; - struct stat st; - int err; - void *buf; - EFI_STATUS status; - - spa = devinfo->devdata; - -#ifdef EFI_DEBUG - { - CHAR16 *text = efi_devpath_name(devinfo->devpath); - DPRINTF("load: '%s' spa: '%s', devpath: %S\n", filepath, - spa->spa_name, text); - efi_free_devpath_name(text); - } -#endif - if ((err = zfs_spa_init(spa)) != 0) { - DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err); - return (EFI_NOT_FOUND); - } - - if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) { - DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err); - return (EFI_NOT_FOUND); - } - - if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) { - if (err == ENOENT) { - DPRINTF("Failed to find '%s' on pool '%s' (%d)\n", - filepath, spa->spa_name, err); - return (EFI_NOT_FOUND); - } - printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath, - spa->spa_name, err); - return (EFI_INVALID_PARAMETER); - } - - if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) { - printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath, - spa->spa_name, err); - return (EFI_INVALID_PARAMETER); - } - - if ((status = BS->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf)) - != EFI_SUCCESS) { - printf("Failed to allocate load buffer %jd for pool '%s' for '%s' " - "(%lu)\n", (intmax_t)st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status)); - return (EFI_INVALID_PARAMETER); - } - - if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) { - printf("Failed to read node from %s (%d)\n", spa->spa_name, - err); - (void)BS->FreePool(buf); - return (EFI_INVALID_PARAMETER); - } - - *bufsize = st.st_size; - *bufp = buf; - - return (EFI_SUCCESS); -} - -static void -status(void) -{ - spa_t *spa; - - spa = STAILQ_FIRST(&zfs_pools); - if (spa == NULL) { - printf("%s found no pools\n", zfs_module.name); - return; - } - - printf("%s found the following pools:", zfs_module.name); - STAILQ_FOREACH(spa, &zfs_pools, spa_link) - printf(" %s", spa->spa_name); - - printf("\n"); -} - -static void -init(void) -{ - - zfs_init(); -} - -static dev_info_t * -_devices(void) -{ - - return (devices); -} - -const boot_module_t zfs_module = -{ - .name = "ZFS", - .init = init, - .probe = probe, - .load = load, - .status = status, - .devices = _devices -}; Index: tools/boot/install-boot.sh =================================================================== --- tools/boot/install-boot.sh +++ tools/boot/install-boot.sh @@ -1,5 +1,4 @@ #!/bin/sh - # $FreeBSD$ # @@ -27,19 +26,96 @@ gpart show $dev | tail +2 | awk '$4 == "'$part'" { print $3; }' } -make_esp() { +get_uefi_bootname() { + + if [ "$TARGET" == "" ]; then + if [ `uname -m` == amd64 ]; then + efibootfile=BOOTx64.EFI + elif [ `uname -m` == arm64 ]; then + efibootfile=BOOTaa64.EFI + elif [ `uname -m` == i386 ]; then + efibootfile=BOOTia32.EFI + else + efibootfile=BOOTarm.EFI + fi + else + if [ $TARGET == amd64 ]; then + efibootfile=BOOTx64.EFI + elif [ $TARGET == arm64 ]; then + efibootfile=BOOTaa64.EFI + elif [ $TARGET == i386 ]; then + efibootfile=BOOTia32.EFI + else + efibootfile=BOOTarm.EFI + fi + fi +} + +make_esp_file() { + local file sizekb loader mntpt fatbits + + file=$1 + sizekb=$2 + loader=$3 + + if [ $sizekb -ge 33292 ]; then + fatbits=32 + elif [ $sizekb -ge 2100 ]; then + fatbits=16 + else + fatbits=12 + fi + + dd if=/dev/zero of=${file} bs=1k count=${sizekb} + device=$(mdconfig -a -t vnode -f ${file}) + newfs_msdos -F ${fatbits} -c 1 -L EFISYS /dev/${device} + mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) + mount -t msdosfs /dev/${device} ${mntpt} + mkdir -p ${mntpt}/EFI/BOOT + get_uefi_bootname + cp "${loader}" ${mntpt}/EFI/BOOT/${efibootfile} + umount ${mntpt} + rmdir ${mntpt} + mdconfig -d -u ${device} +} + +make_esp_device() { local dev dst mntpt + # ESP device node dev=$1 + # destdir dst=$2 - newfs_msdos -a 32 ${dev} mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) - mount -t msdos ${dev} ${mntpt} - mkdir -p ${mntpt}/efi/boot - cp ${dst}/boot/loader.efi ${mntpt}/efi/boot/bootx64.efi + + mount -t msdosfs ${dev} ${mntpt} + if [ $? -ne 0 ]; then + newfs_msdos -F 32 -c 1 -L EFISYS ${dev} + mount -t msdosfs ${dev} + fi + + mkdir -p ${mntpt}/EFI/freebsd + cp ${dst}/boot/loader.efi ${mntpt}/EFI/freebsd/loader.efi + + efibootmgr --create --label FreeBSD --loader ${mntpt}/freebsd/loader.efi umount ${mntpt} rmdir ${mntpt} + + bootorder=`efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex` + bootentry=`echo ${bootorder} | cut -w -f 3``echo ${bootorder} | cut -w -f 2` + efibootmgr --activate ${bootentry} +} + +make_esp() { + file=$1 + dst=$2 + + if [ -f $file ]; then + make_esp_file ${file} 33292 ${dst}/boot/loader.efi + else + make_esp_device ${file} ${dst}/boot/loader.efi + fi } make_esp_mbr() { @@ -53,7 +129,7 @@ die "No ESP slice found" fi fi - make_esp /dev/${dev}s${s} ${dst} + make_esp /dev/${dev}s${s} ${dst}/boot/loader.efi } make_esp_gpt() { @@ -64,7 +140,7 @@ if [ -z "$idx" ] ; then die "No ESP partition found" fi - make_esp /dev/${dev}p${idx} ${dst} + make_esp /dev/${dev}p${idx} ${dst}/boot/loader.efi } boot_nogeli_gpt_ufs_legacy() { @@ -218,12 +294,24 @@ doit gpart bootcode -p ${vtoc8} ${dev} } +usage() { + printf '%s -b bios [-d destdir] -f fs [-g geli] -h [-o optargs] -s scheme bootdev\n\n' $0 + printf '\t bootdev: device to install the boot code on\n' + printf '\t -b bios bios type: legacy, uefi or both\n' + printf '\t -d destdir destination filesystem root\n' + printf '\t -f fs filesystem type: ufs or zfs\n' + printf '\t -g geli yes or no\n' + printf '\t -h help this help/usage text\n' + printf '\t -o optargs optional arguments\n' + printf '\t -s scheme mbr or gpt\n\n' +} + DESTDIR=/ # Note: we really don't support geli boot in this script yet. geli=nogeli -while getopts "b:d:f:g:o:s:" opt; do +while getopts "b:d:f:g:ho:s:" opt; do case "$opt" in b) bios=${OPTARG} @@ -240,6 +328,10 @@ *) geli=nogeli ;; esac ;; + h) + usage + exit 0 + ;; o) opts=${OPTARG} ;; @@ -249,8 +341,7 @@ esac done -shift $((OPTIND-1)) -dev=$1 +dev=$((OPTIND-1)) # For gpt, we need to install pmbr as the primary boot loader # it knows about @@ -267,4 +358,6 @@ # sanity check here -eval boot_${geli}_${scheme}_${fs}_${bios} $dev $DESTDIR $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}" +if [ "${scheme}" != "" -a "${fs}" != "" -a "${bios}" != "" ]; then + eval boot_${geli}_${scheme}_${fs}_${bios} $dev $DESTDIR $opts || echo "Unsupported boot env: ${geli}-${scheme}-${fs}-${bios}" +fi Index: tools/boot/rootgen.sh =================================================================== --- tools/boot/rootgen.sh +++ tools/boot/rootgen.sh @@ -5,8 +5,6 @@ passphrase=passphrase iterations=50000 -do_boot1_efi=0 - # # Builds all the bat-shit crazy combinations we support booting from, # at least for amd64. It assume you have a ~sane kernel in /boot/kernel @@ -16,13 +14,13 @@ # Sad panda sez: this runs as root, but could be userland if someone # creates userland geli and zfs tools. # -# This assumes an external prograam install-boot.sh which will install +# This assumes an external program install-boot.sh which will install # the appropriate boot files in the appropriate locations. # # These images assume ada0 will be the root image. We should likely # use labels, but we don't. # -# ASsumes you've already rebuilt... maybe bad? Also maybe bad: the env +# Assumes you've already rebuilt... maybe bad? Also maybe bad: the env # vars should likely be conditionally set to allow better automation. # @@ -34,29 +32,6 @@ (cd $src ; tar cf - .) | (cd $dst; tar xf -) } -make_esp() -{ - local src dst md mntpt - src=$1 - dst=$2 - - if [ "${do_boot1_efi}" -eq 1 ]; then - cp ${src}/boot/boot1.efifat ${dst} - else - dd if=/dev/zero of=${dst} count=1 seek=$((100 * 1024 * 1024 / 512)) - md=$(mdconfig -f ${dst}) - newfs_msdos -a 32 /dev/${md} - mntpt=$(mktemp -d /tmp/stand-test.XXXXXX) - mount -t msdos /dev/${md} ${mntpt} -# mkdir -p ${mntpt}/efi/freebsd # not yet - mkdir -p ${mntpt}/efi/boot - cp ${src}/boot/loader.efi ${mntpt}/efi/boot/bootx64.efi - umount ${mntpt} - rmdir ${mntpt} - mdconfig -d -u ${md} - fi -} - mk_nogeli_gpt_ufs_legacy() { src=$1 img=$2 @@ -78,7 +53,8 @@ cat > ${src}/etc/fstab < ${src}/etc/fstab < ${src}/etc/fstab < ${src}/etc/fstab < #include #include +#include #include #include @@ -192,7 +193,9 @@ for (i = 0; i < (int)nitems(items); i++) { if (items[i].state == 0) continue; - if (strcmp(items[i].name, "FAT16") == 0) + if (strcmp(items[i].name, "FAT32") == 0) + strcat(command, "-F 32 -c 1"); + else if (strcmp(items[i].name, "FAT16") == 0) strcat(command, "-F 16 "); else if (strcmp(items[i].name, "FAT12") == 0) strcat(command, "-F 12 "); @@ -400,7 +403,7 @@ TRUE); return; } - + bootsize = lseek(bootfd, 0, SEEK_END); boot = malloc(bootsize); lseek(bootfd, 0, SEEK_SET); @@ -706,8 +709,18 @@ if (strcmp(type, "freebsd-swap") == 0) mountpoint = "none"; if (strcmp(type, bootpart_type(scheme, &default_bootmount)) == 0) { - if (default_bootmount == NULL) + if (default_bootmount == NULL) { + + int fd = open("/tmp/bsdinstall-esps", O_CREAT | O_WRONLY | O_APPEND, + 0600); + if (fd > 0) { + write(fd, md->name, strlen(md->name)); + write(fd, " ", 1); + close(fd); + } + md->bootcode = 1; + } else if (mountpoint == NULL || strlen(mountpoint) == 0) mountpoint = default_bootmount; } Index: usr.sbin/bsdinstall/partedit/partedit_arm64.c =================================================================== --- usr.sbin/bsdinstall/partedit/partedit_arm64.c +++ usr.sbin/bsdinstall/partedit/partedit_arm64.c @@ -35,8 +35,7 @@ #include "partedit.h" /* EFI partition size in bytes */ -#define EFI_BOOTPART_SIZE (200 * 1024 * 1024) -#define EFI_BOOTPART_PATH "/boot/boot1.efifat" +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) const char * default_scheme(void) @@ -95,10 +94,7 @@ partcode_path(const char *part_type, const char *fs_type) { - if (strcmp(part_type, "GPT") == 0) - return (EFI_BOOTPART_PATH); - - /* No boot partition data for non-GPT */ + /* No boot partition data for ARM64 */ return (NULL); } Index: usr.sbin/bsdinstall/partedit/partedit_x86.c =================================================================== --- usr.sbin/bsdinstall/partedit/partedit_x86.c +++ usr.sbin/bsdinstall/partedit/partedit_x86.c @@ -35,8 +35,7 @@ #include "partedit.h" /* EFI partition size in bytes */ -#define EFI_BOOTPART_SIZE (200 * 1024 * 1024) -#define EFI_BOOTPART_PATH "/boot/boot1.efifat" +#define EFI_BOOTPART_SIZE (260 * 1024 * 1024) static const char * x86_bootmethod(void) @@ -141,16 +140,14 @@ partcode_path(const char *part_type, const char *fs_type) { - if (strcmp(part_type, "GPT") == 0) { - if (strcmp(x86_bootmethod(), "UEFI") == 0) - return (EFI_BOOTPART_PATH); - else if (strcmp(fs_type, "zfs") == 0) + if (strcmp(part_type, "GPT") == 0 && strcmp(x86_bootmethod(), "UEFI") != 0) { + if (strcmp(fs_type, "zfs") == 0) return ("/boot/gptzfsboot"); else return ("/boot/gptboot"); } - /* No partcode except for GPT */ + /* No partcode except for non-UEFI GPT */ return (NULL); } Index: usr.sbin/bsdinstall/scripts/bootconfig =================================================================== --- usr.sbin/bsdinstall/scripts/bootconfig +++ usr.sbin/bsdinstall/scripts/bootconfig @@ -1,5 +1,6 @@ #!/bin/sh #- +# Copyright (c) 2018 Rebecca Cran # Copyright (c) 2017 Nathan Whitehorn # All rights reserved. # @@ -35,6 +36,50 @@ fi fi -# For new-style EFI booting, add code here -# Add boot0cfg for MBR BIOS booting? +# Update the ESP (EFI System Partition) with the new bootloader +if [ `uname -m` == amd64 -o `uname -m` == i386 ]; then + X86_BOOTMETHOD=`sysctl -n machdep.bootmethod` +fi + +if [ `uname -m` == arm64 -o "$X86_BOOTMETHOD" == "UEFI" ]; then + mkdir $BSDINSTALL_TMPETC/esp + UFSBOOT_ESPS=`cat /tmp/bsdinstall-esps` + + if [ "$ZFSBOOT_DISKS" ]; then + # We're in a ZFS install environment + for disk in $ZFSBOOT_DISKS; do + index=`gpart show $disk | cut -w -f 4,5 | grep "efi" | cut -w -f 1` + ESPS="$ESPS $diskp$index" + done + fi + + if [ "$UFSBOOT_ESPS" ]; then + # We're in a UFS install environment + for partition in $UFSBOOT_ESPS; do + ESPS="$ESPS $partition" + done + fi + + for esp in $ESPS; do + newfs_msdos -F 32 -c 1 -L EFISYS /dev/$esp + mount -t msdosfs /dev/$esp $BSDINSTALL_TMPETC/esp + mkdir -p $BSDINSTALL_TMPETC/esp/EFI/freebsd + cp $BSDINSTALL_CHROOT/boot/loader.efi $BSDINSTALL_TMPETC/esp/EFI/freebsd/loader.efi + + efibootmgr --create --label FreeBSD --loader $BSDINSTALL_TMPETC/esp/EFI/freebsd/loader.efi + + umount $BSDINSTALL_TMPETC/esp + rmdir $BSDINSTALL_TMPETC/esp + + # When creating new entries, efibootmgr doesn't mark them active, so we need to + # do so. It doesn't make it easy to find which entry it just added, so rely on + # the fact that it places the new entry first in BootOrder. + + bootorder=`efivar --name 8be4df61-93ca-11d2-aa0d-00e098032b8c-BootOrder --print --no-name --hex` + bootentry=`echo $bootorder | cut -w -f 3``echo $bootorder | cut -w -f 2` + efibootmgr --activate $bootentry + done +fi + +# Add boot0cfg for MBR BIOS booting? Index: usr.sbin/bsdinstall/scripts/zfsboot =================================================================== --- usr.sbin/bsdinstall/scripts/zfsboot +++ usr.sbin/bsdinstall/scripts/zfsboot @@ -213,7 +213,6 @@ LN_SF='ln -sf "%s" "%s"' MKDIR_P='mkdir -p "%s"' MOUNT_TYPE='mount -t %s "%s" "%s"' -NEWFS_ESP='newfs_msdos -F %s -L "%s" "%s"' PRINTF_CONF="printf '%s=\"%%s\"\\\n' %s >> \"%s\"" PRINTF_FSTAB='printf "$FSTAB_FMT" "%s" "%s" "%s" "%s" "%s" "%s" >> "%s"' SHELL_TRUNCATE=':> "%s"' @@ -851,29 +850,7 @@ "$align_small" efiboot$index efi 200M \ $disk || return $FAILURE - f_eval_catch $funcname mkdir "$MKDIR_P" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE - f_eval_catch $funcname newfs_msdos "$NEWFS_ESP" "16" \ - "EFISYS" "/dev/${disk}p1" || - return $FAILURE - f_eval_catch $funcname mount "$MOUNT_TYPE" "msdosfs" \ - "/dev/${disk}p1" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE - f_eval_catch $funcname mkdir "$MKDIR_P" \ - "$BSDINSTALL_TMPETC/esp/efi/boot" || - return $FAILURE - f_eval_catch $funcname cp "$COPY" "/boot/loader.efi" \ - "$BSDINSTALL_TMPETC/esp/efi/boot/$ZFSBOOT_ESP_NAME" || - return $FAILURE - f_eval_catch $funcname echo "$ECHO_OVERWRITE" \ - "$ZFSBOOT_ESP_NAME" \ - "$BSDINSTALL_TMPETC/esp/efi/boot/startup.nsh" || - return $FAILURE - f_eval_catch $funcname umount "$UMOUNT" \ - "$BSDINSTALL_TMPETC/esp" || - return $FAILURE + # We'll configure the ESP in bootconfig fi if [ "$ZFSBOOT_BOOT_TYPE" = "BIOS" -o \ @@ -1595,20 +1572,6 @@ ;; esac -# -# The EFI loader installed in the ESP (EFI System Partition) must -# have the expected name in order to load correctly. -# -[ "$ZFSBOOT_ESP_NAME" ] || case "${UNAME_m:-$( uname -m )}" in - arm64) ZFSBOOT_ESP_NAME=BOOTaa64.efi ;; - arm) ZFSBOOT_ESP_NAME=BOOTarm.efi ;; - i386) ZFSBOOT_ESP_NAME=BOOTia32.efi ;; - amd64) ZFSBOOT_ESP_NAME=BOOTx64.efi ;; - *) - f_dprintf "Unsupported architecture: %s" $UNAME_m - f_die -esac - # # Loop over the main menu until we've accomplished what we came here to do #