Index: stable/12/stand/efi/Makefile =================================================================== --- stable/12/stand/efi/Makefile (revision 353987) +++ stable/12/stand/efi/Makefile (revision 353988) @@ -1,19 +1,19 @@ # $FreeBSD$ NO_OBJ=t .include # In-tree GCC does not support __attribute__((ms_abi)), but gcc newer # than 4.5 supports it. .if ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500 SUBDIR.${MK_FDT}+= fdt -SUBDIR.yes+= libefi boot1 +SUBDIR.yes+= libefi boot1 gptboot SUBDIR.${MK_FORTH}+= loader_4th SUBDIR.${MK_LOADER_LUA}+= loader_lua SUBDIR.yes+= loader_simp .endif # ${COMPILER_TYPE} != "gcc" || ${COMPILER_VERSION} >= 40500 .include Index: stable/12/stand/efi/boot1/Makefile =================================================================== --- stable/12/stand/efi/boot1/Makefile (revision 353987) +++ stable/12/stand/efi/boot1/Makefile (revision 353988) @@ -1,113 +1,118 @@ # $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.proto.c+= -Wno-format CWARNFLAGS.boot1.c+= -Wno-format +# Disable bogus alignment issues +CWARNFLAGS.ufs_module.c += -Wno-format +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.zfs_module.c+= -I${SYSDIR}/cddl/contrib/opensolaris/uts/common 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 ${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: stable/12/stand/efi/boot1/boot1.c =================================================================== --- stable/12/stand/efi/boot1/boot1.c (revision 353987) +++ stable/12/stand/efi/boot1/boot1.c (revision 353988) @@ -1,519 +1,327 @@ /*- * 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, 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) +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, 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, 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; } status = OpenProtocolByHandle(loaderhandle, &LoadedImageGUID, (void **)&loaded_image); if (status != 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 = OpenProtocolByHandle(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 = OpenProtocolByHandle(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 = efi_devpath_match(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 (EFI_UNSUPPORTED); - } - 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 (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++) { + 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 = OpenProtocolByHandle(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 = OpenProtocolByHandle(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++) - 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(); + 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: stable/12/stand/efi/boot1/boot_module.h =================================================================== --- stable/12/stand/efi/boot1/boot_module.h (revision 353987) +++ stable/12/stand/efi/boot1/boot_module.h (revision 353988) @@ -1,110 +1,112 @@ /*- * 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: stable/12/stand/efi/boot1/proto.c =================================================================== --- stable/12/stand/efi/boot1/proto.c (nonexistent) +++ stable/12/stand/efi/boot1/proto.c (revision 353988) @@ -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: stable/12/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: stable/12/stand/efi/boot1/proto.h =================================================================== --- stable/12/stand/efi/boot1/proto.h (nonexistent) +++ stable/12/stand/efi/boot1/proto.h (revision 353988) @@ -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: stable/12/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 Index: stable/12/stand/efi/boot1/ufs_module.c =================================================================== --- stable/12/stand/efi/boot1/ufs_module.c (revision 353987) +++ stable/12/stand/efi/boot1/ufs_module.c (revision 353988) @@ -1,229 +1,228 @@ /*- * 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 reverved. * * 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 "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("UFS 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); + buf = malloc(size); + if (buf == NULL) { + printf("Failed to allocate read buffer %zu for '%s'\n", + size, filepath); + return (EFI_OUT_OF_RESOURCES); } 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); + free(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: stable/12/stand/efi/boot1/zfs_module.c =================================================================== --- stable/12/stand/efi/boot1/zfs_module.c (revision 353987) +++ stable/12/stand/efi/boot1/zfs_module.c (revision 353988) @@ -1,248 +1,245 @@ /*- * 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); + tdev = malloc(sizeof(*dev)); + if (tdev == NULL) { + DPRINTF("Failed to allocate tdev\n"); + return (EFI_OUT_OF_RESOURCES); } memcpy(tdev, dev, sizeof(*dev)); if (vdev_probe(vdev_read, tdev, &spa) != 0) { - (void)BS->FreePool(tdev); + free(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; + struct zfsmount zmount; 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) { + if ((err = zfs_mount(spa, 0, &zmount)) != 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 = zfs_lookup(&zmount, 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)); + buf = malloc(st.st_size); + if (buf == NULL) { + printf("Failed to allocate load buffer %jd for pool '%s' for '%s' ", + (intmax_t)st.st_size, spa->spa_name, filepath); 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); + free(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: stable/12/stand/efi/gptboot/Makefile =================================================================== --- stable/12/stand/efi/gptboot/Makefile (nonexistent) +++ stable/12/stand/efi/gptboot/Makefile (revision 353988) @@ -0,0 +1,17 @@ +# $FreeBSD$ + +# ZFS is not supported, we want debugging until this is vetted and +# we don't want the gptboot.efifat thing created. +MK_LOADER_ZFS=no +EFI_DEBUG=yes +NOFAT=yes + +BOOT1?= gptboot +.PATH: ${SRCTOP}/stand/efi/boot1 ${SRCTOP}/stand/libsa +CFLAGS+= -I${SRCTOP}/stand/efi/boot1 +CFLAGS+= -I${.CURDIR} +CFLAGS+= -DBOOTPROG=\"gptboot.efi\" +SRCS+= gpt.c +CWARNFLAGS.gpt.c+= -Wno-sign-compare -Wno-cast-align +WARNS=6 +.include "${.CURDIR}/../boot1/Makefile" Property changes on: stable/12/stand/efi/gptboot/Makefile ___________________________________________________________________ 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: stable/12/stand/efi/gptboot/drv.h =================================================================== --- stable/12/stand/efi/gptboot/drv.h (nonexistent) +++ stable/12/stand/efi/gptboot/drv.h (revision 353988) @@ -0,0 +1,41 @@ +/*- + * 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$ + */ + +#ifndef _DRV_H_ +#define _DRV_H_ + +struct dsk { + int part; + daddr_t start; + void *devinfo; /* Really a dev_into_t *, but that's not in scope */ +}; + +int drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); +int drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk); +uint64_t drvsize(struct dsk *dskp); + +#endif /* !_DRV_H_ */ Property changes on: stable/12/stand/efi/gptboot/drv.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 Index: stable/12/stand/efi/gptboot/proto.c =================================================================== --- stable/12/stand/efi/gptboot/proto.c (nonexistent) +++ stable/12/stand/efi/gptboot/proto.c (revision 353988) @@ -0,0 +1,279 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include + +#include "boot_module.h" +#include "paths.h" +#include "proto.h" + +#include "gpt.h" +#include +static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; +static char secbuf[4096]; +static struct dsk dsk; +static dev_info_t *devices = NULL; +static dev_info_t *raw_device = NULL; + +static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL; +static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; + +/* + * Shim routine for the gpt code to read in the gpt table. The + * devinfo is always going to be for the raw device. + */ +int +drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) +{ + int size; + EFI_STATUS status; + dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; + EFI_BLOCK_IO *dev = devinfo->dev; + + lba = lba / (dev->Media->BlockSize / DEV_BSIZE); + size = nblk * DEV_BSIZE; + + status = dev->ReadBlocks(dev, 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, + dev->Media->MediaId, (uintmax_t)lba, size, + EFI_ERROR_CODE(status)); + return (-1); + } + + return (0); +} + +/* + * Shim routine for the gpt code to write in the gpt table. The + * devinfo is always going to be for the raw device. + */ +int +drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk) +{ + int size; + EFI_STATUS status; + dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; + EFI_BLOCK_IO *dev = devinfo->dev; + + if (dev->Media->ReadOnly) + return -1; + + lba = lba / (dev->Media->BlockSize / DEV_BSIZE); + size = nblk * DEV_BSIZE; + + status = dev->WriteBlocks(dev, 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, + dev->Media->MediaId, (uintmax_t)lba, size, + EFI_ERROR_CODE(status)); + return (-1); + } + + return (0); +} + +/* + * Return the number of LBAs the drive has. + */ +uint64_t +drvsize(struct dsk *dskp) +{ + dev_info_t *devinfo = (dev_info_t *)dskp->devinfo; + EFI_BLOCK_IO *dev = devinfo->dev; + + return (dev->Media->LastBlock + 1); +} + +static int +partition_number(EFI_DEVICE_PATH *devpath) +{ + EFI_DEVICE_PATH *md; + HARDDRIVE_DEVICE_PATH *hd; + + md = efi_devpath_last_node(devpath); + if (md == NULL) + return (-1); + if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP) + return (-1); + hd = (HARDDRIVE_DEVICE_PATH *)md; + return (hd->PartitionNumber); +} + +/* + * Find the raw partition for the imgpath and save it + */ +static void +probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath) +{ + dev_info_t *devinfo; + EFI_BLOCK_IO *blkio; + EFI_DEVICE_PATH *devpath, *trimmed = NULL; + EFI_STATUS status; + + /* Figure out if we're dealing with an actual partition. */ + status = BS->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); + if (status != EFI_SUCCESS) + return; +#ifdef EFI_DEBUG + { + CHAR16 *text = efi_devpath_name(devpath); + DPRINTF("probing: %S ", text); + efi_free_devpath_name(text); + } +#endif + /* + * The RAW device is the same as the imgpath with the last + * MEDIA_DEVICE bit trimmed off. imgpath will end with the + * MEDIA_DEVICE for the ESP we booted off of. + */ + if (!efi_devpath_same_disk(imgpath, devpath)) { + trimmed = efi_devpath_trim(imgpath); + if (!efi_devpath_match(trimmed, devpath)) { + free(trimmed); + DPRINTF("Not the same disk\n"); + return; + } + } + status = BS->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); + if (status != EFI_SUCCESS) { + DPRINTF("Can't get the block I/O protocol block\n"); + return; + } + devinfo = malloc(sizeof(*devinfo)); + if (devinfo == NULL) { + DPRINTF("Failed to allocate devinfo\n"); + return; + } + devinfo->dev = blkio; + devinfo->devpath = devpath; + devinfo->devhandle = h; + devinfo->preferred = 1; + devinfo->next = NULL; + devinfo->devdata = NULL; + if (trimmed == NULL) { + DPRINTF("Found partition %d\n", partition_number(devpath)); + add_device(&devices, devinfo); + } else { + free(trimmed); + DPRINTF("Found raw device\n"); + if (raw_device) { + printf(BOOTPROG": Found two raw devices, inconceivable?\n"); + return; + } + raw_device = devinfo; + } +} + +static void +probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) +{ + UINTN i; + + for (i = 0; i < nhandles; i++) + probe_handle(handles[i], imgpath); +} + +static dev_info_t * +find_partition(int part) +{ + dev_info_t *dev; + + if (part == 0) + return (NULL); + for (dev = devices; dev != NULL; dev = dev->next) + if (partition_number(dev->devpath) == part) + break; + return (dev); +} + +void +choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath) +{ + const boot_module_t *mod = &ufs_module; + dev_info_t *bootdev; + void *loaderbuf; + size_t loadersize; + int parts; + const char *fn = PATH_LOADER_EFI; + + /* + * Probe the provided handles to find the partitions that + * are on the same drive. + */ + probe_handles(handles, nhandles, imgpath); + dsk.devinfo = raw_device; + if (dsk.devinfo == NULL) { + printf(BOOTPROG": unable to find raw disk to read gpt\n"); + return; + } + + /* + * Read in the GPT table, and then find the right partition. + * gptread, gptfind and gptfaileboot are shared with the + * BIOS version of the gptboot program. + */ + if (gptread(&dsk, secbuf) == -1) { + printf(BOOTPROG ": unable to load GPT\n"); + return; + } + // XXX: + // real gptboot can parse a command line before trying this loop. + // But since we don't parse anything at all, hard wire the partition + // to be -1 (meaning look for the next one). + parts = 0; + while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) { + parts++; + bootdev = find_partition(dsk.part); + if (bootdev == NULL) { + printf(BOOTPROG": Can't find partition %d\n", + dsk.part); + goto next; + } + if (mod->load(fn, bootdev, &loaderbuf, &loadersize) != + EFI_SUCCESS) { + printf(BOOTPROG": Can't load %s from partition %d\n", + fn, dsk.part); + goto next; + } + try_boot(mod, bootdev, loaderbuf, loadersize); +next: + gptbootfailed(&dsk); + } + if (parts == 0) + printf("%s: no UFS partition was found\n", BOOTPROG); +} Property changes on: stable/12/stand/efi/gptboot/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: stable/12/stand/efi/include/efilib.h =================================================================== --- stable/12/stand/efi/include/efilib.h (revision 353987) +++ stable/12/stand/efi/include/efilib.h (revision 353988) @@ -1,147 +1,148 @@ /*- * Copyright (c) 2000 Doug Rabson * Copyright (c) 2006 Marcel Moolenaar * 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 _LOADER_EFILIB_H #define _LOADER_EFILIB_H #include #include #include extern EFI_HANDLE IH; extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RS; extern struct devsw efipart_fddev; extern struct devsw efipart_cddev; extern struct devsw efipart_hddev; extern struct devsw efinet_dev; extern struct netif_driver efinetif; /* EFI block device data, included here to help efi_zfs_probe() */ typedef STAILQ_HEAD(pdinfo_list, pdinfo) pdinfo_list_t; typedef struct pdinfo { STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */ pdinfo_list_t pd_part; /* list of partitions */ EFI_HANDLE pd_handle; EFI_HANDLE pd_alias; EFI_DEVICE_PATH *pd_devpath; EFI_BLOCK_IO *pd_blkio; uint32_t pd_unit; /* unit number */ uint32_t pd_open; /* reference counter */ void *pd_bcache; /* buffer cache data */ struct pdinfo *pd_parent; /* Linked items (eg partitions) */ struct devsw *pd_devsw; /* Back pointer to devsw */ } pdinfo_t; pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev); pdinfo_t *efiblk_get_pdinfo(struct devdesc *dev); pdinfo_t *efiblk_get_pdinfo_by_handle(EFI_HANDLE h); pdinfo_t *efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path); void *efi_get_table(EFI_GUID *tbl); EFI_STATUS OpenProtocolByHandle(EFI_HANDLE, EFI_GUID *, void **); int efi_getdev(void **vdev, const char *devspec, const char **path); char *efi_fmtdev(void *vdev); int efi_setcurrdev(struct env_var *ev, int flags, const void *value); int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int); EFI_HANDLE efi_find_handle(struct devsw *, int); int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE); EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE); EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *); bool efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_match_node(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_is_prefix(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *); void efi_free_devpath_name(CHAR16 *); +bool efi_devpath_same_disk(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_to_media_path(EFI_DEVICE_PATH *); UINTN efi_devpath_length(EFI_DEVICE_PATH *); EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles); EFI_DEVICE_PATH *efi_name_to_devpath(const char *path); EFI_DEVICE_PATH *efi_name_to_devpath16(CHAR16 *path); void efi_devpath_free(EFI_DEVICE_PATH *dp); int efi_status_to_errno(EFI_STATUS); EFI_STATUS errno_to_efi_status(int errno); void efi_time_init(void); void efi_time_fini(void); EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); EFI_STATUS main(int argc, CHAR16 *argv[]); void efi_exit(EFI_STATUS status) __dead2; void delay(int usecs); /* EFI environment initialization. */ void efi_init_environment(void); /* EFI Memory type strings. */ const char *efi_memory_type(EFI_MEMORY_TYPE); /* CHAR16 utility functions. */ int wcscmp(CHAR16 *, CHAR16 *); void cpy8to16(const char *, CHAR16 *, size_t); void cpy16to8(const CHAR16 *, char *, size_t); /* * Routines for interacting with EFI's env vars in a more unix-like * way than the standard APIs. In addition, convenience routines for * the loader setting / getting FreeBSD specific variables. */ EFI_STATUS efi_delenv(EFI_GUID *guid, const char *varname); EFI_STATUS efi_freebsd_delenv(const char *varname); EFI_STATUS efi_freebsd_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_getenv(EFI_GUID *g, const char *v, void *data, __size_t *len); EFI_STATUS efi_global_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_setenv(EFI_GUID *guid, const char *varname, UINT32 attr, void *data, __size_t len); EFI_STATUS efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr); /* guids and names */ bool efi_guid_to_str(const EFI_GUID *, char **); bool efi_str_to_guid(const char *, EFI_GUID *); bool efi_name_to_guid(const char *, EFI_GUID *); bool efi_guid_to_name(EFI_GUID *, char **); /* efipart.c */ int efipart_inithandles(void); #endif /* _LOADER_EFILIB_H */ Index: stable/12/stand/efi/libefi/devpath.c =================================================================== --- stable/12/stand/efi/libefi/devpath.c (revision 353987) +++ stable/12/stand/efi/libefi/devpath.c (revision 353988) @@ -1,295 +1,319 @@ /*- * Copyright (c) 2016 John Baldwin * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include static EFI_GUID ImageDevicePathGUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; EFI_DEVICE_PATH * efi_lookup_image_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &ImageDevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } EFI_DEVICE_PATH * efi_lookup_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &DevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } CHAR16 * efi_devpath_name(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; if (devpath == NULL) return (NULL); if (toTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, (VOID **)&toTextProtocol); if (EFI_ERROR(status)) toTextProtocol = NULL; } if (toTextProtocol == NULL) return (NULL); return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); } void efi_free_devpath_name(CHAR16 *text) { BS->FreePool(text); } EFI_DEVICE_PATH * efi_name_to_devpath(const char *path) { EFI_DEVICE_PATH *devpath; CHAR16 *uv; size_t ul; uv = NULL; if (utf8_to_ucs2(path, &uv, &ul) != 0) return (NULL); devpath = efi_name_to_devpath16(uv); free(uv); return (devpath); } EFI_DEVICE_PATH * efi_name_to_devpath16(CHAR16 *path) { EFI_STATUS status; if (path == NULL) return (NULL); if (fromTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, (VOID **)&fromTextProtocol); if (EFI_ERROR(status)) fromTextProtocol = NULL; } if (fromTextProtocol == NULL) return (NULL); return (fromTextProtocol->ConvertTextToDevicePath(path)); } void efi_devpath_free(EFI_DEVICE_PATH *devpath) { BS->FreePool(devpath); } EFI_DEVICE_PATH * efi_devpath_last_node(EFI_DEVICE_PATH *devpath) { if (IsDevicePathEnd(devpath)) return (NULL); while (!IsDevicePathEnd(NextDevicePathNode(devpath))) devpath = NextDevicePathNode(devpath); return (devpath); } EFI_DEVICE_PATH * efi_devpath_trim(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *node, *copy; size_t prefix, len; if ((node = efi_devpath_last_node(devpath)) == NULL) return (NULL); prefix = (UINT8 *)node - (UINT8 *)devpath; if (prefix == 0) return (NULL); len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); copy = malloc(len); if (copy != NULL) { memcpy(copy, devpath, prefix); node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); SetDevicePathEndNode(node); } return (copy); } EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; EFI_HANDLE h; /* * There isn't a standard way to locate a handle for a given * device path. However, querying the EFI_DEVICE_PATH protocol * for a given device path should give us a handle for the * closest node in the path to the end that is valid. */ status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); if (EFI_ERROR(status)) return (NULL); return (h); } bool efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { size_t len; if (devpath1 == NULL || devpath2 == NULL) return (false); if (DevicePathType(devpath1) != DevicePathType(devpath2) || DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) return (false); len = DevicePathNodeLength(devpath1); if (len != DevicePathNodeLength(devpath2)) return (false); if (memcmp(devpath1, devpath2, len) != 0) return (false); return (true); } -bool -efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) +static bool +_efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, + bool ignore_media) { if (devpath1 == NULL || devpath2 == NULL) return (false); while (true) { + if (ignore_media && + IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && + IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) + return (true); if (!efi_devpath_match_node(devpath1, devpath2)) return false; if (IsDevicePathEnd(devpath1)) break; devpath1 = NextDevicePathNode(devpath1); devpath2 = NextDevicePathNode(devpath2); } return (true); +} +/* + * Are two devpaths identical? + */ +bool +efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) +{ + return _efi_devpath_match(devpath1, devpath2, false); +} + +/* + * Like efi_devpath_match, but stops at when we hit the media device + * path node that specifies the partition information. If we match + * up to that point, then we're on the same disk. + */ +bool +efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) +{ + return _efi_devpath_match(devpath1, devpath2, true); } bool efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) { size_t len; if (prefix == NULL || path == NULL) return (false); while (1) { if (IsDevicePathEnd(prefix)) break; if (DevicePathType(prefix) != DevicePathType(path) || DevicePathSubType(prefix) != DevicePathSubType(path)) return (false); len = DevicePathNodeLength(prefix); if (len != DevicePathNodeLength(path)) return (false); if (memcmp(prefix, path, len) != 0) return (false); prefix = NextDevicePathNode(prefix); path = NextDevicePathNode(path); } return (true); } /* * Skip over the 'prefix' part of path and return the part of the path * that starts with the first node that's a MEDIA_DEVICE_PATH. */ EFI_DEVICE_PATH * efi_devpath_to_media_path(EFI_DEVICE_PATH *path) { while (!IsDevicePathEnd(path)) { if (DevicePathType(path) == MEDIA_DEVICE_PATH) return (path); path = NextDevicePathNode(path); } return (NULL); } UINTN efi_devpath_length(EFI_DEVICE_PATH *path) { EFI_DEVICE_PATH *start = path; while (!IsDevicePathEnd(path)) path = NextDevicePathNode(path); return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); } EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) { unsigned i; EFI_DEVICE_PATH *media, *devpath; EFI_HANDLE h; media = efi_devpath_to_media_path(path); if (media == NULL) return (NULL); for (i = 0; i < nhandles; i++) { h = handles[i]; devpath = efi_lookup_devpath(h); if (devpath == NULL) continue; if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) continue; return (h); } return (NULL); } Index: stable/12/stand/i386/gptboot/gptboot.c =================================================================== --- stable/12/stand/i386/gptboot/gptboot.c (revision 353987) +++ stable/12/stand/i386/gptboot/gptboot.c (revision 353988) @@ -1,656 +1,656 @@ /*- * Copyright (c) 1998 Robert Nordier * 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 #include #include #include #include "stand.h" #include "bootargs.h" #include "lib.h" #include "rbx.h" #include "drv.h" #include "cons.h" #include "gpt.h" #include "paths.h" #define ARGS 0x900 #define NOPT 14 #define NDEV 3 #define MEM_BASE 0x12 #define MEM_EXT 0x15 #define DRV_HARD 0x80 #define DRV_MASK 0x7f #define TYPE_AD 0 #define TYPE_DA 1 #define TYPE_MAXHARD TYPE_DA #define TYPE_FD 2 extern uint32_t _end; static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS; static const char optstr[NOPT] = "DhaCcdgmnpqrsv"; /* Also 'P', 'S' */ static const unsigned char flags[NOPT] = { RBX_DUAL, RBX_SERIAL, RBX_ASKNAME, RBX_CDROM, RBX_CONFIG, RBX_KDB, RBX_GDB, RBX_MUTE, RBX_NOINTR, RBX_PAUSE, RBX_QUIET, RBX_DFLTROOT, RBX_SINGLE, RBX_VERBOSE }; uint32_t opts; static const char *const dev_nm[NDEV] = {"ad", "da", "fd"}; static const unsigned char dev_maj[NDEV] = {30, 4, 2}; static char kname[1024]; static int comspeed = SIOSPD; static struct bootinfo bootinfo; #ifdef LOADER_GELI_SUPPORT static struct geli_boot_args geliargs; #endif static vm_offset_t high_heap_base; static uint32_t bios_basemem, bios_extmem, high_heap_size; static struct bios_smap smap; /* * The minimum amount of memory to reserve in bios_extmem for the heap. */ #define HEAP_MIN (3 * 1024 * 1024) static char *heap_next; static char *heap_end; static void load(void); static int parse_cmds(char *, int *); static int dskread(void *, daddr_t, unsigned); #ifdef LOADER_GELI_SUPPORT static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes); #endif #include "ufsread.c" #include "gpt.c" #ifdef LOADER_GELI_SUPPORT #include "geliboot.h" static char gelipw[GELI_PW_MAXLEN]; #endif struct gptdsk { struct dsk dsk; #ifdef LOADER_GELI_SUPPORT struct geli_dev *gdev; #endif }; static struct gptdsk gdsk; static inline int xfsread(ufs_ino_t inode, void *buf, size_t nbyte) { if ((size_t)fsread(inode, buf, nbyte) != nbyte) { printf("Invalid %s\n", "format"); return (-1); } return (0); } static void bios_getmem(void) { uint64_t size; /* Parse system memory map */ v86.ebx = 0; do { v86.ctl = V86_FLAGS; v86.addr = MEM_EXT; /* int 0x15 function 0xe820*/ v86.eax = 0xe820; v86.ecx = sizeof(struct bios_smap); v86.edx = SMAP_SIG; v86.es = VTOPSEG(&smap); v86.edi = VTOPOFF(&smap); v86int(); if ((v86.efl & 1) || (v86.eax != SMAP_SIG)) break; /* look for a low-memory segment that's large enough */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0) && (smap.length >= (512 * 1024))) bios_basemem = smap.length; /* look for the first segment in 'extended' memory */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base == 0x100000)) { bios_extmem = smap.length; } /* * Look for the largest segment in 'extended' memory beyond * 1MB but below 4GB. */ if ((smap.type == SMAP_TYPE_MEMORY) && (smap.base > 0x100000) && (smap.base < 0x100000000ull)) { size = smap.length; /* * If this segment crosses the 4GB boundary, * truncate it. */ if (smap.base + size > 0x100000000ull) size = 0x100000000ull - smap.base; if (size > high_heap_size) { high_heap_size = size; high_heap_base = smap.base; } } } while (v86.ebx != 0); /* Fall back to the old compatibility function for base memory */ if (bios_basemem == 0) { v86.ctl = 0; v86.addr = 0x12; /* int 0x12 */ v86int(); bios_basemem = (v86.eax & 0xffff) * 1024; } /* * Fall back through several compatibility functions for extended * memory */ if (bios_extmem == 0) { v86.ctl = V86_FLAGS; v86.addr = 0x15; /* int 0x15 function 0xe801*/ v86.eax = 0xe801; v86int(); if (!(v86.efl & 1)) { bios_extmem = ((v86.ecx & 0xffff) + ((v86.edx & 0xffff) * 64)) * 1024; } } if (bios_extmem == 0) { v86.ctl = 0; v86.addr = 0x15; /* int 0x15 function 0x88*/ v86.eax = 0x8800; v86int(); bios_extmem = (v86.eax & 0xffff) * 1024; } /* * If we have extended memory and did not find a suitable heap * region in the SMAP, use the last 3MB of 'extended' memory as a * high heap candidate. */ if (bios_extmem >= HEAP_MIN && high_heap_size < HEAP_MIN) { high_heap_size = HEAP_MIN; high_heap_base = bios_extmem + 0x100000 - HEAP_MIN; } } static int gptinit(void) { - if (gptread(&freebsd_ufs_uuid, &gdsk.dsk, dmadat->secbuf) == -1) { + if (gptread(&gdsk.dsk, dmadat->secbuf) == -1) { printf("%s: unable to load GPT\n", BOOTPROG); return (-1); } if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, gdsk.dsk.part) == -1) { printf("%s: no UFS partition was found\n", BOOTPROG); return (-1); } #ifdef LOADER_GELI_SUPPORT gdsk.gdev = geli_taste(vdev_read, &gdsk.dsk, (gpttable[curent].ent_lba_end - gpttable[curent].ent_lba_start), "disk%up%u:", gdsk.dsk.unit, curent + 1); if (gdsk.gdev != NULL) { if (geli_havekey(gdsk.gdev) != 0 && geli_passphrase(gdsk.gdev, gelipw) != 0) { printf("%s: unable to decrypt GELI key\n", BOOTPROG); return (-1); } } #endif dsk_meta = 0; return (0); } int main(void); int main(void) { char cmd[512], cmdtmp[512]; ssize_t sz; int autoboot, dskupdated; ufs_ino_t ino; dmadat = (void *)(roundup2(__base + (int32_t)&_end, 0x10000) - __base); bios_getmem(); if (high_heap_size > 0) { heap_end = PTOV(high_heap_base + high_heap_size); heap_next = PTOV(high_heap_base); } else { heap_next = (char *)dmadat + sizeof(*dmadat); heap_end = (char *)PTOV(bios_basemem); } setheap(heap_next, heap_end); v86.ctl = V86_FLAGS; v86.efl = PSL_RESERVED_DEFAULT | PSL_I; gdsk.dsk.drive = *(uint8_t *)PTOV(ARGS); gdsk.dsk.type = gdsk.dsk.drive & DRV_HARD ? TYPE_AD : TYPE_FD; gdsk.dsk.unit = gdsk.dsk.drive & DRV_MASK; gdsk.dsk.part = -1; gdsk.dsk.start = 0; bootinfo.bi_version = BOOTINFO_VERSION; bootinfo.bi_size = sizeof(bootinfo); bootinfo.bi_basemem = bios_basemem / 1024; bootinfo.bi_extmem = bios_extmem / 1024; bootinfo.bi_memsizes_valid++; bootinfo.bi_bios_dev = gdsk.dsk.drive; /* Process configuration file */ if (gptinit() != 0) return (-1); autoboot = 1; *cmd = '\0'; for (;;) { *kname = '\0'; if ((ino = lookup(PATH_CONFIG)) || (ino = lookup(PATH_DOTCONFIG))) { sz = fsread(ino, cmd, sizeof(cmd) - 1); cmd[(sz < 0) ? 0 : sz] = '\0'; } if (*cmd != '\0') { memcpy(cmdtmp, cmd, sizeof(cmdtmp)); if (parse_cmds(cmdtmp, &dskupdated)) break; if (dskupdated && gptinit() != 0) break; if (!OPT_CHECK(RBX_QUIET)) printf("%s: %s", PATH_CONFIG, cmd); *cmd = '\0'; } if (autoboot && keyhit(3)) { if (*kname == '\0') memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); break; } autoboot = 0; /* * Try to exec stage 3 boot loader. If interrupted by a * keypress, or in case of failure, try to load a kernel * directly instead. */ if (*kname != '\0') load(); memcpy(kname, PATH_LOADER, sizeof(PATH_LOADER)); load(); memcpy(kname, PATH_KERNEL, sizeof(PATH_KERNEL)); load(); gptbootfailed(&gdsk.dsk); if (gptfind(&freebsd_ufs_uuid, &gdsk.dsk, -1) == -1) break; dsk_meta = 0; } /* Present the user with the boot2 prompt. */ for (;;) { if (!OPT_CHECK(RBX_QUIET)) { printf("\nFreeBSD/x86 boot\n" "Default: %u:%s(%up%u)%s\n" "boot: ", gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part, kname); } if (ioctrl & IO_SERIAL) sio_flush(); *cmd = '\0'; if (keyhit(0)) getstr(cmd, sizeof(cmd)); else if (!OPT_CHECK(RBX_QUIET)) putchar('\n'); if (parse_cmds(cmd, &dskupdated)) { putchar('\a'); continue; } if (dskupdated && gptinit() != 0) continue; load(); } /* NOTREACHED */ } /* XXX - Needed for btxld to link the boot2 binary; do not remove. */ void exit(int x) { while (1); __unreachable(); } static void load(void) { union { struct exec ex; Elf32_Ehdr eh; } hdr; static Elf32_Phdr ep[2]; static Elf32_Shdr es[2]; caddr_t p; ufs_ino_t ino; uint32_t addr, x; int fmt, i, j; if (!(ino = lookup(kname))) { if (!ls) { printf("%s: No %s on %u:%s(%up%u)\n", BOOTPROG, kname, gdsk.dsk.drive & DRV_MASK, dev_nm[gdsk.dsk.type], gdsk.dsk.unit, gdsk.dsk.part); } return; } if (xfsread(ino, &hdr, sizeof(hdr))) return; if (N_GETMAGIC(hdr.ex) == ZMAGIC) fmt = 0; else if (IS_ELF(hdr.eh)) fmt = 1; else { printf("Invalid %s\n", "format"); return; } if (fmt == 0) { addr = hdr.ex.a_entry & 0xffffff; p = PTOV(addr); fs_off = PAGE_SIZE; if (xfsread(ino, p, hdr.ex.a_text)) return; p += roundup2(hdr.ex.a_text, PAGE_SIZE); if (xfsread(ino, p, hdr.ex.a_data)) return; p += hdr.ex.a_data + roundup2(hdr.ex.a_bss, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); memcpy(p, &hdr.ex.a_syms, sizeof(hdr.ex.a_syms)); p += sizeof(hdr.ex.a_syms); if (hdr.ex.a_syms) { if (xfsread(ino, p, hdr.ex.a_syms)) return; p += hdr.ex.a_syms; if (xfsread(ino, p, sizeof(int))) return; x = *(uint32_t *)p; p += sizeof(int); x -= sizeof(int); if (xfsread(ino, p, x)) return; p += x; } } else { fs_off = hdr.eh.e_phoff; for (j = i = 0; i < hdr.eh.e_phnum && j < 2; i++) { if (xfsread(ino, ep + j, sizeof(ep[0]))) return; if (ep[j].p_type == PT_LOAD) j++; } for (i = 0; i < 2; i++) { p = PTOV(ep[i].p_paddr & 0xffffff); fs_off = ep[i].p_offset; if (xfsread(ino, p, ep[i].p_filesz)) return; } p += roundup2(ep[1].p_memsz, PAGE_SIZE); bootinfo.bi_symtab = VTOP(p); if (hdr.eh.e_shnum == hdr.eh.e_shstrndx + 3) { fs_off = hdr.eh.e_shoff + sizeof(es[0]) * (hdr.eh.e_shstrndx + 1); if (xfsread(ino, &es, sizeof(es))) return; for (i = 0; i < 2; i++) { memcpy(p, &es[i].sh_size, sizeof(es[i].sh_size)); p += sizeof(es[i].sh_size); fs_off = es[i].sh_offset; if (xfsread(ino, p, es[i].sh_size)) return; p += es[i].sh_size; } } addr = hdr.eh.e_entry & 0xffffff; } bootinfo.bi_esymtab = VTOP(p); bootinfo.bi_kernelname = VTOP(kname); bootinfo.bi_bios_dev = gdsk.dsk.drive; #ifdef LOADER_GELI_SUPPORT geliargs.size = sizeof(geliargs); explicit_bzero(gelipw, sizeof(gelipw)); export_geli_boot_data(&geliargs.gelidata); #endif /* * Note that the geliargs struct is passed by value, not by pointer. * Code in btxldr.S copies the values from the entry stack to a fixed * location within loader(8) at startup due to the presence of the * KARGS_FLAGS_EXTARG flag. */ __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[gdsk.dsk.type], gdsk.dsk.part + 1, gdsk.dsk.unit, 0xff), #ifdef LOADER_GELI_SUPPORT KARGS_FLAGS_GELI | KARGS_FLAGS_EXTARG, 0, 0, VTOP(&bootinfo), geliargs #else 0, 0, 0, VTOP(&bootinfo) #endif ); } static int parse_cmds(char *cmdstr, int *dskupdated) { char *arg; char *ep, *p, *q; const char *cp; unsigned int drv; int c, i, j; arg = cmdstr; *dskupdated = 0; while ((c = *arg++)) { if (c == ' ' || c == '\t' || c == '\n') continue; for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++); ep = p; if (*p) *p++ = 0; if (c == '-') { while ((c = *arg++)) { if (c == 'P') { if (*(uint8_t *)PTOV(0x496) & 0x10) { cp = "yes"; } else { opts |= OPT_SET(RBX_DUAL) | OPT_SET(RBX_SERIAL); cp = "no"; } printf("Keyboard: %s\n", cp); continue; } else if (c == 'S') { j = 0; while ((unsigned int)(i = *arg++ - '0') <= 9) j = j * 10 + i; if (j > 0 && i == -'0') { comspeed = j; break; } /* * Fall through to error below * ('S' not in optstr[]). */ } for (i = 0; c != optstr[i]; i++) if (i == NOPT - 1) return (-1); opts ^= OPT_SET(flags[i]); } ioctrl = OPT_CHECK(RBX_DUAL) ? (IO_SERIAL|IO_KEYBOARD) : OPT_CHECK(RBX_SERIAL) ? IO_SERIAL : IO_KEYBOARD; if (ioctrl & IO_SERIAL) { if (sio_init(115200 / comspeed) != 0) ioctrl &= ~IO_SERIAL; } } else { for (q = arg--; *q && *q != '('; q++); if (*q) { drv = -1; if (arg[1] == ':') { drv = *arg - '0'; if (drv > 9) return (-1); arg += 2; } if (q - arg != 2) return (-1); for (i = 0; arg[0] != dev_nm[i][0] || arg[1] != dev_nm[i][1]; i++) if (i == NDEV - 1) return (-1); gdsk.dsk.type = i; arg += 3; gdsk.dsk.unit = *arg - '0'; if (arg[1] != 'p' || gdsk.dsk.unit > 9) return (-1); arg += 2; gdsk.dsk.part = *arg - '0'; if (gdsk.dsk.part < 1 || gdsk.dsk.part > 9) return (-1); arg++; if (arg[0] != ')') return (-1); arg++; if (drv == -1) drv = gdsk.dsk.unit; gdsk.dsk.drive = (gdsk.dsk.type <= TYPE_MAXHARD ? DRV_HARD : 0) + drv; *dskupdated = 1; } if ((i = ep - arg)) { if ((size_t)i >= sizeof(kname)) return (-1); memcpy(kname, arg, i + 1); } } arg = p; } return (0); } static int dskread(void *buf, daddr_t lba, unsigned nblk) { int err; err = drvread(&gdsk.dsk, buf, lba + gdsk.dsk.start, nblk); #ifdef LOADER_GELI_SUPPORT if (err == 0 && gdsk.gdev != NULL) { /* Decrypt */ if (geli_read(gdsk.gdev, lba * DEV_BSIZE, buf, nblk * DEV_BSIZE)) return (err); } #endif return (err); } #ifdef LOADER_GELI_SUPPORT /* * Read function compatible with the ZFS callback, required to keep the GELI * implementation the same for both UFS and ZFS. */ static int vdev_read(void *vdev __unused, void *priv, off_t off, void *buf, size_t bytes) { char *p; daddr_t lba; unsigned int nb; struct gptdsk *dskp; dskp = (struct gptdsk *)priv; if ((off & (DEV_BSIZE - 1)) || (bytes & (DEV_BSIZE - 1))) return (-1); p = buf; lba = off / DEV_BSIZE; lba += dskp->dsk.start; while (bytes > 0) { nb = bytes / DEV_BSIZE; if (nb > VBLKSIZE / DEV_BSIZE) nb = VBLKSIZE / DEV_BSIZE; if (drvread(&dskp->dsk, dmadat->blkbuf, lba, nb)) return (-1); memcpy(p, dmadat->blkbuf, nb * DEV_BSIZE); p += nb * DEV_BSIZE; lba += nb; bytes -= nb * DEV_BSIZE; } return (0); } #endif /* LOADER_GELI_SUPPORT */ Index: stable/12/stand/libsa/gpt.c =================================================================== --- stable/12/stand/libsa/gpt.c (revision 353987) +++ stable/12/stand/libsa/gpt.c (revision 353988) @@ -1,379 +1,377 @@ /*- * Copyright (c) 2010 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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 __FBSDID("$FreeBSD$"); #include #include #ifndef LITTLE_ENDIAN #error gpt.c works only for little endian architectures #endif #include "stand.h" #include "crc32.h" #include "drv.h" #include "gpt.h" static struct gpt_hdr hdr_primary, hdr_backup, *gpthdr; static uint64_t hdr_primary_lba, hdr_backup_lba; static struct gpt_ent table_primary[MAXTBLENTS], table_backup[MAXTBLENTS]; static struct gpt_ent *gpttable; static int curent, bootonce; /* * Buffer below 64kB passed on gptread(), which can hold at least * one sector of data (512 bytes). */ static char *secbuf; static void gptupdate(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, struct gpt_ent *table) { int entries_per_sec, firstent; daddr_t slba; /* * We need to update the following for both primary and backup GPT: * 1. Sector on disk that contains current partition. * 2. Partition table checksum. * 3. Header checksum. * 4. Header on disk. */ entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; slba = curent / entries_per_sec; firstent = slba * entries_per_sec; bcopy(&table[firstent], secbuf, DEV_BSIZE); slba += hdr->hdr_lba_table; if (drvwrite(dskp, secbuf, slba, 1)) { printf("%s: unable to update %s GPT partition table\n", BOOTPROG, which); return; } hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); hdr->hdr_crc_self = 0; hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); bzero(secbuf, DEV_BSIZE); bcopy(hdr, secbuf, hdr->hdr_size); if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) { printf("%s: unable to update %s GPT header\n", BOOTPROG, which); return; } } int gptfind(const uuid_t *uuid, struct dsk *dskp, int part) { struct gpt_ent *ent; int firsttry; if (part >= 0) { if (part == 0 || part > gpthdr->hdr_entries) { printf("%s: invalid partition index\n", BOOTPROG); return (-1); } ent = &gpttable[part - 1]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) { printf("%s: specified partition is not UFS\n", BOOTPROG); return (-1); } curent = part - 1; goto found; } firsttry = (curent == -1); curent++; if (curent >= gpthdr->hdr_entries) { curent = gpthdr->hdr_entries; return (-1); } if (bootonce) { /* * First look for partition with both GPT_ENT_ATTR_BOOTME and * GPT_ENT_ATTR_BOOTONCE flags. */ for (; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTONCE)) continue; /* Ok, found one. */ goto found; } bootonce = 0; curent = 0; } for (; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; if (!(ent->ent_attr & GPT_ENT_ATTR_BOOTME)) continue; if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) continue; /* Ok, found one. */ goto found; } if (firsttry) { /* * No partition with BOOTME flag was found, try to boot from * first UFS partition. */ for (curent = 0; curent < gpthdr->hdr_entries; curent++) { ent = &gpttable[curent]; if (bcmp(&ent->ent_type, uuid, sizeof(uuid_t)) != 0) continue; /* Ok, found one. */ goto found; } } return (-1); found: dskp->part = curent + 1; ent = &gpttable[curent]; dskp->start = ent->ent_lba_start; if (ent->ent_attr & GPT_ENT_ATTR_BOOTONCE) { /* * Clear BOOTME, but leave BOOTONCE set before trying to * boot from this partition. */ if (hdr_primary_lba > 0) { table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; gptupdate("primary", dskp, &hdr_primary, table_primary); } if (hdr_backup_lba > 0) { table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTME; gptupdate("backup", dskp, &hdr_backup, table_backup); } } return (0); } static int gptread_hdr(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, uint64_t hdrlba) { uint32_t crc; if (drvread(dskp, secbuf, hdrlba, 1)) { printf("%s: unable to read %s GPT header\n", BOOTPROG, which); return (-1); } bcopy(secbuf, hdr, sizeof(*hdr)); if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || hdr->hdr_lba_self != hdrlba || hdr->hdr_revision < 0x00010000 || hdr->hdr_entsz < sizeof(struct gpt_ent) || hdr->hdr_entries > MAXTBLENTS || DEV_BSIZE % hdr->hdr_entsz != 0) { printf("%s: invalid %s GPT header\n", BOOTPROG, which); return (-1); } crc = hdr->hdr_crc_self; hdr->hdr_crc_self = 0; if (crc32(hdr, hdr->hdr_size) != crc) { printf("%s: %s GPT header checksum mismatch\n", BOOTPROG, which); return (-1); } hdr->hdr_crc_self = crc; return (0); } void gptbootfailed(struct dsk *dskp) { if (!(gpttable[curent].ent_attr & GPT_ENT_ATTR_BOOTONCE)) return; if (hdr_primary_lba > 0) { table_primary[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; table_primary[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; gptupdate("primary", dskp, &hdr_primary, table_primary); } if (hdr_backup_lba > 0) { table_backup[curent].ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; table_backup[curent].ent_attr |= GPT_ENT_ATTR_BOOTFAILED; gptupdate("backup", dskp, &hdr_backup, table_backup); } } static void gptbootconv(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, struct gpt_ent *table) { struct gpt_ent *ent; daddr_t slba; int table_updated, sector_updated; int entries_per_sec, nent, part; table_updated = 0; entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; for (nent = 0, slba = hdr->hdr_lba_table; slba < hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; slba++, nent += entries_per_sec) { sector_updated = 0; for (part = 0; part < entries_per_sec; part++) { ent = &table[nent + part]; if ((ent->ent_attr & (GPT_ENT_ATTR_BOOTME | GPT_ENT_ATTR_BOOTONCE | GPT_ENT_ATTR_BOOTFAILED)) != GPT_ENT_ATTR_BOOTONCE) { continue; } ent->ent_attr &= ~GPT_ENT_ATTR_BOOTONCE; ent->ent_attr |= GPT_ENT_ATTR_BOOTFAILED; table_updated = 1; sector_updated = 1; } if (!sector_updated) continue; bcopy(&table[nent], secbuf, DEV_BSIZE); if (drvwrite(dskp, secbuf, slba, 1)) { printf("%s: unable to update %s GPT partition table\n", BOOTPROG, which); } } if (!table_updated) return; hdr->hdr_crc_table = crc32(table, hdr->hdr_entries * hdr->hdr_entsz); hdr->hdr_crc_self = 0; hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size); bzero(secbuf, DEV_BSIZE); bcopy(hdr, secbuf, hdr->hdr_size); if (drvwrite(dskp, secbuf, hdr->hdr_lba_self, 1)) printf("%s: unable to update %s GPT header\n", BOOTPROG, which); } static int -gptread_table(const char *which, const uuid_t *uuid, struct dsk *dskp, - struct gpt_hdr *hdr, struct gpt_ent *table) +gptread_table(const char *which, struct dsk *dskp, struct gpt_hdr *hdr, + struct gpt_ent *table) { struct gpt_ent *ent; int entries_per_sec; int part, nent; daddr_t slba; if (hdr->hdr_entries == 0) return (0); entries_per_sec = DEV_BSIZE / hdr->hdr_entsz; slba = hdr->hdr_lba_table; nent = 0; for (;;) { if (drvread(dskp, secbuf, slba, 1)) { printf("%s: unable to read %s GPT partition table\n", BOOTPROG, which); return (-1); } ent = (struct gpt_ent *)secbuf; for (part = 0; part < entries_per_sec; part++, ent++) { bcopy(ent, &table[nent], sizeof(table[nent])); if (++nent >= hdr->hdr_entries) break; } if (nent >= hdr->hdr_entries) break; slba++; } if (crc32(table, nent * hdr->hdr_entsz) != hdr->hdr_crc_table) { printf("%s: %s GPT table checksum mismatch\n", BOOTPROG, which); return (-1); } return (0); } int -gptread(const uuid_t *uuid, struct dsk *dskp, char *buf) +gptread(struct dsk *dskp, char *buf) { uint64_t altlba; /* * Read and verify both GPT headers: primary and backup. */ secbuf = buf; hdr_primary_lba = hdr_backup_lba = 0; curent = -1; bootonce = 1; dskp->start = 0; if (gptread_hdr("primary", dskp, &hdr_primary, 1) == 0 && - gptread_table("primary", uuid, dskp, &hdr_primary, - table_primary) == 0) { + gptread_table("primary", dskp, &hdr_primary, table_primary) == 0) { hdr_primary_lba = hdr_primary.hdr_lba_self; gpthdr = &hdr_primary; gpttable = table_primary; } if (hdr_primary_lba > 0) { /* * If primary header is valid, we can get backup * header location from there. */ altlba = hdr_primary.hdr_lba_alt; } else { altlba = drvsize(dskp); if (altlba > 0) altlba--; } if (altlba == 0) printf("%s: unable to locate backup GPT header\n", BOOTPROG); else if (gptread_hdr("backup", dskp, &hdr_backup, altlba) == 0 && - gptread_table("backup", uuid, dskp, &hdr_backup, - table_backup) == 0) { + gptread_table("backup", dskp, &hdr_backup, table_backup) == 0) { hdr_backup_lba = hdr_backup.hdr_lba_self; if (hdr_primary_lba == 0) { gpthdr = &hdr_backup; gpttable = table_backup; printf("%s: using backup GPT\n", BOOTPROG); } } /* * Convert all BOOTONCE without BOOTME flags into BOOTFAILED. * BOOTONCE without BOOTME means that we tried to boot from it, * but failed after leaving gptboot and machine was rebooted. * We don't want to leave partitions marked as BOOTONCE only, * because when we boot successfully start-up scripts should * find at most one partition with only BOOTONCE flag and this * will mean that we booted from that partition. */ if (hdr_primary_lba != 0) gptbootconv("primary", dskp, &hdr_primary, table_primary); if (hdr_backup_lba != 0) gptbootconv("backup", dskp, &hdr_backup, table_backup); if (hdr_primary_lba == 0 && hdr_backup_lba == 0) return (-1); return (0); } Index: stable/12/stand/libsa/gpt.h =================================================================== --- stable/12/stand/libsa/gpt.h (revision 353987) +++ stable/12/stand/libsa/gpt.h (revision 353988) @@ -1,41 +1,41 @@ /*- * Copyright (c) 2010 Pawel Jakub Dawidek * 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 AUTHORS 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 AUTHORS 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 _GPT_H_ #define _GPT_H_ #include #include #define MAXTBLENTS 128 -int gptread(const uuid_t *uuid, struct dsk *dskp, char *buf); +int gptread(struct dsk *dskp, char *buf); int gptfind(const uuid_t *uuid, struct dsk *dskp, int part); void gptbootfailed(struct dsk *dskp); #endif /* !_GPT_H_ */ Index: stable/12 =================================================================== --- stable/12 (revision 353987) +++ stable/12 (revision 353988) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r348658-348659,348674-348675,348678,348760,348766,348768,348811-348812,348814,349008