Index: sys/boot/common/self_reloc.c =================================================================== --- sys/boot/common/self_reloc.c +++ sys/boot/common/self_reloc.c @@ -61,6 +61,8 @@ #define RELOC_TYPE_RELATIVE R_386_RELATIVE #endif +void self_reloc(Elf_Addr baseaddr, ElfW_Dyn *dynamic); + /* * A simple elf relocator. */ Index: sys/boot/efi/Makefile =================================================================== --- sys/boot/efi/Makefile +++ sys/boot/efi/Makefile @@ -16,4 +16,6 @@ SUBDIR+= loader boot1 .endif +CFLAGSWARN+= -Wall + .include Index: sys/boot/efi/boot1/Makefile =================================================================== --- sys/boot/efi/boot1/Makefile +++ sys/boot/efi/boot1/Makefile @@ -2,7 +2,19 @@ MAN= +CWARNFLAGS?= -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes \ + -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual \ + -Wundef -Wno-pointer-sign -Wmissing-include-dirs \ + -fdiagnostics-show-option -Wno-unknown-pragmas \ + + +# EFI_GUID assignments +CWARNFLAGS+= -Wno-missing-braces +# zfsimpl.c +CWARNFLAGS+= -Wno-unused-function -Wno-array-bounds + .include +.include # In-tree GCC does not support __attribute__((ms_abi)). .if ${COMPILER_TYPE} != "gcc" @@ -13,13 +25,22 @@ INTERNALPROG= # architecture-specific loader code -SRCS= boot1.c self_reloc.c start.S +SRCS= boot1.c self_reloc.c start.S ufs_module.c +.if ${MK_ZFS} != "no" +SRCS+= zfs_module.c +.endif CFLAGS+= -I. CFLAGS+= -I${.CURDIR}/../include CFLAGS+= -I${.CURDIR}/../include/${MACHINE} CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. +CFLAGS+= -DEFI_UFS_BOOT +.if ${MK_ZFS} != "no" +CFLAGS+= -I${.CURDIR}/../../zfs/ +CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs/ +CFLAGS+= -DEFI_ZFS_BOOT +.endif # Always add MI sources and REGULAR efi loader bits .PATH: ${.CURDIR}/../loader/arch/${MACHINE} Index: sys/boot/efi/boot1/boot1.c =================================================================== --- sys/boot/efi/boot1/boot1.c +++ sys/boot/efi/boot1/boot1.c @@ -5,6 +5,8 @@ * 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 @@ -21,17 +23,24 @@ __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include +#include "boot_module.h" + #define _PATH_LOADER "/boot/loader.efi" -#define _PATH_KERNEL "/boot/kernel/kernel" -#define BSIZEMAX 16384 +#ifdef EFI_DEBUG +#define DPRINTF(fmt, args...) \ + do { \ + printf(fmt, ##args) \ + } while (0) +#else +#define DPRINTF(fmt, args...) {} +#endif typedef int putc_func_t(char c, void *arg); @@ -41,28 +50,132 @@ u_int sp_size; }; -static const char digits[] = "0123456789abcdef"; +static const boot_module_t *boot_modules[] = +{ +#ifdef EFI_ZFS_BOOT + &zfs_module, +#endif +#ifdef EFI_UFS_BOOT + &ufs_module +#endif +}; -static void panic(const char *fmt, ...) __dead2; -static int printf(const char *fmt, ...); -static int putchar(char c, void *arg); -static int vprintf(const char *fmt, va_list ap); -static int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); +#define NUM_BOOT_MODULES (sizeof(boot_modules) / sizeof(boot_module_t*)) + +static const char digits[] = "0123456789abcdef"; static int __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap); -static int __putc(char c, void *arg); static int __puts(const char *s, putc_func_t *putc, void *arg); static int __sputc(char c, void *arg); static char *__uitoa(char *buf, u_int val, int base); static char *__ultoa(char *buf, u_long val, int base); +static int putchr(char c, void *arg); -static int domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet); -static void load(const char *fname); +static void try_load(const boot_module_t* mod); +static EFI_STATUS probe_handle(EFI_HANDLE h); -static EFI_SYSTEM_TABLE *systab; +EFI_SYSTEM_TABLE *systab; +EFI_BOOT_SERVICES *bs; static EFI_HANDLE *image; -static void +void * +Malloc(size_t len, const char *file, int line) +{ + void *out; + + if (bs->AllocatePool(EfiLoaderData, len, &out) == EFI_SUCCESS) + return (out); + + return (NULL); +} + +char * +strcpy(char * __restrict to, const char * __restrict from) +{ + char *save = to; + + for (; (*to = *from) != 0; ++from, ++to); + + return (save); +} + +char * +strchr(const char *s, int c) +{ + int i; + + for (i = 0; s[i]; i++) + if (s[i] == c) + return ((char*)(s + i)); + + return (NULL); +} + +int +strncmp(const char *s1, const char *s2, size_t n) +{ + + if (n == 0) + return (0); + do { + if (*s1 != *s2++) + return (*(const unsigned char *)s1 - + *(const unsigned char *)(s2 - 1)); + if (*s1++ == '\0') + break; + } while (--n != 0); + return (0); +} + +size_t +strlen(const char *s) +{ + size_t len = 0; + + for (; *s != '\0'; s++, len++) + ; + + return (len); +} + +char * +strdup(const char *s) +{ + size_t len; + char *out; + + len = strlen(s) + 1; + out = malloc(len); + if (out == NULL) + return (NULL); + + bcopy(s, out, len); + + return (out); +} + +int +bcmp(const void *a, const void *b, size_t len) +{ + const char *sa = a; + const char *sb = b; + int i; + + for (i = 0; i < len; i++) + if (sa[i] != sb[i]) + return (1); + + return (0); +} + +int +memcmp(const void *a, const void *b, size_t len) +{ + + return bcmp(a, b, len); +} + +void bcopy(const void *src, void *dst, size_t len) { const char *s = src; @@ -72,55 +185,119 @@ *d++ = *s++; } -static void +void * memcpy(void *dst, const void *src, size_t len) { + bcopy(src, dst, len); + + return (dst); } -static void -bzero(void *b, size_t len) +void * +memset(void *b, int val, size_t len) { char *p = b; while (len-- != 0) - *p++ = 0; + *p++ = val; + + return (b); } -static int +int strcmp(const char *s1, const char *s2) { + for (; *s1 == *s2 && *s1; s1++, s2++) ; return ((u_char)*s1 - (u_char)*s2); } +static int +putchr(char c, void *arg) +{ + CHAR16 buf[2]; + + if (c == '\n') + putchr('\r', arg); + + buf[0] = c; + buf[1] = 0; + systab->ConOut->OutputString(systab->ConOut, buf); + + return (1); +} + 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; -static EFI_BLOCK_IO *bootdev; -static EFI_DEVICE_PATH *bootdevpath; -static EFI_HANDLE *bootdevhandle; +/* + * This function only returns if it fails to load the kernel. If it + * succeeds, it simply boots the kernel. + */ +void +try_load(const boot_module_t *mod) +{ + size_t bufsize; + void *buf; + dev_info_t *dev; + EFI_HANDLE loaderhandle; + EFI_LOADED_IMAGE *loaded_image; + EFI_STATUS status; + + status = mod->load(_PATH_LOADER, &dev, &buf, &bufsize); + if (status == EFI_NOT_FOUND) + return; -EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab) + if (status != EFI_SUCCESS) { + printf("%s failed to load %s (%lu)\n", mod->name, _PATH_LOADER, + status); + return; + } + + if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize, + &loaderhandle)) != EFI_SUCCESS) { + printf("Failed to load image provided by %s (%lu)\n", mod->name, + status); + return; + } + + if ((status = bs->HandleProtocol(loaderhandle, &LoadedImageGUID, + (VOID**)&loaded_image)) != EFI_SUCCESS) { + printf("Failed to query LoadedImage provided by %s (%lu)\n", + mod->name, status); + return; + } + + loaded_image->DeviceHandle = dev->devhandle; + + if ((status = bs->StartImage(loaderhandle, NULL, NULL)) != + EFI_SUCCESS) { + printf("Failed start image provided by %s (%lu)\n", mod->name, + status); + return; + } +} + +void +efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) { - EFI_HANDLE handles[128]; - EFI_BLOCK_IO *blkio; - UINTN i, nparts = sizeof(handles), cols, rows, max_dim, best_mode; + EFI_HANDLE *handles; EFI_STATUS status; - EFI_DEVICE_PATH *devpath; - EFI_BOOT_SERVICES *BS; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; - char *path = _PATH_LOADER; + UINTN i, max_dim, best_mode, cols, rows, hsize, nhandles; + /* Basic initialization*/ systab = Xsystab; image = Ximage; + bs = Xsystab->BootServices; - BS = systab->BootServices; - status = BS->LocateProtocol(&ConsoleControlGUID, NULL, + /* Set up the console, so printf works. */ + status = bs->LocateProtocol(&ConsoleControlGUID, NULL, (VOID **)&ConsoleControl); if (status == EFI_SUCCESS) (void)ConsoleControl->SetMode(ConsoleControl, @@ -132,8 +309,7 @@ conout->Reset(conout, TRUE); max_dim = best_mode = 0; for (i = 0; ; i++) { - status = conout->QueryMode(conout, i, - &cols, &rows); + status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) break; if (cols * rows > max_dim) { @@ -146,205 +322,159 @@ conout->EnableCursor(conout, TRUE); conout->ClearScreen(conout); - printf("\n" - ">> FreeBSD EFI boot block\n"); - printf(" Loader path: %s\n", path); - - status = systab->BootServices->LocateHandle(ByProtocol, - &BlockIoProtocolGUID, NULL, &nparts, handles); - nparts /= sizeof(handles[0]); - - for (i = 0; i < nparts; i++) { - status = systab->BootServices->HandleProtocol(handles[i], - &DevicePathGUID, (void **)&devpath); - if (EFI_ERROR(status)) + printf("\n>> FreeBSD EFI boot block\n"); + printf(" Loader path: %s\n\n", _PATH_LOADER); + printf(" Initializing modules:"); + for (i = 0; i < NUM_BOOT_MODULES; i++) { + if (boot_modules[i] == NULL) continue; - while (!IsDevicePathEnd(NextDevicePathNode(devpath))) - devpath = NextDevicePathNode(devpath); - - status = systab->BootServices->HandleProtocol(handles[i], - &BlockIoProtocolGUID, (void **)&blkio); - if (EFI_ERROR(status)) - continue; + printf(" %s", boot_modules[i]->name); + if (boot_modules[i]->init != NULL) + boot_modules[i]->init(); + } + putchr('\n', NULL); - if (!blkio->Media->LogicalPartition) - continue; + /* Get all the device handles */ + hsize = sizeof(EFI_HANDLE) * 24; + if (bs->AllocatePool(EfiLoaderData, hsize, (void **)&handles) != EFI_SUCCESS) + panic("Failed to allocate handles"); + + status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, + &hsize, handles); + switch (status) { + case EFI_SUCCESS: + break; + case EFI_BUFFER_TOO_SMALL: + (void)bs->FreePool(handles); + if ((status = bs->AllocatePool(EfiLoaderData, hsize, + (void **)&handles) != EFI_SUCCESS)) { + panic("Failed to allocate %lu handles (%lu)", hsize / + sizeof(*handles), status); + } + status = bs->LocateHandle(ByProtocol, &BlockIoProtocolGUID, + NULL, &hsize, handles); + if (status != EFI_SUCCESS) + panic("Failed to get device handles (%lu)\n", status); + break; + default: + panic("Failed to get device handles (%lu)", status); + } - if (domount(devpath, blkio, 1) >= 0) + /* Scan all partitions, probing with all modules. */ + nhandles = hsize / sizeof(*handles); + printf(" Probing %lu block devices...", nhandles); + for (i = 0; i < nhandles; i++) { + status = probe_handle(handles[i]); + switch (status) { + case EFI_UNSUPPORTED: + printf("."); + break; + case EFI_SUCCESS: + printf("+"); + break; + default: + printf("x"); break; + } } + printf(" done\n"); - if (i == nparts) - panic("No bootable partition found"); - - bootdevhandle = handles[i]; - load(path); + /* Status summary. */ + for (i = 0; i < NUM_BOOT_MODULES; i++) { + if (boot_modules[i] != NULL) { + printf(" "); + boot_modules[i]->status(); + } + } - panic("Load failed"); + /* Select a partition to boot by trying each module in order. */ + for (i = 0; i < NUM_BOOT_MODULES; i++) + if (boot_modules[i] != NULL) + try_load(boot_modules[i]); - return EFI_SUCCESS; + /* If we get here, we're out of luck... */ + panic("No bootable partitions found!"); } -static int -dskread(void *buf, u_int64_t lba, int nblk) +static EFI_STATUS +probe_handle(EFI_HANDLE h) { + dev_info_t *devinfo; + EFI_BLOCK_IO *blkio; + EFI_DEVICE_PATH *devpath; EFI_STATUS status; - int size; - - lba = lba / (bootdev->Media->BlockSize / DEV_BSIZE); - size = nblk * DEV_BSIZE; - status = bootdev->ReadBlocks(bootdev, bootdev->Media->MediaId, lba, - size, buf); + UINTN i; - if (EFI_ERROR(status)) - return (-1); - - return (0); -} + /* Figure out if we're dealing with an actual partition. */ + status = bs->HandleProtocol(h, &DevicePathGUID, (void **)&devpath); + if (status == EFI_UNSUPPORTED) + return (status); + + if (status != EFI_SUCCESS) { + DPRINTF("\nFailed to query DevicePath (%lu)\n", status); + return (status); + } -#include "ufsread.c" + while (!IsDevicePathEnd(NextDevicePathNode(devpath))) + devpath = NextDevicePathNode(devpath); -static ssize_t -fsstat(ufs_ino_t inode) -{ -#ifndef UFS2_ONLY - static struct ufs1_dinode dp1; - ufs1_daddr_t addr1; -#endif -#ifndef UFS1_ONLY - static struct ufs2_dinode dp2; -#endif - static struct fs fs; - static ufs_ino_t inomap; - char *blkbuf; - void *indbuf; - size_t n, nb, size, off, vboff; - ufs_lbn_t lbn; - ufs2_daddr_t addr2, vbaddr; - static ufs2_daddr_t blkmap, indmap; - u_int u; - - blkbuf = dmadat->blkbuf; - indbuf = dmadat->indbuf; - if (!dsk_meta) { - inomap = 0; - for (n = 0; sblock_try[n] != -1; n++) { - if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE, - SBLOCKSIZE / DEV_BSIZE)) - return -1; - memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); - if (( -#if defined(UFS1_ONLY) - fs.fs_magic == FS_UFS1_MAGIC -#elif defined(UFS2_ONLY) - (fs.fs_magic == FS_UFS2_MAGIC && - fs.fs_sblockloc == sblock_try[n]) -#else - fs.fs_magic == FS_UFS1_MAGIC || - (fs.fs_magic == FS_UFS2_MAGIC && - fs.fs_sblockloc == sblock_try[n]) -#endif - ) && - fs.fs_bsize <= MAXBSIZE && - fs.fs_bsize >= sizeof(struct fs)) - break; - } - if (sblock_try[n] == -1) { - printf("Not ufs\n"); - return -1; - } - dsk_meta++; - } else - memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); - if (!inode) - return 0; - if (inomap != inode) { - n = IPERVBLK(&fs); - if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK)) - return -1; - n = INO_TO_VBO(n, inode); -#if defined(UFS1_ONLY) - memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n, - sizeof(struct ufs1_dinode)); -#elif defined(UFS2_ONLY) - memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n, - sizeof(struct ufs2_dinode)); -#else - if (fs.fs_magic == FS_UFS1_MAGIC) - memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n, - sizeof(struct ufs1_dinode)); - else - memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n, - sizeof(struct ufs2_dinode)); -#endif - inomap = inode; - fs_off = 0; - blkmap = indmap = 0; + status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); + if (status == EFI_UNSUPPORTED) + return (status); + + if (status != EFI_SUCCESS) { + DPRINTF("\nFailed to query BlockIoProtocol (%lu)\n", status); + return (status); } - size = DIP(di_size); - n = size - fs_off; - return (n); -} -static struct dmadat __dmadat; + if (!blkio->Media->LogicalPartition) + return (EFI_UNSUPPORTED); -static int -domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet) -{ + /* Run through each module, see if it can load this partition */ + for (i = 0; i < NUM_BOOT_MODULES; i++ ) { + if (boot_modules[i] == NULL) + continue; - dmadat = &__dmadat; - bootdev = blkio; - bootdevpath = device; - if (fsread(0, NULL, 0)) { - if (!quiet) - printf("domount: can't read superblock\n"); - return (-1); + if ((status = bs->AllocatePool(EfiLoaderData, + sizeof(*devinfo), (void **)&devinfo)) != + EFI_SUCCESS) { + DPRINTF("\nFailed to allocate devinfo (%lu)\n", + status); + continue; + } + devinfo->dev = blkio; + devinfo->devpath = devpath; + devinfo->devhandle = h; + devinfo->devdata = NULL; + devinfo->next = NULL; + + status = boot_modules[i]->probe(devinfo); + if (status == EFI_SUCCESS) + return (EFI_SUCCESS); + (void)bs->FreePool(devinfo); } - if (!quiet) - printf("Succesfully mounted UFS filesystem\n"); - return (0); + + return (EFI_UNSUPPORTED); } -static void -load(const char *fname) +void +add_device(dev_info_t **devinfop, dev_info_t *devinfo) { - ufs_ino_t ino; - EFI_STATUS status; - EFI_HANDLE loaderhandle; - EFI_LOADED_IMAGE *loaded_image; - void *buffer; - size_t bufsize; - - if ((ino = lookup(fname)) == 0) { - printf("File %s not found\n", fname); + dev_info_t *dev; + + if (*devinfop == NULL) { + *devinfop = devinfo; return; } - bufsize = fsstat(ino); - status = systab->BootServices->AllocatePool(EfiLoaderData, - bufsize, &buffer); - fsread(ino, buffer, bufsize); - - /* XXX: For secure boot, we need our own loader here */ - status = systab->BootServices->LoadImage(TRUE, image, bootdevpath, - buffer, bufsize, &loaderhandle); - if (EFI_ERROR(status)) - printf("LoadImage failed with error %lx\n", status); - - status = systab->BootServices->HandleProtocol(loaderhandle, - &LoadedImageGUID, (VOID**)&loaded_image); - if (EFI_ERROR(status)) - printf("HandleProtocol failed with error %lx\n", status); - - loaded_image->DeviceHandle = bootdevhandle; - - status = systab->BootServices->StartImage(loaderhandle, NULL, NULL); - if (EFI_ERROR(status)) - printf("StartImage failed with error %lx\n", status); + for (dev = *devinfop; dev->next != NULL; dev = dev->next) + ; + + dev->next = devinfo; } -static void +void panic(const char *fmt, ...) { char buf[128]; @@ -358,7 +488,7 @@ while (1) {} } -static int +int printf(const char *fmt, ...) { va_list ap; @@ -369,38 +499,17 @@ return 0; va_start(ap, fmt); - ret = vprintf(fmt, ap); + ret = __printf(fmt, putchr, 0, ap); va_end(ap); return (ret); } -static int -putchar(char c, void *arg) -{ - CHAR16 buf[2]; - - if (c == '\n') { - buf[0] = '\r'; - buf[1] = 0; - systab->ConOut->OutputString(systab->ConOut, buf); - } - buf[0] = c; - buf[1] = 0; - systab->ConOut->OutputString(systab->ConOut, buf); - return (1); -} - -static int -vprintf(const char *fmt, va_list ap) +void vprintf(const char *fmt, va_list ap) { - int ret; - - ret = __printf(fmt, putchar, 0, ap); - return (ret); + __printf(fmt, putchr, 0, ap); } -static int -vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) +int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap) { struct sp_data sp; int ret; @@ -416,15 +525,10 @@ __printf(const char *fmt, putc_func_t *putc, void *arg, va_list ap) { char buf[(sizeof(long) * 8) + 1]; - char *nbuf; + char *nbuf, *s; u_long ul; u_int ui; - int lflag; - int sflag; - char *s; - int pad; - int ret; - int c; + int lflag, sflag, pad, ret, c; nbuf = &buf[sizeof buf - 1]; ret = 0; Index: sys/boot/efi/boot1/boot_module.h =================================================================== --- /dev/null +++ sys/boot/efi/boot1/boot_module.h @@ -0,0 +1,115 @@ +/*- + * 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...) \ + do { \ + printf(fmt, ##args) \ + } while (0) +#else +#define DPRINTF(fmt, args...) {} +#endif + +/* EFI device info */ +typedef struct dev_info +{ + EFI_BLOCK_IO *dev; + EFI_DEVICE_PATH *devpath; + EFI_HANDLE *devhandle; + void *devdata; + 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)(); + + /* + * 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 it. + * + * 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 *loader_path, dev_info_t **devinfo, + void **buf, size_t *bufsize); + + /* status outputs information about the probed devices. */ + void (*status)(); + +} boot_module_t; + +/* Standard boot modules. */ +#ifdef EFI_UFS_BOOT +extern const boot_module_t ufs_module; +#endif +#ifdef EFI_ZFS_BOOT +extern const boot_module_t zfs_module; +#endif + +/* Functions available to modules. */ +extern void add_device(dev_info_t **devinfop, dev_info_t *devinfo); +extern int strcmp(const char *s1, const char *s2); +extern void bcopy(const void *src, void *dst, size_t len); +extern void panic(const char *fmt, ...) __dead2; +extern int printf(const char *fmt, ...); +extern int vsnprintf(char *str, size_t sz, const char *fmt, va_list ap); + +extern EFI_SYSTEM_TABLE *systab; +extern EFI_BOOT_SERVICES *bs; + +#endif Index: sys/boot/efi/boot1/ufs_module.c =================================================================== --- /dev/null +++ sys/boot/efi/boot1/ufs_module.c @@ -0,0 +1,241 @@ +/*- + * 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 "boot_module.h" + +static dev_info_t *devinfo; +static dev_info_t *devices; + +static int +dskread(void *buf, u_int64_t lba, int nblk) +{ + int size; + + lba = lba / (devinfo->dev->Media->BlockSize / DEV_BSIZE); + size = nblk * DEV_BSIZE; + + if (devinfo->dev->ReadBlocks(devinfo->dev, + devinfo->dev->Media->MediaId, lba, size, buf) != EFI_SUCCESS) + return (-1); + + return (0); +} + +#include "ufsread.c" + +static ssize_t +fsstat(ufs_ino_t inode) +{ +#ifndef UFS2_ONLY + static struct ufs1_dinode dp1; +#endif +#ifndef UFS1_ONLY + static struct ufs2_dinode dp2; +#endif + static struct fs fs; + static ufs_ino_t inomap; + char *blkbuf; + void *indbuf; + size_t n, size; + static ufs2_daddr_t blkmap, indmap; + + blkbuf = dmadat->blkbuf; + indbuf = dmadat->indbuf; + if (!dsk_meta) { + inomap = 0; + for (n = 0; sblock_try[n] != -1; n++) { + if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE, + SBLOCKSIZE / DEV_BSIZE)) + return (-1); + memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); + if (( +#if defined(UFS1_ONLY) + fs.fs_magic == FS_UFS1_MAGIC +#elif defined(UFS2_ONLY) + (fs.fs_magic == FS_UFS2_MAGIC && + fs.fs_sblockloc == sblock_try[n]) +#else + fs.fs_magic == FS_UFS1_MAGIC || + (fs.fs_magic == FS_UFS2_MAGIC && + fs.fs_sblockloc == sblock_try[n]) +#endif + ) && + fs.fs_bsize <= MAXBSIZE && + fs.fs_bsize >= sizeof(struct fs)) + break; + } + if (sblock_try[n] == -1) { + return (-1); + } + dsk_meta++; + } else + memcpy(&fs, dmadat->sbbuf, sizeof(struct fs)); + if (!inode) + return (0); + if (inomap != inode) { + n = IPERVBLK(&fs); + if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK)) + return (-1); + n = INO_TO_VBO(n, inode); +#if defined(UFS1_ONLY) + memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n, + sizeof(struct ufs1_dinode)); +#elif defined(UFS2_ONLY) + memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n, + sizeof(struct ufs2_dinode)); +#else + if (fs.fs_magic == FS_UFS1_MAGIC) + memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n, + sizeof(struct ufs1_dinode)); + else + memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n, + sizeof(struct ufs2_dinode)); +#endif + inomap = inode; + fs_off = 0; + blkmap = indmap = 0; + } + size = DIP(di_size); + n = size - fs_off; + + return (n); +} + +static struct dmadat __dmadat; + +static EFI_STATUS +probe(dev_info_t* dev) +{ + + devinfo = dev; + dmadat = &__dmadat; + if (fsread(0, NULL, 0) < 0) + return (EFI_UNSUPPORTED); + + add_device(&devices, dev); + + return (EFI_SUCCESS); +} + +static EFI_STATUS +try_load(dev_info_t *dev, const char *loader_path, void **bufp, size_t *bufsize) +{ + ufs_ino_t ino; + EFI_STATUS status; + size_t size; + void *buf; + + devinfo = dev; + if ((ino = lookup(loader_path)) == 0) + return (EFI_NOT_FOUND); + + size = fsstat(ino); + if (size <= 0) { + printf("Failed to fsstat %s ino: %d\n", loader_path, ino); + return (EFI_INVALID_PARAMETER); + } + + if ((status = bs->AllocatePool(EfiLoaderData, size, &buf)) != + EFI_SUCCESS) { + printf("Failed to allocate read buffer (%lu)\n", status); + return (status); + } + + if (fsread(ino, *bufp, size) < 0) { + printf("Failed to read %s (%lu)\n", loader_path, status); + (void)bs->FreePool(buf); + return (EFI_INVALID_PARAMETER); + } + + *bufp = buf; + *bufsize = size; + + return (EFI_SUCCESS); +} + +static EFI_STATUS +load(const char *loader_path, dev_info_t **devinfop, void **buf, + size_t *bufsize) +{ + dev_info_t *devinfo; + EFI_STATUS status; + + for (devinfo = devices; devinfo != NULL; devinfo = devinfo->next) { + status = try_load(devinfo, loader_path, buf, bufsize); + if (status == EFI_SUCCESS) { + *devinfop = devinfo; + return (EFI_SUCCESS); + } else if (status != EFI_NOT_FOUND) { + return (status); + } + } + + return (EFI_NOT_FOUND); +} + +static void +status() +{ + 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); + } +} + +const boot_module_t ufs_module = +{ + .name = "UFS", + .probe = probe, + .load = load, + .status = status +}; Index: sys/boot/efi/boot1/zfs_module.c =================================================================== --- /dev/null +++ sys/boot/efi/boot1/zfs_module.c @@ -0,0 +1,191 @@ +/*- + * 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; + +static int +vdev_read(vdev_t *vdev, void *priv, off_t off, void *buf, size_t bytes) +{ + dev_info_t *devinfo; + off_t lba; + + devinfo = (dev_info_t *)priv; + lba = off / devinfo->dev->Media->BlockSize; + + if (devinfo->dev->ReadBlocks(devinfo->dev, + devinfo->dev->Media->MediaId, lba, bytes, buf) != EFI_SUCCESS) + return (-1); + + return (0); +} + +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", status); + return (status); + } + memcpy(tdev, dev, sizeof(*dev)); + + if (vdev_probe(vdev_read, tdev, &spa) != 0) { + (void)bs->FreePool(tdev); + return (EFI_UNSUPPORTED); + } + + dev->devdata = spa; + add_device(&devices, dev); + + return (EFI_SUCCESS); +} + +static EFI_STATUS +try_load(dev_info_t *devinfo, const char *loader_path, void **bufp, size_t *bufsize) +{ + spa_t *spa; + struct zfsmount zfsmount; + dnode_phys_t dn; + struct stat st; + int err; + void *buf; + EFI_STATUS status; + + spa = devinfo->devdata; + if (zfs_spa_init(spa) != 0) { + /* Init failed, don't report this loudly. */ + return (EFI_NOT_FOUND); + } + + if (zfs_mount(spa, 0, &zfsmount) != 0) { + /* Mount failed, don't report this loudly. */ + return (EFI_NOT_FOUND); + } + + if ((err = zfs_lookup(&zfsmount, loader_path, &dn)) != 0) { + printf("Failed to lookup %s on pool %s (%d)\n", loader_path, + spa->spa_name, err); + return (EFI_INVALID_PARAMETER); + } + + if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) { + printf("Failed to lookup %s on pool %s (%d)\n", loader_path, + 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 for pool %s (%lu)\n", + spa->spa_name, status); + return (EFI_INVALID_PARAMETER); + } + + if ((err = dnode_read(spa, &dn, 0, buf, st.st_size)) != 0) { + printf("Failed to read node from %s (%d)\n", spa->spa_name, + err); + (void)bs->FreePool(buf); + return (EFI_INVALID_PARAMETER); + } + + *bufsize = st.st_size; + *bufp = buf; + + return (EFI_SUCCESS); +} + +static EFI_STATUS +load(const char *loader_path, dev_info_t **devinfop, void **bufp, + size_t *bufsize) +{ + dev_info_t *devinfo; + EFI_STATUS status; + + for (devinfo = devices; devinfo != NULL; devinfo = devinfo->next) { + status = try_load(devinfo, loader_path, bufp, bufsize); + if (status == EFI_SUCCESS) { + *devinfop = devinfo; + return (EFI_SUCCESS); + } else if (status != EFI_NOT_FOUND) { + return (status); + } + } + + return (EFI_NOT_FOUND); +} + +static void +status() +{ + 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() +{ + + zfs_init(); +} + +const boot_module_t zfs_module = +{ + .name = "ZFS", + .init = init, + .probe = probe, + .load = load, + .status = status +}; Index: sys/boot/efi/include/amd64/efibind.h =================================================================== --- sys/boot/efi/include/amd64/efibind.h +++ sys/boot/efi/include/amd64/efibind.h @@ -39,7 +39,7 @@ // No ANSI C 1999/2000 stdint.h integer width declarations - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS // Use Microsoft C compiler integer width declarations @@ -164,7 +164,7 @@ #endif #ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler #else #define EFIAPI // Substitute expresion to force C calling convention @@ -265,7 +265,7 @@ #endif #endif /* __FreeBSD__ */ -#if _MSC_EXTENSIONS +#ifdef _MSC_EXTENSIONS #pragma warning ( disable : 4731 ) // Suppress warnings about modification of EBP #endif Index: sys/boot/efi/include/arm64/efibind.h =================================================================== --- sys/boot/efi/include/arm64/efibind.h +++ sys/boot/efi/include/arm64/efibind.h @@ -39,7 +39,7 @@ // No ANSI C 1999/2000 stdint.h integer width declarations - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS // Use Microsoft C compiler integer width declarations @@ -159,7 +159,7 @@ // #ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler #else #define EFIAPI // Substitute expresion to force C calling convention Index: sys/boot/efi/include/efilib.h =================================================================== --- sys/boot/efi/include/efilib.h +++ sys/boot/efi/include/efilib.h @@ -43,7 +43,8 @@ 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 *); +int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); +int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); int efi_status_to_errno(EFI_STATUS); time_t efi_time(EFI_TIME *); Index: sys/boot/efi/include/i386/efibind.h =================================================================== --- sys/boot/efi/include/i386/efibind.h +++ sys/boot/efi/include/i386/efibind.h @@ -39,7 +39,7 @@ // No ANSI C 1999/2000 stdint.h integer width declarations - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS // Use Microsoft C compiler integer width declarations @@ -160,7 +160,7 @@ // #ifndef EFIAPI // Forces EFI calling conventions reguardless of compiler options - #if _MSC_EXTENSIONS + #ifdef _MSC_EXTENSIONS #define EFIAPI __cdecl // Force C calling convention for Microsoft C compiler #else #define EFIAPI // Substitute expresion to force C calling convention @@ -261,7 +261,7 @@ #endif #endif /* __FreeBSD__ */ -#if _MSC_EXTENSIONS +#ifdef _MSC_EXTENSIONS #pragma warning ( disable : 4731 ) // Suppress warnings about modification of EBP #endif Index: sys/boot/efi/libefi/handles.c =================================================================== --- sys/boot/efi/libefi/handles.c +++ sys/boot/efi/libefi/handles.c @@ -35,6 +35,7 @@ EFI_HANDLE alias; struct devsw *dev; int unit; + uint64_t extra; }; struct entry *entry; @@ -79,7 +80,7 @@ } int -efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit) +efi_handle_lookup(EFI_HANDLE h, struct devsw **dev, int *unit, uint64_t *extra) { int idx; @@ -90,7 +91,28 @@ *dev = entry[idx].dev; if (unit != NULL) *unit = entry[idx].unit; + if (extra != NULL) + *extra = entry[idx].extra; return (0); } return (ENOENT); } + +int +efi_handle_update_dev(EFI_HANDLE h, struct devsw *dev, int unit, + uint64_t guid) +{ + int idx; + + for (idx = 0; idx < nentries; idx++) { + if (entry[idx].handle != h) + continue; + entry[idx].dev = dev; + entry[idx].unit = unit; + entry[idx].alias = NULL; + entry[idx].extra = guid; + return (0); + } + + return (ENOENT); +} Index: sys/boot/efi/loader/Makefile =================================================================== --- sys/boot/efi/loader/Makefile +++ sys/boot/efi/loader/Makefile @@ -23,6 +23,11 @@ smbios.c \ vers.c +.if ${MK_ZFS} != "no" +SRCS+= zfs.c +.PATH: ${.CURDIR}/../../zfs +.endif + .PATH: ${.CURDIR}/arch/${MACHINE} # For smbios.c .PATH: ${.CURDIR}/../../i386/libi386 @@ -35,6 +40,13 @@ CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. CFLAGS+= -I${.CURDIR}/../../i386/libi386 +.if ${MK_ZFS} != "no" +CFLAGS+= -I${.CURDIR}/../../zfs +CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs +CFLAGS+= -DZFS_EFI_BOOT +# For zfsimpl.c +CWARNFLAGS+= -Wno-array-bounds +.endif CFLAGS+= -DNO_PCI -DEFI # make buildenv doesn't set DESTDIR, this means LIBSTAND @@ -67,7 +79,7 @@ CFLAGS+= -DEFI_STAGING_SIZE=${EFI_STAGING_SIZE} .endif -# Always add MI sources +# Always add MI sources .PATH: ${.CURDIR}/../../common .include "${.CURDIR}/../../common/Makefile.inc" CFLAGS+= -I${.CURDIR}/../../common Index: sys/boot/efi/loader/conf.c =================================================================== --- sys/boot/efi/loader/conf.c +++ sys/boot/efi/loader/conf.c @@ -31,14 +31,23 @@ #include #include #include +#ifdef ZFS_EFI_BOOT +#include "../zfs/libzfs.h" +#endif struct devsw *devsw[] = { &efipart_dev, &efinet_dev, +#ifdef ZFS_EFI_BOOT + &zfs_dev, +#endif NULL }; struct fs_ops *file_system[] = { +#ifdef ZFS_EFI_BOOT + &zfs_fsops, +#endif &dosfs_fsops, &ufs_fsops, &cd9660_fsops, Index: sys/boot/efi/loader/devicename.c =================================================================== --- sys/boot/efi/loader/devicename.c +++ sys/boot/efi/loader/devicename.c @@ -31,14 +31,18 @@ #include #include #include +#include #include "bootstrap.h" +#ifdef ZFS_EFI_BOOT +#include "libzfs.h" +#endif #include #include static int efi_parsedev(struct devdesc **, const char *, const char **); -/* +/* * Point (dev) at an allocated device specifier for the device matching the * path in (devspec). If it contains an explicit device specification, * use that. If not, use the default device. @@ -84,7 +88,7 @@ struct devsw *dv; char *cp; const char *np; - int i, err; + int i; /* minimum length check */ if (strlen(devspec) < 2) @@ -99,24 +103,44 @@ if (devsw[i] == NULL) return (ENOENT); - idev = malloc(sizeof(struct devdesc)); - if (idev == NULL) - return (ENOMEM); - - idev->d_dev = dv; - idev->d_type = dv->dv_type; - idev->d_unit = -1; - - err = 0; np = devspec + strlen(dv->dv_name); - if (*np != '\0' && *np != ':') { - idev->d_unit = strtol(np, &cp, 0); - if (cp == np) { - idev->d_unit = -1; + +#ifdef ZFS_EFI_BOOT + if (dv->dv_type == DEVT_ZFS) { + int err; + + idev = malloc(sizeof(struct zfs_devdesc)); + if (idev == NULL) + return (ENOMEM); + + err = zfs_parsedev((struct zfs_devdesc*)idev, np, path); + if (err != 0) { free(idev); - return (EUNIT); + return (err); } + *dev = idev; + cp = strchr(np + 1, ':'); + } else { +#endif + idev = malloc(sizeof(struct devdesc)); + if (idev == NULL) + return (ENOMEM); + + idev->d_dev = dv; + idev->d_type = dv->dv_type; + idev->d_unit = -1; + if (*np != '\0' && *np != ':') { + idev->d_unit = strtol(np, &cp, 0); + if (cp == np) { + idev->d_unit = -1; + free(idev); + return (EUNIT); + } + } +#ifdef ZFS_EFI_BOOT } +#endif + if (*cp != '\0' && *cp != ':') { free(idev); return (EINVAL); @@ -135,9 +159,13 @@ efi_fmtdev(void *vdev) { struct devdesc *dev = (struct devdesc *)vdev; - static char buf[32]; /* XXX device length constant? */ + static char buf[SPECNAMELEN + 1]; switch(dev->d_type) { +#ifdef ZFS_EFI_BOOT + case DEVT_ZFS: + return (zfs_fmtdev(dev)); +#endif case DEVT_NONE: strcpy(buf, "(no device)"); break; @@ -147,7 +175,7 @@ break; } - return(buf); + return (buf); } /* @@ -161,7 +189,7 @@ rv = efi_parsedev(&ncurr, value, NULL); if (rv != 0) - return(rv); + return (rv); free(ncurr); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); Index: sys/boot/efi/loader/main.c =================================================================== --- sys/boot/efi/loader/main.c +++ sys/boot/efi/loader/main.c @@ -28,6 +28,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include #include @@ -39,13 +40,13 @@ #include #include "loader_efi.h" +#include "libzfs.h" extern char bootprog_name[]; extern char bootprog_rev[]; extern char bootprog_date[]; extern char bootprog_maker[]; -struct devdesc currdev; /* our current device */ struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; @@ -61,13 +62,41 @@ EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; +static void efi_zfs_probe(void); + +/* + * Need this because EFI uses UTF-16 unicode string constants, but we + * use UTF-8. We can't use printf due to the possiblity of \0 and we + * don't support support wide characters either. + */ +static void +print_str16(const CHAR16 *str) +{ + int i; + + for (i = 0; str[i]; i++) + printf("%c", (char)str[i]); +} + EFI_STATUS main(int argc, CHAR16 *argv[]) { char vendor[128]; EFI_LOADED_IMAGE *img; EFI_GUID *guid; - int i; + int i, unit; + struct devsw *dev; + uint64_t pool_guid; + + archsw.arch_autoload = efi_autoload; + archsw.arch_getdev = efi_getdev; + archsw.arch_copyin = efi_copyin; + archsw.arch_copyout = efi_copyout; + archsw.arch_readin = efi_readin; +#ifdef ZFS_EFI_BOOT + /* Note this needs to be set before ZFS init. */ + archsw.arch_zfs_probe = efi_zfs_probe; +#endif /* * XXX Chicken-and-egg problem; we want to have console output @@ -92,6 +121,13 @@ /* Get our loaded image protocol interface structure. */ BS->HandleProtocol(IH, &imgid, (VOID**)&img); + printf("Command line arguments:"); + for (i = 0; i < argc; i++) { + printf(" "); + print_str16(argv[i]); + } + printf("\n"); + printf("Image base: 0x%lx\n", (u_long)img->ImageBase); printf("EFI version: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); @@ -105,9 +141,6 @@ printf("%s, Revision %s\n", bootprog_name, bootprog_rev); printf("(%s, %s)\n", bootprog_maker, bootprog_date); - efi_handle_lookup(img->DeviceHandle, &currdev.d_dev, &currdev.d_unit); - currdev.d_type = currdev.d_dev->dv_type; - /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we @@ -119,18 +152,43 @@ */ BS->SetWatchdogTimer(0, 0, 0, NULL); - env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), - efi_setcurrdev, env_nounset); - env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, - env_nounset); + if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid) != 0) + return (EFI_NOT_FOUND); - setenv("LINES", "24", 1); /* optional */ + switch (dev->dv_type) { +#ifdef ZFS_EFI_BOOT + case DEVT_ZFS: { + struct zfs_devdesc currdev; + + currdev.d_dev = dev; + currdev.d_unit = unit; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_opendata = NULL; + currdev.pool_guid = pool_guid; + currdev.root_guid = 0; + env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), + efi_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, + env_nounset); + break; + } +#endif + default: { + struct devdesc currdev; - archsw.arch_autoload = efi_autoload; - archsw.arch_getdev = efi_getdev; - archsw.arch_copyin = efi_copyin; - archsw.arch_copyout = efi_copyout; - archsw.arch_readin = efi_readin; + currdev.d_dev = dev; + currdev.d_unit = unit; + currdev.d_opendata = NULL; + currdev.d_type = currdev.d_dev->dv_type; + env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), + efi_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, + env_nounset); + break; + } + } + + setenv("LINES", "24", 1); /* optional */ for (i = 0; i < ST->NumberOfTableEntries; i++) { guid = &ST->ConfigurationTable[i].VendorGuid; @@ -204,50 +262,47 @@ status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); if (status != EFI_BUFFER_TOO_SMALL) { printf("Can't determine memory map size\n"); - return CMD_ERROR; + return (CMD_ERROR); } map = malloc(sz); status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); if (EFI_ERROR(status)) { printf("Can't read memory map\n"); - return CMD_ERROR; + return (CMD_ERROR); } ndesc = sz / dsz; printf("%23s %12s %12s %8s %4s\n", - "Type", "Physical", "Virtual", "#Pages", "Attr"); + "Type", "Physical", "Virtual", "#Pages", "Attr"); for (i = 0, p = map; i < ndesc; - i++, p = NextMemoryDescriptor(p, dsz)) { - printf("%23s %012lx %012lx %08lx ", - types[p->Type], - p->PhysicalStart, - p->VirtualStart, - p->NumberOfPages); - if (p->Attribute & EFI_MEMORY_UC) - printf("UC "); - if (p->Attribute & EFI_MEMORY_WC) - printf("WC "); - if (p->Attribute & EFI_MEMORY_WT) - printf("WT "); - if (p->Attribute & EFI_MEMORY_WB) - printf("WB "); - if (p->Attribute & EFI_MEMORY_UCE) - printf("UCE "); - if (p->Attribute & EFI_MEMORY_WP) - printf("WP "); - if (p->Attribute & EFI_MEMORY_RP) - printf("RP "); - if (p->Attribute & EFI_MEMORY_XP) - printf("XP "); - printf("\n"); + i++, p = NextMemoryDescriptor(p, dsz)) { + printf("%23s %012lx %012lx %08lx ", types[p->Type], + p->PhysicalStart, p->VirtualStart, p->NumberOfPages); + if (p->Attribute & EFI_MEMORY_UC) + printf("UC "); + if (p->Attribute & EFI_MEMORY_WC) + printf("WC "); + if (p->Attribute & EFI_MEMORY_WT) + printf("WT "); + if (p->Attribute & EFI_MEMORY_WB) + printf("WB "); + if (p->Attribute & EFI_MEMORY_UCE) + printf("UCE "); + if (p->Attribute & EFI_MEMORY_WP) + printf("WP "); + if (p->Attribute & EFI_MEMORY_RP) + printf("RP "); + if (p->Attribute & EFI_MEMORY_XP) + printf("XP "); + printf("\n"); } - return CMD_OK; + return (CMD_OK); } -COMMAND_SET(configuration, "configuration", - "print configuration tables", command_configuration); +COMMAND_SET(configuration, "configuration", "print configuration tables", + command_configuration); static const char * guid_to_string(EFI_GUID *guid) @@ -295,7 +350,7 @@ printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); } - return CMD_OK; + return (CMD_OK); } @@ -370,20 +425,17 @@ status = RS->GetNextVariableName(&varsz, NULL, NULL); for (; status != EFI_NOT_FOUND; ) { - status = RS->GetNextVariableName(&varsz, var, - &varguid); + status = RS->GetNextVariableName(&varsz, var, &varguid); //if (EFI_ERROR(status)) //break; conout->OutputString(conout, var); printf("="); datasz = 0; - status = RS->GetVariable(var, &varguid, NULL, &datasz, - NULL); + status = RS->GetVariable(var, &varguid, NULL, &datasz, NULL); /* XXX: check status */ data = malloc(datasz); - status = RS->GetVariable(var, &varguid, NULL, &datasz, - data); + status = RS->GetVariable(var, &varguid, NULL, &datasz, data); if (EFI_ERROR(status)) printf(""); else { @@ -402,6 +454,27 @@ return (CMD_OK); } +COMMAND_SET(lszfs, "lszfs", "list child datasets of a zfs dataset", + command_lszfs); + +static int +command_lszfs(int argc, char *argv[]) +{ + int err; + + if (argc != 2) { + command_errmsg = "wrong number of arguments"; + return (CMD_ERROR); + } + + err = zfs_list(argv[1]); + if (err != 0) { + command_errmsg = strerror(err); + return (CMD_ERROR); + } + return (CMD_OK); +} + #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); @@ -420,3 +493,21 @@ COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif + +static void +efi_zfs_probe(void) +{ + EFI_HANDLE h; + u_int unit; + int i; + char dname[SPECNAMELEN + 1]; + uint64_t guid; + + unit = 0; + h = efi_find_handle(&efipart_dev, 0); + for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { + snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i); + if (zfs_probe_dev(dname, &guid) == 0) + (void)efi_handle_update_dev(h, &zfs_dev, unit++, guid); + } +} Index: sys/boot/zfs/zfs.c =================================================================== --- sys/boot/zfs/zfs.c +++ sys/boot/zfs/zfs.c @@ -140,7 +140,7 @@ n = size; if (fp->f_seekp + n > sb.st_size) n = sb.st_size - fp->f_seekp; - + rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, start, n); if (rc) return (rc); @@ -493,7 +493,7 @@ } } close(pa.fd); - return (0); + return (ret); } /* Index: sys/cddl/boot/zfs/lz4.c =================================================================== --- sys/cddl/boot/zfs/lz4.c +++ sys/cddl/boot/zfs/lz4.c @@ -52,7 +52,7 @@ * Returns 0 on success (decompression function returned non-negative) * and non-zero on failure (decompression function returned negative). */ - return (LZ4_uncompress_unknownOutputSize(s_start + 4, d_start, bufsiz, + return (LZ4_uncompress_unknownOutputSize((const char *)s_start + 4, d_start, bufsiz, d_len) < 0); }