Index: head/stand/efi/boot1/Makefile =================================================================== --- head/stand/efi/boot1/Makefile (revision 348810) +++ head/stand/efi/boot1/Makefile (revision 348811) @@ -1,119 +1,119 @@ # $FreeBSD$ .include BOOT1?= boot1 PROG= ${BOOT1}.sym INTERNALPROG= -WARNS?= 6 +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 bogus alignment issues CWARNFLAGS.ufs_module.c += -Wno-cast-align # 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 devpath.c +SRCS+= boot1.c proto.c self_reloc.c start.S ufs_module.c devpath.c .if ${MK_LOADER_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} .PATH: ${EFISRC}/libefi CFLAGS+= -I${LDRSRC} FILES= ${BOOT1}.efi FILESMODE_${BOOT1}.efi= ${BINMODE} .ifndef(NOFAT) FILES+= ${BOOT1}.efifat .endif 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} .ifndef(NOFAT) # 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 .endif .include Index: head/stand/efi/boot1/boot1.c =================================================================== --- head/stand/efi/boot1/boot1.c (revision 348810) +++ head/stand/efi/boot1/boot1.c (revision 348811) @@ -1,497 +1,326 @@ /*- * 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" +#include "proto.h" static void efi_panic(EFI_STATUS s, const char *fmt, ...) __dead2 __printflike(2, 3); -static const boot_module_t *boot_modules[] = +const boot_module_t *boot_modules[] = { #ifdef EFI_ZFS_BOOT &zfs_module, #endif #ifdef EFI_UFS_BOOT &ufs_module #endif }; +const UINTN num_boot_modules = nitems(boot_modules); -#define NUM_BOOT_MODULES nitems(boot_modules) - 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 / Calloc 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); } void * Calloc(size_t n1, size_t n2, const char *file, int line) { size_t bytes; void *res; bytes = n1 * n2; if ((res = Malloc(bytes, file, line)) != NULL) bzero(res, bytes); return (res); } /* - * 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, int 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) +EFI_STATUS +try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize) { - size_t bufsize, loadersize, cmdsize; - void *buf, *loaderbuf; + size_t bufsize, cmdsize; + void *buf; 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, 1); - if (status != EFI_SUCCESS) { - status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0); - 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, efi_devpath_last_node(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 0. - */ -static int -probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) -{ - dev_info_t *devinfo; - EFI_BLOCK_IO *blkio; - EFI_DEVICE_PATH *devpath; - EFI_STATUS status; - UINTN i; - int preferred; - - /* Figure out if we're dealing with an actual partition. */ - status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); - if (status == EFI_UNSUPPORTED) - return (0); - - if (status != EFI_SUCCESS) { - DPRINTF("\nFailed to query DevicePath (%lu)\n", - EFI_ERROR_CODE(status)); - return (-1); - } -#ifdef EFI_DEBUG - { - CHAR16 *text = efi_devpath_name(devpath); - DPRINTF("probing: %S ", text); - efi_free_devpath_name(text); - } -#endif - status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); - if (status == EFI_UNSUPPORTED) - return (0); - - if (status != EFI_SUCCESS) { - DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", - EFI_ERROR_CODE(status)); - return (-1); - } - - if (!blkio->Media->LogicalPartition) - return (0); - - preferred = efi_devpath_same_disk(imgpath, devpath); - - /* Run through each module, see if it can load this partition */ - devinfo = malloc(sizeof(*devinfo)); - if (devinfo == NULL) { - DPRINTF("\nFailed to allocate devinfo\n"); - return (-1); - } - devinfo->dev = blkio; - devinfo->devpath = devpath; - devinfo->devhandle = h; - devinfo->preferred = preferred; - devinfo->next = NULL; - - for (i = 0; i < NUM_BOOT_MODULES; i++) { - devinfo->devdata = NULL; - - status = boot_modules[i]->probe(devinfo); - if (status == EFI_SUCCESS) - return (preferred + 1); - } - free(devinfo); - - return (0); -} - -const char *prio_str[] = { - "error", - "not supported", - "good", - "better" -}; - 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]; - int rv; /* 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++) { + 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'); + /* Fetch all the block I/O handles, we have to search through them later */ + hsize = 0; + BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, + &hsize, NULL); + handles = malloc(hsize); + if (handles == NULL) + efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", + hsize); + status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, + NULL, &hsize, handles); + if (status != EFI_SUCCESS) + efi_panic(status, "Failed to get device handles\n"); + nhandles = hsize / sizeof(*handles); + /* 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 - - hsize = 0; - BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, - &hsize, NULL); - handles = malloc(hsize); - if (handles == NULL) - efi_panic(EFI_OUT_OF_RESOURCES, "Failed to allocate %d handles\n", - hsize); - status = BS->LocateHandle(ByProtocol, &BlockIoProtocolGUID, - NULL, &hsize, handles); - if (status != EFI_SUCCESS) - efi_panic(status, "Failed to get device handles\n"); - - /* 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++) { - rv = probe_handle(handles[i], imgpath); -#ifdef EFI_DEBUG - printf("%c", "x.+*"[rv + 1]); -#else - printf("%s\n", prio_str[rv + 1]); -#endif - } - printf(" done\n"); - - /* Status summary. */ - for (i = 0; i < NUM_BOOT_MODULES; i++) { - printf(" "); - boot_modules[i]->status(); - } - - try_boot(); + choice_protocol(handles, nhandles, imgpath); /* 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: head/stand/efi/boot1/boot_module.h =================================================================== --- head/stand/efi/boot1/boot_module.h (revision 348810) +++ head/stand/efi/boot1/boot_module.h (revision 348811) @@ -1,110 +1,113 @@ /*- * 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; +extern const boot_module_t *boot_modules[]; +extern const UINTN num_boot_modules; + /* 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: head/stand/efi/boot1/proto.c =================================================================== --- head/stand/efi/boot1/proto.c (nonexistent) +++ head/stand/efi/boot1/proto.c (revision 348811) @@ -0,0 +1,223 @@ +/*- + * 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" +#include "proto.h" + +static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; +static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; + +static const char *prio_str[] = { + "error", + "not supported", + "good", + "better" +}; + +/* + * 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 int +probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) +{ + dev_info_t *devinfo; + EFI_BLOCK_IO *blkio; + EFI_DEVICE_PATH *devpath; + EFI_STATUS status; + UINTN i; + int preferred; + + /* Figure out if we're dealing with an actual partition. */ + status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); + if (status == EFI_UNSUPPORTED) + return (0); + + if (status != EFI_SUCCESS) { + DPRINTF("\nFailed to query DevicePath (%lu)\n", + EFI_ERROR_CODE(status)); + return (-1); + } +#ifdef EFI_DEBUG + { + CHAR16 *text = efi_devpath_name(devpath); + DPRINTF("probing: %S ", text); + efi_free_devpath_name(text); + } +#endif + status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); + if (status == EFI_UNSUPPORTED) + return (0); + + if (status != EFI_SUCCESS) { + DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", + EFI_ERROR_CODE(status)); + return (-1); + } + + if (!blkio->Media->LogicalPartition) + return (0); + + preferred = efi_devpath_same_disk(imgpath, devpath); + + /* Run through each module, see if it can load this partition */ + devinfo = malloc(sizeof(*devinfo)); + if (devinfo == NULL) { + DPRINTF("\nFailed to allocate devinfo\n"); + return (-1); + } + devinfo->dev = blkio; + devinfo->devpath = devpath; + devinfo->devhandle = h; + devinfo->preferred = preferred; + devinfo->next = NULL; + + for (i = 0; i < num_boot_modules; i++) { + devinfo->devdata = NULL; + + status = boot_modules[i]->probe(devinfo); + if (status == EFI_SUCCESS) + return (preferred + 1); + } + free(devinfo); + + return (0); +} + +/* + * 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, int 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); +} + +void +choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) +{ + UINT16 boot_current; + size_t sz; + UINT16 boot_order[100]; + unsigned i; + int rv; + EFI_STATUS status; + const boot_module_t *mod; + dev_info_t *dev; + void *loaderbuf; + size_t loadersize; + + /* Report UEFI Boot Manager Protocol details */ + 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 + + /* Scan all partitions, probing with all modules. */ + printf(" Probing %zu block devices...", nhandles); + DPRINTF("\n"); + for (i = 0; i < nhandles; i++) { + rv = probe_handle(handles[i], imgpath); +#ifdef EFI_DEBUG + printf("%c", "x.+*"[rv + 1]); +#else + printf("%s\n", prio_str[rv + 1]); +#endif + } + printf(" done\n"); + + + /* Status summary. */ + for (i = 0; i < num_boot_modules; i++) { + printf(" "); + boot_modules[i]->status(); + } + + status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 1); + if (status != EFI_SUCCESS) { + status = load_loader(&mod, &dev, &loaderbuf, &loadersize, 0); + if (status != EFI_SUCCESS) { + printf("Failed to load '%s'\n", PATH_LOADER_EFI); + return; + } + } + + try_boot(mod, dev, loaderbuf, loadersize); +} Property changes on: head/stand/efi/boot1/proto.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/stand/efi/boot1/proto.h =================================================================== --- head/stand/efi/boot1/proto.h (nonexistent) +++ head/stand/efi/boot1/proto.h (revision 348811) @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2019 Netflix, Inc + * + * 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$ + */ + +void choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath); +EFI_STATUS try_boot(const boot_module_t *mod, dev_info_t *dev, void *loaderbuf, size_t loadersize); Property changes on: head/stand/efi/boot1/proto.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property