diff options
Diffstat (limited to 'usr/src/lib/libefi')
-rw-r--r-- | usr/src/lib/libefi/common/crc32_efi.c | 5 | ||||
-rw-r--r-- | usr/src/lib/libefi/common/rdwr_efi.c | 112 |
2 files changed, 97 insertions, 20 deletions
diff --git a/usr/src/lib/libefi/common/crc32_efi.c b/usr/src/lib/libefi/common/crc32_efi.c index 1dba28ad72..73039f1478 100644 --- a/usr/src/lib/libefi/common/crc32_efi.c +++ b/usr/src/lib/libefi/common/crc32_efi.c @@ -23,8 +23,9 @@ * Copyright 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2019, Joyent, Inc. + */ #include <sys/crc32.h> diff --git a/usr/src/lib/libefi/common/rdwr_efi.c b/usr/src/lib/libefi/common/rdwr_efi.c index afbc4c4f00..74f65378b1 100644 --- a/usr/src/lib/libefi/common/rdwr_efi.c +++ b/usr/src/lib/libefi/common/rdwr_efi.c @@ -132,12 +132,98 @@ int efi_debug = 0; extern unsigned int efi_crc32(const unsigned char *, unsigned int); static int efi_read(int, struct dk_gpt *); +/* + * In normal operation, libefi just passes everything down to the kernel driver + * (and - usually - cmlb), as that code needs to react to any partitioning + * changes by changing device nodes under /dev/?dsk/ and the like. + * + * However, if we are running against an un-labeled lofi device on an older + * version of illumos, these ioctl()s aren't emulated. This can be a problem if + * we're in a non-global zone, which doesn't support labeled lofi, and our + * kernel is downrev. + * + * In this case, we'll simply emulate the ioctl()s that libefi actually needs, + * except those for efi_type(). They basically boil down to simple reads and + * writes, though this does skip a bunch of error checking. + * + * As a final wrinkle, rather than rely on an updated libefi, smartos-live's + * format_image tool directly builds and uses this source. + */ +static int +do_ioctl(int fd, int cmd, void *arg) +{ + struct dk_cinfo cinfo; + struct dk_minfo minfo; + dk_efi_t *efi = arg; + int saved_errno; + size_t len; + int error; + + error = ioctl(fd, cmd, arg); + + saved_errno = errno; + + if (error != -1 || errno != ENOTTY || + ioctl(fd, DKIOCINFO, (caddr_t)&cinfo) != 0 || + strcmp(cinfo.dki_cname, "lofi") != 0 || + ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&minfo) != 0) { + errno = saved_errno; + return (error); + } + + switch (cmd) { + case DKIOCGMBOOT: + len = (size_t)pread(fd, arg, minfo.dki_lbsize, 0); + error = (len == minfo.dki_lbsize) ? 0 : -1; + break; + + case DKIOCSMBOOT: + len = (size_t)pwrite(fd, arg, minfo.dki_lbsize, 0); + error = (len == minfo.dki_lbsize) ? 0 : -1; + break; + + case DKIOCGETEFI: + len = (size_t)pread(fd, (caddr_t)(uintptr_t)efi->dki_data_64, + efi->dki_length, efi->dki_lba * minfo.dki_lbsize); + error = (len == efi->dki_length) ? 0 : -1; + break; + + case DKIOCSETEFI: + len = (size_t)pwrite(fd, (caddr_t)(uintptr_t)efi->dki_data_64, + efi->dki_length, efi->dki_lba * minfo.dki_lbsize); + error = (len == efi->dki_length) ? 0 : -1; + break; + + default: + errno = saved_errno; + break; + } + + if (error == 0) + errno = 0; + + return (error); +} + +static int +efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) +{ + void *data = dk_ioc->dki_data; + int error; + + dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; + error = do_ioctl(fd, cmd, (void *)dk_ioc); + dk_ioc->dki_data = data; + + return (error); +} + static int read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize) { struct dk_minfo disk_info; - if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1) + if ((do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1) return (errno); *capacity = disk_info.dki_capacity; *lbsize = disk_info.dki_lbsize; @@ -196,6 +282,7 @@ efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc) "the maximum number of partitions supported is %lu\n", MAX_PARTS); } + errno = EINVAL; return (-1); } @@ -246,7 +333,7 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) if ((mbr = calloc(1, lbsize)) == NULL) return (VT_ERROR); - if ((ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) { + if ((do_ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) { free(mbr); return (VT_ERROR); } @@ -305,19 +392,6 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc) } static int -efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) -{ - void *data = dk_ioc->dki_data; - int error; - - dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; - error = ioctl(fd, cmd, (void *)dk_ioc); - dk_ioc->dki_data = data; - - return (error); -} - -static int check_label(int fd, dk_efi_t *dk_ioc) { efi_gpt_t *efi; @@ -380,7 +454,7 @@ efi_read(int fd, struct dk_gpt *vtoc) /* * get the partition number for this file descriptor. */ - if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { if (efi_debug) { (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); } @@ -404,7 +478,7 @@ efi_read(int fd, struct dk_gpt *vtoc) } /* get the LBA size */ - if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { + if (do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { if (efi_debug) { (void) fprintf(stderr, "assuming LBA 512 bytes %d\n", @@ -997,7 +1071,7 @@ efi_write(int fd, struct dk_gpt *vtoc) int nblocks; diskaddr_t lba_backup_gpt_hdr; - if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { + if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) { if (efi_debug) (void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno); switch (errno) { @@ -1181,6 +1255,8 @@ efi_free(struct dk_gpt *ptr) * Input: File descriptor * Output: 1 if disk has an EFI label, or > 2TB with no VTOC or legacy MBR. * Otherwise 0. + * + * This always returns 0 for an un-labeled lofi device. */ int efi_type(int fd) |