Index: head/lib/libstand/stand.h =================================================================== --- head/lib/libstand/stand.h (revision 313332) +++ head/lib/libstand/stand.h (revision 313333) @@ -1,421 +1,422 @@ /* * Copyright (c) 1998 Michael Smith. * 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$ * From $NetBSD: stand.h,v 1.22 1997/06/26 19:17:40 drochner Exp $ */ /*- * Copyright (c) 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * @(#)stand.h 8.1 (Berkeley) 6/11/93 */ #ifndef STAND_H #define STAND_H #include #include #include #include /* this header intentionally exports NULL from */ #include #define CHK(fmt, args...) printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args) #define PCHK(fmt, args...) {printf("%s(%d): " fmt "\n", __func__, __LINE__ , ##args); getchar();} /* Avoid unwanted userlandish components */ #define _KERNEL #include #undef _KERNEL /* special stand error codes */ #define EADAPT (ELAST+1) /* bad adaptor */ #define ECTLR (ELAST+2) /* bad controller */ #define EUNIT (ELAST+3) /* bad unit */ #define ESLICE (ELAST+4) /* bad slice */ #define EPART (ELAST+5) /* bad partition */ #define ERDLAB (ELAST+6) /* can't read disk label */ #define EUNLAB (ELAST+7) /* unlabeled disk */ #define EOFFSET (ELAST+8) /* relative seek not supported */ #define ESALAST (ELAST+8) /* */ struct open_file; /* * This structure is used to define file system operations in a file system * independent way. * * XXX note that filesystem providers should export a pointer to their fs_ops * struct, so that consumers can reference this and thus include the * filesystems that they require. */ struct fs_ops { const char *fs_name; int (*fo_open)(const char *path, struct open_file *f); int (*fo_close)(struct open_file *f); int (*fo_read)(struct open_file *f, void *buf, size_t size, size_t *resid); int (*fo_write)(struct open_file *f, void *buf, size_t size, size_t *resid); off_t (*fo_seek)(struct open_file *f, off_t offset, int where); int (*fo_stat)(struct open_file *f, struct stat *sb); int (*fo_readdir)(struct open_file *f, struct dirent *d); }; /* * libstand-supplied filesystems */ extern struct fs_ops ufs_fsops; extern struct fs_ops tftp_fsops; extern struct fs_ops nfs_fsops; extern struct fs_ops cd9660_fsops; extern struct fs_ops nandfs_fsops; extern struct fs_ops gzipfs_fsops; extern struct fs_ops bzipfs_fsops; extern struct fs_ops dosfs_fsops; extern struct fs_ops ext2fs_fsops; extern struct fs_ops splitfs_fsops; extern struct fs_ops pkgfs_fsops; /* where values for lseek(2) */ #define SEEK_SET 0 /* set file offset to offset */ #define SEEK_CUR 1 /* set file offset to current plus offset */ #define SEEK_END 2 /* set file offset to EOF plus offset */ /* * Device switch */ struct devsw { const char dv_name[8]; int dv_type; /* opaque type constant, arch-dependant */ int (*dv_init)(void); /* early probe call */ int (*dv_strategy)(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize); int (*dv_open)(struct open_file *f, ...); int (*dv_close)(struct open_file *f); int (*dv_ioctl)(struct open_file *f, u_long cmd, void *data); int (*dv_print)(int verbose); /* print device information */ void (*dv_cleanup)(void); }; /* * libstand-supplied device switch */ extern struct devsw netdev; extern int errno; /* * Generic device specifier; architecture-dependent * versions may be larger, but should be allowed to * overlap. */ struct devdesc { struct devsw *d_dev; int d_type; #define DEVT_NONE 0 #define DEVT_DISK 1 #define DEVT_NET 2 #define DEVT_CD 3 #define DEVT_ZFS 4 +#define DEVT_FD 5 int d_unit; void *d_opendata; }; struct open_file { int f_flags; /* see F_* below */ struct devsw *f_dev; /* pointer to device operations */ void *f_devdata; /* device specific data */ struct fs_ops *f_ops; /* pointer to file system operations */ void *f_fsdata; /* file system specific data */ off_t f_offset; /* current file offset */ char *f_rabuf; /* readahead buffer pointer */ size_t f_ralen; /* valid data in readahead buffer */ off_t f_raoffset; /* consumer offset in readahead buffer */ #define SOPEN_RASIZE 512 }; #define SOPEN_MAX 64 extern struct open_file files[]; /* f_flags values */ #define F_READ 0x0001 /* file opened for reading */ #define F_WRITE 0x0002 /* file opened for writing */ #define F_RAW 0x0004 /* raw device open - no file system */ #define F_NODEV 0x0008 /* network open - no device */ #define isascii(c) (((c) & ~0x7F) == 0) static __inline int isupper(int c) { return c >= 'A' && c <= 'Z'; } static __inline int islower(int c) { return c >= 'a' && c <= 'z'; } static __inline int isspace(int c) { return c == ' ' || (c >= 0x9 && c <= 0xd); } static __inline int isdigit(int c) { return c >= '0' && c <= '9'; } static __inline int isxdigit(int c) { return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } static __inline int isalpha(int c) { return isupper(c) || islower(c); } static __inline int isalnum(int c) { return isalpha(c) || isdigit(c); } static __inline int toupper(int c) { return islower(c) ? c - 'a' + 'A' : c; } static __inline int tolower(int c) { return isupper(c) ? c - 'A' + 'a' : c; } /* sbrk emulation */ extern void setheap(void *base, void *top); extern char *sbrk(int incr); /* Matt Dillon's zalloc/zmalloc */ extern void *malloc(size_t bytes); extern void free(void *ptr); /*#define free(p) {CHK("free %p", p); free(p);} */ /* use for catching guard violations */ extern void *calloc(size_t n1, size_t n2); extern void *realloc(void *ptr, size_t size); extern void *reallocf(void *ptr, size_t size); extern void mallocstats(void); extern int printf(const char *fmt, ...) __printflike(1, 2); extern void vprintf(const char *fmt, __va_list); extern int sprintf(char *buf, const char *cfmt, ...) __printflike(2, 3); extern int snprintf(char *buf, size_t size, const char *cfmt, ...) __printflike(3, 4); extern void vsprintf(char *buf, const char *cfmt, __va_list); extern void twiddle(u_int callerdiv); extern void twiddle_divisor(u_int globaldiv); extern void ngets(char *, int); #define gets(x) ngets((x), 0) extern int fgetstr(char *buf, int size, int fd); extern int open(const char *, int); #define O_RDONLY 0x0 #define O_WRONLY 0x1 #define O_RDWR 0x2 extern int close(int); extern void closeall(void); extern ssize_t read(int, void *, size_t); extern ssize_t write(int, void *, size_t); extern struct dirent *readdirfd(int); extern void srandom(u_long seed); extern u_long random(void); /* imports from stdlib, locally modified */ extern long strtol(const char *, char **, int); extern unsigned long strtoul(const char *, char **, int); extern char *optarg; /* getopt(3) external variables */ extern int optind, opterr, optopt, optreset; extern int getopt(int, char * const [], const char *); /* pager.c */ extern void pager_open(void); extern void pager_close(void); extern int pager_output(const char *lines); extern int pager_file(const char *fname); /* No signal state to preserve */ #define setjmp _setjmp #define longjmp _longjmp /* environment.c */ #define EV_DYNAMIC (1<<0) /* value was dynamically allocated, free if changed/unset */ #define EV_VOLATILE (1<<1) /* value is volatile, make a copy of it */ #define EV_NOHOOK (1<<2) /* don't call hook when setting */ struct env_var; typedef char *(ev_format_t)(struct env_var *ev); typedef int (ev_sethook_t)(struct env_var *ev, int flags, const void *value); typedef int (ev_unsethook_t)(struct env_var *ev); struct env_var { char *ev_name; int ev_flags; void *ev_value; ev_sethook_t *ev_sethook; ev_unsethook_t *ev_unsethook; struct env_var *ev_next, *ev_prev; }; extern struct env_var *environ; extern struct env_var *env_getenv(const char *name); extern int env_setenv(const char *name, int flags, const void *value, ev_sethook_t sethook, ev_unsethook_t unsethook); extern char *getenv(const char *name); extern int setenv(const char *name, const char *value, int overwrite); extern int putenv(const char *string); extern int unsetenv(const char *name); extern ev_sethook_t env_noset; /* refuse set operation */ extern ev_unsethook_t env_nounset; /* refuse unset operation */ /* BCD conversions (undocumented) */ extern u_char const bcd2bin_data[]; extern u_char const bin2bcd_data[]; extern char const hex2ascii_data[]; #define bcd2bin(bcd) (bcd2bin_data[bcd]) #define bin2bcd(bin) (bin2bcd_data[bin]) #define hex2ascii(hex) (hex2ascii_data[hex]) /* min/max (undocumented) */ static __inline int imax(int a, int b) { return (a > b ? a : b); } static __inline int imin(int a, int b) { return (a < b ? a : b); } static __inline long lmax(long a, long b) { return (a > b ? a : b); } static __inline long lmin(long a, long b) { return (a < b ? a : b); } static __inline u_int max(u_int a, u_int b) { return (a > b ? a : b); } static __inline u_int min(u_int a, u_int b) { return (a < b ? a : b); } static __inline quad_t qmax(quad_t a, quad_t b) { return (a > b ? a : b); } static __inline quad_t qmin(quad_t a, quad_t b) { return (a < b ? a : b); } static __inline u_long ulmax(u_long a, u_long b) { return (a > b ? a : b); } static __inline u_long ulmin(u_long a, u_long b) { return (a < b ? a : b); } /* null functions for device/filesystem switches (undocumented) */ extern int nodev(void); extern int noioctl(struct open_file *, u_long, void *); extern void nullsys(void); extern int null_open(const char *path, struct open_file *f); extern int null_close(struct open_file *f); extern int null_read(struct open_file *f, void *buf, size_t size, size_t *resid); extern int null_write(struct open_file *f, void *buf, size_t size, size_t *resid); extern off_t null_seek(struct open_file *f, off_t offset, int where); extern int null_stat(struct open_file *f, struct stat *sb); extern int null_readdir(struct open_file *f, struct dirent *d); /* * Machine dependent functions and data, must be provided or stubbed by * the consumer */ extern int getchar(void); extern int ischar(void); extern void putchar(int); extern int devopen(struct open_file *, const char *, const char **); extern int devclose(struct open_file *f); extern void panic(const char *, ...) __dead2 __printflike(1, 2); extern struct fs_ops *file_system[]; extern struct fs_ops *exclusive_file_system; extern struct devsw *devsw[]; /* * Expose byteorder(3) functions. */ #ifndef _BYTEORDER_PROTOTYPED #define _BYTEORDER_PROTOTYPED extern uint32_t htonl(uint32_t); extern uint16_t htons(uint16_t); extern uint32_t ntohl(uint32_t); extern uint16_t ntohs(uint16_t); #endif #ifndef _BYTEORDER_FUNC_DEFINED #define _BYTEORDER_FUNC_DEFINED #define htonl(x) __htonl(x) #define htons(x) __htons(x) #define ntohl(x) __ntohl(x) #define ntohs(x) __ntohs(x) #endif void *Malloc(size_t, const char *, int); void *Calloc(size_t, size_t, const char *, int); void *Realloc(void *, size_t, const char *, int); void Free(void *, const char *, int); #if 1 #define malloc(x) Malloc(x, __FILE__, __LINE__) #define calloc(x, y) Calloc(x, y, __FILE__, __LINE__) #define free(x) Free(x, __FILE__, __LINE__) #define realloc(x, y) Realloc(x, y, __FILE__, __LINE__) #else #define malloc(x) Malloc(x, NULL, 0) #define calloc(x, y) Calloc(x, y, NULL, 0) #define free(x) Free(x, NULL, 0) #define realloc(x, y) Realloc(x, y, NULL, 0) #endif #endif /* STAND_H */ Index: head/sys/boot/efi/include/efilib.h =================================================================== --- head/sys/boot/efi/include/efilib.h (revision 313332) +++ head/sys/boot/efi/include/efilib.h (revision 313333) @@ -1,76 +1,98 @@ /*- * 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 extern EFI_HANDLE IH; extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RS; -extern struct devsw efipart_dev; +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; /* link of partitions */ + EFI_HANDLE pd_handle; + EFI_HANDLE pd_alias; + EFI_DEVICE_PATH *pd_devpath; + EFI_BLOCK_IO *pd_blkio; + int pd_unit; /* unit number */ + int pd_open; /* reference counter */ + void *pd_bcache; /* buffer cache data */ +} pdinfo_t; + +pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev); + void *efi_get_table(EFI_GUID *tbl); 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 *); +int efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *); void efi_free_devpath_name(CHAR16 *); int efi_status_to_errno(EFI_STATUS); void efi_time_init(void); void efi_time_fini(void); EFI_STATUS main(int argc, CHAR16 *argv[]); void exit(EFI_STATUS status); void delay(int usecs); /* EFI environment initialization. */ void efi_init_environment(void); /* CHAR16 utility functions. */ int wcscmp(CHAR16 *, CHAR16 *); void cpy8to16(const char *, CHAR16 *, size_t); void cpy16to8(const CHAR16 *, char *, size_t); #endif /* _LOADER_EFILIB_H */ Index: head/sys/boot/efi/libefi/devpath.c =================================================================== --- head/sys/boot/efi/libefi/devpath.c (revision 313332) +++ head/sys/boot/efi/libefi/devpath.c (revision 313333) @@ -1,140 +1,168 @@ /*- * 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 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 *textProtocol; EFI_DEVICE_PATH * efi_lookup_image_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = BS->HandleProtocol(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 = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } CHAR16 * efi_devpath_name(EFI_DEVICE_PATH *devpath) { static int once = 1; EFI_STATUS status; if (devpath == NULL) return (NULL); if (once) { status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, (VOID **)&textProtocol); if (EFI_ERROR(status)) textProtocol = NULL; once = 0; } if (textProtocol == NULL) return (NULL); return (textProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); } void efi_free_devpath_name(CHAR16 *text) { BS->FreePool(text); } 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); } + +int +efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) +{ + int len; + + if (devpath1 == NULL || devpath2 == NULL) + return (0); + + while (1) { + if (DevicePathType(devpath1) != DevicePathType(devpath2) || + DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) + return (0); + + len = DevicePathNodeLength(devpath1); + if (len != DevicePathNodeLength(devpath2)) + return (0); + + if (memcmp(devpath1, devpath2, (size_t)len) != 0) + return (0); + + if (IsDevicePathEnd(devpath1)) + break; + devpath1 = NextDevicePathNode(devpath1); + devpath2 = NextDevicePathNode(devpath2); + } + return (1); +} Index: head/sys/boot/efi/libefi/efipart.c =================================================================== --- head/sys/boot/efi/libefi/efipart.c (revision 313332) +++ head/sys/boot/efi/libefi/efipart.c (revision 313333) @@ -1,372 +1,860 @@ /*- * Copyright (c) 2010 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. */ #include __FBSDID("$FreeBSD$"); +#include #include #include +#include #include #include #include #include #include #include +#include static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; -static int efipart_init(void); +static int efipart_initfd(void); +static int efipart_initcd(void); +static int efipart_inithd(void); + static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *); static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *); + static int efipart_open(struct open_file *, ...); static int efipart_close(struct open_file *); -static int efipart_print(int); +static int efipart_ioctl(struct open_file *, u_long, void *); -struct devsw efipart_dev = { - .dv_name = "part", - .dv_type = DEVT_DISK, - .dv_init = efipart_init, +static int efipart_printfd(int); +static int efipart_printcd(int); +static int efipart_printhd(int); + +struct devsw efipart_fddev = { + .dv_name = "fd", + .dv_type = DEVT_FD, + .dv_init = efipart_initfd, .dv_strategy = efipart_strategy, .dv_open = efipart_open, .dv_close = efipart_close, - .dv_ioctl = noioctl, - .dv_print = efipart_print, + .dv_ioctl = efipart_ioctl, + .dv_print = efipart_printfd, .dv_cleanup = NULL }; -/* - * info structure to support bcache - */ -struct pdinfo { - int pd_unit; /* unit number */ - int pd_open; /* reference counter */ - void *pd_bcache; /* buffer cache data */ +struct devsw efipart_cddev = { + .dv_name = "cd", + .dv_type = DEVT_CD, + .dv_init = efipart_initcd, + .dv_strategy = efipart_strategy, + .dv_open = efipart_open, + .dv_close = efipart_close, + .dv_ioctl = efipart_ioctl, + .dv_print = efipart_printcd, + .dv_cleanup = NULL }; -static struct pdinfo *pdinfo; -static int npdinfo = 0; -#define PD(dev) (pdinfo[(dev)->d_unit]) +struct devsw efipart_hddev = { + .dv_name = "disk", + .dv_type = DEVT_DISK, + .dv_init = efipart_inithd, + .dv_strategy = efipart_strategy, + .dv_open = efipart_open, + .dv_close = efipart_close, + .dv_ioctl = efipart_ioctl, + .dv_print = efipart_printhd, + .dv_cleanup = NULL +}; +static pdinfo_list_t fdinfo; +static pdinfo_list_t cdinfo; +static pdinfo_list_t hdinfo; + +static EFI_HANDLE *efipart_handles = NULL; +static UINTN efipart_nhandles = 0; + +static pdinfo_t * +efiblk_get_pdinfo(pdinfo_list_t *pdi, int unit) +{ + pdinfo_t *pd; + + STAILQ_FOREACH(pd, pdi, pd_link) { + if (pd->pd_unit == unit) + return (pd); + } + return (NULL); +} + static int -efipart_init(void) +efiblk_pdinfo_count(pdinfo_list_t *pdi) { - EFI_BLOCK_IO *blkio; - EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; - EFI_HANDLE *hin, *hout, *aliases, handle; - EFI_STATUS status; + pdinfo_t *pd; + int i = 0; + + STAILQ_FOREACH(pd, pdi, pd_link) { + i++; + } + return (i); +} + +static int +efipart_inithandles(void) +{ UINTN sz; - u_int n, nin, nout, nrdisk; - int err; + EFI_HANDLE *hin; + EFI_STATUS status; + if (efipart_nhandles != 0) { + free(efipart_handles); + efipart_handles = NULL; + efipart_nhandles = 0; + } + sz = 0; hin = NULL; - status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0); + status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (status == EFI_BUFFER_TOO_SMALL) { - hin = (EFI_HANDLE *)malloc(sz * 3); + hin = malloc(sz); status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return (efi_status_to_errno(status)); - /* Filter handles to only include FreeBSD partitions. */ - nin = sz / sizeof(EFI_HANDLE); - hout = hin + nin; - aliases = hout + nin; - nout = 0; - nrdisk = 0; + efipart_handles = hin; + efipart_nhandles = sz; + return (0); +} - bzero(aliases, nin * sizeof(EFI_HANDLE)); - pdinfo = malloc(nin * sizeof(*pdinfo)); - if (pdinfo == NULL) +static ACPI_HID_DEVICE_PATH * +efipart_floppy(EFI_DEVICE_PATH *node) +{ + ACPI_HID_DEVICE_PATH *acpi = NULL; + + if (DevicePathType(node) == ACPI_DEVICE_PATH && + DevicePathSubType(node) == ACPI_DP) { + acpi = (ACPI_HID_DEVICE_PATH *) node; + if (acpi->HID == EISA_PNP_ID(0x604) || + acpi->HID == EISA_PNP_ID(0x700) || + acpi->HID == EISA_ID(0x41d1, 0x701)) { + return (acpi); + } + } + return (acpi); +} + +/* + * Add or update entries with new handle data. + */ +static int +efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath) +{ + pdinfo_t *fd; + + fd = malloc(sizeof(pdinfo_t)); + if (fd == NULL) { + printf("Failed to register floppy %d, out of memory\n", uid); return (ENOMEM); + } + memset(fd, 0, sizeof(pdinfo_t)); + STAILQ_INIT(&fd->pd_part); - for (n = 0; n < nin; n++) { - devpath = efi_lookup_devpath(hin[n]); - if (devpath == NULL) { + fd->pd_unit = uid; + fd->pd_handle = handle; + fd->pd_devpath = devpath; + STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link); + return (0); +} + +static void +efipart_updatefd(void) +{ + EFI_DEVICE_PATH *devpath, *node; + ACPI_HID_DEVICE_PATH *acpi; + int i, nin; + + nin = efipart_nhandles / sizeof (*efipart_handles); + for (i = 0; i < nin; i++) { + devpath = efi_lookup_devpath(efipart_handles[i]); + if (devpath == NULL) continue; - } - status = BS->HandleProtocol(hin[n], &blkio_guid, - (void**)&blkio); - if (EFI_ERROR(status)) + if ((node = efi_devpath_last_node(devpath)) == NULL) continue; - if (!blkio->Media->LogicalPartition) { - nrdisk++; - continue; + if ((acpi = efipart_floppy(node)) != NULL) { + efipart_fdinfo_add(efipart_handles[i], acpi->UID, + devpath); } + } +} +static int +efipart_initfd(void) +{ + int rv; + + rv = efipart_inithandles(); + if (rv != 0) + return (rv); + STAILQ_INIT(&fdinfo); + + efipart_updatefd(); + + bcache_add_dev(efiblk_pdinfo_count(&fdinfo)); + return (0); +} + +/* + * Add or update entries with new handle data. + */ +static int +efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias, + EFI_DEVICE_PATH *devpath) +{ + int unit; + pdinfo_t *cd; + pdinfo_t *pd; + + unit = 0; + STAILQ_FOREACH(pd, &cdinfo, pd_link) { + if (efi_devpath_match(pd->pd_devpath, devpath) != 0) { + pd->pd_handle = handle; + pd->pd_alias = alias; + return (0); + } + unit++; + } + + cd = malloc(sizeof(pdinfo_t)); + if (cd == NULL) { + printf("Failed to add cd %d, out of memory\n", unit); + return (ENOMEM); + } + memset(cd, 0, sizeof(pdinfo_t)); + STAILQ_INIT(&cd->pd_part); + + cd->pd_handle = handle; + cd->pd_unit = unit; + cd->pd_alias = alias; + cd->pd_devpath = devpath; + STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); + return (0); +} + +static void +efipart_updatecd(void) +{ + int i, nin; + EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; + EFI_HANDLE handle; + EFI_BLOCK_IO *blkio; + EFI_STATUS status; + + nin = efipart_nhandles / sizeof (*efipart_handles); + for (i = 0; i < nin; i++) { + devpath = efi_lookup_devpath(efipart_handles[i]); + if (devpath == NULL) + continue; + + if ((node = efi_devpath_last_node(devpath)) == NULL) + continue; + if (efipart_floppy(node) != NULL) + continue; + + status = BS->HandleProtocol(efipart_handles[i], + &blkio_guid, (void **)&blkio); + if (EFI_ERROR(status)) + continue; /* * If we come across a logical partition of subtype CDROM * it doesn't refer to the CD filesystem itself, but rather * to any usable El Torito boot image on it. In this case * we try to find the parent device and add that instead as * that will be the CD filesystem. */ - if ((node = efi_devpath_last_node(devpath)) == NULL) - continue; if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_CDROM_DP) { devpathcpy = efi_devpath_trim(devpath); if (devpathcpy == NULL) continue; tmpdevpath = devpathcpy; status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, &handle); free(devpathcpy); if (EFI_ERROR(status)) continue; - hout[nout] = handle; - aliases[nout] = hin[n]; - } else - hout[nout] = hin[n]; - nout++; - pdinfo[npdinfo].pd_open = 0; - pdinfo[npdinfo].pd_bcache = NULL; - pdinfo[npdinfo].pd_unit = npdinfo; - npdinfo++; + devpath = efi_lookup_devpath(handle); + efipart_cdinfo_add(handle, efipart_handles[i], + devpath); + continue; + } + + if (DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_ATAPI_DP) { + efipart_cdinfo_add(efipart_handles[i], NULL, + devpath); + continue; + } + + /* USB or SATA cd without the media. */ + if (blkio->Media->RemovableMedia && + !blkio->Media->MediaPresent) { + efipart_cdinfo_add(efipart_handles[i], NULL, + devpath); + } } +} - bcache_add_dev(npdinfo); - err = efi_register_handles(&efipart_dev, hout, aliases, nout); - free(hin); +static int +efipart_initcd(void) +{ + int rv; - if (nout == 0 && nrdisk > 0) - printf("Found %d disk(s) but no logical partition\n", nrdisk); - return (err); + rv = efipart_inithandles(); + if (rv != 0) + return (rv); + STAILQ_INIT(&cdinfo); + + efipart_updatecd(); + + bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); + return (0); } static int -efipart_print(int verbose) +efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) { - char line[80]; + EFI_DEVICE_PATH *disk_devpath, *part_devpath; + HARDDRIVE_DEVICE_PATH *node; + int unit; + pdinfo_t *hd, *pd, *last; + + disk_devpath = efi_lookup_devpath(disk_handle); + part_devpath = efi_lookup_devpath(part_handle); + if (disk_devpath == NULL || part_devpath == NULL) { + return (ENOENT); + } + + pd = malloc(sizeof(pdinfo_t)); + if (pd == NULL) { + printf("Failed to add disk, out of memory\n"); + return (ENOMEM); + } + memset(pd, 0, sizeof(pdinfo_t)); + STAILQ_INIT(&pd->pd_part); + node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath); + + STAILQ_FOREACH(hd, &hdinfo, pd_link) { + if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) { + /* Add the partition. */ + pd->pd_handle = part_handle; + pd->pd_unit = node->PartitionNumber; + pd->pd_devpath = part_devpath; + STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); + return (0); + } + } + + last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); + if (last != NULL) + unit = last->pd_unit + 1; + else + unit = 0; + + /* Add the disk. */ + hd = pd; + hd->pd_handle = disk_handle; + hd->pd_unit = unit; + hd->pd_devpath = disk_devpath; + STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); + + pd = malloc(sizeof(pdinfo_t)); + if (pd == NULL) { + printf("Failed to add partition, out of memory\n"); + return (ENOMEM); + } + memset(pd, 0, sizeof(pdinfo_t)); + STAILQ_INIT(&pd->pd_part); + + /* Add the partition. */ + pd->pd_handle = part_handle; + pd->pd_unit = node->PartitionNumber; + pd->pd_devpath = part_devpath; + STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); + + return (0); +} + +static void +efipart_updatehd(void) +{ + int i, nin; + EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; + EFI_HANDLE handle; EFI_BLOCK_IO *blkio; - EFI_HANDLE h; EFI_STATUS status; - u_int unit; + + nin = efipart_nhandles / sizeof (*efipart_handles); + for (i = 0; i < nin; i++) { + devpath = efi_lookup_devpath(efipart_handles[i]); + if (devpath == NULL) + continue; + + if ((node = efi_devpath_last_node(devpath)) == NULL) + continue; + if (efipart_floppy(node) != NULL) + continue; + + status = BS->HandleProtocol(efipart_handles[i], + &blkio_guid, (void **)&blkio); + if (EFI_ERROR(status)) + continue; + + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { + devpathcpy = efi_devpath_trim(devpath); + if (devpathcpy == NULL) + continue; + tmpdevpath = devpathcpy; + status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, + &handle); + free(devpathcpy); + if (EFI_ERROR(status)) + continue; + /* + * We do not support nested partitions. + */ + devpathcpy = efi_lookup_devpath(handle); + if (devpathcpy == NULL) + continue; + if ((node = efi_devpath_last_node(devpathcpy)) == NULL) + continue; + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) + continue; + efipart_hdinfo_add(handle, efipart_handles[i]); + continue; + } + } +} + +static int +efipart_inithd(void) +{ + int rv; + + rv = efipart_inithandles(); + if (rv != 0) + return (rv); + STAILQ_INIT(&hdinfo); + + efipart_updatehd(); + + bcache_add_dev(efiblk_pdinfo_count(&hdinfo)); + return (0); +} + +static int +efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose) +{ int ret = 0; + EFI_BLOCK_IO *blkio; + EFI_STATUS status; + EFI_HANDLE h; + pdinfo_t *pd; + CHAR16 *text; + struct disk_devdesc pd_dev; + char line[80]; - printf("%s devices:", efipart_dev.dv_name); + if (STAILQ_EMPTY(pdlist)) + return (0); + + printf("%s devices:", dev->dv_name); if ((ret = pager_output("\n")) != 0) return (ret); - for (unit = 0, h = efi_find_handle(&efipart_dev, 0); - h != NULL; h = efi_find_handle(&efipart_dev, ++unit)) { - snprintf(line, sizeof(line), " %s%d:", - efipart_dev.dv_name, unit); - if ((ret = pager_output(line)) != 0) - break; - + STAILQ_FOREACH(pd, pdlist, pd_link) { + h = pd->pd_handle; + if (verbose) { /* Output the device path. */ + text = efi_devpath_name(efi_lookup_devpath(h)); + if (text != NULL) { + printf(" %S", text); + efi_free_devpath_name(text); + if ((ret = pager_output("\n")) != 0) + break; + } + } + snprintf(line, sizeof(line), + " %s%d", dev->dv_name, pd->pd_unit); + printf("%s:", line); status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio); if (!EFI_ERROR(status)) { - snprintf(line, sizeof(line), " %llu blocks", - (unsigned long long)(blkio->Media->LastBlock + 1)); - if ((ret = pager_output(line)) != 0) + printf(" %llu", + blkio->Media->LastBlock == 0? 0: + (unsigned long long) (blkio->Media->LastBlock + 1)); + if (blkio->Media->LastBlock != 0) { + printf(" X %u", blkio->Media->BlockSize); + } + printf(" blocks"); + if (blkio->Media->MediaPresent) { + if (blkio->Media->RemovableMedia) + printf(" (removable)"); + } else + printf(" (no media)"); + if ((ret = pager_output("\n")) != 0) break; - if (blkio->Media->RemovableMedia) - if ((ret = pager_output(" (removable)")) != 0) - break; + if (!blkio->Media->MediaPresent) + continue; + + pd->pd_blkio = blkio; + pd_dev.d_dev = dev; + pd_dev.d_unit = pd->pd_unit; + pd_dev.d_slice = -1; + pd_dev.d_partition = -1; + pd_dev.d_opendata = blkio; + ret = disk_open(&pd_dev, blkio->Media->BlockSize * + (blkio->Media->LastBlock + 1), + blkio->Media->BlockSize, + blkio->Media->RemovableMedia? DISK_F_NOCACHE: 0); + if (ret == 0) { + ret = disk_print(&pd_dev, line, verbose); + disk_close(&pd_dev); + if (ret != 0) + return (ret); + } else { + /* Do not fail from disk_open() */ + ret = 0; + } + } else { + if ((ret = pager_output("\n")) != 0) + break; } - if ((ret = pager_output("\n")) != 0) - break; } return (ret); } static int +efipart_printfd(int verbose) +{ + return (efipart_print_common(&efipart_fddev, &fdinfo, verbose)); +} + +static int +efipart_printcd(int verbose) +{ + return (efipart_print_common(&efipart_cddev, &cdinfo, verbose)); +} + +static int +efipart_printhd(int verbose) +{ + return (efipart_print_common(&efipart_hddev, &hdinfo, verbose)); +} + +pdinfo_list_t * +efiblk_get_pdinfo_list(struct devsw *dev) +{ + if (dev->dv_type == DEVT_DISK) + return (&hdinfo); + if (dev->dv_type == DEVT_CD) + return (&cdinfo); + if (dev->dv_type == DEVT_FD) + return (&fdinfo); + return (NULL); +} + +static int efipart_open(struct open_file *f, ...) { va_list args; - struct devdesc *dev; + struct disk_devdesc *dev; + pdinfo_list_t *pdi; + pdinfo_t *pd; EFI_BLOCK_IO *blkio; - EFI_HANDLE h; EFI_STATUS status; va_start(args, f); - dev = va_arg(args, struct devdesc*); + dev = va_arg(args, struct disk_devdesc*); va_end(args); + if (dev == NULL) + return (EINVAL); - h = efi_find_handle(&efipart_dev, dev->d_unit); - if (h == NULL) + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) return (EINVAL); - status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio); - if (EFI_ERROR(status)) - return (efi_status_to_errno(status)); + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) + return (EIO); + + if (pd->pd_blkio == NULL) { + status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, + (void **)&pd->pd_blkio); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); + } + blkio = pd->pd_blkio; if (!blkio->Media->MediaPresent) return (EAGAIN); - dev->d_opendata = blkio; - PD(dev).pd_open++; - if (PD(dev).pd_bcache == NULL) - PD(dev).pd_bcache = bcache_allocate(); + pd->pd_open++; + if (pd->pd_bcache == NULL) + pd->pd_bcache = bcache_allocate(); + + if (dev->d_dev->dv_type == DEVT_DISK) { + return (disk_open(dev, + blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), + blkio->Media->BlockSize, + blkio->Media->RemovableMedia? DISK_F_NOCACHE: 0)); + } return (0); } static int efipart_close(struct open_file *f) { - struct devdesc *dev; + struct disk_devdesc *dev; + pdinfo_list_t *pdi; + pdinfo_t *pd; - dev = (struct devdesc *)(f->f_devdata); - if (dev->d_opendata == NULL) + dev = (struct disk_devdesc *)(f->f_devdata); + if (dev == NULL) return (EINVAL); + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) + return (EINVAL); - dev->d_opendata = NULL; - PD(dev).pd_open--; - if (PD(dev).pd_open == 0) { - bcache_free(PD(dev).pd_bcache); - PD(dev).pd_bcache = NULL; + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) + return (EINVAL); + + pd->pd_open--; + if (pd->pd_open == 0) { + pd->pd_blkio = NULL; + bcache_free(pd->pd_bcache); + pd->pd_bcache = NULL; } + if (dev->d_dev->dv_type == DEVT_DISK) + return (disk_close(dev)); return (0); } +static int +efipart_ioctl(struct open_file *f, u_long cmd, void *data) +{ + struct disk_devdesc *dev; + pdinfo_list_t *pdi; + pdinfo_t *pd; + int rc; + + dev = (struct disk_devdesc *)(f->f_devdata); + if (dev == NULL) + return (EINVAL); + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) + return (EINVAL); + + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) + return (EINVAL); + + if (dev->d_dev->dv_type == DEVT_DISK) { + rc = disk_ioctl(dev, cmd, data); + if (rc != ENOTTY) + return (rc); + } + + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = pd->pd_blkio->Media->BlockSize; + break; + case DIOCGMEDIASIZE: + *(off_t *)data = pd->pd_blkio->Media->BlockSize * + (pd->pd_blkio->Media->LastBlock + 1); + break; + default: + return (ENOTTY); + } + + return (0); +} + /* * efipart_readwrite() * Internal equivalent of efipart_strategy(), which operates on the * media-native block size. This function expects all I/O requests * to be within the media size and returns an error if such is not * the case. */ static int efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, char *buf) { EFI_STATUS status; if (blkio == NULL) return (ENXIO); if (blk < 0 || blk > blkio->Media->LastBlock) return (EIO); if ((blk + nblks - 1) > blkio->Media->LastBlock) return (EIO); switch (rw) { case F_READ: status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; case F_WRITE: if (blkio->Media->ReadOnly) return (EROFS); status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; default: return (ENOSYS); } if (EFI_ERROR(status)) printf("%s: rw=%d, status=%lu\n", __func__, rw, (u_long)status); return (efi_status_to_errno(status)); } static int efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct bcache_devdata bcd; - struct devdesc *dev; + struct disk_devdesc *dev; + pdinfo_list_t *pdi; + pdinfo_t *pd; - dev = (struct devdesc *)devdata; + dev = (struct disk_devdesc *)devdata; + if (dev == NULL) + return (EINVAL); + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) + return (EINVAL); + + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) + return (EINVAL); + bcd.dv_strategy = efipart_realstrategy; bcd.dv_devdata = devdata; - bcd.dv_cache = PD(dev).pd_bcache; + bcd.dv_cache = pd->pd_bcache; + + if (dev->d_dev->dv_type == DEVT_DISK) { + return (bcache_strategy(&bcd, rw, blk + dev->d_offset, + size, buf, rsize)); + } return (bcache_strategy(&bcd, rw, blk, size, buf, rsize)); } static int efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { - struct devdesc *dev = (struct devdesc *)devdata; + struct disk_devdesc *dev = (struct disk_devdesc *)devdata; + pdinfo_list_t *pdi; + pdinfo_t *pd; EFI_BLOCK_IO *blkio; off_t off; char *blkbuf; size_t blkoff, blksz; int error; if (dev == NULL || blk < 0) return (EINVAL); - blkio = dev->d_opendata; + pdi = efiblk_get_pdinfo_list(dev->d_dev); + if (pdi == NULL) + return (EINVAL); + + pd = efiblk_get_pdinfo(pdi, dev->d_unit); + if (pd == NULL) + return (EINVAL); + + blkio = pd->pd_blkio; if (blkio == NULL) return (ENXIO); if (size == 0 || (size % 512) != 0) return (EIO); off = blk * 512; /* make sure we don't read past disk end */ if ((off + size) / blkio->Media->BlockSize - 1 > blkio->Media->LastBlock) { size = blkio->Media->LastBlock + 1 - off / blkio->Media->BlockSize; size = size * blkio->Media->BlockSize; } if (rsize != NULL) *rsize = size; if ((size % blkio->Media->BlockSize == 0) && ((blk * 512) % blkio->Media->BlockSize == 0)) return (efipart_readwrite(blkio, rw, blk * 512 / blkio->Media->BlockSize, size / blkio->Media->BlockSize, buf)); /* * The block size of the media is not a multiple of I/O. */ blkbuf = malloc(blkio->Media->BlockSize); if (blkbuf == NULL) return (ENOMEM); error = 0; blk = off / blkio->Media->BlockSize; blkoff = off % blkio->Media->BlockSize; blksz = blkio->Media->BlockSize - blkoff; while (size > 0) { error = efipart_readwrite(blkio, rw, blk, 1, blkbuf); if (error) break; if (size < blksz) blksz = size; bcopy(blkbuf + blkoff, buf, blksz); buf += blksz; size -= blksz; blk++; blkoff = 0; blksz = blkio->Media->BlockSize; } free(blkbuf); return (error); } Index: head/sys/boot/efi/loader/conf.c =================================================================== --- head/sys/boot/efi/loader/conf.c (revision 313332) +++ head/sys/boot/efi/loader/conf.c (revision 313333) @@ -1,79 +1,81 @@ /*- * 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 ``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 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 #ifdef EFI_ZFS_BOOT #include #endif struct devsw *devsw[] = { - &efipart_dev, + &efipart_fddev, + &efipart_cddev, + &efipart_hddev, &efinet_dev, #ifdef EFI_ZFS_BOOT &zfs_dev, #endif NULL }; struct fs_ops *file_system[] = { #ifdef EFI_ZFS_BOOT &zfs_fsops, #endif &dosfs_fsops, &ufs_fsops, &cd9660_fsops, &tftp_fsops, &nfs_fsops, &gzipfs_fsops, &bzipfs_fsops, NULL }; struct netif_driver *netif_drivers[] = { &efinetif, NULL }; extern struct console efi_console; #if defined(__amd64__) || defined(__i386__) extern struct console comconsole; extern struct console nullconsole; #endif struct console *consoles[] = { &efi_console, #if defined(__amd64__) || defined(__i386__) &comconsole, &nullconsole, #endif NULL }; Index: head/sys/boot/efi/loader/devicename.c =================================================================== --- head/sys/boot/efi/loader/devicename.c (revision 313332) +++ head/sys/boot/efi/loader/devicename.c (revision 313333) @@ -1,197 +1,217 @@ /*- * Copyright (c) 1998 Michael Smith * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include +#include #ifdef EFI_ZFS_BOOT #include #endif #include #include #include "loader_efi.h" 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. */ int efi_getdev(void **vdev, const char *devspec, const char **path) { 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. */ if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) { rv = efi_parsedev(dev, getenv("currdev"), NULL); if (rv == 0 && path != NULL) *path = devspec; return (rv); } /* Parse the device name off the beginning of the devspec. */ return (efi_parsedev(dev, devspec, path)); } /* * Point (dev) at an allocated device specifier matching the string version * at the beginning of (devspec). Return a pointer to the remaining * text in (path). * * In all cases, the beginning of (devspec) is compared to the names * of known devices in the device switch, and then any following text * is parsed according to the rules applied to the device type. * * For disk-type devices, the syntax is: * * fs: */ static int efi_parsedev(struct devdesc **dev, const char *devspec, const char **path) { struct devdesc *idev; struct devsw *dv; char *cp; const char *np; - int i; + int i, err; /* minimum length check */ if (strlen(devspec) < 2) return (EINVAL); /* look for a device that matches */ for (i = 0; devsw[i] != NULL; i++) { dv = devsw[i]; if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name))) break; } if (devsw[i] == NULL) return (ENOENT); np = devspec + strlen(dv->dv_name); + err = 0; -#ifdef EFI_ZFS_BOOT - if (dv->dv_type == DEVT_ZFS) { - int err; + switch (dv->dv_type) { + case DEVT_NONE: + break; + case DEVT_DISK: + idev = malloc(sizeof(struct disk_devdesc)); + if (idev == NULL) + return (ENOMEM); + + err = disk_parsedev((struct disk_devdesc *)idev, np, path); + if (err != 0) { + free(idev); + return (err); + } + break; + +#ifdef EFI_ZFS_BOOT + case DEVT_ZFS: 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 (err); } - cp = strchr(np + 1, ':'); - } else + break; #endif - { + default: idev = malloc(sizeof(struct devdesc)); if (idev == NULL) return (ENOMEM); - idev->d_dev = dv; - idev->d_type = dv->dv_type; idev->d_unit = -1; + cp = (char *)np; 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); + } - if (*cp != '\0' && *cp != ':') { - free(idev); - return (EINVAL); + if (path != NULL) + *path = (*cp == 0) ? cp : cp + 1; + break; } - if (path != NULL) - *path = (*cp == 0) ? cp : cp + 1; + idev->d_dev = dv; + idev->d_type = dv->dv_type; + if (dev != NULL) *dev = idev; else free(idev); return (0); } char * efi_fmtdev(void *vdev) { struct devdesc *dev = (struct devdesc *)vdev; static char buf[SPECNAMELEN + 1]; switch(dev->d_type) { -#ifdef EFI_ZFS_BOOT - case DEVT_ZFS: - return (zfs_fmtdev(dev)); -#endif case DEVT_NONE: strcpy(buf, "(no device)"); break; + case DEVT_DISK: + return (disk_fmtdev(vdev)); + +#ifdef EFI_ZFS_BOOT + case DEVT_ZFS: + return (zfs_fmtdev(dev)); +#endif default: sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); break; } return (buf); } /* * Set currdev to suit the value being supplied in (value) */ int efi_setcurrdev(struct env_var *ev, int flags, const void *value) { struct devdesc *ncurr; int rv; rv = efi_parsedev(&ncurr, value, NULL); if (rv != 0) return (rv); free(ncurr); env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); return (0); } Index: head/sys/boot/efi/loader/main.c =================================================================== --- head/sys/boot/efi/loader/main.c (revision 313332) +++ head/sys/boot/efi/loader/main.c (revision 313333) @@ -1,753 +1,859 @@ /*- * Copyright (c) 2008-2010 Rui Paulo * 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 ``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 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 #include #include #include #include #include #ifdef EFI_ZFS_BOOT #include #endif #include "loader_efi.h" extern char bootprog_info[]; struct arch_switch archsw; /* MI/MD interface boundary */ EFI_GUID acpi = ACPI_TABLE_GUID; EFI_GUID acpi20 = ACPI_20_TABLE_GUID; EFI_GUID devid = DEVICE_PATH_PROTOCOL; EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; EFI_GUID mps = MPS_TABLE_GUID; EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL; EFI_GUID smbios = SMBIOS_TABLE_GUID; EFI_GUID dxe = DXE_SERVICES_TABLE_GUID; EFI_GUID hoblist = HOB_LIST_TABLE_GUID; EFI_GUID memtype = MEMORY_TYPE_INFORMATION_TABLE_GUID; EFI_GUID debugimg = DEBUG_IMAGE_INFO_TABLE_GUID; EFI_GUID fdtdtb = FDT_TABLE_GUID; EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL; #ifdef EFI_ZFS_BOOT static void efi_zfs_probe(void); +static uint64_t pool_guid; #endif static int has_keyboard(void) { EFI_STATUS status; EFI_DEVICE_PATH *path; EFI_HANDLE *hin, *hin_end, *walker; UINTN sz; int retval = 0; /* * Find all the handles that support the SIMPLE_TEXT_INPUT_PROTOCOL and * do the typical dance to get the right sized buffer. */ sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, 0); if (status == EFI_BUFFER_TOO_SMALL) { hin = (EFI_HANDLE *)malloc(sz); status = BS->LocateHandle(ByProtocol, &inputid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return retval; /* * Look at each of the handles. If it supports the device path protocol, * use it to get the device path for this handle. Then see if that * device path matches either the USB device path for keyboards or the * legacy device path for keyboards. */ hin_end = &hin[sz / sizeof(*hin)]; for (walker = hin; walker < hin_end; walker++) { status = BS->HandleProtocol(*walker, &devid, (VOID **)&path); if (EFI_ERROR(status)) continue; while (!IsDevicePathEnd(path)) { /* * Check for the ACPI keyboard node. All PNP3xx nodes * are keyboards of different flavors. Note: It is * unclear of there's always a keyboard node when * there's a keyboard controller, or if there's only one * when a keyboard is detected at boot. */ if (DevicePathType(path) == ACPI_DEVICE_PATH && (DevicePathSubType(path) == ACPI_DP || DevicePathSubType(path) == ACPI_EXTENDED_DP)) { ACPI_HID_DEVICE_PATH *acpi; acpi = (ACPI_HID_DEVICE_PATH *)(void *)path; if ((EISA_ID_TO_NUM(acpi->HID) & 0xff00) == 0x300 && (acpi->HID & 0xffff) == PNP_EISA_ID_CONST) { retval = 1; goto out; } /* * Check for USB keyboard node, if present. Unlike a * PS/2 keyboard, these definitely only appear when * connected to the system. */ } else if (DevicePathType(path) == MESSAGING_DEVICE_PATH && DevicePathSubType(path) == MSG_USB_CLASS_DP) { USB_CLASS_DEVICE_PATH *usb; usb = (USB_CLASS_DEVICE_PATH *)(void *)path; if (usb->DeviceClass == 3 && /* HID */ usb->DeviceSubClass == 1 && /* Boot devices */ usb->DeviceProtocol == 1) { /* Boot keyboards */ retval = 1; goto out; } } path = NextDevicePathNode(path); } } out: free(hin); return retval; } +static void +set_devdesc_currdev(struct devsw *dev, int unit) +{ + struct devdesc currdev; + char *devname; + + currdev.d_dev = dev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = unit; + currdev.d_opendata = NULL; + devname = efi_fmtdev(&currdev); + + env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, + env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset); +} + static int -find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit, - uint64_t *extra) +find_currdev(EFI_LOADED_IMAGE *img) { + pdinfo_list_t *pdi_list; + pdinfo_t *dp, *pp; EFI_DEVICE_PATH *devpath, *copy; EFI_HANDLE h; + char *devname; + struct devsw *dev; + int unit; + uint64_t extra; + /* Did efi_zfs_probe() detect the boot pool? */ + if (pool_guid != 0) { + struct zfs_devdesc currdev; + + currdev.d_dev = &zfs_dev; + currdev.d_unit = 0; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_opendata = NULL; + currdev.pool_guid = pool_guid; + currdev.root_guid = 0; + devname = efi_fmtdev(&currdev); + + env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev, + env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, env_noset, + env_nounset); + return (0); + } + + /* We have device lists for hd, cd, fd, walk them all. */ + pdi_list = efiblk_get_pdinfo_list(&efipart_hddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + struct disk_devdesc currdev; + + currdev.d_dev = &efipart_hddev; + currdev.d_type = currdev.d_dev->dv_type; + currdev.d_unit = dp->pd_unit; + currdev.d_opendata = NULL; + currdev.d_slice = -1; + currdev.d_partition = -1; + + if (dp->pd_handle == img->DeviceHandle) { + devname = efi_fmtdev(&currdev); + + env_setenv("currdev", EV_VOLATILE, devname, + efi_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, + env_noset, env_nounset); + return (0); + } + /* Assuming GPT partitioning. */ + STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { + if (pp->pd_handle == img->DeviceHandle) { + currdev.d_slice = pp->pd_unit; + currdev.d_partition = 255; + devname = efi_fmtdev(&currdev); + + env_setenv("currdev", EV_VOLATILE, devname, + efi_setcurrdev, env_nounset); + env_setenv("loaddev", EV_VOLATILE, devname, + env_noset, env_nounset); + return (0); + } + } + } + + pdi_list = efiblk_get_pdinfo_list(&efipart_cddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (dp->pd_handle == img->DeviceHandle || + dp->pd_alias == img->DeviceHandle) { + set_devdesc_currdev(&efipart_cddev, dp->pd_unit); + return (0); + } + } + + pdi_list = efiblk_get_pdinfo_list(&efipart_fddev); + STAILQ_FOREACH(dp, pdi_list, pd_link) { + if (dp->pd_handle == img->DeviceHandle) { + set_devdesc_currdev(&efipart_fddev, dp->pd_unit); + return (0); + } + } + /* * Try the device handle from our loaded image first. If that * fails, use the device path from the loaded image and see if * any of the nodes in that path match one of the enumerated * handles. */ - if (efi_handle_lookup(img->DeviceHandle, dev, unit, extra) == 0) + if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) { + set_devdesc_currdev(dev, unit); return (0); + } copy = NULL; devpath = efi_lookup_image_devpath(IH); while (devpath != NULL) { h = efi_devpath_handle(devpath); if (h == NULL) break; free(copy); copy = NULL; - if (efi_handle_lookup(h, dev, unit, extra) == 0) + if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) { + set_devdesc_currdev(dev, unit); return (0); + } devpath = efi_lookup_devpath(h); if (devpath != NULL) { copy = efi_devpath_trim(devpath); devpath = copy; } } free(copy); - /* Try to fallback on first device */ - if (devsw[0] != NULL) { - *dev = devsw[0]; - return (0); - } return (ENOENT); } EFI_STATUS main(int argc, CHAR16 *argv[]) { char var[128]; EFI_LOADED_IMAGE *img; EFI_GUID *guid; - int i, j, vargood, unit, howto; - struct devsw *dev; - uint64_t pool_guid; + int i, j, vargood, howto; UINTN k; int has_kbd; char buf[40]; 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 EFI_ZFS_BOOT /* Note this needs to be set before ZFS init. */ archsw.arch_zfs_probe = efi_zfs_probe; #endif /* Init the time source */ efi_time_init(); has_kbd = has_keyboard(); /* * 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 * printf() etc. once this is done. */ cons_probe(); /* * Initialise the block cache. Set the upper limit. */ bcache_init(32768, 512); /* * Parse the args to set the console settings, etc * boot1.efi passes these in, if it can read /boot.config or /boot/config * or iPXE may be setup to pass these in. * * Loop through the args, and for each one that contains an '=' that is * not the first character, add it to the environment. This allows * loader and kernel env vars to be passed on the command line. Convert * args from UCS-2 to ASCII (16 to 8 bit) as they are copied. */ howto = 0; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { for (j = 1; argv[i][j] != 0; j++) { int ch; ch = argv[i][j]; switch (ch) { case 'a': howto |= RB_ASKNAME; break; case 'd': howto |= RB_KDB; break; case 'D': howto |= RB_MULTIPLE; break; case 'h': howto |= RB_SERIAL; break; case 'm': howto |= RB_MUTE; break; case 'p': howto |= RB_PAUSE; break; case 'P': if (!has_kbd) howto |= RB_SERIAL | RB_MULTIPLE; break; case 'r': howto |= RB_DFLTROOT; break; case 's': howto |= RB_SINGLE; break; case 'S': if (argv[i][j + 1] == 0) { if (i + 1 == argc) { setenv("comconsole_speed", "115200", 1); } else { cpy16to8(&argv[i + 1][0], var, sizeof(var)); setenv("comconsole_speedspeed", var, 1); } i++; break; } else { cpy16to8(&argv[i][j + 1], var, sizeof(var)); setenv("comconsole_speed", var, 1); break; } case 'v': howto |= RB_VERBOSE; break; } } } else { vargood = 0; for (j = 0; argv[i][j] != 0; j++) { if (j == sizeof(var)) { vargood = 0; break; } if (j > 0 && argv[i][j] == '=') vargood = 1; var[j] = (char)argv[i][j]; } if (vargood) { var[j] = 0; putenv(var); } } } for (i = 0; howto_names[i].ev != NULL; i++) if (howto & howto_names[i].mask) setenv(howto_names[i].ev, "YES", 1); if (howto & RB_MULTIPLE) { if (howto & RB_SERIAL) setenv("console", "comconsole efi" , 1); else setenv("console", "efi comconsole" , 1); } else if (howto & RB_SERIAL) { setenv("console", "comconsole" , 1); } if (efi_copy_init()) { printf("failed to allocate staging area\n"); return (EFI_BUFFER_TOO_SMALL); } /* * March through the device switch probing for things. */ for (i = 0; devsw[i] != NULL; i++) if (devsw[i]->dv_init != NULL) (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(" %S", 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); printf("EFI Firmware: %S (rev %d.%02d)\n", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); printf("\n%s", bootprog_info); /* * Disable the watchdog timer. By default the boot manager sets * the timer to 5 minutes before invoking a boot option. If we * want to return to the boot manager, we have to disable the * watchdog timer and since we're an interactive program, we don't * want to wait until the user types "quit". The timer may have * fired by then. We don't care if this fails. It does not prevent * normal functioning in any way... */ BS->SetWatchdogTimer(0, 0, 0, NULL); - if (find_currdev(img, &dev, &unit, &pool_guid) != 0) + if (find_currdev(img) != 0) return (EFI_NOT_FOUND); - switch (dev->dv_type) { -#ifdef EFI_ZFS_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); - init_zfs_bootenv(zfs_fmtdev(&currdev)); - break; - } -#endif - 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; - } - } - efi_init_environment(); setenv("LINES", "24", 1); /* optional */ for (k = 0; k < ST->NumberOfTableEntries; k++) { guid = &ST->ConfigurationTable[k].VendorGuid; if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) { snprintf(buf, sizeof(buf), "%p", ST->ConfigurationTable[k].VendorTable); setenv("hint.smbios.0.mem", buf, 1); smbios_detect(ST->ConfigurationTable[k].VendorTable); break; } } interact(NULL); /* doesn't return */ return (EFI_SUCCESS); /* keep compiler happy */ } COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot); static int command_reboot(int argc, char *argv[]) { int i; for (i = 0; devsw[i] != NULL; ++i) if (devsw[i]->dv_cleanup != NULL) (devsw[i]->dv_cleanup)(); RS->ResetSystem(EfiResetCold, EFI_SUCCESS, 23, (CHAR16 *)"Reboot from the loader"); /* NOTREACHED */ return (CMD_ERROR); } COMMAND_SET(quit, "quit", "exit the loader", command_quit); static int command_quit(int argc, char *argv[]) { exit(0); return (CMD_OK); } COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); static int command_memmap(int argc, char *argv[]) { UINTN sz; EFI_MEMORY_DESCRIPTOR *map, *p; UINTN key, dsz; UINT32 dver; EFI_STATUS status; int i, ndesc; char line[80]; static char *types[] = { "Reserved", "LoaderCode", "LoaderData", "BootServicesCode", "BootServicesData", "RuntimeServicesCode", "RuntimeServicesData", "ConventionalMemory", "UnusableMemory", "ACPIReclaimMemory", "ACPIMemoryNVS", "MemoryMappedIO", "MemoryMappedIOPortSpace", "PalCode" }; sz = 0; 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); } 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); } ndesc = sz / dsz; snprintf(line, sizeof(line), "%23s %12s %12s %8s %4s\n", "Type", "Physical", "Virtual", "#Pages", "Attr"); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0, p = map; i < ndesc; i++, p = NextMemoryDescriptor(p, dsz)) { printf("%23s %012jx %012jx %08jx ", types[p->Type], (uintmax_t)p->PhysicalStart, (uintmax_t)p->VirtualStart, (uintmax_t)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 "); if (pager_output("\n")) break; } pager_close(); return (CMD_OK); } COMMAND_SET(configuration, "configuration", "print configuration tables", command_configuration); static const char * guid_to_string(EFI_GUID *guid) { static char buf[40]; sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); return (buf); } static int command_configuration(int argc, char *argv[]) { char line[80]; UINTN i; snprintf(line, sizeof(line), "NumberOfTableEntries=%lu\n", (unsigned long)ST->NumberOfTableEntries); pager_open(); if (pager_output(line)) { pager_close(); return (CMD_OK); } for (i = 0; i < ST->NumberOfTableEntries; i++) { EFI_GUID *guid; printf(" "); guid = &ST->ConfigurationTable[i].VendorGuid; if (!memcmp(guid, &mps, sizeof(EFI_GUID))) printf("MPS Table"); else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) printf("ACPI Table"); else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) printf("ACPI 2.0 Table"); else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) printf("SMBIOS Table %p", ST->ConfigurationTable[i].VendorTable); else if (!memcmp(guid, &dxe, sizeof(EFI_GUID))) printf("DXE Table"); else if (!memcmp(guid, &hoblist, sizeof(EFI_GUID))) printf("HOB List Table"); else if (!memcmp(guid, &memtype, sizeof(EFI_GUID))) printf("Memory Type Information Table"); else if (!memcmp(guid, &debugimg, sizeof(EFI_GUID))) printf("Debug Image Info Table"); else if (!memcmp(guid, &fdtdtb, sizeof(EFI_GUID))) printf("FDT Table"); else printf("Unknown Table (%s)", guid_to_string(guid)); snprintf(line, sizeof(line), " at %p\n", ST->ConfigurationTable[i].VendorTable); if (pager_output(line)) break; } pager_close(); return (CMD_OK); } COMMAND_SET(mode, "mode", "change or display EFI text modes", command_mode); static int command_mode(int argc, char *argv[]) { UINTN cols, rows; unsigned int mode; int i; char *cp; char rowenv[8]; EFI_STATUS status; SIMPLE_TEXT_OUTPUT_INTERFACE *conout; extern void HO(void); conout = ST->ConOut; if (argc > 1) { mode = strtol(argv[1], &cp, 0); if (cp[0] != '\0') { printf("Invalid mode\n"); return (CMD_ERROR); } status = conout->QueryMode(conout, mode, &cols, &rows); if (EFI_ERROR(status)) { printf("invalid mode %d\n", mode); return (CMD_ERROR); } status = conout->SetMode(conout, mode); if (EFI_ERROR(status)) { printf("couldn't set mode %d\n", mode); return (CMD_ERROR); } sprintf(rowenv, "%u", (unsigned)rows); setenv("LINES", rowenv, 1); HO(); /* set cursor */ return (CMD_OK); } printf("Current mode: %d\n", conout->Mode->Mode); for (i = 0; i <= conout->Mode->MaxMode; i++) { status = conout->QueryMode(conout, i, &cols, &rows); if (EFI_ERROR(status)) continue; printf("Mode %d: %u columns, %u rows\n", i, (unsigned)cols, (unsigned)rows); } if (i != 0) printf("Select a mode with the command \"mode \"\n"); return (CMD_OK); } #ifdef EFI_ZFS_BOOT 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); } COMMAND_SET(reloadbe, "reloadbe", "refresh the list of ZFS Boot Environments", command_reloadbe); static int command_reloadbe(int argc, char *argv[]) { int err; char *root; if (argc > 2) { command_errmsg = "wrong number of arguments"; return (CMD_ERROR); } if (argc == 2) { err = zfs_bootenv(argv[1]); } else { root = getenv("zfs_be_root"); if (root == NULL) { return (CMD_OK); } err = zfs_bootenv(root); } if (err != 0) { command_errmsg = strerror(err); return (CMD_ERROR); } return (CMD_OK); } #endif #ifdef LOADER_FDT_SUPPORT extern int command_fdt_internal(int argc, char *argv[]); /* * Since proper fdt command handling function is defined in fdt_loader_cmd.c, * and declaring it as extern is in contradiction with COMMAND_SET() macro * (which uses static pointer), we're defining wrapper function, which * calls the proper fdt handling routine. */ static int command_fdt(int argc, char *argv[]) { return (command_fdt_internal(argc, argv)); } COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt); #endif #ifdef EFI_ZFS_BOOT static void +efipart_probe_img(pdinfo_list_t *hdi) +{ + EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; + EFI_LOADED_IMAGE *img; + pdinfo_t *hd, *pd = NULL; + char devname[SPECNAMELEN + 1]; + + BS->HandleProtocol(IH, &imgid, (VOID**)&img); + + /* + * Search for the booted image device handle from hard disk list. + * Note, this does also include usb sticks, and we assume there is no + * ZFS on floppies nor cd. + * However, we might have booted from floppy (unlikely) or CD, + * so we should not surprised if we can not find the handle. + */ + STAILQ_FOREACH(hd, hdi, pd_link) { + if (hd->pd_handle == img->DeviceHandle) + break; + STAILQ_FOREACH(pd, &hd->pd_part, pd_link) { + if (pd->pd_handle == img->DeviceHandle) + break; + } + if (pd != NULL) + break; + } + if (hd != NULL) { + if (pd != NULL) { + snprintf(devname, sizeof(devname), "%s%dp%d:", + efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit); + } else { + snprintf(devname, sizeof(devname), "%s%d:", + efipart_hddev.dv_name, hd->pd_unit); + } + (void) zfs_probe_dev(devname, &pool_guid); + } +} + +static void efi_zfs_probe(void) { - EFI_HANDLE h; - u_int unit; - int i; - char dname[SPECNAMELEN + 1]; - uint64_t guid; + pdinfo_list_t *hdi; + pdinfo_t *hd; + char devname[SPECNAMELEN + 1]; - 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); + hdi = efiblk_get_pdinfo_list(&efipart_hddev); + /* + * First probe the boot device (from where loader.efi was read), + * and set pool_guid global variable if we are booting from zfs. + * Since loader is running, we do have an access to the device, + * however, it might not be zfs. + */ + + if (pool_guid == 0) + efipart_probe_img(hdi); + + STAILQ_FOREACH(hd, hdi, pd_link) { + snprintf(devname, sizeof(devname), "%s%d:", + efipart_hddev.dv_name, hd->pd_unit); + (void) zfs_probe_dev(devname, NULL); } } #endif Index: head/sys/boot/zfs/zfs.c =================================================================== --- head/sys/boot/zfs/zfs.c (revision 313332) +++ head/sys/boot/zfs/zfs.c (revision 313333) @@ -1,907 +1,912 @@ /*- * Copyright (c) 2007 Doug Rabson * 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 __FBSDID("$FreeBSD$"); /* * Stand-alone file reading package. */ #include #include #include #include #include #include #include #include #include #include #include "libzfs.h" #include "zfsimpl.c" /* Define the range of indexes to be populated with ZFS Boot Environments */ #define ZFS_BE_FIRST 4 #define ZFS_BE_LAST 8 static int zfs_open(const char *path, struct open_file *f); static int zfs_write(struct open_file *f, void *buf, size_t size, size_t *resid); static int zfs_close(struct open_file *f); static int zfs_read(struct open_file *f, void *buf, size_t size, size_t *resid); static off_t zfs_seek(struct open_file *f, off_t offset, int where); static int zfs_stat(struct open_file *f, struct stat *sb); static int zfs_readdir(struct open_file *f, struct dirent *d); struct devsw zfs_dev; struct fs_ops zfs_fsops = { "zfs", zfs_open, zfs_close, zfs_read, zfs_write, zfs_seek, zfs_stat, zfs_readdir }; /* * In-core open file. */ struct file { off_t f_seekp; /* seek pointer */ dnode_phys_t f_dnode; uint64_t f_zap_type; /* zap type for readdir */ uint64_t f_num_leafs; /* number of fzap leaf blocks */ zap_leaf_phys_t *f_zap_leaf; /* zap leaf buffer */ }; static int zfs_env_index; static int zfs_env_count; SLIST_HEAD(zfs_be_list, zfs_be_entry) zfs_be_head = SLIST_HEAD_INITIALIZER(zfs_be_head); struct zfs_be_list *zfs_be_headp; struct zfs_be_entry { const char *name; SLIST_ENTRY(zfs_be_entry) entries; } *zfs_be, *zfs_be_tmp; /* * Open a file. */ static int zfs_open(const char *upath, struct open_file *f) { struct zfsmount *mount = (struct zfsmount *)f->f_devdata; struct file *fp; int rc; if (f->f_dev != &zfs_dev) return (EINVAL); /* allocate file system specific data structure */ fp = malloc(sizeof(struct file)); bzero(fp, sizeof(struct file)); f->f_fsdata = (void *)fp; rc = zfs_lookup(mount, upath, &fp->f_dnode); fp->f_seekp = 0; if (rc) { f->f_fsdata = NULL; free(fp); } return (rc); } static int zfs_close(struct open_file *f) { struct file *fp = (struct file *)f->f_fsdata; dnode_cache_obj = 0; f->f_fsdata = (void *)0; if (fp == (struct file *)0) return (0); free(fp); return (0); } /* * Copy a portion of a file into kernel memory. * Cross block boundaries when necessary. */ static int zfs_read(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; struct stat sb; size_t n; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); 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); if (0) { int i; for (i = 0; i < n; i++) putchar(((char*) start)[i]); } fp->f_seekp += n; if (resid) *resid = size - n; return (0); } /* * Don't be silly - the bootstrap has no business writing anything. */ static int zfs_write(struct open_file *f, void *start, size_t size, size_t *resid /* out */) { return (EROFS); } static off_t zfs_seek(struct open_file *f, off_t offset, int where) { struct file *fp = (struct file *)f->f_fsdata; switch (where) { case SEEK_SET: fp->f_seekp = offset; break; case SEEK_CUR: fp->f_seekp += offset; break; case SEEK_END: { struct stat sb; int error; error = zfs_stat(f, &sb); if (error != 0) { errno = error; return (-1); } fp->f_seekp = sb.st_size - offset; break; } default: errno = EINVAL; return (-1); } return (fp->f_seekp); } static int zfs_stat(struct open_file *f, struct stat *sb) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; return (zfs_dnode_stat(spa, &fp->f_dnode, sb)); } static int zfs_readdir(struct open_file *f, struct dirent *d) { const spa_t *spa = ((struct zfsmount *)f->f_devdata)->spa; struct file *fp = (struct file *)f->f_fsdata; mzap_ent_phys_t mze; struct stat sb; size_t bsize = fp->f_dnode.dn_datablkszsec << SPA_MINBLOCKSHIFT; int rc; rc = zfs_stat(f, &sb); if (rc) return (rc); if (!S_ISDIR(sb.st_mode)) return (ENOTDIR); /* * If this is the first read, get the zap type. */ if (fp->f_seekp == 0) { rc = dnode_read(spa, &fp->f_dnode, 0, &fp->f_zap_type, sizeof(fp->f_zap_type)); if (rc) return (rc); if (fp->f_zap_type == ZBT_MICRO) { fp->f_seekp = offsetof(mzap_phys_t, mz_chunk); } else { rc = dnode_read(spa, &fp->f_dnode, offsetof(zap_phys_t, zap_num_leafs), &fp->f_num_leafs, sizeof(fp->f_num_leafs)); if (rc) return (rc); fp->f_seekp = bsize; fp->f_zap_leaf = (zap_leaf_phys_t *)malloc(bsize); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } } if (fp->f_zap_type == ZBT_MICRO) { mzap_next: if (fp->f_seekp >= bsize) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, &mze, sizeof(mze)); if (rc) return (rc); fp->f_seekp += sizeof(mze); if (!mze.mze_name[0]) goto mzap_next; d->d_fileno = ZFS_DIRENT_OBJ(mze.mze_value); d->d_type = ZFS_DIRENT_TYPE(mze.mze_value); strcpy(d->d_name, mze.mze_name); d->d_namlen = strlen(d->d_name); return (0); } else { zap_leaf_t zl; zap_leaf_chunk_t *zc, *nc; int chunk; size_t namelen; char *p; uint64_t value; /* * Initialise this so we can use the ZAP size * calculating macros. */ zl.l_bs = ilog2(bsize); zl.l_phys = fp->f_zap_leaf; /* * Figure out which chunk we are currently looking at * and consider seeking to the next leaf. We use the * low bits of f_seekp as a simple chunk index. */ fzap_next: chunk = fp->f_seekp & (bsize - 1); if (chunk == ZAP_LEAF_NUMCHUNKS(&zl)) { fp->f_seekp = rounddown2(fp->f_seekp, bsize) + bsize; chunk = 0; /* * Check for EOF and read the new leaf. */ if (fp->f_seekp >= bsize * fp->f_num_leafs) return (ENOENT); rc = dnode_read(spa, &fp->f_dnode, fp->f_seekp, fp->f_zap_leaf, bsize); if (rc) return (rc); } zc = &ZAP_LEAF_CHUNK(&zl, chunk); fp->f_seekp++; if (zc->l_entry.le_type != ZAP_CHUNK_ENTRY) goto fzap_next; namelen = zc->l_entry.le_name_numints; if (namelen > sizeof(d->d_name)) namelen = sizeof(d->d_name); /* * Paste the name back together. */ nc = &ZAP_LEAF_CHUNK(&zl, zc->l_entry.le_name_chunk); p = d->d_name; while (namelen > 0) { int len; len = namelen; if (len > ZAP_LEAF_ARRAY_BYTES) len = ZAP_LEAF_ARRAY_BYTES; memcpy(p, nc->l_array.la_array, len); p += len; namelen -= len; nc = &ZAP_LEAF_CHUNK(&zl, nc->l_array.la_next); } d->d_name[sizeof(d->d_name) - 1] = 0; /* * Assume the first eight bytes of the value are * a uint64_t. */ value = fzap_leaf_value(&zl, zc); d->d_fileno = ZFS_DIRENT_OBJ(value); d->d_type = ZFS_DIRENT_TYPE(value); d->d_namlen = strlen(d->d_name); return (0); } } static int vdev_read(vdev_t *vdev, void *priv, off_t offset, void *buf, size_t size) { int fd; fd = (uintptr_t) priv; lseek(fd, offset, SEEK_SET); if (read(fd, buf, size) == size) { return 0; } else { return (EIO); } } static int zfs_dev_init(void) { spa_t *spa; spa_t *next; spa_t *prev; zfs_init(); if (archsw.arch_zfs_probe == NULL) return (ENXIO); archsw.arch_zfs_probe(); prev = NULL; spa = STAILQ_FIRST(&zfs_pools); while (spa != NULL) { next = STAILQ_NEXT(spa, spa_link); if (zfs_spa_init(spa)) { if (prev == NULL) STAILQ_REMOVE_HEAD(&zfs_pools, spa_link); else STAILQ_REMOVE_AFTER(&zfs_pools, prev, spa_link); } else prev = spa; spa = next; } return (0); } struct zfs_probe_args { int fd; const char *devname; uint64_t *pool_guid; u_int secsz; }; static int zfs_diskread(void *arg, void *buf, size_t blocks, uint64_t offset) { struct zfs_probe_args *ppa; ppa = (struct zfs_probe_args *)arg; return (vdev_read(NULL, (void *)(uintptr_t)ppa->fd, offset * ppa->secsz, buf, blocks * ppa->secsz)); } static int zfs_probe(int fd, uint64_t *pool_guid) { spa_t *spa; int ret; ret = vdev_probe(vdev_read, (void *)(uintptr_t)fd, &spa); if (ret == 0 && pool_guid != NULL) *pool_guid = spa->spa_guid; return (ret); } static int zfs_probe_partition(void *arg, const char *partname, const struct ptable_entry *part) { struct zfs_probe_args *ppa, pa; struct ptable *table; char devname[32]; int ret; /* Probe only freebsd-zfs and freebsd partitions */ if (part->type != PART_FREEBSD && part->type != PART_FREEBSD_ZFS) return (0); ppa = (struct zfs_probe_args *)arg; strncpy(devname, ppa->devname, strlen(ppa->devname) - 1); devname[strlen(ppa->devname) - 1] = '\0'; sprintf(devname, "%s%s:", devname, partname); pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return (0); ret = zfs_probe(pa.fd, ppa->pool_guid); if (ret == 0) return (0); /* Do we have BSD label here? */ if (part->type == PART_FREEBSD) { pa.devname = devname; pa.pool_guid = ppa->pool_guid; pa.secsz = ppa->secsz; table = ptable_open(&pa, part->end - part->start + 1, ppa->secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); return (0); } int zfs_probe_dev(const char *devname, uint64_t *pool_guid) { struct ptable *table; struct zfs_probe_args pa; uint64_t mediasz; int ret; + if (pool_guid) + *pool_guid = 0; pa.fd = open(devname, O_RDONLY); if (pa.fd == -1) return (ENXIO); /* Probe the whole disk */ ret = zfs_probe(pa.fd, pool_guid); if (ret == 0) return (0); + /* Probe each partition */ ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz); if (ret == 0) ret = ioctl(pa.fd, DIOCGSECTORSIZE, &pa.secsz); if (ret == 0) { pa.devname = devname; pa.pool_guid = pool_guid; table = ptable_open(&pa, mediasz / pa.secsz, pa.secsz, zfs_diskread); if (table != NULL) { ptable_iterate(table, &pa, zfs_probe_partition); ptable_close(table); } } close(pa.fd); + if (pool_guid && *pool_guid == 0) + ret = ENXIO; return (ret); } /* * Print information about ZFS pools */ static int zfs_dev_print(int verbose) { spa_t *spa; char line[80]; int ret = 0; if (STAILQ_EMPTY(&zfs_pools)) return (0); printf("%s devices:", zfs_dev.dv_name); if ((ret = pager_output("\n")) != 0) return (ret); if (verbose) { return (spa_all_status()); } STAILQ_FOREACH(spa, &zfs_pools, spa_link) { snprintf(line, sizeof(line), " zfs:%s\n", spa->spa_name); ret = pager_output(line); if (ret != 0) break; } return (ret); } /* * Attempt to open the pool described by (dev) for use by (f). */ static int zfs_dev_open(struct open_file *f, ...) { va_list args; struct zfs_devdesc *dev; struct zfsmount *mount; spa_t *spa; int rv; va_start(args, f); dev = va_arg(args, struct zfs_devdesc *); va_end(args); if (dev->pool_guid == 0) spa = STAILQ_FIRST(&zfs_pools); else spa = spa_find_by_guid(dev->pool_guid); if (!spa) return (ENXIO); mount = malloc(sizeof(*mount)); rv = zfs_mount(spa, dev->root_guid, mount); if (rv != 0) { free(mount); return (rv); } if (mount->objset.os_type != DMU_OST_ZFS) { printf("Unexpected object set type %ju\n", (uintmax_t)mount->objset.os_type); free(mount); return (EIO); } f->f_devdata = mount; free(dev); return (0); } static int zfs_dev_close(struct open_file *f) { free(f->f_devdata); f->f_devdata = NULL; return (0); } static int zfs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize) { return (ENOSYS); } struct devsw zfs_dev = { .dv_name = "zfs", .dv_type = DEVT_ZFS, .dv_init = zfs_dev_init, .dv_strategy = zfs_dev_strategy, .dv_open = zfs_dev_open, .dv_close = zfs_dev_close, .dv_ioctl = noioctl, .dv_print = zfs_dev_print, .dv_cleanup = NULL }; int zfs_parsedev(struct zfs_devdesc *dev, const char *devspec, const char **path) { static char rootname[ZFS_MAXNAMELEN]; static char poolname[ZFS_MAXNAMELEN]; spa_t *spa; const char *end; const char *np; const char *sep; int rv; np = devspec; if (*np != ':') return (EINVAL); np++; end = strchr(np, ':'); if (end == NULL) return (EINVAL); sep = strchr(np, '/'); if (sep == NULL || sep >= end) sep = end; memcpy(poolname, np, sep - np); poolname[sep - np] = '\0'; if (sep < end) { sep++; memcpy(rootname, sep, end - sep); rootname[end - sep] = '\0'; } else rootname[0] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); dev->pool_guid = spa->spa_guid; rv = zfs_lookup_dataset(spa, rootname, &dev->root_guid); if (rv != 0) return (rv); if (path != NULL) *path = (*end == '\0') ? end : end + 1; dev->d_dev = &zfs_dev; dev->d_type = zfs_dev.dv_type; return (0); } char * zfs_fmtdev(void *vdev) { static char rootname[ZFS_MAXNAMELEN]; static char buf[2 * ZFS_MAXNAMELEN + 8]; struct zfs_devdesc *dev = (struct zfs_devdesc *)vdev; spa_t *spa; buf[0] = '\0'; if (dev->d_type != DEVT_ZFS) return (buf); if (dev->pool_guid == 0) { spa = STAILQ_FIRST(&zfs_pools); dev->pool_guid = spa->spa_guid; } else spa = spa_find_by_guid(dev->pool_guid); if (spa == NULL) { printf("ZFS: can't find pool by guid\n"); return (buf); } if (dev->root_guid == 0 && zfs_get_root(spa, &dev->root_guid)) { printf("ZFS: can't find root filesystem\n"); return (buf); } if (zfs_rlookup(spa, dev->root_guid, rootname)) { printf("ZFS: can't find filesystem by guid\n"); return (buf); } if (rootname[0] == '\0') sprintf(buf, "%s:%s:", dev->d_dev->dv_name, spa->spa_name); else sprintf(buf, "%s:%s/%s:", dev->d_dev->dv_name, spa->spa_name, rootname); return (buf); } int zfs_list(const char *name) { static char poolname[ZFS_MAXNAMELEN]; uint64_t objid; spa_t *spa; const char *dsname; int len; int rv; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; memcpy(poolname, name, len); poolname[len] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return (rv); return (zfs_list_dataset(spa, objid)); } void init_zfs_bootenv(char *currdev) { char *beroot; if (strlen(currdev) == 0) return; if(strncmp(currdev, "zfs:", 4) != 0) return; /* Remove the trailing : */ currdev[strlen(currdev) - 1] = '\0'; setenv("zfs_be_active", currdev, 1); setenv("zfs_be_currpage", "1", 1); /* Forward past zfs: */ currdev = strchr(currdev, ':'); currdev++; /* Remove the last element (current bootenv) */ beroot = strrchr(currdev, '/'); if (beroot != NULL) beroot[0] = '\0'; beroot = currdev; setenv("zfs_be_root", beroot, 1); } int zfs_bootenv(const char *name) { static char poolname[ZFS_MAXNAMELEN], *dsname, *root; char becount[4]; uint64_t objid; spa_t *spa; int len, rv, pages, perpage, currpage; if (name == NULL) return (EINVAL); if ((root = getenv("zfs_be_root")) == NULL) return (EINVAL); if (strcmp(name, root) != 0) { if (setenv("zfs_be_root", name, 1) != 0) return (ENOMEM); } SLIST_INIT(&zfs_be_head); zfs_env_count = 0; len = strlen(name); dsname = strchr(name, '/'); if (dsname != NULL) { len = dsname - name; dsname++; } else dsname = ""; memcpy(poolname, name, len); poolname[len] = '\0'; spa = spa_find_by_name(poolname); if (!spa) return (ENXIO); rv = zfs_lookup_dataset(spa, dsname, &objid); if (rv != 0) return (rv); rv = zfs_callback_dataset(spa, objid, zfs_belist_add); /* Calculate and store the number of pages of BEs */ perpage = (ZFS_BE_LAST - ZFS_BE_FIRST + 1); pages = (zfs_env_count / perpage) + ((zfs_env_count % perpage) > 0 ? 1 : 0); snprintf(becount, 4, "%d", pages); if (setenv("zfs_be_pages", becount, 1) != 0) return (ENOMEM); /* Roll over the page counter if it has exceeded the maximum */ currpage = strtol(getenv("zfs_be_currpage"), NULL, 10); if (currpage > pages) { if (setenv("zfs_be_currpage", "1", 1) != 0) return (ENOMEM); } /* Populate the menu environment variables */ zfs_set_env(); /* Clean up the SLIST of ZFS BEs */ while (!SLIST_EMPTY(&zfs_be_head)) { zfs_be = SLIST_FIRST(&zfs_be_head); SLIST_REMOVE_HEAD(&zfs_be_head, entries); free(zfs_be); } return (rv); } int zfs_belist_add(const char *name, uint64_t value __unused) { /* Skip special datasets that start with a $ character */ if (strncmp(name, "$", 1) == 0) { return (0); } /* Add the boot environment to the head of the SLIST */ zfs_be = malloc(sizeof(struct zfs_be_entry)); if (zfs_be == NULL) { return (ENOMEM); } zfs_be->name = name; SLIST_INSERT_HEAD(&zfs_be_head, zfs_be, entries); zfs_env_count++; return (0); } int zfs_set_env(void) { char envname[32], envval[256]; char *beroot, *pagenum; int rv, page, ctr; beroot = getenv("zfs_be_root"); if (beroot == NULL) { return (1); } pagenum = getenv("zfs_be_currpage"); if (pagenum != NULL) { page = strtol(pagenum, NULL, 10); } else { page = 1; } ctr = 1; rv = 0; zfs_env_index = ZFS_BE_FIRST; SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) { /* Skip to the requested page number */ if (ctr <= ((ZFS_BE_LAST - ZFS_BE_FIRST + 1) * (page - 1))) { ctr++; continue; } snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); snprintf(envval, sizeof(envval), "%s", zfs_be->name); rv = setenv(envname, envval, 1); if (rv != 0) { break; } snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); rv = setenv(envname, envval, 1); if (rv != 0){ break; } snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); rv = setenv(envname, "set_bootenv", 1); if (rv != 0){ break; } snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); snprintf(envval, sizeof(envval), "zfs:%s/%s", beroot, zfs_be->name); rv = setenv(envname, envval, 1); if (rv != 0){ break; } zfs_env_index++; if (zfs_env_index > ZFS_BE_LAST) { break; } } for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenvansi_caption[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenvmenu_command[%d]", zfs_env_index); (void)unsetenv(envname); snprintf(envname, sizeof(envname), "bootenv_root[%d]", zfs_env_index); (void)unsetenv(envname); } return (rv); }