Index: sys/boot/efi/boot1/Makefile =================================================================== --- sys/boot/efi/boot1/Makefile +++ sys/boot/efi/boot1/Makefile @@ -13,13 +13,15 @@ INTERNALPROG= # architecture-specific loader code -SRCS= boot1.c self_reloc.c start.S +SRCS= boot1.c self_reloc.c start.S ufs_module.c zfs_module.c CFLAGS+= -I. CFLAGS+= -I${.CURDIR}/../include CFLAGS+= -I${.CURDIR}/../include/${MACHINE} CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. +CFLAGS+= -I${.CURDIR}/../../zfs/ +CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs/ # 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) 2014 Eric McCorkle + * All rights reverved. * * Redistribution and use in source and binary forms are freely * permitted provided that the above copyright notice and this @@ -21,13 +23,14 @@ __FBSDID("$FreeBSD$"); #include -#include #include #include #include #include +#include "boot_module.h" + #define _PATH_LOADER "/boot/loader.efi" #define _PATH_KERNEL "/boot/kernel/kernel" @@ -41,13 +44,19 @@ u_int sp_size; }; -static const char digits[] = "0123456789abcdef"; +static const boot_module_t* const boot_modules[] = +{ +#ifdef ZFS_EFI_BOOT + &zfs_module, +#endif +#ifdef UFS_EFI_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); @@ -62,8 +71,79 @@ static EFI_SYSTEM_TABLE *systab; static EFI_HANDLE *image; -static void -bcopy(const void *src, void *dst, size_t len) + +void* Malloc(size_t len, const char* file, int line) +{ + void* out; + if (systab->BootServices->AllocatePool(EfiLoaderData, + len, &out) != + EFI_SUCCESS) { + printf("Can't allocate memory pool\n"); + return NULL; + } + return out; +} + +char* strcpy(char* dst, const char* src) { + for(int i = 0; src[i]; i++) + dst[i] = src[i]; + + return dst; +} + +char* strchr(const char* s, int c) { + for(int i = 0; s[i]; i++) + if (s[i] == c) + return (char*)(s + i); + + return NULL; +} + +int strncmp(const char *a, const char *b, size_t len) +{ + for (int i = 0; i < len; i++) + if(a[i] == '\0' && b[i] == '\0') { + return 0; + } else if(a[i] < b[i]) { + return -1; + } else if(a[i] > b[i]) { + return 1; + } + + return 0; +} + +char* strdup(const char* s) { + int len; + + for(len = 1; s[len]; len++); + + char* out = malloc(len); + + for(int i = 0; i < len; i++) + out[i] = s[i]; + + return out; +} + +int bcmp(const void *a, const void *b, size_t len) +{ + const char *sa = a; + const char *sb = b; + + for (int 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; char *d = dst; @@ -72,53 +152,123 @@ *d++ = *s++; } -static void -memcpy(void *dst, const void *src, size_t len) +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 -strcmp(const char *s1, const char *s2) +int strcmp(const char *s1, const char *s2) { for (; *s1 == *s2 && *s1; s1++, s2++) ; return ((u_char)*s1 - (u_char)*s2); } +int putchr(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 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_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; -static EFI_BLOCK_IO *bootdev; -static EFI_DEVICE_PATH *bootdevpath; -static EFI_HANDLE *bootdevhandle; +#define MAX_DEVS 128 -EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab) -{ - EFI_HANDLE handles[128]; +void try_load(const boot_module_t* const mod, + const dev_info_t devs[], + size_t ndevs) +{ + int idx; + size_t bufsize; + void* const buffer = mod->load(devs, ndevs, _PATH_LOADER, &idx, &bufsize); + EFI_HANDLE loaderhandle; + EFI_LOADED_IMAGE *loaded_image; + + if (NULL == buffer) { + printf("Could not load file\n"); + return; + } + //printf("Loaded file %s, image at %p\n" + // "Attempting to load as bootable image...", + // _PATH_LOADER, image); + if (systab->BootServices->LoadImage(TRUE, image, devs[idx].devpath, + buffer, bufsize, &loaderhandle) != + EFI_SUCCESS) { + //printf("failed\n"); + return; + } + //printf("success\n" + // "Preparing to execute image..."); + + if (systab->BootServices->HandleProtocol(loaderhandle, + &LoadedImageGUID, + (VOID**)&loaded_image) != + EFI_SUCCESS) { + //printf("failed\n"); + return; + } + + //printf("success\n"); + + loaded_image->DeviceHandle = devs[idx].devhandle; + + //printf("Image prepared, attempting to execute\n"); + // XXX Set up command args first + if (systab->BootServices->StartImage(loaderhandle, NULL, NULL) != + EFI_SUCCESS) { + //printf("Failed to execute loader\n"); + return; + } + //printf("Shouldn't be here!\n"); +} + +void efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab) +{ + EFI_HANDLE handles[MAX_DEVS]; + dev_info_t module_devs[NUM_BOOT_MODULES][MAX_DEVS]; + size_t dev_offsets[NUM_BOOT_MODULES]; EFI_BLOCK_IO *blkio; - UINTN i, nparts = sizeof(handles), cols, rows, max_dim, best_mode; + UINTN nparts = sizeof(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; + // Basic initialization systab = Xsystab; image = Ximage; + for(int i = 0; i < NUM_BOOT_MODULES; i++) + { + dev_offsets[i] = 0; + } + + // Set up the console, so printf works. BS = systab->BootServices; status = BS->LocateProtocol(&ConsoleControlGUID, NULL, (VOID **)&ConsoleControl); @@ -128,10 +278,14 @@ /* * Reset the console and find the best text mode. */ + UINTN max_dim; + UINTN best_mode; + UINTN cols; + UINTN rows; conout = systab->ConOut; conout->Reset(conout, TRUE); max_dim = best_mode = 0; - for (i = 0; ; i++) { + for (int i = 0; ; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) @@ -141,211 +295,100 @@ best_mode = i; } } + if (max_dim > 0) conout->SetMode(conout, best_mode); conout->EnableCursor(conout, TRUE); conout->ClearScreen(conout); printf("\n" - ">> FreeBSD EFI boot block\n"); - printf(" Loader path: %s\n", path); + ">> FreeBSD ZFS-enabled EFI boot block\n"); + printf(" Loader path: %s\n\n", _PATH_LOADER); + + printf(" Initializing modules:"); + for(int i = 0; i < NUM_BOOT_MODULES; i++) + { + if (NULL != boot_modules[i]) + { + printf(" %s", boot_modules[i]->name); + boot_modules[i]->init(image, systab, BS); + } + } + putchr('\n', NULL); + // Get all the device handles status = systab->BootServices->LocateHandle(ByProtocol, &BlockIoProtocolGUID, NULL, &nparts, handles); nparts /= sizeof(handles[0]); + //printf(" Scanning %lu device handles\n", nparts); - for (i = 0; i < nparts; i++) { + // Scan all partitions, probing with all modules. + for (int i = 0; i < nparts; i++) { + dev_info_t devinfo; + + // Figure out if we're dealing with an actual partition status = systab->BootServices->HandleProtocol(handles[i], &DevicePathGUID, (void **)&devpath); - if (EFI_ERROR(status)) + if (EFI_ERROR(status)) { + //printf(" Not a device path protocol\n"); continue; + } - while (!IsDevicePathEnd(NextDevicePathNode(devpath))) + while (!IsDevicePathEnd(NextDevicePathNode(devpath))) { + //printf(" Advancing to next device\n"); devpath = NextDevicePathNode(devpath); + } status = systab->BootServices->HandleProtocol(handles[i], &BlockIoProtocolGUID, (void **)&blkio); - if (EFI_ERROR(status)) + if (EFI_ERROR(status)) { + //printf(" Not a block device\n"); continue; + } - if (!blkio->Media->LogicalPartition) + if (!blkio->Media->LogicalPartition) { + //printf(" Logical partition\n"); continue; + } - if (domount(devpath, blkio, 1) >= 0) - break; - } - - if (i == nparts) - panic("No bootable partition found"); - - bootdevhandle = handles[i]; - load(path); - - panic("Load failed"); - - return EFI_SUCCESS; -} - -static int -dskread(void *buf, u_int64_t lba, int nblk) -{ - 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); - - if (EFI_ERROR(status)) - return (-1); - - return (0); -} - -#include "ufsread.c" - -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; - } - size = DIP(di_size); - n = size - fs_off; - return (n); -} - -static struct dmadat __dmadat; - -static int -domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet) -{ - - dmadat = &__dmadat; - bootdev = blkio; - bootdevpath = device; - if (fsread(0, NULL, 0)) { - if (!quiet) - printf("domount: can't read superblock\n"); - return (-1); + // Setup devinfo + devinfo.dev = blkio; + devinfo.devpath = devpath; + devinfo.devhandle = handles[i]; + devinfo.devdata = NULL; + + // Run through each module, see if it can load this partition + for (int j = 0; j < NUM_BOOT_MODULES; j++ ) + { + if (NULL != boot_modules[j] && + boot_modules[j]->probe(&devinfo)) + { + // If it can, save it to the device list for + // that module + module_devs[j][dev_offsets[j]++] = devinfo; + } + } } - if (!quiet) - printf("Succesfully mounted UFS filesystem\n"); - return (0); -} -static void -load(const char *fname) -{ - 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); - return; - } + // Select a partition to boot. We do this by trying each + // module in order. + for (int i = 0; i < NUM_BOOT_MODULES; i++) + { + if (NULL != boot_modules[i]) + { + //printf(" Trying to load from %lu %s partitions\n", + // dev_offsets[i], boot_modules[i]->name); + try_load(boot_modules[i], module_devs[i], + dev_offsets[i]); + //printf(" Failed\n"); + } + } - 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); + // If we get here, we're out of luck... + panic("No bootable partitions found!"); } -static void -panic(const char *fmt, ...) +void panic(const char *fmt, ...) { char buf[128]; va_list ap; @@ -358,49 +401,24 @@ while (1) {} } -static int -printf(const char *fmt, ...) +int printf(const char *fmt, ...) { va_list ap; int ret; - /* Don't annoy the user as we probe for partitions */ - if (strcmp(fmt,"Not ufs\n") == 0) - 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) +void vprintf(const char *fmt, va_list ap) { - 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) -{ - 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; Index: sys/boot/efi/boot1/boot_module.h =================================================================== --- sys/boot/efi/boot1/boot_module.h +++ sys/boot/efi/boot1/boot_module.h @@ -0,0 +1,60 @@ +#ifndef _BOOT_MODULE_H_ +#define _BOOT_MODULE_H_ + +#include + +#include +#include +#include + +#define UFS_EFI_BOOT 1 +#define ZFS_EFI_BOOT 1 + +// EFI device info +typedef struct dev_info_t +{ + EFI_BLOCK_IO *dev; + EFI_DEVICE_PATH *devpath; + EFI_HANDLE *devhandle; + void *devdata; +} 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* const name; + + // Initialize the module. + void (* const init)(EFI_HANDLE image, + EFI_SYSTEM_TABLE* systab, + EFI_BOOT_SERVICES *bootsrv); + + // Check to see if curr_dev is a device that this module can handle. + bool (* const probe)(dev_info_t* dev); + + // Select the best out of a set of devices that probe indicated were + // loadable, and load it. + void* (* const load)(const dev_info_t devs[], + size_t ndevs, + const char* loader_path, + int* idxref, + size_t* bufsizeref); +} boot_module_t; + +// Standard boot modules +#ifdef UFS_EFI_BOOT +extern const boot_module_t ufs_module; +#endif +#ifdef ZFS_EFI_BOOT +extern const boot_module_t zfs_module; +#endif + +// Functions available to modules +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); + +#endif Index: sys/boot/efi/boot1/ufs_module.c =================================================================== --- sys/boot/efi/boot1/ufs_module.c +++ sys/boot/efi/boot1/ufs_module.c @@ -0,0 +1,210 @@ +/*- + * 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 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 +#include + +#include +#include + +#include + +#include "boot_module.h" + +static EFI_HANDLE image; +static EFI_SYSTEM_TABLE* systab; +static EFI_BOOT_SERVICES *bootsrv; +static dev_info_t devinfo; +static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL; + +static int +dskread(void *buf, u_int64_t lba, int nblk) +{ + EFI_STATUS status; + int size; + + 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 (EFI_ERROR(status)) + return (-1); + + return (0); +} + +#include "ufsread.c" + +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) { + 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 bool +probe(dev_info_t* const dev) +{ + devinfo = *dev; + dmadat = &__dmadat; + if (fsread(0, NULL, 0)) { + return 0; + } + return 1; +} + +static void* +try_load(const dev_info_t dev, + const char* const loader_path, + size_t* const bufsizeref) +{ + ufs_ino_t ino; + EFI_STATUS status; + void *buffer; + size_t bufsize; + + devinfo = dev; + if ((ino = lookup(loader_path)) == 0) { + printf("File %s not found\n", loader_path); + return NULL; + } + + bufsize = fsstat(ino); + *bufsizeref = bufsize; + status = systab->BootServices->AllocatePool(EfiLoaderData, + bufsize, &buffer); + fsread(ino, buffer, bufsize); + return buffer; +} + +static void* +load(const dev_info_t devs[], + const size_t ndevs, + const char* const loader_path, + int* const idxref, + size_t* const bufsizeref) +{ + for(int i = 0; i < ndevs; i++) + { + void* const out = try_load(devs[i], loader_path, bufsizeref); + if (out != NULL) + { + *idxref = i; + return out; + } + } + return NULL; +} + + +static void init(EFI_HANDLE xImage, + EFI_SYSTEM_TABLE* xSystab, + EFI_BOOT_SERVICES * xBootsrv) +{ + image = xImage; + systab = xSystab; + bootsrv = xBootsrv; +} + +const boot_module_t ufs_module = +{ + .name = "UFS", + .init = init, + .probe = probe, + .load = load +}; Index: sys/boot/efi/boot1/zfs_module.c =================================================================== --- sys/boot/efi/boot1/zfs_module.c +++ sys/boot/efi/boot1/zfs_module.c @@ -0,0 +1,201 @@ +/* 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. + * + * 3. Neither the name of the author nor the names of any contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 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 +#include +#include + +#include +#include +#include + +#include + +#include "boot_module.h" + +#include "libzfs.h" +#include "zfsimpl.c" + +#define PATH_CONFIG "/boot/config" +#define PATH_DOTCONFIG "/boot/.config" + +static EFI_HANDLE image; +static EFI_SYSTEM_TABLE* systab; +static EFI_BOOT_SERVICES *bootsrv; + +static int +vdev_read(vdev_t * const vdev, + void * const priv, + const off_t off, + void * const buf, + const size_t bytes) +{ + const dev_info_t* const devinfo = (const dev_info_t*) priv; + const off_t lba = off / devinfo->dev->Media->BlockSize; + const EFI_STATUS status = + devinfo->dev->ReadBlocks(devinfo->dev, + devinfo->dev->Media->MediaId, + lba, bytes, buf); + if (EFI_ERROR(status)) + return (-1); + + return (0); +} + +static bool probe(dev_info_t* const dev) +{ + spa_t* spa; + int result = vdev_probe(vdev_read, dev, &spa); + dev->devdata = spa; + + return result == 0; +} + +static void* try_load(const dev_info_t devinfo, + const char* const loader_path, + size_t* const bufsizeref) +{ + spa_t *spa = devinfo.devdata; + struct zfsmount zfsmount; + dnode_phys_t dn; + bool autoboot = true; + + if (zfs_spa_init(spa) != 0) { + // Mount failed. Don't report this loudly + return NULL; + } + + // First, try mounting the ZFS volume + if (zfs_mount(spa, 0, &zfsmount) != 0) { + // Mount failed. Don't report this loudly + return NULL; + } + + //vdev_t * const primary_vdev = spa_get_primary_vdev(spa); + + if (zfs_lookup(&zfsmount, loader_path, &dn) != 0) { + return NULL; + } + + struct stat st; + if (zfs_dnode_stat(spa, &dn, &st)) { + return NULL; + } + + const size_t bufsize = st.st_size; + void* buffer; + EFI_STATUS status; + + *bufsizeref = bufsize; + + if (systab->BootServices->AllocatePool(EfiLoaderData, + bufsize, &buffer) != + EFI_SUCCESS) { + return NULL; + } + + if (dnode_read(spa, &dn, 0, buffer, bufsize) < 0) { + return NULL; + } + + return buffer; +} + +static int zfs_mount_ds(const char * const dsname, + struct zfsmount * const zfsmount, + spa_t ** const spa) +{ + uint64_t newroot; + spa_t *newspa; + char *q; + + q = strchr(dsname, '/'); + if (q) + *q++ = '\0'; + newspa = spa_find_by_name(dsname); + if (newspa == NULL) { + printf("\nCan't find ZFS pool %s\n", dsname); + return -1; + } + + if (zfs_spa_init(newspa)) + return -1; + + newroot = 0; + if (q) { + if (zfs_lookup_dataset(newspa, q, &newroot)) { + printf("\nCan't find dataset %s in ZFS pool %s\n", + q, newspa->spa_name); + return -1; + } + } + if (zfs_mount(newspa, newroot, zfsmount)) { + printf("\nCan't mount ZFS dataset\n"); + return -1; + } + *spa = newspa; + return (0); +} + +static void* load(const dev_info_t devs[], + const size_t ndevs, + const char* const loader_path, + int* const idxref, + size_t* const bufsizeref) +{ + for(int i = 0; i < ndevs; i++) { + void* const out = try_load(devs[i], loader_path, bufsizeref); + if (out != NULL) + { + *idxref = i; + return out; + } + } + return NULL; +} + +static void init(EFI_HANDLE xImage, + EFI_SYSTEM_TABLE* xSystab, + EFI_BOOT_SERVICES * xBootsrv) +{ + image = xImage; + systab = xSystab; + bootsrv = xBootsrv; + zfs_init(); +} + +const boot_module_t zfs_module = +{ + .name = "ZFS", + .init = init, + .probe = probe, + .load = load +}; 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 *); +void efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); +int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); int efi_status_to_errno(EFI_STATUS); time_t efi_time(EFI_TIME *); 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; @@ -78,8 +79,28 @@ return (NULL); } +void efi_handle_update_dev(const EFI_HANDLE handle, + struct devsw * const dev, + int unit, + uint64_t guid) +{ + int idx; + + for (idx = 0; idx < nentries; idx++) { + if (entry[idx].handle != handle) + continue; + entry[idx].dev = dev; + entry[idx].unit = unit; + entry[idx].alias = NULL; + entry[idx].extra = guid; + } +} + 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,6 +111,8 @@ *dev = entry[idx].dev; if (unit != NULL) *unit = entry[idx].unit; + if (extra != NULL) + *extra = entry[idx].extra; return (0); } return (ENOENT); Index: sys/boot/efi/loader/Makefile =================================================================== --- sys/boot/efi/loader/Makefile +++ sys/boot/efi/loader/Makefile @@ -21,7 +21,8 @@ main.c \ self_reloc.c \ smbios.c \ - vers.c + vers.c \ + ${.CURDIR}/zfs.c .PATH: ${.CURDIR}/arch/${MACHINE} # For smbios.c @@ -35,6 +36,8 @@ CFLAGS+= -I${.CURDIR}/../../../contrib/dev/acpica/include CFLAGS+= -I${.CURDIR}/../../.. CFLAGS+= -I${.CURDIR}/../../i386/libi386 +CFLAGS+= -I${.CURDIR}/../../zfs +CFLAGS+= -I${.CURDIR}/../../../cddl/boot/zfs CFLAGS+= -DNO_PCI -DEFI # make buildenv doesn't set DESTDIR, this means LIBSTAND @@ -67,7 +70,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 @@ -78,13 +81,16 @@ LDSCRIPT= ${.CURDIR}/arch/${MACHINE}/ldscript.${MACHINE} LDFLAGS+= -Wl,-T${LDSCRIPT} -Wl,-Bsymbolic -shared -CLEANFILES+= vers.c loader.efi +CLEANFILES+= zfs.c vers.c loader.efi NEWVERSWHAT= "EFI loader" ${MACHINE} vers.c: ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/../../efi/loader/version sh ${.CURDIR}/../../common/newvers.sh ${.CURDIR}/version ${NEWVERSWHAT} +zfs.c: + cp ${.CURDIR}/../../zfs/zfs.c ${.CURDIR} + OBJCOPY?= objcopy OBJDUMP?= objdump @@ -108,9 +114,9 @@ LIBEFI= ${.OBJDIR}/../libefi/libefi.a -DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} \ +DPADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ../../../../lib/libstand/libstand.a \ ${LDSCRIPT} -LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ${LIBSTAND} +LDADD= ${LIBFICL} ${LIBEFI} ${LIBFDT} ${LIBEFI_FDT} ../../../../lib/libstand/libstand.a .endif # ${COMPILER_TYPE} != "gcc" Index: sys/boot/efi/loader/conf.c =================================================================== --- sys/boot/efi/loader/conf.c +++ sys/boot/efi/loader/conf.c @@ -31,14 +31,17 @@ #include #include #include +#include "../zfs/libzfs.h" struct devsw *devsw[] = { &efipart_dev, &efinet_dev, + &zfs_dev, NULL }; struct fs_ops *file_system[] = { + &zfs_fsops, &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 @@ -32,13 +32,14 @@ #include #include #include "bootstrap.h" +#include "libzfs.h" #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. @@ -48,7 +49,6 @@ { struct devdesc **dev = (struct devdesc **)vdev; int rv; - /* * If it looks like this is just a path and no device, then * use the current device instead. @@ -61,7 +61,8 @@ } /* Parse the device name off the beginning of the devspec. */ - return (efi_parsedev(dev, devspec, path)); + const int out = efi_parsedev(dev, devspec, path); + return out; } /* @@ -87,8 +88,9 @@ int i, err; /* minimum length check */ - if (strlen(devspec) < 2) + if (strlen(devspec) < 2) { return (EINVAL); + } /* look for a device that matches */ for (i = 0; devsw[i] != NULL; i++) { @@ -96,27 +98,39 @@ if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name))) break; } - if (devsw[i] == NULL) + if (devsw[i] == NULL) { return (ENOENT); + } + np = devspec + strlen(dv->dv_name); - 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 (DEVT_ZFS == dv->dv_type) { + idev = malloc(sizeof(struct zfs_devdesc)); + int out = zfs_parsedev((struct zfs_devdesc*)idev, np, path); + if (0 == out) { + *dev = idev; + cp = strchr(np + 1, ':'); + } else { + free(idev); + return out; + } + } else { + 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); + } + } + } 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; - free(idev); - return (EUNIT); - } - } if (*cp != '\0' && *cp != ':') { free(idev); return (EINVAL); @@ -138,10 +152,11 @@ static char buf[32]; /* XXX device length constant? */ switch(dev->d_type) { + case DEVT_ZFS: + return zfs_fmtdev(dev); case DEVT_NONE: strcpy(buf, "(no device)"); break; - default: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; Index: sys/boot/efi/loader/main.c =================================================================== --- sys/boot/efi/loader/main.c +++ sys/boot/efi/loader/main.c @@ -39,14 +39,16 @@ #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 */ +/* our current device */ +/* MI/MD interface boundary */ +struct arch_switch archsw; EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; @@ -61,6 +63,70 @@ EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; +static void efi_zfs_probe(void); + +static void +print_str16(const CHAR16* const str) +{ + for(int i; str[i]; i++) + { + printf("%c", str[i]); + } +} + +/* +static int +str16cmp(const CHAR16 const *a, + const char* const b) +{ + for(int i = 0; a[i] || b[i]; i++) + { + const CHAR16 achr = a[i]; + const CHAR16 bchr = b[i]; + if (achr < bchr) + { + return -1; + } else if (achr > bchr) + { + return 1; + } + } + return 0; +} + +// Split an arg of the form "argname=argval", replacing the '=' with a \0 +static CHAR16* +split_arg(CHAR16 *const str) +{ + for (int i = 0; str[i]; i++) + { + if ('=' == str[i]) + { + str[i] = 0; + return str + i + 1; + } + } + return NULL; +} + +static void +handle_arg(CHAR16 *const arg) +{ + const CHAR16* const argval = split_arg(arg); + const CHAR16* const argname = arg; + + if (NULL != argval) + { + printf("Unrecognized argument \""); + print_arg(argname); + printf("\n"); + } else { + printf("Unrecognized argument \""); + print_arg(argname); + printf("\n"); + } +} +*/ EFI_STATUS main(int argc, CHAR16 *argv[]) { @@ -69,7 +135,15 @@ EFI_GUID *guid; int i; - /* + 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; + // Note this needs to be set before ZFS init + archsw.arch_zfs_probe = efi_zfs_probe; + + /* * XXX Chicken-and-egg problem; we want to have console output * early, but some console attributes may depend on reading from * eg. the boot device, which we can't do yet. We can use @@ -85,13 +159,22 @@ /* * March through the device switch probing for things. */ - for (i = 0; devsw[i] != NULL; i++) - if (devsw[i]->dv_init != NULL) + for (i = 0; devsw[i] != NULL; i++) { + if (devsw[i]->dv_init != NULL) { + printf("Initializing %s\n", devsw[i]->dv_name); (devsw[i]->dv_init)(); - + } + } /* 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,8 +188,13 @@ 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; + // Handle command-line arguments + /* + for(i = 1; i < argc; i++) + { + handle_arg(argv[i]); + } + */ /* * Disable the watchdog timer. By default the boot manager sets @@ -119,19 +207,39 @@ */ 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); + struct devsw *dev; + int unit; + uint64_t pool_guid; + efi_handle_lookup(img->DeviceHandle, &dev, &unit, &pool_guid); + switch (dev->dv_type) { + 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; + default: { + struct devdesc currdev; + 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 */ - 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; - for (i = 0; i < ST->NumberOfTableEntries; i++) { guid = &ST->ConfigurationTable[i].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { @@ -350,7 +458,6 @@ return (CMD_OK); } - COMMAND_SET(nvram, "nvram", "get or set NVRAM variables", command_nvram); static int @@ -402,6 +509,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 +548,23 @@ COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif + +static void +efi_zfs_probe(void) +{ + EFI_BLOCK_IO *blkio; + EFI_HANDLE h; + EFI_STATUS status; + u_int unit = 0; + char devname[32]; + uint64_t pool_guid; + + for (int i = 0, h = efi_find_handle(&efipart_dev, 0); + h != NULL; h = efi_find_handle(&efipart_dev, ++i)) { + snprintf(devname, sizeof devname, "%s%d:", + efipart_dev.dv_name, i); + if(0 == zfs_probe_dev(devname, &pool_guid)) { + efi_handle_update_dev(h, &zfs_dev, unit++, pool_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); } /*