summaryrefslogtreecommitdiff
path: root/cddl/contrib/opensolaris/lib/libzfs
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2018-02-21 00:18:57 +0000
committerAlexander Motin <mav@FreeBSD.org>2018-02-21 00:18:57 +0000
commite5a4a83784d26b3804df1c9519078e64776bf10b (patch)
treeba06d0fb4b60e26c2b5cc942efdfe2c91db7e262 /cddl/contrib/opensolaris/lib/libzfs
parente73c944fc079f7f08a32270eb83854e3270b05e4 (diff)
parent9df2d6f729b7642daf9a1d5d3d610959ba894c3d (diff)
Notes
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libzfs')
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h16
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c170
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) {
/*