summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorachartre <none@none>2008-01-22 09:22:05 -0800
committerachartre <none@none>2008-01-22 09:22:05 -0800
commitedcc07547a39d6570197493a9836083bd6b2a197 (patch)
treef65b17fe910ea6c01e06c0a4a8f2ebf974c3eb02
parent3a4c5e1c4354dc2608127dc1988c4fe0b4730d56 (diff)
downloadillumos-gate-edcc07547a39d6570197493a9836083bd6b2a197.tar.gz
6528974 Usage of DKIOCGETEFI has changed and impacts vdisks using zfs volumes
6558966 Virtual disks created from files do not support EFI labels
-rw-r--r--usr/src/uts/sun4v/io/vdc.c110
-rw-r--r--usr/src/uts/sun4v/io/vds.c647
-rw-r--r--usr/src/uts/sun4v/io/vdsk_common.c540
-rw-r--r--usr/src/uts/sun4v/sys/vdc.h3
-rw-r--r--usr/src/uts/sun4v/sys/vdsk_common.h46
-rw-r--r--usr/src/uts/sun4v/vds/Makefile3
6 files changed, 699 insertions, 650 deletions
diff --git a/usr/src/uts/sun4v/io/vdc.c b/usr/src/uts/sun4v/io/vdc.c
index 2b09f6ee4a..f7a3dbb3bb 100644
--- a/usr/src/uts/sun4v/io/vdc.c
+++ b/usr/src/uts/sun4v/io/vdc.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -137,7 +137,7 @@ static void vdc_terminate_ldc(vdc_t *vdc);
static int vdc_init_descriptor_ring(vdc_t *vdc);
static void vdc_destroy_descriptor_ring(vdc_t *vdc);
static int vdc_setup_devid(vdc_t *vdc);
-static void vdc_store_label_efi(vdc_t *vdc, struct dk_gpt *efi);
+static void vdc_store_label_efi(vdc_t *, efi_gpt_t *, efi_gpe_t *);
static void vdc_store_label_vtoc(vdc_t *, struct dk_geom *, struct vtoc *);
static void vdc_store_label_unk(vdc_t *vdc);
static boolean_t vdc_is_opened(vdc_t *vdc);
@@ -182,7 +182,7 @@ static int vdc_verify_seq_num(vdc_t *vdc, vio_dring_msg_t *dring_msg);
/* dkio */
static int vd_process_ioctl(dev_t dev, int cmd, caddr_t arg, int mode,
int *rvalp);
-static int vd_process_efi_ioctl(dev_t dev, int cmd, caddr_t arg, int mode);
+static int vd_process_efi_ioctl(void *vdisk, int cmd, uintptr_t arg);
static void vdc_create_fake_geometry(vdc_t *vdc);
static int vdc_validate_geometry(vdc_t *vdc);
static void vdc_validate(vdc_t *vdc);
@@ -327,7 +327,6 @@ _init(void)
return (status);
if ((status = mod_install(&modlinkage)) != 0)
ddi_soft_state_fini(&vdc_state);
- vdc_efi_init(vd_process_efi_ioctl);
return (status);
}
@@ -344,7 +343,6 @@ _fini(void)
if ((status = mod_remove(&modlinkage)) != 0)
return (status);
- vdc_efi_fini();
ddi_soft_state_fini(&vdc_state);
return (0);
}
@@ -1023,7 +1021,6 @@ vdc_create_device_nodes_props(vdc_t *vdc)
int i;
ASSERT(vdc != NULL);
- ASSERT(vdc->vtoc != NULL);
instance = vdc->instance;
dip = vdc->dip;
@@ -1055,10 +1052,10 @@ vdc_create_device_nodes_props(vdc_t *vdc)
dev = makedevice(ddi_driver_major(dip),
VD_MAKE_DEV(instance, i));
- size = vdc->vtoc->v_part[i].p_size * vdc->vtoc->v_sectorsz;
+ size = vdc->slice[i].nblocks * vdc->block_size;
DMSG(vdc, 0, "[%d] sz %ld (%ld Mb) p_size %lx\n",
instance, size, size / (1024 * 1024),
- vdc->vtoc->v_part[i].p_size);
+ vdc->slice[i].nblocks);
rv = ddi_prop_update_int64(dev, dip, VDC_SIZE_PROP_NAME, size);
if (rv != DDI_PROP_SUCCESS) {
@@ -1247,7 +1244,7 @@ vdc_open(dev_t *dev, int flag, int otyp, cred_t *cred)
mutex_enter(&vdc->lock);
if (vdc->vdisk_label == VD_DISK_LABEL_UNK ||
- vdc->vtoc->v_part[slice].p_size == 0) {
+ vdc->slice[slice].nblocks == 0) {
vdc_mark_closed(vdc, slice, flag, otyp);
status = EIO;
}
@@ -6185,18 +6182,20 @@ static vdc_dk_ioctl_t dk_ioctl[] = {
};
/*
- * The signature of vd_process_ioctl() has changed to include the return value
- * pointer. However we don't want to change vd_efi_* functions now so we add
- * this wrapper function so that we can use it with vdc_efi_init().
- *
- * vd_efi_* functions need some changes to fix 6528974 and so we will eventually
- * remove this function when fixing that bug.
+ * This function handles ioctl requests from the vd_efi_alloc_and_read()
+ * function and forward them to the vdisk.
*/
static int
-vd_process_efi_ioctl(dev_t dev, int cmd, caddr_t arg, int mode)
+vd_process_efi_ioctl(void *vdisk, int cmd, uintptr_t arg)
{
+ vdc_t *vdc = (vdc_t *)vdisk;
+ dev_t dev;
int rval;
- return (vd_process_ioctl(dev, cmd, arg, mode, &rval));
+
+ dev = makedevice(ddi_driver_major(vdc->dip),
+ VD_MAKE_DEV(vdc->instance, 0));
+
+ return (vd_process_ioctl(dev, cmd, (caddr_t)arg, FKIOCTL, &rval));
}
/*
@@ -7116,6 +7115,9 @@ vdc_validate_geometry(vdc_t *vdc)
struct dk_label label;
struct dk_geom geom;
struct vtoc vtoc;
+ efi_gpt_t *gpt;
+ efi_gpe_t *gpe;
+ vd_efi_dev_t edev;
ASSERT(vdc != NULL);
ASSERT(vdc->vtoc != NULL && vdc->geom != NULL);
@@ -7135,11 +7137,21 @@ vdc_validate_geometry(vdc_t *vdc)
/*
* If the device does not support VTOC then we try
* to read an EFI label.
+ *
+ * We need to know the block size and the disk size to
+ * be able to read an EFI label.
*/
- struct dk_gpt *efi;
- size_t efi_len;
+ if (vdc->vdisk_size == 0) {
+ if ((rv = vdc_check_capacity(vdc)) != 0) {
+ mutex_enter(&vdc->lock);
+ vdc_store_label_unk(vdc);
+ return (rv);
+ }
+ }
+
+ VD_EFI_DEV_SET(edev, vdc, vd_process_efi_ioctl);
- rv = vdc_efi_alloc_and_read(dev, &efi, &efi_len);
+ rv = vd_efi_alloc_and_read(&edev, &gpt, &gpe);
if (rv) {
DMSG(vdc, 0, "[%d] Failed to get EFI (err=%d)",
@@ -7150,8 +7162,8 @@ vdc_validate_geometry(vdc_t *vdc)
}
mutex_enter(&vdc->lock);
- vdc_store_label_efi(vdc, efi);
- vd_efi_free(efi, efi_len);
+ vdc_store_label_efi(vdc, gpt, gpe);
+ vd_efi_free(&edev, gpt, gpe);
return (ENOTSUP);
}
@@ -7258,7 +7270,7 @@ static void
vdc_validate(vdc_t *vdc)
{
vd_disk_label_t old_label;
- struct vtoc old_vtoc;
+ vd_slice_t old_slice[V_NUMPAR];
int rv;
ASSERT(!MUTEX_HELD(&vdc->lock));
@@ -7267,7 +7279,7 @@ vdc_validate(vdc_t *vdc)
/* save the current label and vtoc */
old_label = vdc->vdisk_label;
- bcopy(vdc->vtoc, &old_vtoc, sizeof (struct vtoc));
+ bcopy(vdc->slice, &old_slice, sizeof (vd_slice_t) * V_NUMPAR);
/* check the geometry */
(void) vdc_validate_geometry(vdc);
@@ -7287,7 +7299,7 @@ vdc_validate(vdc_t *vdc)
}
/* if the vtoc has changed, update device nodes properties */
- if (bcmp(vdc->vtoc, &old_vtoc, sizeof (struct vtoc)) != 0) {
+ if (bcmp(vdc->slice, &old_slice, sizeof (vd_slice_t) * V_NUMPAR) != 0) {
if (vdc_create_device_nodes_props(vdc) != 0) {
DMSG(vdc, 0, "![%d] Failed to update device nodes"
@@ -7414,39 +7426,54 @@ vdc_setup_devid(vdc_t *vdc)
}
static void
-vdc_store_label_efi(vdc_t *vdc, struct dk_gpt *efi)
+vdc_store_label_efi(vdc_t *vdc, efi_gpt_t *gpt, efi_gpe_t *gpe)
{
- struct vtoc *vtoc = vdc->vtoc;
+ int i, nparts;
ASSERT(MUTEX_HELD(&vdc->lock));
vdc->vdisk_label = VD_DISK_LABEL_EFI;
+ bzero(vdc->vtoc, sizeof (struct vtoc));
bzero(vdc->geom, sizeof (struct dk_geom));
- vd_efi_to_vtoc(efi, vtoc);
- if (vdc->vdisk_type == VD_DISK_TYPE_SLICE) {
- /*
- * vd_efi_to_vtoc() will store information about the EFI Sun
- * reserved partition (representing the entire disk) into
- * partition 7. However single-slice device will only have
- * that single partition and the vdc driver expects to find
- * information about that partition in slice 0. So we need
- * to copy information from slice 7 to slice 0.
- */
- vtoc->v_part[0].p_tag = vtoc->v_part[VD_EFI_WD_SLICE].p_tag;
- vtoc->v_part[0].p_flag = vtoc->v_part[VD_EFI_WD_SLICE].p_flag;
- vtoc->v_part[0].p_start = vtoc->v_part[VD_EFI_WD_SLICE].p_start;
- vtoc->v_part[0].p_size = vtoc->v_part[VD_EFI_WD_SLICE].p_size;
+ bzero(vdc->slice, sizeof (vd_slice_t) * V_NUMPAR);
+
+ nparts = gpt->efi_gpt_NumberOfPartitionEntries;
+
+ for (i = 0; i < nparts && i < VD_EFI_WD_SLICE; i++) {
+
+ if (gpe[i].efi_gpe_StartingLBA == 0 ||
+ gpe[i].efi_gpe_EndingLBA == 0) {
+ continue;
+ }
+
+ vdc->slice[i].start = gpe[i].efi_gpe_StartingLBA;
+ vdc->slice[i].nblocks = gpe[i].efi_gpe_EndingLBA -
+ gpe[i].efi_gpe_StartingLBA + 1;
}
+
+ ASSERT(vdc->vdisk_size != 0);
+ vdc->slice[VD_EFI_WD_SLICE].start = 0;
+ vdc->slice[VD_EFI_WD_SLICE].nblocks = vdc->vdisk_size;
+
}
static void
vdc_store_label_vtoc(vdc_t *vdc, struct dk_geom *geom, struct vtoc *vtoc)
{
+ int i;
+
ASSERT(MUTEX_HELD(&vdc->lock));
+ ASSERT(vdc->block_size == vtoc->v_sectorsz);
vdc->vdisk_label = VD_DISK_LABEL_VTOC;
bcopy(vtoc, vdc->vtoc, sizeof (struct vtoc));
bcopy(geom, vdc->geom, sizeof (struct dk_geom));
+ bzero(vdc->slice, sizeof (vd_slice_t) * V_NUMPAR);
+
+ for (i = 0; i < vtoc->v_nparts; i++) {
+ vdc->slice[i].start = vtoc->v_part[i].p_start;
+ vdc->slice[i].nblocks = vtoc->v_part[i].p_size;
+ }
}
static void
@@ -7457,4 +7484,5 @@ vdc_store_label_unk(vdc_t *vdc)
vdc->vdisk_label = VD_DISK_LABEL_UNK;
bzero(vdc->vtoc, sizeof (struct vtoc));
bzero(vdc->geom, sizeof (struct dk_geom));
+ bzero(vdc->slice, sizeof (vd_slice_t) * V_NUMPAR);
}
diff --git a/usr/src/uts/sun4v/io/vds.c b/usr/src/uts/sun4v/io/vds.c
index b864adbc5e..97496daa4e 100644
--- a/usr/src/uts/sun4v/io/vds.c
+++ b/usr/src/uts/sun4v/io/vds.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -98,6 +98,9 @@
/* Timeout for SCSI I/O */
#define VD_SCSI_RDWR_TIMEOUT 30 /* 30 secs */
+/* Maximum number of logical partitions */
+#define VD_MAXPART (NDKMAP + 1)
+
/*
* By Solaris convention, slice/partition 2 represents the entire disk;
* unfortunately, this convention does not appear to be codified.
@@ -356,10 +359,12 @@ typedef struct vd {
vnode_t *file_vnode; /* file vnode */
size_t file_size; /* file size */
ddi_devid_t file_devid; /* devid for disk image */
- struct dk_efi dk_efi; /* synthetic for slice type */
+ efi_gpt_t efi_gpt; /* EFI GPT for slice type */
+ efi_gpe_t efi_gpe; /* EFI GPE for slice type */
+ int efi_reserved; /* EFI reserved slice */
struct dk_geom dk_geom; /* synthetic for slice type */
- struct dk_minfo dk_minfo; /* synthetic for slice type */
struct vtoc vtoc; /* synthetic for slice type */
+ vd_slice_t slices[VD_MAXPART]; /* logical partitions */
boolean_t ownership; /* disk ownership status */
ldc_status_t ldc_state; /* LDC connection state */
ldc_handle_t ldc_handle; /* handle for LDC comm */
@@ -466,6 +471,9 @@ static int vd_file_validate_geometry(vd_t *vd);
static boolean_t vd_file_is_iso_image(vd_t *vd);
static void vd_set_exported_operations(vd_t *vd);
static void vd_reset_access(vd_t *vd);
+static int vd_backend_ioctl(vd_t *vd, int cmd, caddr_t arg);
+static int vds_efi_alloc_and_read(vd_t *, efi_gpt_t **, efi_gpe_t **);
+static void vds_efi_free(vd_t *, efi_gpt_t *, efi_gpe_t *);
/*
* Function:
@@ -521,27 +529,36 @@ vd_file_rw(vd_t *vd, int slice, int operation, caddr_t data, size_t blk,
* from v1.1 onwards must do the right thing.
*/
if (vd->vdisk_label == VD_DISK_LABEL_UNK &&
- vio_ver_is_supported(vd->version, 1, 1) &&
- vd_file_validate_geometry(vd) != 0) {
- PR0("Unknown disk label, can't do I/O from slice %d",
- slice);
- return (-1);
+ vio_ver_is_supported(vd->version, 1, 1)) {
+ (void) vd_file_validate_geometry(vd);
+ if (vd->vdisk_label == VD_DISK_LABEL_UNK) {
+ PR0("Unknown disk label, can't do I/O "
+ "from slice %d", slice);
+ return (-1);
+ }
+ }
+
+ if (vd->vdisk_label == VD_DISK_LABEL_VTOC) {
+ ASSERT(vd->vtoc.v_sectorsz == DEV_BSIZE);
+ } else {
+ ASSERT(vd->vdisk_label == VD_DISK_LABEL_EFI);
+ ASSERT(vd->vdisk_block_size == DEV_BSIZE);
}
- if (blk >= vd->vtoc.v_part[slice].p_size) {
+ if (blk >= vd->slices[slice].nblocks) {
/* address past the end of the slice */
PR0("req_addr (0x%lx) > psize (0x%lx)",
- blk, vd->vtoc.v_part[slice].p_size);
+ blk, vd->slices[slice].nblocks);
return (0);
}
- offset = (vd->vtoc.v_part[slice].p_start + blk) * DEV_BSIZE;
+ offset = (vd->slices[slice].start + blk) * DEV_BSIZE;
/*
* If the requested size is greater than the size
* of the partition, truncate the read/write.
*/
- maxlen = (vd->vtoc.v_part[slice].p_size - blk) * DEV_BSIZE;
+ maxlen = (vd->slices[slice].nblocks - blk) * DEV_BSIZE;
if (len > maxlen) {
PR0("I/O size truncated to %lu bytes from %lu bytes",
@@ -556,8 +573,8 @@ vd_file_rw(vd_t *vd, int slice, int operation, caddr_t data, size_t blk,
* s0 instead s2) the system can try to access slices that
* are not included into the disk image.
*/
- if ((offset + len) >= vd->file_size) {
- PR0("offset + nbytes (0x%lx + 0x%lx) >= "
+ if ((offset + len) > vd->file_size) {
+ PR0("offset + nbytes (0x%lx + 0x%lx) > "
"file_size (0x%lx)", offset, len, vd->file_size);
return (-1);
}
@@ -630,10 +647,11 @@ vd_file_build_default_label(vd_t *vd, struct dk_label *label)
{
size_t size;
char prefix;
- int slice, nparts;
- uint16_t tag;
ASSERT(vd->file);
+ ASSERT(vd->vdisk_type == VD_DISK_TYPE_DISK);
+
+ bzero(label, sizeof (struct dk_label));
/*
* We must have a resonable number of cylinders and sectors so
@@ -664,17 +682,8 @@ vd_file_build_default_label(vd_t *vd, struct dk_label *label)
label->dkl_acyl = 0;
- if (vd->vdisk_type == VD_DISK_TYPE_SLICE) {
- nparts = 1;
- slice = 0;
- tag = V_UNASSIGNED;
- } else {
- if (label->dkl_pcyl > 2)
- label->dkl_acyl = 2;
- nparts = V_NUMPAR;
- slice = VD_ENTIRE_DISK_SLICE;
- tag = V_BACKUP;
- }
+ if (label->dkl_pcyl > 2)
+ label->dkl_acyl = 2;
label->dkl_nsect = vd->file_size /
(DEV_BSIZE * label->dkl_pcyl);
@@ -719,12 +728,13 @@ vd_file_build_default_label(vd_t *vd, struct dk_label *label)
/* default VTOC */
label->dkl_vtoc.v_version = V_VERSION;
- label->dkl_vtoc.v_nparts = nparts;
+ label->dkl_vtoc.v_nparts = V_NUMPAR;
label->dkl_vtoc.v_sanity = VTOC_SANE;
- label->dkl_vtoc.v_part[slice].p_tag = tag;
- label->dkl_map[slice].dkl_cylno = 0;
- label->dkl_map[slice].dkl_nblk = label->dkl_ncyl *
+ label->dkl_vtoc.v_part[VD_ENTIRE_DISK_SLICE].p_tag = V_BACKUP;
+ label->dkl_map[VD_ENTIRE_DISK_SLICE].dkl_cylno = 0;
+ label->dkl_map[VD_ENTIRE_DISK_SLICE].dkl_nblk = label->dkl_ncyl *
label->dkl_nhead * label->dkl_nsect;
+ label->dkl_magic = DKL_MAGIC;
label->dkl_cksum = vd_lbl2cksum(label);
}
@@ -820,6 +830,29 @@ vd_file_get_devid_block(vd_t *vd, size_t *blkp)
diskaddr_t spc, head, cyl;
ASSERT(vd->file);
+
+ if (vd->vdisk_label == VD_DISK_LABEL_UNK) {
+ /*
+ * If no label is defined we don't know where to find
+ * a device id.
+ */
+ return (ENOSPC);
+ }
+
+ if (vd->vdisk_label == VD_DISK_LABEL_EFI) {
+ /*
+ * For an EFI disk, the devid is at the beginning of
+ * the reserved slice
+ */
+ if (vd->efi_reserved == -1) {
+ PR0("EFI disk has no reserved slice");
+ return (ENOSPC);
+ }
+
+ *blkp = vd->slices[vd->efi_reserved].start;
+ return (0);
+ }
+
ASSERT(vd->vdisk_label == VD_DISK_LABEL_VTOC);
/* this geometry doesn't allow us to have a devid */
@@ -956,6 +989,11 @@ vd_file_write_devid(vd_t *vd, ddi_devid_t devid)
size_t blk;
int status;
+ if (devid == NULL) {
+ /* nothing to write */
+ return (0);
+ }
+
if ((status = vd_file_get_devid_block(vd, &blk)) != 0)
return (status);
@@ -1921,38 +1959,6 @@ vd_scsicmd_out(void *ioctl_arg, void *vd_buf)
}
}
-static vd_disk_label_t
-vd_read_vtoc(vd_t *vd, struct vtoc *vtoc)
-{
- int status, rval;
- struct dk_gpt *efi;
- size_t efi_len;
-
- ASSERT(vd->ldi_handle[0] != NULL);
-
- status = ldi_ioctl(vd->ldi_handle[0], DKIOCGVTOC, (intptr_t)vtoc,
- (vd->open_flags | FKIOCTL), kcred, &rval);
-
- if (status == 0) {
- return (VD_DISK_LABEL_VTOC);
- } else if (status != ENOTSUP) {
- PR0("ldi_ioctl(DKIOCGVTOC) returned error %d", status);
- return (VD_DISK_LABEL_UNK);
- }
-
- status = vds_efi_alloc_and_read(vd->ldi_handle[0], &efi, &efi_len);
-
- if (status) {
- PR0("vds_efi_alloc_and_read returned error %d", status);
- return (VD_DISK_LABEL_UNK);
- }
-
- vd_efi_to_vtoc(efi, vtoc);
- vd_efi_free(efi, efi_len);
-
- return (VD_DISK_LABEL_EFI);
-}
-
static ushort_t
vd_lbl2cksum(struct dk_label *label)
{
@@ -1982,10 +1988,23 @@ static int
vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
{
dk_efi_t *dk_ioc;
+ int rval;
+
+ ASSERT(vd->vdisk_type == VD_DISK_TYPE_SLICE);
+
+ if (cmd == DKIOCFLUSHWRITECACHE) {
+ if (vd->file) {
+ return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL));
+ } else {
+ return (ldi_ioctl(vd->ldi_handle[0], cmd,
+ (intptr_t)ioctl_arg, vd->open_flags | FKIOCTL,
+ kcred, &rval));
+ }
+ }
switch (vd->vdisk_label) {
- /* ioctls for a slice from a disk with a VTOC label */
+ /* ioctls for a single slice disk with a VTOC label */
case VD_DISK_LABEL_VTOC:
switch (cmd) {
@@ -2001,17 +2020,49 @@ vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
return (ENOTSUP);
}
- /* ioctls for a slice from a disk with an EFI label */
+ /* ioctls for a single slice disk with an EFI label */
case VD_DISK_LABEL_EFI:
switch (cmd) {
case DKIOCGETEFI:
ASSERT(ioctl_arg != NULL);
dk_ioc = (dk_efi_t *)ioctl_arg;
- if (dk_ioc->dki_length < vd->dk_efi.dki_length)
+
+ /*
+ * For a single slice disk with an EFI label, we define
+ * a fake EFI label with the GPT at LBA 1 and one GPE
+ * at LBA 2. So we return the GPT or the GPE depending
+ * on which LBA is requested.
+ */
+ if (dk_ioc->dki_lba == 1) {
+
+ /* return the EFI GPT */
+ if (dk_ioc->dki_length < sizeof (efi_gpt_t))
+ return (EINVAL);
+
+ bcopy(&vd->efi_gpt, dk_ioc->dki_data,
+ sizeof (efi_gpt_t));
+
+ /* also return the GPE if possible */
+ if (dk_ioc->dki_length >= sizeof (efi_gpt_t) +
+ sizeof (efi_gpe_t)) {
+ bcopy(&vd->efi_gpe, dk_ioc->dki_data +
+ 1, sizeof (efi_gpe_t));
+ }
+
+ } else if (dk_ioc->dki_lba == 2) {
+
+ /* return the EFI GPE */
+ if (dk_ioc->dki_length < sizeof (efi_gpe_t))
+ return (EINVAL);
+
+ bcopy(&vd->efi_gpe, dk_ioc->dki_data,
+ sizeof (efi_gpe_t));
+
+ } else {
return (EINVAL);
- bcopy(vd->dk_efi.dki_data, dk_ioc->dki_data,
- vd->dk_efi.dki_length);
+ }
+
return (0);
default:
return (ENOTSUP);
@@ -2023,6 +2074,74 @@ vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
}
}
+static int
+vds_efi_alloc_and_read(vd_t *vd, efi_gpt_t **gpt, efi_gpe_t **gpe)
+{
+ vd_efi_dev_t edev;
+ int status;
+
+ VD_EFI_DEV_SET(edev, vd, (vd_efi_ioctl_func)vd_backend_ioctl);
+
+ status = vd_efi_alloc_and_read(&edev, gpt, gpe);
+
+ return (status);
+}
+
+static void
+vds_efi_free(vd_t *vd, efi_gpt_t *gpt, efi_gpe_t *gpe)
+{
+ vd_efi_dev_t edev;
+
+ VD_EFI_DEV_SET(edev, vd, (vd_efi_ioctl_func)vd_backend_ioctl);
+
+ vd_efi_free(&edev, gpt, gpe);
+}
+
+static int
+vd_file_validate_efi(vd_t *vd)
+{
+ efi_gpt_t *gpt;
+ efi_gpe_t *gpe;
+ int i, nparts, status;
+ struct uuid efi_reserved = EFI_RESERVED;
+
+ if ((status = vds_efi_alloc_and_read(vd, &gpt, &gpe)) != 0)
+ return (status);
+
+ bzero(&vd->vtoc, sizeof (struct vtoc));
+ bzero(&vd->dk_geom, sizeof (struct dk_geom));
+ bzero(vd->slices, sizeof (vd_slice_t) * VD_MAXPART);
+
+ vd->efi_reserved = -1;
+
+ nparts = gpt->efi_gpt_NumberOfPartitionEntries;
+
+ for (i = 0; i < nparts && i < VD_MAXPART; i++) {
+
+ if (gpe[i].efi_gpe_StartingLBA == 0 ||
+ gpe[i].efi_gpe_EndingLBA == 0) {
+ continue;
+ }
+
+ vd->slices[i].start = gpe[i].efi_gpe_StartingLBA;
+ vd->slices[i].nblocks = gpe[i].efi_gpe_EndingLBA -
+ gpe[i].efi_gpe_StartingLBA + 1;
+
+ if (bcmp(&gpe[i].efi_gpe_PartitionTypeGUID, &efi_reserved,
+ sizeof (struct uuid)) == 0)
+ vd->efi_reserved = i;
+
+ }
+
+ ASSERT(vd->vdisk_size != 0);
+ vd->slices[VD_EFI_WD_SLICE].start = 0;
+ vd->slices[VD_EFI_WD_SLICE].nblocks = vd->vdisk_size;
+
+ vds_efi_free(vd, gpt, gpe);
+
+ return (status);
+}
+
/*
* Function:
* vd_file_validate_geometry
@@ -2034,7 +2153,7 @@ vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
*
* If no valid label is found, the label is set to unknown and the
* function returns EINVAL, but a default vtoc and geometry are provided
- * to the driver.
+ * to the driver. If an EFI label is found, ENOTSUP is returned.
*
* Parameters:
* vd - disk on which the operation is performed.
@@ -2043,6 +2162,7 @@ vd_do_slice_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
* 0 - success.
* EIO - error reading the label from the disk image.
* EINVAL - unknown disk label.
+ * ENOTSUP - geometry not applicable (EFI label).
*/
static int
vd_file_validate_geometry(vd_t *vd)
@@ -2054,34 +2174,26 @@ vd_file_validate_geometry(vd_t *vd)
int status = 0;
ASSERT(vd->file);
+ ASSERT(vd->vdisk_type == VD_DISK_TYPE_DISK);
- if (vd->vdisk_type == VD_DISK_TYPE_SLICE) {
- /*
- * For single slice disk we always fake the geometry, and we
- * only need to do it once because the geometry will never
- * change.
- */
- if (vd->vdisk_label == VD_DISK_LABEL_VTOC)
- /* geometry was already validated */
- return (0);
+ if (VD_FILE_LABEL_READ(vd, &label) < 0)
+ return (EIO);
- ASSERT(vd->vdisk_label == VD_DISK_LABEL_UNK);
- vd_file_build_default_label(vd, &label);
- vd->vdisk_label = VD_DISK_LABEL_VTOC;
- } else {
- if (VD_FILE_LABEL_READ(vd, &label) < 0)
- return (EIO);
+ if (label.dkl_magic != DKL_MAGIC ||
+ label.dkl_cksum != vd_lbl2cksum(&label) ||
+ label.dkl_vtoc.v_sanity != VTOC_SANE ||
+ label.dkl_vtoc.v_nparts != V_NUMPAR) {
- if (label.dkl_magic != DKL_MAGIC ||
- label.dkl_cksum != vd_lbl2cksum(&label) ||
- label.dkl_vtoc.v_sanity != VTOC_SANE ||
- label.dkl_vtoc.v_nparts != V_NUMPAR) {
- vd->vdisk_label = VD_DISK_LABEL_UNK;
- vd_file_build_default_label(vd, &label);
- status = EINVAL;
- } else {
- vd->vdisk_label = VD_DISK_LABEL_VTOC;
+ if (vd_file_validate_efi(vd) == 0) {
+ vd->vdisk_label = VD_DISK_LABEL_EFI;
+ return (ENOTSUP);
}
+
+ vd->vdisk_label = VD_DISK_LABEL_UNK;
+ vd_file_build_default_label(vd, &label);
+ status = EINVAL;
+ } else {
+ vd->vdisk_label = VD_DISK_LABEL_VTOC;
}
/* Update the driver geometry */
@@ -2131,6 +2243,15 @@ vd_file_validate_geometry(vd_t *vd)
bcopy(label.dkl_vtoc.v_volume, vtoc->v_volume,
LEN_DKL_VVOL);
+ /* Update logical partitions */
+ bzero(vd->slices, sizeof (vd_slice_t) * VD_MAXPART);
+ if (vd->vdisk_label != VD_DISK_LABEL_UNK) {
+ for (i = 0; i < vtoc->v_nparts; i++) {
+ vd->slices[i].start = vtoc->v_part[i].p_start;
+ vd->slices[i].nblocks = vtoc->v_part[i].p_size;
+ }
+ }
+
return (status);
}
@@ -2147,9 +2268,11 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
struct dk_label label;
struct dk_geom *geom;
struct vtoc *vtoc;
+ dk_efi_t *efi;
int i, rc;
ASSERT(vd->file);
+ ASSERT(vd->vdisk_type == VD_DISK_TYPE_DISK);
switch (cmd) {
@@ -2158,11 +2281,8 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
geom = (struct dk_geom *)ioctl_arg;
rc = vd_file_validate_geometry(vd);
- if (rc != 0 && rc != EINVAL) {
- ASSERT(vd->vdisk_type != VD_DISK_TYPE_SLICE);
+ if (rc != 0 && rc != EINVAL)
return (rc);
- }
-
bcopy(&vd->dk_geom, geom, sizeof (struct dk_geom));
return (0);
@@ -2171,11 +2291,8 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
vtoc = (struct vtoc *)ioctl_arg;
rc = vd_file_validate_geometry(vd);
- if (rc != 0 && rc != EINVAL) {
- ASSERT(vd->vdisk_type != VD_DISK_TYPE_SLICE);
+ if (rc != 0 && rc != EINVAL)
return (rc);
- }
-
bcopy(&vd->vtoc, vtoc, sizeof (struct vtoc));
return (0);
@@ -2183,10 +2300,6 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
ASSERT(ioctl_arg != NULL);
geom = (struct dk_geom *)ioctl_arg;
- /* geometry can only be changed for full disk */
- if (vd->vdisk_type != VD_DISK_TYPE_DISK)
- return (ENOTSUP);
-
if (geom->dkg_nhead == 0 || geom->dkg_nsect == 0)
return (EINVAL);
@@ -2205,10 +2318,6 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
vd->dk_geom.dkg_nsect != 0);
vtoc = (struct vtoc *)ioctl_arg;
- /* vtoc can only be changed for full disk */
- if (vd->vdisk_type != VD_DISK_TYPE_DISK)
- return (ENOTSUP);
-
if (vtoc->v_sanity != VTOC_SANE ||
vtoc->v_sectorsz != DEV_BSIZE ||
vtoc->v_nparts != V_NUMPAR)
@@ -2263,28 +2372,87 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
if ((rc = vd_file_set_vtoc(vd, &label)) != 0)
return (rc);
- /* check the geometry and update the driver info */
- if ((rc = vd_file_validate_geometry(vd)) != 0)
- return (rc);
+ break;
- /*
- * The disk geometry may have changed, so we need to write
- * the devid (if there is one) so that it is stored at the
- * right location.
- */
- if (vd->file_devid != NULL &&
- vd_file_write_devid(vd, vd->file_devid) != 0) {
- PR0("Fail to write devid");
- }
+ case DKIOCFLUSHWRITECACHE:
+ return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL));
+
+ case DKIOCGETEFI:
+ ASSERT(ioctl_arg != NULL);
+ efi = (dk_efi_t *)ioctl_arg;
+
+ if (vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BREAD,
+ (caddr_t)efi->dki_data, efi->dki_lba, efi->dki_length) < 0)
+ return (EIO);
return (0);
- case DKIOCFLUSHWRITECACHE:
- return (VOP_FSYNC(vd->file_vnode, FSYNC, kcred, NULL));
+ case DKIOCSETEFI:
+ ASSERT(ioctl_arg != NULL);
+ efi = (dk_efi_t *)ioctl_arg;
+
+ if (vd_file_rw(vd, VD_SLICE_NONE, VD_OP_BWRITE,
+ (caddr_t)efi->dki_data, efi->dki_lba, efi->dki_length) < 0)
+ return (EIO);
+
+ break;
+
default:
return (ENOTSUP);
}
+
+ ASSERT(cmd == DKIOCSVTOC || cmd == DKIOCSETEFI);
+
+ /* label has changed, revalidate the geometry */
+ (void) vd_file_validate_geometry(vd);
+
+ /*
+ * The disk geometry may have changed, so we need to write
+ * the devid (if there is one) so that it is stored at the
+ * right location.
+ */
+ if (vd_file_write_devid(vd, vd->file_devid) != 0) {
+ PR0("Fail to write devid");
+ }
+
+ return (0);
+}
+
+static int
+vd_backend_ioctl(vd_t *vd, int cmd, caddr_t arg)
+{
+ int rval = 0, status;
+
+ /*
+ * Call the appropriate function to execute the ioctl depending
+ * on the type of vdisk.
+ */
+ if (vd->vdisk_type == VD_DISK_TYPE_SLICE) {
+
+ /* slice, file or volume exported as a single slice disk */
+ status = vd_do_slice_ioctl(vd, cmd, arg);
+
+ } else if (vd->file) {
+
+ /* file or volume exported as a full disk */
+ status = vd_do_file_ioctl(vd, cmd, arg);
+
+ } else {
+
+ /* disk device exported as a full disk */
+ status = ldi_ioctl(vd->ldi_handle[0], cmd, (intptr_t)arg,
+ vd->open_flags | FKIOCTL, kcred, &rval);
+ }
+
+#ifdef DEBUG
+ if (rval != 0) {
+ PR0("ioctl %x set rval = %d, which is not being returned"
+ " to caller", cmd, rval);
+ }
+#endif /* DEBUG */
+
+ return (status);
}
/*
@@ -2303,7 +2471,7 @@ vd_do_file_ioctl(vd_t *vd, int cmd, void *ioctl_arg)
static int
vd_do_ioctl(vd_t *vd, vd_dring_payload_t *request, void* buf, vd_ioctl_t *ioctl)
{
- int rval = 0, status = 0;
+ int status = 0;
size_t nbytes = request->nbytes; /* modifiable copy */
@@ -2350,29 +2518,9 @@ vd_do_ioctl(vd_t *vd, vd_dring_payload_t *request, void* buf, vd_ioctl_t *ioctl)
}
/*
- * Handle single-slice block devices internally; otherwise, have the
- * real driver perform the ioctl()
+ * Send the ioctl to the disk backend.
*/
- if (vd->file) {
- request->status =
- vd_do_file_ioctl(vd, ioctl->cmd, (void *)ioctl->arg);
-
- } else if (vd->vdisk_type == VD_DISK_TYPE_SLICE && !vd->pseudo) {
- request->status =
- vd_do_slice_ioctl(vd, ioctl->cmd, (void *)ioctl->arg);
-
- } else {
- request->status = ldi_ioctl(vd->ldi_handle[request->slice],
- ioctl->cmd, (intptr_t)ioctl->arg, vd->open_flags | FKIOCTL,
- kcred, &rval);
-
-#ifdef DEBUG
- if (rval != 0) {
- PR0("%s set rval = %d, which is not being returned to"
- " client", ioctl->cmd_name, rval);
- }
-#endif /* DEBUG */
- }
+ request->status = vd_backend_ioctl(vd, ioctl->cmd, ioctl->arg);
if (request->status != 0) {
PR0("ioctl(%s) = errno %d", ioctl->cmd_name, request->status);
@@ -4459,6 +4607,36 @@ vd_setup_full_disk(vd_t *vd)
return (0);
}
+/*
+ * When a slice or a volume is exported as a single-slice disk, we want
+ * the disk backend (i.e. the slice or volume) to be entirely mapped as
+ * a slice without the addition of any metadata.
+ *
+ * So when exporting the disk as a VTOC disk, we fake a disk with the following
+ * layout:
+ *
+ * 0 1 N+1
+ * +-+--------------------------+
+ * virtual disk: |L| slice 0 |
+ * +-+--------------------------+
+ * ^: :
+ * |: :
+ * VTOC LABEL--+: :
+ * +--------------------------+
+ * disk backend: | slice/volume |
+ * +--------------------------+
+ * 0 N
+ *
+ * N is the number of blocks in the slice/volume.
+ *
+ * We simulate a disk with N+1 blocks. The first block (block 0) is faked and
+ * can not be changed. The remaining blocks (1 to N+1) defines slice 0 and are
+ * mapped to the exported slice or volume:
+ *
+ * - block 0 (L) can return a fake VTOC label if raw read was implemented.
+ * - block 1 to N+1 is mapped to the exported slice or volume.
+ *
+ */
static int
vd_setup_partition_vtoc(vd_t *vd)
{
@@ -4483,7 +4661,7 @@ vd_setup_partition_vtoc(vd_t *vd)
PRN("%s geometry claims 0 heads", device_path);
return (EIO);
}
- vd->dk_geom.dkg_ncyl = vd->vdisk_size / vd->dk_geom.dkg_nsect /
+ vd->dk_geom.dkg_ncyl = (vd->vdisk_size + 1) / vd->dk_geom.dkg_nsect /
vd->dk_geom.dkg_nhead;
vd->dk_geom.dkg_acyl = 0;
vd->dk_geom.dkg_pcyl = vd->dk_geom.dkg_ncyl + vd->dk_geom.dkg_acyl;
@@ -4496,34 +4674,74 @@ vd_setup_partition_vtoc(vd_t *vd)
vd->vtoc.v_nparts = 1;
vd->vtoc.v_part[0].p_tag = V_UNASSIGNED;
vd->vtoc.v_part[0].p_flag = 0;
- vd->vtoc.v_part[0].p_start = 0;
+ vd->vtoc.v_part[0].p_start = 1;
vd->vtoc.v_part[0].p_size = vd->vdisk_size;
bcopy(VD_ASCIILABEL, vd->vtoc.v_asciilabel,
MIN(sizeof (VD_ASCIILABEL), sizeof (vd->vtoc.v_asciilabel)));
+ /* adjust the vdisk_size, we emulate the first block */
+ vd->vdisk_size += 1;
+
return (0);
}
+/*
+ * When a slice, volume or file is exported as a single-slice disk, we want
+ * the disk backend (i.e. the slice, volume or file) to be entirely mapped
+ * as a slice without the addition of any metadata.
+ *
+ * So when exporting the disk as an EFI disk, we fake a disk with the following
+ * layout:
+ *
+ * 0 1 2 3 34 34+N
+ * +-+-+-+-------+--------------------------+
+ * virtual disk: |X|T|E|XXXXXXX| slice 0 |
+ * +-+-+-+-------+--------------------------+
+ * ^ ^ : :
+ * | | : :
+ * GPT-+ +-GPE : :
+ * +--------------------------+
+ * disk backend: | slice/volume/file |
+ * +--------------------------+
+ * 0 N
+ *
+ * N is the number of blocks in the slice/volume/file.
+ *
+ * We simulate a disk with 34+N blocks. The first 34 blocks (0 to 33) are
+ * emulated and can not be changed. The remaining blocks (34 to 34+N) defines
+ * slice 0 and are mapped to the exported slice, volume or file:
+ *
+ * - block 0 (X) is unused and can return 0 if raw read was implemented.
+ * - block 1 (T) returns a fake EFI GPT (via DKIOCGETEFI)
+ * - block 2 (E) returns a fake EFI GPE (via DKIOCGETEFI)
+ * - block 3 to 33 (X) are unused and return 0 if raw read is implemented.
+ * - block 34 to 34+N is mapped to the exported slice, volume or file.
+ *
+ */
static int
vd_setup_partition_efi(vd_t *vd)
{
efi_gpt_t *gpt;
efi_gpe_t *gpe;
- struct uuid uuid = EFI_RESERVED;
+ struct uuid uuid = EFI_USR;
uint32_t crc;
- int length;
- length = sizeof (efi_gpt_t) + sizeof (efi_gpe_t);
+ gpt = &vd->efi_gpt;
+ gpe = &vd->efi_gpe;
- gpt = kmem_zalloc(length, KM_SLEEP);
- gpe = (efi_gpe_t *)(gpt + 1);
+ bzero(gpt, sizeof (efi_gpt_t));
+ bzero(gpe, sizeof (efi_gpe_t));
+
+ /* adjust the vdisk_size, we emulate the first 34 blocks */
+ vd->vdisk_size += 34;
gpt->efi_gpt_Signature = LE_64(EFI_SIGNATURE);
gpt->efi_gpt_Revision = LE_32(EFI_VERSION_CURRENT);
gpt->efi_gpt_HeaderSize = LE_32(sizeof (efi_gpt_t));
- gpt->efi_gpt_FirstUsableLBA = LE_64(0ULL);
+ gpt->efi_gpt_FirstUsableLBA = LE_64(34ULL);
gpt->efi_gpt_LastUsableLBA = LE_64(vd->vdisk_size - 1);
gpt->efi_gpt_NumberOfPartitionEntries = LE_32(1);
+ gpt->efi_gpt_PartitionEntryLBA = LE_64(2ULL);
gpt->efi_gpt_SizeOfPartitionEntry = LE_32(sizeof (efi_gpe_t));
UUID_LE_CONVERT(gpe->efi_gpe_PartitionTypeGUID, uuid);
@@ -4536,10 +4754,6 @@ vd_setup_partition_efi(vd_t *vd)
CRC32(crc, gpt, sizeof (efi_gpt_t), -1U, crc32_table);
gpt->efi_gpt_HeaderCRC32 = LE_32(~crc);
- vd->dk_efi.dki_lba = 0;
- vd->dk_efi.dki_length = length;
- vd->dk_efi.dki_data = gpt;
-
return (0);
}
@@ -4592,37 +4806,12 @@ vd_setup_backend_vnode(vd_t *vd)
return (EIO);
}
- /*
- * Find and validate the geometry of a disk image. For a single slice
- * disk image, this will build a fake geometry and vtoc.
- */
- status = vd_file_validate_geometry(vd);
- if (status != 0 && status != EINVAL) {
- PRN("Failed to read label from %s", file_path);
- return (EIO);
- }
-
/* sector size = block size = DEV_BSIZE */
vd->block_size = DEV_BSIZE;
vd->vdisk_block_size = DEV_BSIZE;
vd->vdisk_size = vd->file_size / DEV_BSIZE;
vd->max_xfer_sz = maxphys / DEV_BSIZE; /* default transfer size */
- if (vd_file_is_iso_image(vd)) {
- /*
- * Indicate whether to call this a CD or DVD from the size
- * of the ISO image (images for both drive types are stored
- * in the ISO-9600 format). CDs can store up to just under 1Gb
- */
- if ((vd->vdisk_size * vd->vdisk_block_size) >
- (1024 * 1024 * 1024))
- vd->vdisk_media = VD_MEDIA_DVD;
- else
- vd->vdisk_media = VD_MEDIA_CD;
- } else {
- vd->vdisk_media = VD_MEDIA_FIXED;
- }
-
/*
* Get max_xfer_sz from the device where the file is or from the device
* itself if we have a pseudo device.
@@ -4666,10 +4855,38 @@ vd_setup_backend_vnode(vd_t *vd)
PR0("using file %s, dev %s, max_xfer = %u blks",
file_path, dev_path, vd->max_xfer_sz);
- /* Setup devid for the disk image */
-
- if (vd->vdisk_type == VD_DISK_TYPE_SLICE)
+ if (vd->vdisk_type == VD_DISK_TYPE_SLICE) {
+ ASSERT(!vd->pseudo);
+ vd->vdisk_label = VD_DISK_LABEL_EFI;
+ status = vd_setup_partition_efi(vd);
return (0);
+ }
+
+ /*
+ * Find and validate the geometry of a disk image.
+ */
+ status = vd_file_validate_geometry(vd);
+ if (status != 0 && status != EINVAL && status != ENOTSUP) {
+ PRN("Failed to read label from %s", file_path);
+ return (EIO);
+ }
+
+ if (vd_file_is_iso_image(vd)) {
+ /*
+ * Indicate whether to call this a CD or DVD from the size
+ * of the ISO image (images for both drive types are stored
+ * in the ISO-9600 format). CDs can store up to just under 1Gb
+ */
+ if ((vd->vdisk_size * vd->vdisk_block_size) >
+ (1024 * 1024 * 1024))
+ vd->vdisk_media = VD_MEDIA_DVD;
+ else
+ vd->vdisk_media = VD_MEDIA_CD;
+ } else {
+ vd->vdisk_media = VD_MEDIA_FIXED;
+ }
+
+ /* Setup devid for the disk image */
if (vd->vdisk_label != VD_DISK_LABEL_UNK) {
@@ -4870,8 +5087,6 @@ vd_setup_backend_ldi(vd_t *vd)
return (EIO);
}
- vd->vdisk_label = vd_read_vtoc(vd, &vd->vtoc);
-
/* Store the device's max transfer size for return to the client */
vd->max_xfer_sz = dk_cinfo.dki_maxtransfer;
@@ -4917,7 +5132,7 @@ vd_setup_backend_ldi(vd_t *vd)
static int
vd_setup_single_slice_disk(vd_t *vd)
{
- int status;
+ int status, rval;
char *device_path = vd->device_path;
/* Get size of backing device */
@@ -4931,42 +5146,41 @@ vd_setup_single_slice_disk(vd_t *vd)
vd->vdisk_media = VD_MEDIA_FIXED;
if (vd->pseudo) {
-
ASSERT(vd->vdisk_type == VD_DISK_TYPE_SLICE);
-
- /*
- * Currently we only support exporting pseudo devices which
- * provide a valid disk label.
- */
- if (vd->vdisk_label == VD_DISK_LABEL_UNK) {
- PRN("%s is a pseudo device with an invalid disk "
- "label\n", device_path);
- return (EINVAL);
- }
- return (0); /* ...and we're done */
- }
-
- /* We can only export a slice if the disk has a valid label */
- if (vd->vdisk_label == VD_DISK_LABEL_UNK) {
- PRN("%s is a slice from a disk with an unknown disk label\n",
- device_path);
- return (EINVAL);
}
/*
* We export the slice as a single slice disk even if the "slice"
* option was not specified.
*/
- vd->vdisk_type = VD_DISK_TYPE_SLICE;
+ vd->vdisk_type = VD_DISK_TYPE_SLICE;
vd->nslices = 1;
- if (vd->vdisk_label == VD_DISK_LABEL_EFI) {
- /* Slice from a disk with an EFI label */
- status = vd_setup_partition_efi(vd);
- } else {
- /* Slice from a disk with a VTOC label */
- ASSERT(vd->vdisk_label == VD_DISK_LABEL_VTOC);
+ /*
+ * When exporting a slice or a device as a single slice disk, we don't
+ * care about any partitioning exposed by the backend. The goal is just
+ * to export the backend as a flat storage. We provide a fake partition
+ * table (either a VTOC or EFI), which presents only one slice, to
+ * accommodate tools expecting a disk label.
+ *
+ * We check the label of the backend to export the device as a slice
+ * using the same type of label (VTOC or EFI). If there is no label
+ * then we create a fake EFI label.
+ *
+ * Note that the partition table we are creating could also be faked
+ * by the client based on the size of the backend device.
+ */
+ status = ldi_ioctl(vd->ldi_handle[0], DKIOCGVTOC, (intptr_t)&vd->vtoc,
+ (vd->open_flags | FKIOCTL), kcred, &rval);
+
+ if (status == 0) {
+ /* export with a fake VTOC label */
+ vd->vdisk_label = VD_DISK_LABEL_VTOC;
status = vd_setup_partition_vtoc(vd);
+ } else {
+ /* export with a fake EFI label */
+ vd->vdisk_label = VD_DISK_LABEL_EFI;
+ status = vd_setup_partition_efi(vd);
}
return (status);
@@ -5263,9 +5477,6 @@ vds_destroy_vd(void *arg)
PR0("Destroying vdisk state");
- if (vd->dk_efi.dki_data != NULL)
- kmem_free(vd->dk_efi.dki_data, vd->dk_efi.dki_length);
-
/* Disable queuing requests for the vdisk */
if (vd->initialized & VD_LOCKING) {
mutex_enter(&vd->lock);
diff --git a/usr/src/uts/sun4v/io/vdsk_common.c b/usr/src/uts/sun4v/io/vdsk_common.c
index faa9dce977..07c00232e1 100644
--- a/usr/src/uts/sun4v/io/vdsk_common.c
+++ b/usr/src/uts/sun4v/io/vdsk_common.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -45,77 +45,23 @@
*/
/*
- * This code is a port of the functions efi_alloc_read() and efi_free() from
- * the libefi userland library to the kernel so that the vDisk drivers (vdc
- * and vds) can read EFI data. We will certaintly be able to remove that code
- * once RFE 6213117 is implemented.
+ * This code provides generic functions to the vds and vdc drivers to read
+ * EFI labels from the disk backend and to get the EFI GPT and GPE. This is
+ * inspired from the libefi userland library and the cmlb driver. We will
+ * certainly be able to remove that code if RFE 6213117 is ever implemented.
*/
-#define VD_IOCTL_FLAGS (FEXCL | FREAD | FWRITE | FKIOCTL)
-
#define VD_EFI_DEBUG if (vd_efi_debug) vd_efi_print
-/*
- * The number of blocks the EFI label takes up (round up to nearest
- * block)
- */
-#define NBLOCKS(p, l) (1 + ((((p) * (int)sizeof (efi_gpe_t)) + \
- ((l) - 1)) / (l)))
-/* number of partitions -- limited by what we can malloc */
-#define MAX_PARTS ((4294967295UL - sizeof (struct dk_gpt)) / \
- sizeof (struct dk_part))
-
-/*
- * The vd_efi_alloc_and_read() function will use some ioctls to get EFI data
- * but the way we issue ioctl is different depending if we are on the vDisk
- * server side (vds) or on the vDisk client side.
- *
- * On the server side (vds), we reference a layered device (ldi_handle_t) so we
- * will use the LDI interface to execute ioctls (ldi_ioctl()). On the client
- * side (vdc), we reference a vdc device (with a dev_t) so we directly invoke
- * the function of the vdc driver implementing ioctls (vd_process_ioctl()).
- */
-#define VD_EFI_CALLER_VDS 0
-#define VD_EFI_CALLER_VDC 1
-
-typedef struct vd_efi_dev {
- int caller;
- union {
- ldi_handle_t vds;
- dev_t vdc;
- } ioctl_dev;
-} vd_efi_dev_t;
-
-static int (*vdc_ioctl_func)(dev_t dev, int cmd, caddr_t arg, int mode) = NULL;
-
+#ifdef DEBUG
static int vd_efi_debug = 1;
+#else
+static int vd_efi_debug = 0;
+#endif
-static struct uuid_to_ptag {
- struct uuid uuid;
-} conversion_array[] = {
- { EFI_UNUSED },
- { EFI_BOOT },
- { EFI_ROOT },
- { EFI_SWAP },
- { EFI_USR },
- { EFI_BACKUP },
- { 0 }, /* STAND is never used */
- { EFI_VAR },
- { EFI_HOME },
- { EFI_ALTSCTR },
- { 0 }, /* CACHE (cachefs) is never used */
- { EFI_RESERVED },
- { EFI_SYSTEM },
- { EFI_LEGACY_MBR },
- { EFI_RESV3 },
- { EFI_RESV4 },
- { EFI_MSFT_RESV },
- { EFI_DELL_BASIC },
- { EFI_DELL_RAID },
- { EFI_DELL_SWAP },
- { EFI_DELL_LVM },
- { EFI_DELL_RESV }
-};
+#define VD_EFI_GPE_LEN(vdisk, nparts) \
+ ((((sizeof (efi_gpe_t) * (nparts) - 1) / (vdisk)->block_size) + 1) * \
+ (vdisk)->block_size)
static void
vd_efi_print(const char *format, ...)
@@ -144,51 +90,79 @@ vd_efi_crc32(const unsigned char *s, unsigned int len)
}
static int
-vd_ioctl(vd_efi_dev_t *dev, int cmd, void *arg, int flag,
- cred_t *cred, int *rvalp)
+vd_efi_ioctl(vd_efi_dev_t *dev, int cmd, void *arg)
{
- int error;
-
- if (dev->caller == VD_EFI_CALLER_VDS) {
- error = ldi_ioctl(dev->ioctl_dev.vds, cmd,
- (intptr_t)arg, flag, cred, rvalp);
- } else {
- ASSERT(vdc_ioctl_func != NULL);
- error = (*vdc_ioctl_func)(dev->ioctl_dev.vdc, cmd,
- arg, flag);
- }
+ int status;
+
+ ASSERT(dev->vdisk_ioctl != NULL);
+ ASSERT(dev->vdisk != NULL);
+ status = (*dev->vdisk_ioctl)(dev->vdisk, cmd, (uintptr_t)arg);
- return (error);
+ return (status);
}
-static int
-vd_efi_ioctl(vd_efi_dev_t *dev, int cmd, dk_efi_t *dk_ioc)
+/*
+ * Swap GPT data to match with the system endianness.
+ */
+static void
+vd_efi_swap_gpt(efi_gpt_t *gpt)
{
- void *data = dk_ioc->dki_data;
- int error;
+ gpt->efi_gpt_Signature = LE_64(gpt->efi_gpt_Signature);
+ gpt->efi_gpt_Revision = LE_32(gpt->efi_gpt_Revision);
+ gpt->efi_gpt_HeaderSize = LE_32(gpt->efi_gpt_HeaderSize);
+ gpt->efi_gpt_HeaderCRC32 = LE_32(gpt->efi_gpt_HeaderCRC32);
+ gpt->efi_gpt_MyLBA = LE_64(gpt->efi_gpt_MyLBA);
+ gpt->efi_gpt_AlternateLBA = LE_64(gpt->efi_gpt_AlternateLBA);
+ gpt->efi_gpt_FirstUsableLBA = LE_64(gpt->efi_gpt_FirstUsableLBA);
+ gpt->efi_gpt_LastUsableLBA = LE_64(gpt->efi_gpt_LastUsableLBA);
+ UUID_LE_CONVERT(gpt->efi_gpt_DiskGUID, gpt->efi_gpt_DiskGUID);
+ gpt->efi_gpt_PartitionEntryLBA = LE_64(gpt->efi_gpt_PartitionEntryLBA);
+ gpt->efi_gpt_NumberOfPartitionEntries =
+ LE_32(gpt->efi_gpt_NumberOfPartitionEntries);
+ gpt->efi_gpt_SizeOfPartitionEntry =
+ LE_32(gpt->efi_gpt_SizeOfPartitionEntry);
+ gpt->efi_gpt_PartitionEntryArrayCRC32 =
+ LE_32(gpt->efi_gpt_PartitionEntryArrayCRC32);
+}
- dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
- error = vd_ioctl(dev, cmd, (caddr_t)dk_ioc, VD_IOCTL_FLAGS,
- kcred, NULL);
- dk_ioc->dki_data = data;
+/*
+ * Swap GPE data to match with the system endianness.
+ */
+static void
+vd_efi_swap_gpe(efi_gpe_t *gpe, int nparts)
+{
+ int i, j;
- return (error);
+ for (i = 0; i < nparts; i++) {
+ UUID_LE_CONVERT(gpe[i].efi_gpe_PartitionTypeGUID,
+ gpe[i].efi_gpe_PartitionTypeGUID);
+ UUID_LE_CONVERT(gpe[i].efi_gpe_UniquePartitionGUID,
+ gpe[i].efi_gpe_UniquePartitionGUID);
+ gpe[i].efi_gpe_StartingLBA = LE_64(gpe[i].efi_gpe_StartingLBA);
+ gpe[i].efi_gpe_EndingLBA = LE_64(gpe[i].efi_gpe_EndingLBA);
+ gpe[i].efi_gpe_Attributes.PartitionAttrs =
+ LE_16(gpe[i].efi_gpe_Attributes.PartitionAttrs);
+ for (j = 0; j < EFI_PART_NAME_LEN; j++) {
+ gpe[i].efi_gpe_PartitionName[j] =
+ LE_16(gpe[i].efi_gpe_PartitionName[j]);
+ }
+ }
}
+/*
+ * Check that an EFI GPT is valid. This function should be called with a raw
+ * EFI GPT i.e. GPT data should be in little endian format as indicated in the
+ * EFI specification and they should not have been swapped to match with the
+ * system endianness.
+ */
static int
-vd_efi_check_label(vd_efi_dev_t *dev, dk_efi_t *dk_ioc)
+vd_efi_check_gpt(vd_efi_dev_t *dev, efi_gpt_t *gpt)
{
- efi_gpt_t *efi;
- uint_t crc;
- int status;
+ uint_t crc_stored, crc_computed;
- if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, dk_ioc)) != 0)
- return (status);
-
- efi = dk_ioc->dki_data;
- if (efi->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) {
+ if (gpt->efi_gpt_Signature != LE_64(EFI_SIGNATURE)) {
VD_EFI_DEBUG("Bad EFI signature: 0x%llx != 0x%llx\n",
- (long long)efi->efi_gpt_Signature,
+ (long long)gpt->efi_gpt_Signature,
(long long)LE_64(EFI_SIGNATURE));
return (EINVAL);
}
@@ -197,318 +171,140 @@ vd_efi_check_label(vd_efi_dev_t *dev, dk_efi_t *dk_ioc)
* check CRC of the header; the size of the header should
* never be larger than one block
*/
- crc = efi->efi_gpt_HeaderCRC32;
- efi->efi_gpt_HeaderCRC32 = 0;
+ if (LE_32(gpt->efi_gpt_HeaderSize) > dev->block_size) {
+ VD_EFI_DEBUG("Header size (%u bytes) larger than one block"
+ "(%u bytes)\n", LE_32(gpt->efi_gpt_HeaderSize),
+ dev->block_size);
+ return (EINVAL);
+ }
- if (((len_t)LE_32(efi->efi_gpt_HeaderSize) > dk_ioc->dki_length) ||
- crc != LE_32(vd_efi_crc32((unsigned char *)efi,
- LE_32(efi->efi_gpt_HeaderSize)))) {
+ crc_stored = LE_32(gpt->efi_gpt_HeaderCRC32);
+ gpt->efi_gpt_HeaderCRC32 = LE_32(0);
+ crc_computed = vd_efi_crc32((unsigned char *)gpt,
+ LE_32(gpt->efi_gpt_HeaderSize));
+ gpt->efi_gpt_HeaderCRC32 = LE_32(crc_stored);
+
+ if (crc_stored != crc_computed) {
VD_EFI_DEBUG("Bad EFI CRC: 0x%x != 0x%x\n",
- crc, LE_32(vd_efi_crc32((unsigned char *)efi,
- sizeof (struct efi_gpt))));
+ crc_stored, crc_computed);
return (EINVAL);
}
return (0);
}
-static int
-vd_efi_read(vd_efi_dev_t *dev, struct dk_gpt *vtoc)
+/*
+ * Allocate and read the EFI GPT and GPE from the disk backend. Note that the
+ * on-disk GPT and GPE are stored in little endian format but this function
+ * returns them using the endianness of the system so that any field in the
+ * GPT/GPE structures can be directly accessible without any further conversion.
+ * The caller is responsible for freeing the allocated structures by calling
+ * vd_efi_free().
+ */
+int
+vd_efi_alloc_and_read(vd_efi_dev_t *dev, efi_gpt_t **efi_gpt,
+ efi_gpe_t **efi_gpe)
{
- int i, j, status;
- int label_len;
- int md_flag = 0;
- struct dk_minfo disk_info;
- dk_efi_t dk_ioc;
- efi_gpt_t *efi;
- efi_gpe_t *efi_parts;
- struct dk_cinfo dki_info;
- uint32_t user_length;
+ dk_efi_t dk_efi;
+ efi_gpt_t *gpt = NULL;
+ efi_gpe_t *gpe = NULL;
+ size_t gpt_len, gpe_len;
+ int nparts, status;
+
+ ASSERT(dev->block_size >= sizeof (efi_gpt_t));
+ gpt_len = dev->block_size;
+ gpt = kmem_zalloc(gpt_len, KM_SLEEP);
/*
- * get the partition number for this file descriptor.
- */
- if ((status = vd_ioctl(dev, DKIOCINFO, &dki_info, VD_IOCTL_FLAGS,
- kcred, NULL)) != 0) {
- VD_EFI_DEBUG("DKIOCINFO error 0x%x\n", status);
- return (status);
- }
- if ((strncmp(dki_info.dki_cname, "pseudo", 7) == 0) &&
- (strncmp(dki_info.dki_dname, "md", 3) == 0)) {
- md_flag++;
- }
- /* get the LBA size */
- if ((status = vd_ioctl(dev, DKIOCGMEDIAINFO, &disk_info, VD_IOCTL_FLAGS,
- kcred, NULL)) != 0) {
- VD_EFI_DEBUG("assuming LBA 512 bytes %d\n", status);
- disk_info.dki_lbsize = DEV_BSIZE;
- }
- if (disk_info.dki_lbsize == 0) {
- VD_EFI_DEBUG("efi_read: assuming LBA 512 bytes\n");
- disk_info.dki_lbsize = DEV_BSIZE;
- }
- /*
- * Read the EFI GPT to figure out how many partitions we need
- * to deal with.
+ * Read the EFI GPT.
*/
- dk_ioc.dki_lba = 1;
- if (NBLOCKS(vtoc->efi_nparts, disk_info.dki_lbsize) < 34) {
- label_len = EFI_MIN_ARRAY_SIZE + disk_info.dki_lbsize;
- } else {
- label_len = vtoc->efi_nparts * (int) sizeof (efi_gpe_t) +
- disk_info.dki_lbsize;
- if (label_len % disk_info.dki_lbsize) {
- /* pad to physical sector size */
- label_len += disk_info.dki_lbsize;
- label_len &= ~(disk_info.dki_lbsize - 1);
- }
+ dk_efi.dki_lba = 1;
+ dk_efi.dki_data = gpt;
+ dk_efi.dki_length = gpt_len;
+
+ if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
+ VD_EFI_DEBUG("DKIOCGETEFI (GPT, LBA=1) error %d\n", status);
+ goto errdone;
}
- dk_ioc.dki_data = kmem_alloc(label_len, KM_SLEEP);
- dk_ioc.dki_length = label_len;
- user_length = vtoc->efi_nparts;
- efi = dk_ioc.dki_data;
- if (md_flag) {
- if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_ioc)) != 0)
- return (status);
- } else if ((status = vd_efi_check_label(dev, &dk_ioc)) == EINVAL) {
- /* no valid label here; try the alternate */
- dk_ioc.dki_lba = disk_info.dki_capacity - 1;
- dk_ioc.dki_length = disk_info.dki_lbsize;
- if (vd_efi_check_label(dev, &dk_ioc) == 0) {
- VD_EFI_DEBUG("efi_read: primary label corrupt; "
- "using backup\n");
- dk_ioc.dki_lba = LE_64(efi->efi_gpt_PartitionEntryLBA);
- vtoc->efi_flags |= EFI_GPT_PRIMARY_CORRUPT;
- vtoc->efi_nparts =
- LE_32(efi->efi_gpt_NumberOfPartitionEntries);
- /*
- * partitions are between last usable LBA and
- * backup partition header
- */
- dk_ioc.dki_data++;
- dk_ioc.dki_length = disk_info.dki_capacity -
- dk_ioc.dki_lba - 1;
- dk_ioc.dki_length *= disk_info.dki_lbsize;
- if (dk_ioc.dki_length > (len_t)label_len) {
- status = EINVAL;
- } else {
- status = vd_efi_ioctl(dev, DKIOCGETEFI,
- &dk_ioc);
- }
+ if ((status = vd_efi_check_gpt(dev, gpt)) != 0) {
+ /*
+ * No valid label here; try the alternate. The alternate GPT is
+ * located in the last block of the disk.
+ */
+ dk_efi.dki_lba = dev->disk_size - 1;
+ dk_efi.dki_data = gpt;
+ dk_efi.dki_length = gpt_len;
+
+ if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
+ VD_EFI_DEBUG("DKIOCGETEFI (LBA=%lu) error %d\n",
+ dev->disk_size - 1, status);
+ goto errdone;
}
- }
- if (status != 0) {
- kmem_free(efi, label_len);
- return (status);
+
+ if ((status = vd_efi_check_gpt(dev, gpt)) != 0)
+ goto errdone;
+
+ VD_EFI_DEBUG("efi_read: primary label corrupt; using backup\n");
}
- /* partitions start in the next block */
- /* LINTED -- always longlong aligned */
- efi_parts = (efi_gpe_t *)(((char *)efi) + disk_info.dki_lbsize);
+ /* swap GPT data after checking the GPT is valid */
+ vd_efi_swap_gpt(gpt);
/*
- * Assemble this into a "dk_gpt" struct for easier
- * digestibility by applications.
+ * Read the EFI GPE.
*/
- vtoc->efi_version = LE_32(efi->efi_gpt_Revision);
- vtoc->efi_nparts = LE_32(efi->efi_gpt_NumberOfPartitionEntries);
- vtoc->efi_part_size = LE_32(efi->efi_gpt_SizeOfPartitionEntry);
- vtoc->efi_lbasize = disk_info.dki_lbsize;
- vtoc->efi_last_lba = disk_info.dki_capacity - 1;
- vtoc->efi_first_u_lba = LE_64(efi->efi_gpt_FirstUsableLBA);
- vtoc->efi_last_u_lba = LE_64(efi->efi_gpt_LastUsableLBA);
- UUID_LE_CONVERT(vtoc->efi_disk_uguid, efi->efi_gpt_DiskGUID);
+ nparts = gpt->efi_gpt_NumberOfPartitionEntries;
- /*
- * If the array the user passed in is too small, set the length
- * to what it needs to be and return
- */
- if (user_length < vtoc->efi_nparts) {
- kmem_free(efi, label_len);
- return (EINVAL);
+ if (nparts > NDKMAP + 1) {
+ VD_EFI_DEBUG("Too many EFI partitions (%u)", nparts);
+ status = EINVAL;
+ goto errdone;
}
- for (i = 0; i < vtoc->efi_nparts; i++) {
-
- UUID_LE_CONVERT(vtoc->efi_parts[i].p_guid,
- efi_parts[i].efi_gpe_PartitionTypeGUID);
-
- for (j = 0;
- j < sizeof (conversion_array) / sizeof (struct uuid_to_ptag);
- j++) {
-
- if (bcmp(&vtoc->efi_parts[i].p_guid,
- &conversion_array[j].uuid,
- sizeof (struct uuid)) == 0) {
- vtoc->efi_parts[i].p_tag = j;
- break;
- }
- }
- if (vtoc->efi_parts[i].p_tag == V_UNASSIGNED)
- continue;
- vtoc->efi_parts[i].p_flag =
- LE_16(efi_parts[i].efi_gpe_Attributes.PartitionAttrs);
- vtoc->efi_parts[i].p_start =
- LE_64(efi_parts[i].efi_gpe_StartingLBA);
- vtoc->efi_parts[i].p_size =
- LE_64(efi_parts[i].efi_gpe_EndingLBA) -
- vtoc->efi_parts[i].p_start + 1;
- for (j = 0; j < EFI_PART_NAME_LEN; j++) {
- vtoc->efi_parts[i].p_name[j] =
- (uchar_t)LE_16(efi_parts[i].efi_gpe_PartitionName[j]);
- }
-
- UUID_LE_CONVERT(vtoc->efi_parts[i].p_uguid,
- efi_parts[i].efi_gpe_UniquePartitionGUID);
+ if (nparts == 0) {
+ VD_EFI_DEBUG("No partition defined");
+ status = EINVAL;
+ goto errdone;
}
- kmem_free(efi, label_len);
- return (0);
-}
+ gpe_len = VD_EFI_GPE_LEN(dev, nparts);
+ gpe = kmem_zalloc(gpe_len, KM_SLEEP);
-/*
- * Read EFI - return 0 upon success.
- */
-static int
-vd_efi_alloc_and_read(vd_efi_dev_t *dev, struct dk_gpt **vtoc, size_t *vtoc_len)
-{
- int status;
- uint32_t nparts;
- int length;
-
- /* figure out the number of entries that would fit into 16K */
- nparts = EFI_MIN_ARRAY_SIZE / sizeof (efi_gpe_t);
- length = (int) sizeof (struct dk_gpt) +
- (int) sizeof (struct dk_part) * (nparts - 1);
-
- *vtoc = kmem_zalloc(length, KM_SLEEP);
- (*vtoc)->efi_nparts = nparts;
- status = vd_efi_read(dev, *vtoc);
-
- if ((status == EINVAL) && (*vtoc)->efi_nparts > nparts) {
- kmem_free(*vtoc, length);
- length = (int) sizeof (struct dk_gpt) +
- (int) sizeof (struct dk_part) *
- ((*vtoc)->efi_nparts - 1);
- nparts = (*vtoc)->efi_nparts;
- *vtoc = kmem_alloc(length, KM_SLEEP);
- status = vd_efi_read(dev, *vtoc);
- }
+ dk_efi.dki_lba = gpt->efi_gpt_PartitionEntryLBA;
+ dk_efi.dki_data = (efi_gpt_t *)gpe;
+ dk_efi.dki_length = gpe_len;
- if (status != 0) {
- VD_EFI_DEBUG("read of EFI table failed with error=%d\n",
- status);
- kmem_free(*vtoc, length);
- *vtoc = NULL;
- *vtoc_len = 0;
- return (status);
+ if ((status = vd_efi_ioctl(dev, DKIOCGETEFI, &dk_efi)) != 0) {
+ VD_EFI_DEBUG("DKIOCGETEFI (GPE, LBA=%lu) error %d\n",
+ gpt->efi_gpt_PartitionEntryLBA, status);
+ goto errdone;
}
- *vtoc_len = length;
- return (0);
-}
-
-int
-vdc_efi_alloc_and_read(dev_t dev, struct dk_gpt **vtoc, size_t *vtoc_len)
-{
- vd_efi_dev_t efi_dev;
-
- ASSERT(vdc_ioctl_func != NULL);
-
- efi_dev.caller = VD_EFI_CALLER_VDC;
- efi_dev.ioctl_dev.vdc = dev;
+ vd_efi_swap_gpe(gpe, nparts);
- return (vd_efi_alloc_and_read(&efi_dev, vtoc, vtoc_len));
-}
-
-int
-vds_efi_alloc_and_read(ldi_handle_t dev, struct dk_gpt **vtoc, size_t *vtoc_len)
-{
- vd_efi_dev_t efi_dev;
-
- efi_dev.caller = VD_EFI_CALLER_VDS;
- efi_dev.ioctl_dev.vds = dev;
+ *efi_gpt = gpt;
+ *efi_gpe = gpe;
- return (vd_efi_alloc_and_read(&efi_dev, vtoc, vtoc_len));
-}
+ return (0);
-void
-vd_efi_free(struct dk_gpt *ptr, size_t length)
-{
- kmem_free(ptr, length);
-}
+errdone:
-void
-vdc_efi_init(int (*func)(dev_t, int, caddr_t, int))
-{
- vdc_ioctl_func = func;
-}
+ if (gpe != NULL)
+ kmem_free(gpe, gpe_len);
+ if (gpt != NULL)
+ kmem_free(gpt, gpt_len);
-void
-vdc_efi_fini(void)
-{
- vdc_ioctl_func = NULL;
+ return (status);
}
/*
- * This function stores EFI data (as returned by efi_alloc_and_read()) into
- * a vtoc structure. The vDisk driver uses a vtoc structure to store generic
- * information about disk partitions.
+ * Free the EFI GPE and GPT structures returned by vd_efi_alloc_and_read().
*/
void
-vd_efi_to_vtoc(struct dk_gpt *efi, struct vtoc *vtoc)
+vd_efi_free(vd_efi_dev_t *dev, efi_gpt_t *gpt, efi_gpe_t *gpe)
{
- int i, nparts;
-
- bzero(vtoc, sizeof (struct vtoc));
-
- vtoc->v_sanity = VTOC_SANE;
-
- nparts = efi->efi_nparts;
- for (i = 0; i < nparts; i++) {
- if (efi->efi_parts[i].p_tag != V_RESERVED)
- continue;
- bcopy(efi->efi_parts[i].p_name, vtoc->v_volume,
- LEN_DKL_VVOL);
- bcopy(efi->efi_parts[i].p_name, vtoc->v_asciilabel,
- EFI_PART_NAME_LEN);
- break;
- }
-
- vtoc->v_sectorsz = efi->efi_lbasize;
- vtoc->v_nparts = nparts;
- for (i = 0; i < nparts; i++) {
- /*
- * EFI can have more than 8 partitions. However the current
- * implementation of EFI on Solaris only support 7 partitions
- * (s0 to s6). There is no partition s7 but the minor number
- * corresponding to slice 7 is used to represent the whole
- * disk which data are stored in the "Sun Reserved" partition.
- * So we use the entry 7 of the vtoc structure to store
- * information about the whole disk.
- */
- if (efi->efi_parts[i].p_tag == V_RESERVED) {
- vtoc->v_part[VD_EFI_WD_SLICE].p_tag =
- efi->efi_parts[i].p_tag;
- vtoc->v_part[VD_EFI_WD_SLICE].p_flag =
- efi->efi_parts[i].p_flag;
- vtoc->v_part[VD_EFI_WD_SLICE].p_start =
- efi->efi_parts[i].p_start;
- vtoc->v_part[VD_EFI_WD_SLICE].p_size =
- efi->efi_parts[i].p_size;
- continue;
- }
-
- if (i >= VD_EFI_WD_SLICE) {
- continue;
- }
-
- vtoc->v_part[i].p_tag = efi->efi_parts[i].p_tag;
- if (efi->efi_parts[i].p_tag != V_UNASSIGNED) {
- vtoc->v_part[i].p_flag = efi->efi_parts[i].p_flag;
- vtoc->v_part[i].p_start = efi->efi_parts[i].p_start;
- vtoc->v_part[i].p_size = efi->efi_parts[i].p_size;
- }
- }
+ kmem_free(gpe, VD_EFI_GPE_LEN(dev,
+ gpt->efi_gpt_NumberOfPartitionEntries));
+ kmem_free(gpt, dev->block_size);
}
diff --git a/usr/src/uts/sun4v/sys/vdc.h b/usr/src/uts/sun4v/sys/vdc.h
index 715a25fd04..cd2cd60409 100644
--- a/usr/src/uts/sun4v/sys/vdc.h
+++ b/usr/src/uts/sun4v/sys/vdc.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -263,6 +263,7 @@ typedef struct vdc {
vd_disk_label_t vdisk_label; /* label type of device/disk imported */
struct vtoc *vtoc; /* structure to store VTOC data */
struct dk_geom *geom; /* structure to store geometry data */
+ vd_slice_t slice[V_NUMPAR]; /* logical partitions */
kthread_t *msg_proc_thr; /* main msg processing thread */
diff --git a/usr/src/uts/sun4v/sys/vdsk_common.h b/usr/src/uts/sun4v/sys/vdsk_common.h
index e5909a4787..370db971fb 100644
--- a/usr/src/uts/sun4v/sys/vdsk_common.h
+++ b/usr/src/uts/sun4v/sys/vdsk_common.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -216,6 +216,14 @@ typedef struct vd_dring_entry {
vd_dring_payload_t payload; /* disk specific data */
} vd_dring_entry_t;
+/*
+ * vDisk logical partition
+ */
+typedef struct vd_slice {
+ daddr_t start; /* block number of slice start */
+ daddr_t nblocks; /* number of blocks in the slice */
+} vd_slice_t;
+
/*
* vDisk control operation structures
@@ -493,22 +501,28 @@ typedef struct vd_scsi {
* The EFI alloc_and_read() function will use some ioctls to get EFI data
* but the device reference we will use is different depending if the command
* is issued from the vDisk server side (vds) or from the vDisk client side
- * (vdc). From the server side (vds), we will have a layered device reference
- * (ldi_handle_t) while on the client side (vdc) we will have a regular device
- * reference (dev_t).
- */
-#ifdef _SUN4V_VDS
-int vds_efi_alloc_and_read(ldi_handle_t dev, struct dk_gpt **vtoc,
- size_t *vtoc_len);
-#else
-void vdc_efi_init(int (*func)(dev_t, int, caddr_t, int));
-void vdc_efi_fini(void);
-int vdc_efi_alloc_and_read(dev_t dev, struct dk_gpt **vtoc,
- size_t *vtoc_len);
-#endif
+ * (vdc). The vd_efi_dev structure is filled by vdc/vds to indicate the ioctl
+ * function to call back and to provide information about the virtual disk.
+ */
+typedef int (*vd_efi_ioctl_func)(void *, int, uintptr_t);
+
+typedef struct vd_efi_dev {
+ void *vdisk; /* opaque pointer to the vdisk */
+ size_t block_size; /* vdisk block size */
+ size_t disk_size; /* vdisk size in blocks */
+ vd_efi_ioctl_func vdisk_ioctl; /* vdisk ioctl function */
+} vd_efi_dev_t;
+
+#define VD_EFI_DEV_SET(efi_dev, vdsk, ioctl) \
+ (efi_dev).vdisk = vdsk; \
+ (efi_dev).vdisk_ioctl = ioctl; \
+ (efi_dev).block_size = (vdsk)->block_size; \
+ (efi_dev).disk_size = (vdsk)->vdisk_size;
+
+
+int vd_efi_alloc_and_read(vd_efi_dev_t *dev, efi_gpt_t **gpt, efi_gpe_t **gpe);
+void vd_efi_free(vd_efi_dev_t *dev, efi_gpt_t *gpt, efi_gpe_t *gpe);
-void vd_efi_free(struct dk_gpt *ptr, size_t length);
-void vd_efi_to_vtoc(struct dk_gpt *efi, struct vtoc *vtoc);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/sun4v/vds/Makefile b/usr/src/uts/sun4v/vds/Makefile
index eb9d23499e..8d1d43a29a 100644
--- a/usr/src/uts/sun4v/vds/Makefile
+++ b/usr/src/uts/sun4v/vds/Makefile
@@ -20,7 +20,7 @@
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# uts/sun4v/vds/Makefile
@@ -68,7 +68,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
# lint pass one enforcement
#
CFLAGS += $(CCVERBOSE)
-CPPFLAGS += -D_SUN4V_VDS
#
# module dependencies