diff options
author | Alexander Motin <mav@FreeBSD.org> | 2018-02-21 00:18:57 +0000 |
---|---|---|
committer | Alexander Motin <mav@FreeBSD.org> | 2018-02-21 00:18:57 +0000 |
commit | e5a4a83784d26b3804df1c9519078e64776bf10b (patch) | |
tree | ba06d0fb4b60e26c2b5cc942efdfe2c91db7e262 /cddl/contrib/opensolaris/lib/libzfs | |
parent | e73c944fc079f7f08a32270eb83854e3270b05e4 (diff) | |
parent | 9df2d6f729b7642daf9a1d5d3d610959ba894c3d (diff) |
Notes
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libzfs')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h | 16 | ||||
-rw-r--r-- | cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c | 170 |
2 files changed, 155 insertions, 31 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h index 9133238cb848..3e15dd1c814d 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -135,6 +135,18 @@ typedef enum zfs_error { } zfs_error_t; /* + * UEFI boot support parameters. When creating whole disk boot pool, + * zpool create should allow to create EFI System partition for UEFI boot + * program. In case of BIOS, the EFI System partition is not used + * even if it does exist. + */ +typedef enum zpool_boot_label { + ZPOOL_NO_BOOT_LABEL = 0, + ZPOOL_CREATE_BOOT_LABEL, + ZPOOL_COPY_BOOT_LABEL +} zpool_boot_label_t; + +/* * The following data structures are all part * of the zfs_allow_t data structure which is * used for printing 'allow' permissions. @@ -266,7 +278,8 @@ extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *, boolean_t *, boolean_t *); extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *, boolean_t *, boolean_t *, boolean_t *); -extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *); +extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *, + zpool_boot_label_t, uint64_t, int *); /* * Functions to manage pool properties @@ -349,6 +362,7 @@ extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **); extern nvlist_t *zpool_get_features(zpool_handle_t *); extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *); extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **); +extern boolean_t zpool_is_bootable(zpool_handle_t *); /* * Import and export functions diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c index 962b3ff2b61b..612f37ebd4b8 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c @@ -49,7 +49,7 @@ #include "zfs_comutil.h" #include "zfeature_common.h" -static int read_efi_label(nvlist_t *config, diskaddr_t *sb); +static int read_efi_label(nvlist_t *, diskaddr_t *, boolean_t *); static boolean_t zpool_vdev_is_interior(const char *name); #define BACKUP_SLICE "s2" @@ -316,6 +316,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len, (void) zfs_nicenum(intval, buf, len); } break; + case ZPOOL_PROP_BOOTSIZE: case ZPOOL_PROP_EXPANDSZ: if (intval == 0) { (void) strlcpy(buf, "-", len); @@ -517,6 +518,16 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, } break; + case ZPOOL_PROP_BOOTSIZE: + if (!flags.create) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property '%s' can only be set during pool " + "creation"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + break; + case ZPOOL_PROP_BOOTFS: if (flags.create || flags.import) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -1990,8 +2001,9 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, /* * Search for the requested value. Special cases: * - * - ZPOOL_CONFIG_PATH for whole disk entries. These end in - * "s0" or "s0/old". The "s0" part is hidden from the user, + * - ZPOOL_CONFIG_PATH for whole disk entries. To support + * UEFI boot, these end in "s0" or "s0/old" or "s1" or + * "s1/old". The "s0" or "s1" part is hidden from the user, * but included in the string, so this matches around it. * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE). * @@ -2022,14 +2034,16 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, /* * strings identical except trailing "s0" */ - if (strcmp(&val[vlen - 2], "s0") == 0 && + if ((strcmp(&val[vlen - 2], "s0") == 0 || + strcmp(&val[vlen - 2], "s1") == 0) && strncmp(srchval, val, slen) == 0) return (nv); /* * strings identical except trailing "s0/old" */ - if (strcmp(&val[vlen - 6], "s0/old") == 0 && + if ((strcmp(&val[vlen - 6], "s0/old") == 0 || + strcmp(&val[vlen - 6], "s1/old") == 0) && strcmp(&srchval[slen - 4], "/old") == 0 && strncmp(srchval, val, slen - 4) == 0) return (nv); @@ -3460,15 +3474,17 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, char *tmp = zfs_strdup(hdl, path); /* - * If it starts with c#, and ends with "s0", chop - * the "s0" off, or if it ends with "s0/old", remove - * the "s0" from the middle. + * If it starts with c#, and ends with "s0" or "s1", + * chop the slice off, or if it ends with "s0/old" or + * "s1/old", remove the slice from the middle. */ if (CTD_CHECK(tmp)) { - if (strcmp(&tmp[pathlen - 2], "s0") == 0) { + if (strcmp(&tmp[pathlen - 2], "s0") == 0 || + strcmp(&tmp[pathlen - 2], "s1") == 0) { tmp[pathlen - 2] = '\0'; } else if (pathlen > 6 && - strcmp(&tmp[pathlen - 6], "s0/old") == 0) { + (strcmp(&tmp[pathlen - 6], "s0/old") == 0 || + strcmp(&tmp[pathlen - 6], "s1/old") == 0)) { (void) strcpy(&tmp[pathlen - 6], "/old"); } @@ -3873,15 +3889,18 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, * Read the EFI label from the config, if a label does not exist then * pass back the error to the caller. If the caller has passed a non-NULL * diskaddr argument then we set it to the starting address of the EFI - * partition. + * partition. If the caller has passed a non-NULL boolean argument, then + * we set it to indicate if the disk does have efi system partition. */ static int -read_efi_label(nvlist_t *config, diskaddr_t *sb) +read_efi_label(nvlist_t *config, diskaddr_t *sb, boolean_t *system) { char *path; int fd; char diskname[MAXPATHLEN]; + boolean_t boot = B_FALSE; int err = -1; + int slice; if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0) return (err); @@ -3892,8 +3911,16 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb) struct dk_gpt *vtoc; if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) { - if (sb != NULL) - *sb = vtoc->efi_parts[0].p_start; + for (slice = 0; slice < vtoc->efi_nparts; slice++) { + if (vtoc->efi_parts[slice].p_tag == V_SYSTEM) + boot = B_TRUE; + if (vtoc->efi_parts[slice].p_tag == V_USR) + break; + } + if (sb != NULL && vtoc->efi_parts[slice].p_tag == V_USR) + *sb = vtoc->efi_parts[slice].p_start; + if (system != NULL) + *system = boot; efi_free(vtoc); } (void) close(fd); @@ -3920,7 +3947,7 @@ find_start_block(nvlist_t *config) &wholedisk) != 0 || !wholedisk) { return (MAXOFFSET_T); } - if (read_efi_label(config, &sb) < 0) + if (read_efi_label(config, &sb, NULL) < 0) sb = MAXOFFSET_T; return (sb); } @@ -3940,7 +3967,8 @@ find_start_block(nvlist_t *config) * stripped of any leading /dev path. */ int -zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name) +zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name, + zpool_boot_label_t boot_type, uint64_t boot_size, int *slice) { #ifdef illumos char path[MAXPATHLEN]; @@ -3999,15 +4027,6 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name) return (zfs_error(hdl, EZFS_NOCAP, errbuf)); } - slice_size = vtoc->efi_last_u_lba + 1; - slice_size -= EFI_MIN_RESV_SIZE; - if (start_block == MAXOFFSET_T) - start_block = NEW_START_BLOCK; - slice_size -= start_block; - - vtoc->efi_parts[0].p_start = start_block; - vtoc->efi_parts[0].p_size = slice_size; - /* * Why we use V_USR: V_BACKUP confuses users, and is considered * disposable by some EFI utilities (since EFI doesn't have a backup @@ -4016,12 +4035,103 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name) * etc. were all pretty specific. V_USR is as close to reality as we * can get, in the absence of V_OTHER. */ - vtoc->efi_parts[0].p_tag = V_USR; - (void) strcpy(vtoc->efi_parts[0].p_name, "zfs"); + /* first fix the partition start block */ + if (start_block == MAXOFFSET_T) + start_block = NEW_START_BLOCK; - vtoc->efi_parts[8].p_start = slice_size + start_block; - vtoc->efi_parts[8].p_size = resv; - vtoc->efi_parts[8].p_tag = V_RESERVED; + /* + * EFI System partition is using slice 0. + * ZFS is on slice 1 and slice 8 is reserved. + * We assume the GPT partition table without system + * partition has zfs p_start == NEW_START_BLOCK. + * If start_block != NEW_START_BLOCK, it means we have + * system partition. Correct solution would be to query/cache vtoc + * from existing vdev member. + */ + if (boot_type == ZPOOL_CREATE_BOOT_LABEL) { + if (boot_size % vtoc->efi_lbasize != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "boot partition size must be a multiple of %d"), + vtoc->efi_lbasize); + (void) close(fd); + efi_free(vtoc); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + /* + * System partition size checks. + * Note the 1MB is quite arbitrary value, since we + * are creating dedicated pool, it should be enough + * to hold fat + efi bootloader. May need to be + * adjusted if the bootloader size will grow. + */ + if (boot_size < 1024 * 1024) { + char buf[64]; + zfs_nicenum(boot_size, buf, sizeof (buf)); + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "Specified size %s for EFI System partition is too " + "small, the minimum size is 1MB."), buf); + (void) close(fd); + efi_free(vtoc); + return (zfs_error(hdl, EZFS_LABELFAILED, errbuf)); + } + /* 33MB is tested with mkfs -F pcfs */ + if (hdl->libzfs_printerr && + ((vtoc->efi_lbasize == 512 && + boot_size < 33 * 1024 * 1024) || + (vtoc->efi_lbasize == 4096 && + boot_size < 256 * 1024 * 1024))) { + char buf[64]; + zfs_nicenum(boot_size, buf, sizeof (buf)); + (void) fprintf(stderr, dgettext(TEXT_DOMAIN, + "Warning: EFI System partition size %s is " + "not allowing to create FAT32 file\nsystem, which " + "may result in unbootable system.\n"), buf); + } + /* Adjust zfs partition start by size of system partition. */ + start_block += boot_size / vtoc->efi_lbasize; + } + + if (start_block == NEW_START_BLOCK) { + /* + * Use default layout. + * ZFS is on slice 0 and slice 8 is reserved. + */ + slice_size = vtoc->efi_last_u_lba + 1; + slice_size -= EFI_MIN_RESV_SIZE; + slice_size -= start_block; + if (slice != NULL) + *slice = 0; + + vtoc->efi_parts[0].p_start = start_block; + vtoc->efi_parts[0].p_size = slice_size; + + vtoc->efi_parts[0].p_tag = V_USR; + (void) strcpy(vtoc->efi_parts[0].p_name, "zfs"); + + vtoc->efi_parts[8].p_start = slice_size + start_block; + vtoc->efi_parts[8].p_size = resv; + vtoc->efi_parts[8].p_tag = V_RESERVED; + } else { + slice_size = start_block - NEW_START_BLOCK; + vtoc->efi_parts[0].p_start = NEW_START_BLOCK; + vtoc->efi_parts[0].p_size = slice_size; + vtoc->efi_parts[0].p_tag = V_SYSTEM; + (void) strcpy(vtoc->efi_parts[0].p_name, "loader"); + if (slice != NULL) + *slice = 1; + /* prepare slice 1 */ + slice_size = vtoc->efi_last_u_lba + 1 - slice_size; + slice_size -= resv; + slice_size -= NEW_START_BLOCK; + vtoc->efi_parts[1].p_start = start_block; + vtoc->efi_parts[1].p_size = slice_size; + vtoc->efi_parts[1].p_tag = V_USR; + (void) strcpy(vtoc->efi_parts[1].p_name, "zfs"); + + vtoc->efi_parts[8].p_start = slice_size + start_block; + vtoc->efi_parts[8].p_size = resv; + vtoc->efi_parts[8].p_tag = V_RESERVED; + } if (efi_write(fd, vtoc) != 0) { /* |