4855218 sparc: Need EFI label support on IDE
authorlclee
Fri, 28 Oct 2005 23:47:40 -0700
changeset 786 5c0d97122ae6
parent 785 a0797646b335
child 787 23f0d2857a4b
4855218 sparc: Need EFI label support on IDE 6275431 x86: Need EFI label support on IDE 6340778 cmlb: common disk label code 6326547 format: converting EFI to VTOC, fdisk info is taken from a random disk 6335611 format: Need to warn users that data will be lost when converting to and from EFI labels
usr/src/cmd/format/Makefile
usr/src/cmd/format/auto_sense.c
usr/src/cmd/format/auto_sense.h
usr/src/cmd/format/ix_altsctr.c
usr/src/cmd/format/label.c
usr/src/cmd/format/menu_command.c
usr/src/cmd/format/menu_fdisk.c
usr/src/pkgdefs/SUNWckr/prototype_i386
usr/src/pkgdefs/SUNWckr/prototype_sparc
usr/src/pkgdefs/SUNWhea/prototype_com
usr/src/pkgdefs/SUNWpsdcr/prototype_i386
usr/src/pkgdefs/SUNWpsh/prototype_i386
usr/src/tools/scripts/bfu.sh
usr/src/uts/common/Makefile.files
usr/src/uts/common/io/cmlb.c
usr/src/uts/common/io/dktp/disk/cmdk.c
usr/src/uts/common/sys/Makefile
usr/src/uts/common/sys/cmlb.h
usr/src/uts/common/sys/cmlb_impl.h
usr/src/uts/common/sys/dktp/altsctr.h
usr/src/uts/common/sys/dktp/cmdk.h
usr/src/uts/intel/Makefile.files
usr/src/uts/intel/Makefile.intel
usr/src/uts/intel/cmdk/Makefile
usr/src/uts/intel/cmlb/Makefile
usr/src/uts/sparc/Makefile.sparc
usr/src/uts/sparc/cmlb/Makefile
usr/src/uts/sun/Makefile.files
--- a/usr/src/cmd/format/Makefile	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/Makefile	Fri Oct 28 23:47:40 2005 -0700
@@ -56,7 +56,7 @@
 $(ROOTETCDATA) := OWNER = root
 $(ROOTETCDATA) := GROUP = sys
 
-LDLIBS +=	-ladm -lefi -ldiskmgt -lnvpair
+LDLIBS +=	-ladm -lefi -ldiskmgt -lnvpair -ldevid
 
 .KEEP_STATE:
 
--- a/usr/src/cmd/format/auto_sense.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/auto_sense.c	Fri Oct 28 23:47:40 2005 -0700
@@ -206,6 +206,16 @@
 				struct dk_label	*label);
 static struct disk_info	*find_scsi_disk_info(
 				struct dk_cinfo	*dkinfo);
+
+static struct disk_type *new_direct_disk_type(int fd, char *disk_name,
+    struct dk_label *label);
+
+static struct disk_info *find_direct_disk_info(struct dk_cinfo *dkinfo);
+static int efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc);
+static int auto_label_init(struct dk_label *label);
+static struct ctlr_info *find_direct_ctlr_info(struct dk_cinfo	*dkinfo);
+static  struct disk_info *find_direct_disk_info(struct dk_cinfo *dkinfo);
+
 static char		*get_sun_disk_name(
 				char		*disk_name,
 				struct scsi_inquiry *inquiry);
@@ -339,6 +349,382 @@
 	return (disk);
 }
 
+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 struct ctlr_info *
+find_direct_ctlr_info(
+	struct dk_cinfo		*dkinfo)
+{
+	struct ctlr_info	*ctlr;
+
+	if (dkinfo->dki_ctype != DKC_DIRECT)
+		return (NULL);
+
+	for (ctlr = ctlr_list; ctlr != NULL; ctlr = ctlr->ctlr_next) {
+		if (ctlr->ctlr_addr == dkinfo->dki_addr &&
+		    ctlr->ctlr_space == dkinfo->dki_space &&
+		    ctlr->ctlr_ctype->ctype_ctype == DKC_DIRECT) {
+			return (ctlr);
+		}
+	}
+
+	impossible("no DIRECT controller info");
+	/*NOTREACHED*/
+}
+
+static  struct disk_info *
+find_direct_disk_info(
+	struct dk_cinfo		*dkinfo)
+{
+	struct disk_info	*disk;
+	struct dk_cinfo		*dp;
+
+	for (disk = disk_list; disk != NULL; disk = disk->disk_next) {
+		assert(dkinfo->dki_ctype == DKC_DIRECT);
+		dp = &disk->disk_dkinfo;
+		if (dp->dki_ctype == dkinfo->dki_ctype &&
+		    dp->dki_cnum == dkinfo->dki_cnum &&
+		    dp->dki_unit == dkinfo->dki_unit &&
+		    strcmp(dp->dki_dname, dkinfo->dki_dname) == 0) {
+			return (disk);
+		}
+	}
+
+	impossible("No DIRECT disk info instance\n");
+	/*NOTREACHED*/
+}
+
+/*
+ * To convert EFI to SMI labels, we need to get label geometry.
+ * Unfortunately at this time there is no good way to do so.
+ * DKIOCGGEOM will fail if disk is EFI labeled. So we hack around
+ * it and clear EFI label, do a DKIOCGGEOM and put the EFI label
+ * back on disk.
+ * This routine gets the label geometry and initializes the label
+ * It uses cur_file as opened device.
+ * returns 0 if succeeds or -1 if failed.
+ */
+static int
+auto_label_init(struct dk_label *label)
+{
+	dk_efi_t	dk_ioc;
+	dk_efi_t	dk_ioc_back;
+	efi_gpt_t	*data = NULL;
+	efi_gpt_t	*databack = NULL;
+	struct dk_geom	disk_geom;
+	struct dk_minfo	disk_info;
+	efi_gpt_t 	*backsigp;
+	int		fd = cur_file;
+	int		rval = -1;
+	int		efisize = EFI_LABEL_SIZE * 2;
+	int		success = 0;
+	uint64_t	sig;
+	uint64_t	backsig;
+
+	if ((data = calloc(efisize, 1)) == NULL) {
+		err_print("auto_label_init: calloc failed\n");
+		goto auto_label_init_out;
+	}
+
+	dk_ioc.dki_data = data;
+	dk_ioc.dki_lba = 1;
+	dk_ioc.dki_length = efisize;
+
+	if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) != 0) {
+		err_print("auto_label_init: GETEFI failed\n");
+		goto auto_label_init_out;
+	}
+
+	if ((databack = calloc(efisize, 1)) == NULL) {
+		err_print("auto_label_init calloc2 failed");
+		goto auto_label_init_out;
+	}
+
+	/* get the LBA size and capacity */
+	if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) {
+		err_print("auto_label_init: dkiocgmediainfo failed\n");
+		goto auto_label_init_out;
+	}
+
+	if (disk_info.dki_lbsize == 0) {
+		if (option_msg && diag_msg) {
+			err_print("auto_lbal_init: assuming 512 byte"
+			    "block size");
+		}
+		disk_info.dki_lbsize = DEV_BSIZE;
+	}
+
+	if (disk_info.dki_lbsize != DEV_BSIZE) {
+		err_print("auto_label_init: lbasize is not 512\n");
+		goto auto_label_init_out;
+	}
+
+	dk_ioc_back.dki_data = databack;
+
+	/*
+	 * back up efi label goes to capacity - 1, we are reading an extra block
+	 * before the back up label.
+	 */
+	dk_ioc_back.dki_lba = disk_info.dki_capacity - 1 - 1;
+	dk_ioc_back.dki_length = efisize;
+
+	if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc_back) != 0) {
+		err_print("auto_label_init: GETEFI backup failed\n");
+		goto auto_label_init_out;
+	}
+
+	sig = dk_ioc.dki_data->efi_gpt_Signature;
+	dk_ioc.dki_data->efi_gpt_Signature = 0x0;
+
+	enter_critical();
+
+	if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) {
+		err_print("auto_label_init: SETEFI failed\n");
+		exit_critical();
+		goto auto_label_init_out;
+	}
+
+	backsigp = (efi_gpt_t *)((uintptr_t)dk_ioc_back.dki_data + DEV_BSIZE);
+
+	backsig = backsigp->efi_gpt_Signature;
+
+	backsigp->efi_gpt_Signature = 0;
+
+	if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc_back) == -1) {
+		err_print("auto_label_init: SETEFI backup failed\n");
+	}
+
+	if (ioctl(cur_file, DKIOCGGEOM, &disk_geom) != 0)
+		err_print("auto_label_init: GGEOM failed\n");
+	else
+		success = 1;
+
+	dk_ioc.dki_data->efi_gpt_Signature = sig;
+	backsigp->efi_gpt_Signature = backsig;
+
+	if (efi_ioctl(cur_file, DKIOCSETEFI, &dk_ioc_back) == -1) {
+		err_print("auto_label_init: SETEFI revert backup failed\n");
+		success = 0;
+	}
+
+	if (efi_ioctl(cur_file, DKIOCSETEFI, &dk_ioc) == -1) {
+		err_print("auto_label_init: SETEFI revert failed\n");
+		success = 0;
+	}
+
+	exit_critical();
+
+	if (success == 0)
+		goto auto_label_init_out;
+
+	ncyl = disk_geom.dkg_ncyl;
+	acyl = disk_geom.dkg_acyl;
+	nhead =  disk_geom.dkg_nhead;
+	nsect = disk_geom.dkg_nsect;
+	pcyl = ncyl + acyl;
+
+	label->dkl_pcyl = pcyl;
+	label->dkl_ncyl = ncyl;
+	label->dkl_acyl = acyl;
+	label->dkl_nhead = nhead;
+	label->dkl_nsect = nsect;
+	label->dkl_apc = 0;
+	label->dkl_intrlv = 1;
+	label->dkl_rpm = disk_geom.dkg_rpm;
+
+	label->dkl_magic = DKL_MAGIC;
+
+	(void) snprintf(label->dkl_asciilabel, sizeof (label->dkl_asciilabel),
+	    "%s cyl %d alt %d hd %d sec %d",
+	    "DEFAULT", ncyl, acyl, nhead, nsect);
+
+	rval = 0;
+#if defined(_FIRMWARE_NEEDS_FDISK)
+	(void) auto_solaris_part(label);
+	ncyl = label->dkl_ncyl;
+
+#endif	/* defined(_FIRMWARE_NEEDS_FDISK) */
+
+	if (!build_default_partition(label, DKC_DIRECT)) {
+		rval = -1;
+	}
+
+	(void) checksum(label, CK_MAKESUM);
+
+
+auto_label_init_out:
+	if (data)
+		free(data);
+	if (databack)
+		free(databack);
+
+	return (rval);
+}
+
+static struct disk_type *
+new_direct_disk_type(
+	int		fd,
+	char		*disk_name,
+	struct dk_label	*label)
+{
+	struct disk_type	*dp;
+	struct disk_type	*disk;
+	struct ctlr_info	*ctlr;
+	struct dk_cinfo		dkinfo;
+	struct partition_info	*part = NULL;
+	struct partition_info	*pt;
+	struct disk_info	*disk_info;
+	int			i;
+
+	/*
+	 * Get the disk controller info for this disk
+	 */
+	if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) {
+		if (option_msg && diag_msg) {
+			err_print("DKIOCINFO failed\n");
+		}
+		return (NULL);
+	}
+
+	/*
+	 * Find the ctlr_info for this disk.
+	 */
+	ctlr = find_direct_ctlr_info(&dkinfo);
+
+	/*
+	 * Allocate a new disk type for the direct controller.
+	 */
+	disk = (struct disk_type *)zalloc(sizeof (struct disk_type));
+
+	/*
+	 * Find the disk_info instance for this disk.
+	 */
+	disk_info = find_direct_disk_info(&dkinfo);
+
+	/*
+	 * The controller and the disk should match.
+	 */
+	assert(disk_info->disk_ctlr == ctlr);
+
+	/*
+	 * Link the disk into the list of disks
+	 */
+	dp = ctlr->ctlr_ctype->ctype_dlist;
+	if (dp == NULL) {
+		ctlr->ctlr_ctype->ctype_dlist = dp;
+	} else {
+		while (dp->dtype_next != NULL) {
+			dp = dp->dtype_next;
+		}
+		dp->dtype_next = disk;
+	}
+	disk->dtype_next = NULL;
+
+	/*
+	 * Allocate and initialize the disk name.
+	 */
+	disk->dtype_asciilabel = alloc_string(disk_name);
+
+	/*
+	 * Initialize disk geometry info
+	 */
+	disk->dtype_pcyl = label->dkl_pcyl;
+	disk->dtype_ncyl = label->dkl_ncyl;
+	disk->dtype_acyl = label->dkl_acyl;
+	disk->dtype_nhead = label->dkl_nhead;
+	disk->dtype_nsect = label->dkl_nsect;
+	disk->dtype_rpm = label->dkl_rpm;
+
+	part = (struct partition_info *)
+		zalloc(sizeof (struct partition_info));
+	pt = disk->dtype_plist;
+	if (pt == NULL) {
+		disk->dtype_plist = part;
+	} else {
+		while (pt->pinfo_next != NULL) {
+			pt = pt->pinfo_next;
+		}
+		pt->pinfo_next = part;
+	}
+
+	part->pinfo_next = NULL;
+
+	/*
+	 * Set up the partition name
+	 */
+	part->pinfo_name = alloc_string("default");
+
+	/*
+	 * Fill in the partition info from the label
+	 */
+	for (i = 0; i < NDKMAP; i++) {
+
+#if defined(_SUNOS_VTOC_8)
+		part->pinfo_map[i] = label->dkl_map[i];
+
+#elif defined(_SUNOS_VTOC_16)
+		part->pinfo_map[i].dkl_cylno =
+			label->dkl_vtoc.v_part[i].p_start /
+				((int)(disk->dtype_nhead *
+					disk->dtype_nsect - apc));
+		part->pinfo_map[i].dkl_nblk =
+			label->dkl_vtoc.v_part[i].p_size;
+#else
+#error No VTOC format defined.
+#endif				/* defined(_SUNOS_VTOC_8) */
+	}
+
+	/*
+	 * Use the VTOC if valid, or install a default
+	 */
+	if (label->dkl_vtoc.v_version == V_VERSION) {
+		(void) memcpy(disk_info->v_volume, label->dkl_vtoc.v_volume,
+			LEN_DKL_VVOL);
+		part->vtoc = label->dkl_vtoc;
+	} else {
+		(void) memset(disk_info->v_volume, 0, LEN_DKL_VVOL);
+		set_vtoc_defaults(part);
+	}
+
+	/*
+	 * Link the disk to the partition map
+	 */
+	disk_info->disk_parts = part;
+
+	return (disk);
+}
+
+/*
+ * Get a disk type that has label info. This is used to convert
+ * EFI label to SMI label
+ */
+struct disk_type *
+auto_direct_get_geom_label(int fd, struct dk_label *label)
+{
+	struct disk_type		*disk_type;
+
+	if (auto_label_init(label) != 0) {
+		err_print("auto_direct_get_geom_label: failed to get label"
+		    "geometry");
+		return (NULL);
+	} else {
+		disk_type = new_direct_disk_type(fd, "DEFAULT", label);
+		return (disk_type);
+	}
+}
+
+
 /*
  * Auto-sense a scsi disk configuration, ie get the information
  * necessary to construct a label.  We have two different
@@ -1091,7 +1477,6 @@
 		}
 		return (0);
 	}
-
 #if defined(i386)
 	/*
 	 * Set the default boot partition to 1 cylinder
@@ -1153,12 +1538,18 @@
 #error No VTOC format defined.
 #endif				/* defined(_SUNOS_VTOC_16) */
 
-		if (i == 2 || ncyls[i] == 0)
+		if (i == 2 || ncyls[i] == 0) {
+#if defined(_SUNOS_VTOC_8)
+			if (i != 2) {
+				label->dkl_map[i].dkl_cylno = 0;
+				label->dkl_map[i].dkl_nblk = 0;
+			}
+#endif
 			continue;
+		}
 #if defined(_SUNOS_VTOC_8)
 		label->dkl_map[i].dkl_cylno = cyl;
 		label->dkl_map[i].dkl_nblk = ncyls[i] * blks_per_cyl;
-
 #elif defined(_SUNOS_VTOC_16)
 		label->dkl_vtoc.v_part[i].p_start = cyl * blks_per_cyl;
 		label->dkl_vtoc.v_part[i].p_size = ncyls[i] * blks_per_cyl;
--- a/usr/src/cmd/format/auto_sense.h	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/auto_sense.h	Fri Oct 28 23:47:40 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1993-2002 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -51,11 +51,14 @@
 				struct dk_label *label,
 				int		ctrl_type);
 
+struct disk_type *auto_direct_get_geom_label(int fd, struct dk_label *label);
 #else
 
 struct disk_type	*auto_sense();
 struct disk_type	*auto_efi_sense();
 int			build_default_partition();
+struct disk_type *auto_direct_get_geom_label();
+
 
 #endif	/* __STDC__ */
 
--- a/usr/src/cmd/format/ix_altsctr.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/ix_altsctr.c	Fri Oct 28 23:47:40 2005 -0700
@@ -534,7 +534,7 @@
 			ap->ap_map_sectot - 1, cluster, ALTS_MAP_DOWN);
 	    if (alts_ind == NULL) {
 		(void) fprintf(stderr,
-	"Unable to allocate alternates for bad starting sector %ld.\n",
+	"Unable to allocate alternates for bad starting sector %u.\n",
 			(ap->ap_entp)[i].bad_start);
 		return (65);
 	    }
--- a/usr/src/cmd/format/label.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/label.c	Fri Oct 28 23:47:40 2005 -0700
@@ -44,6 +44,7 @@
 #include <sys/vtoc.h>
 #include <sys/uuid.h>
 #include <errno.h>
+#include <devid.h>
 
 #if defined(_FIRMWARE_NEEDS_FDISK)
 #include <sys/dktp/fdisk.h>
@@ -579,6 +580,64 @@
 	return (vtoc_to_label(label, &vtoc, &geom));
 }
 
+int
+get_disk_info_from_devid(int fd, struct efi_info *label)
+{
+	ddi_devid_t	devid;
+	char		*s;
+	int		n;
+	char		*vid, *pid;
+	int		nvid, npid;
+	struct dk_minfo	minf;
+	struct dk_cinfo	dkinfo;
+
+
+	if (devid_get(fd, &devid)) {
+		if (option_msg && diag_msg)
+			err_print("devid_get failed\n");
+		return (-1);
+	}
+
+	n = devid_sizeof(devid);
+	s = (char *)devid;
+
+	if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) {
+		if (option_msg && diag_msg)
+			err_print("DKIOCINFO failed\n");
+		return (-1);
+	}
+
+	if (dkinfo.dki_ctype != DKC_DIRECT)
+		return (-1);
+
+	vid = s+12;
+	if (!(pid = strchr(vid, '=')))
+		return (-1);
+	nvid = pid - vid;
+	pid += 1;
+	npid = n - nvid - 13;
+
+	if (nvid > 9)
+		nvid = 9;
+	if (npid > 17) {
+		pid = pid + npid - 17;
+		npid = 17;
+	}
+
+	if (ioctl(fd, DKIOCGMEDIAINFO, &minf) == -1) {
+		devid_free(devid);
+		return (-1);
+	}
+
+	(void) strlcpy(label->vendor, vid, nvid);
+	(void) strlcpy(label->product, pid, npid);
+	(void) strlcpy(label->revision, "0001", 5);
+	label->capacity = minf.dki_capacity * minf.dki_lbsize / 512;
+
+	devid_free(devid);
+	return (0);
+}
+
 /*
  * Issue uscsi_inquiry and read_capacity commands to
  * retrieve the disk's Vendor, Product, Revision and
@@ -590,6 +649,9 @@
 	struct scsi_inquiry	inquiry;
 	struct scsi_capacity_16	capacity;
 
+	if (!get_disk_info_from_devid(fd, label))
+		return (0);
+
 	if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) {
 		err_print("Inquiry failed on %d\n", fd);
 		return (-1);
--- a/usr/src/cmd/format/menu_command.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/menu_command.c	Fri Oct 28 23:47:40 2005 -0700
@@ -1444,26 +1444,38 @@
 		    return (-1);
 		}
 
-		dptr = auto_sense(cur_file, 1, &label);
-		pcyl = label.dkl_pcyl;
-		ncyl = label.dkl_ncyl;
-		acyl = label.dkl_acyl;
-		nhead = label.dkl_nhead;
-		nsect = label.dkl_nsect;
+
+		fmt_print("Warning: This disk has an EFI label. Changing to "
+		    "SMI label will erase all\ncurrent partitions.\n");
+
+		if (check("Continue"))
+			return (-1);
 
+		(void) memset((char *)&label, 0, sizeof (struct dk_label));
+
+		if (cur_ctype->ctype_ctype == DKC_DIRECT)
+			dptr = auto_direct_get_geom_label(cur_file,  &label);
+		else
+			dptr = auto_sense(cur_file, 1, &label);
 		if (dptr == NULL) {
-		    fmt_print("Autoconfiguration failed.\n");
-		    return (-1);
+			fmt_print("Autoconfiguration failed.\n");
+			return (-1);
 		}
 
 		if (cur_disk->fdisk_part.systid == EFI_PMBR) {
 			fmt_print("You must use fdisk to delete the current "
 				"EFI partition and create a new\n"
 				"Solaris partition before you can convert the "
-				"label\n");
+				"label.\n");
 			return (-1);
 		}
 
+		pcyl = label.dkl_pcyl;
+		ncyl = label.dkl_ncyl;
+		acyl = label.dkl_acyl;
+		nhead = label.dkl_nhead;
+		nsect = label.dkl_nsect;
+
 		cur_label = L_TYPE_SOLARIS;
 		cur_disk->label_type = L_TYPE_SOLARIS;
 		free(cur_parts->etoc);
@@ -1475,23 +1487,24 @@
 		cur_dtype = dptr;
 		cur_parts = dptr->dtype_plist;
 		dptr->dtype_next = NULL;
-		break;
+
+		if (status = write_label())
+			err_print("Label failed.\n");
+		return (status);
+
+
 	    case 1:
 		/*
 		 * SMI label to EFI label
 		 */
-#ifdef i386
-		fmt_print("WARNING: converting this device to EFI labels will "
-			"erase all current fdisk\n"
-			"partition information.");
-		if (check(" Continue")) {
+
+
+		fmt_print("Warning: This disk has an SMI label. Changing to "
+		    "EFI label will erase all\ncurrent partitions.\n");
+
+		if (check("Continue")) {
 			return (-1);
 		}
-#else /* i386 */
-		if (check("Ready to label disk; continue")) {
-		    return (-1);
-		}
-#endif /* i386 */
 
 		if (get_disk_info(cur_file, &efinfo) != 0) {
 		    return (-1);
--- a/usr/src/cmd/format/menu_fdisk.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/cmd/format/menu_fdisk.c	Fri Oct 28 23:47:40 2005 -0700
@@ -575,14 +575,17 @@
 	int		status, i, fd;
 	struct mboot	mboot;
 	struct ipart	ip;
-	char		buf[80];
 	char		*bootptr;
+	char		pbuf[MAXPATHLEN];
+
 
-	(void) snprintf(buf, sizeof (buf), "/dev/rdsk/%sp0", x86_devname);
-	if ((fd = open(buf, O_RDONLY)) < 0) {
-		err_print("Error: can't open selected disk '%s'.\n", buf);
+	get_pname(&pbuf[0]);
+
+	if ((fd = open_disk(pbuf, O_RDONLY)) < 0) {
+		err_print("Error: can't open selected disk '%s'.\n", pbuf);
 		return (-1);
 	}
+
 	status = read(fd, (caddr_t)&mboot, sizeof (struct mboot));
 
 	if (status != sizeof (struct mboot)) {
--- a/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_i386	Fri Oct 28 23:47:40 2005 -0700
@@ -146,6 +146,7 @@
 d none boot/acpi/tables 755 root sys
 f none kernel/misc/acpica 755 root sys
 f none kernel/misc/busra 755 root sys
+f none kernel/misc/cmlb 755 root sys
 f none kernel/misc/consconfig 755 root sys
 f none kernel/misc/ctf 755 root sys
 l none kernel/misc/des=../../kernel/crypto/des
@@ -311,6 +312,7 @@
 d none kernel/misc/amd64 755 root sys
 f none kernel/misc/amd64/acpica 755 root sys
 f none kernel/misc/amd64/busra 755 root sys
+f none kernel/misc/amd64/cmlb 755 root sys
 f none kernel/misc/amd64/consconfig 755 root sys
 f none kernel/misc/amd64/ctf 755 root sys
 l none kernel/misc/amd64/des=../../../kernel/crypto/amd64/des
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Fri Oct 28 23:47:40 2005 -0700
@@ -148,6 +148,7 @@
 f none kernel/ipp/sparcv9/ipgpc 755 root sys
 d none kernel/misc/sparcv9 755 root sys
 f none kernel/misc/sparcv9/busra 755 root sys
+f none kernel/misc/sparcv9/cmlb 755 root sys
 f none kernel/misc/sparcv9/consconfig 755 root sys
 f none kernel/misc/sparcv9/ctf 755 root sys
 f none kernel/misc/sparcv9/dada 755 root sys
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Fri Oct 28 23:47:40 2005 -0700
@@ -525,6 +525,7 @@
 f none usr/include/sys/cladm.h 644 root bin
 f none usr/include/sys/class.h 644 root bin
 f none usr/include/sys/clconf.h 644 root bin
+f none usr/include/sys/cmlb.h 644 root bin
 f none usr/include/sys/cmn_err.h 644 root bin
 f none usr/include/sys/compress.h 644 root bin
 f none usr/include/sys/condvar.h 644 root bin
--- a/usr/src/pkgdefs/SUNWpsdcr/prototype_i386	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/pkgdefs/SUNWpsdcr/prototype_i386	Fri Oct 28 23:47:40 2005 -0700
@@ -61,7 +61,6 @@
 d none kernel/misc 755 root sys
 f none kernel/misc/dadk 755 root sys
 f none kernel/misc/gda 755 root sys
-f none kernel/misc/snlb 755 root sys
 f none kernel/misc/strategy 755 root sys
 d none kernel/drv/amd64 755 root sys
 f none kernel/drv/amd64/asy 755 root sys
@@ -70,7 +69,6 @@
 d none kernel/misc/amd64 755 root sys
 f none kernel/misc/amd64/dadk 755 root sys
 f none kernel/misc/amd64/gda 755 root sys
-f none kernel/misc/amd64/snlb 755 root sys
 f none kernel/misc/amd64/strategy 755 root sys
 e preserve kernel/misc/sysinit 755 root sys
 e preserve kernel/misc/amd64/sysinit 755 root sys
--- a/usr/src/pkgdefs/SUNWpsh/prototype_i386	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/pkgdefs/SUNWpsh/prototype_i386	Fri Oct 28 23:47:40 2005 -0700
@@ -57,7 +57,6 @@
 f none usr/include/sys/dktp/controller.h 644 root bin
 f none usr/include/sys/dktp/dadev.h 644 root bin
 f none usr/include/sys/dktp/dadk.h 644 root bin
-f none usr/include/sys/dktp/dklb.h 644 root bin
 f none usr/include/sys/dktp/fctypes.h 644 root bin
 f none usr/include/sys/dktp/flowctrl.h 644 root bin
 f none usr/include/sys/dktp/gda.h 644 root bin
@@ -68,7 +67,6 @@
 f none usr/include/sys/dktp/scdk.h 644 root bin
 f none usr/include/sys/dktp/scdkwatch.h 644 root bin
 f none usr/include/sys/dktp/sctarget.h 644 root bin
-f none usr/include/sys/dktp/snlb.h 644 root bin
 f none usr/include/sys/dktp/tgcd.h 644 root bin
 f none usr/include/sys/dktp/tgcom.h 644 root bin
 f none usr/include/sys/dktp/tgdk.h 644 root bin
--- a/usr/src/tools/scripts/bfu.sh	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/tools/scripts/bfu.sh	Fri Oct 28 23:47:40 2005 -0700
@@ -5475,6 +5475,14 @@
 	rm -rf $usr/include/sys/dktp/mlx
 
 	#
+	# Remove snlb
+	#
+	rm -f $root/kernel/misc/snlb
+	rm -f $root/kernel/misc/amd64/snlb
+	rm -f $root/usr/include/sys/dktp/dklb.h
+	rm -f $root/usr/include/sys/dktp/snlb.h
+
+	#
 	# Remove objmgr
 	#
 	rm -f $root/kernel/drv/objmgr.conf
--- a/usr/src/uts/common/Makefile.files	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/common/Makefile.files	Fri Oct 28 23:47:40 2005 -0700
@@ -676,8 +676,6 @@
 
 GDA_OBJS +=	gda.o
 
-SNLB_OBJS +=	snlb.o
-
 STRATEGY_OBJS += strategy.o
 
 CMDK_OBJS +=	cmdk.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/cmlb.c	Fri Oct 28 23:47:40 2005 -0700
@@ -0,0 +1,4091 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This module provides support for labeling operations for target
+ * drivers.
+ */
+
+#include <sys/scsi/scsi.h>
+#include <sys/sunddi.h>
+#include <sys/dklabel.h>
+#include <sys/dkio.h>
+#include <sys/vtoc.h>
+#include <sys/dktp/fdisk.h>
+#include <sys/vtrace.h>
+#include <sys/efi_partition.h>
+#include <sys/cmlb.h>
+#include <sys/cmlb_impl.h>
+
+
+/*
+ * Driver minor node structure and data table
+ */
+struct driver_minor_data {
+	char	*name;
+	minor_t	minor;
+	int	type;
+};
+
+static struct driver_minor_data dk_minor_data[] = {
+	{"a", 0, S_IFBLK},
+	{"b", 1, S_IFBLK},
+	{"c", 2, S_IFBLK},
+	{"d", 3, S_IFBLK},
+	{"e", 4, S_IFBLK},
+	{"f", 5, S_IFBLK},
+	{"g", 6, S_IFBLK},
+	{"h", 7, S_IFBLK},
+#if defined(_SUNOS_VTOC_16)
+	{"i", 8, S_IFBLK},
+	{"j", 9, S_IFBLK},
+	{"k", 10, S_IFBLK},
+	{"l", 11, S_IFBLK},
+	{"m", 12, S_IFBLK},
+	{"n", 13, S_IFBLK},
+	{"o", 14, S_IFBLK},
+	{"p", 15, S_IFBLK},
+#endif			/* defined(_SUNOS_VTOC_16) */
+#if defined(_FIRMWARE_NEEDS_FDISK)
+	{"q", 16, S_IFBLK},
+	{"r", 17, S_IFBLK},
+	{"s", 18, S_IFBLK},
+	{"t", 19, S_IFBLK},
+	{"u", 20, S_IFBLK},
+#endif			/* defined(_FIRMWARE_NEEDS_FDISK) */
+	{"a,raw", 0, S_IFCHR},
+	{"b,raw", 1, S_IFCHR},
+	{"c,raw", 2, S_IFCHR},
+	{"d,raw", 3, S_IFCHR},
+	{"e,raw", 4, S_IFCHR},
+	{"f,raw", 5, S_IFCHR},
+	{"g,raw", 6, S_IFCHR},
+	{"h,raw", 7, S_IFCHR},
+#if defined(_SUNOS_VTOC_16)
+	{"i,raw", 8, S_IFCHR},
+	{"j,raw", 9, S_IFCHR},
+	{"k,raw", 10, S_IFCHR},
+	{"l,raw", 11, S_IFCHR},
+	{"m,raw", 12, S_IFCHR},
+	{"n,raw", 13, S_IFCHR},
+	{"o,raw", 14, S_IFCHR},
+	{"p,raw", 15, S_IFCHR},
+#endif			/* defined(_SUNOS_VTOC_16) */
+#if defined(_FIRMWARE_NEEDS_FDISK)
+	{"q,raw", 16, S_IFCHR},
+	{"r,raw", 17, S_IFCHR},
+	{"s,raw", 18, S_IFCHR},
+	{"t,raw", 19, S_IFCHR},
+	{"u,raw", 20, S_IFCHR},
+#endif			/* defined(_FIRMWARE_NEEDS_FDISK) */
+	{0}
+};
+
+static struct driver_minor_data dk_minor_data_efi[] = {
+	{"a", 0, S_IFBLK},
+	{"b", 1, S_IFBLK},
+	{"c", 2, S_IFBLK},
+	{"d", 3, S_IFBLK},
+	{"e", 4, S_IFBLK},
+	{"f", 5, S_IFBLK},
+	{"g", 6, S_IFBLK},
+	{"wd", 7, S_IFBLK},
+#if defined(_FIRMWARE_NEEDS_FDISK)
+	{"q", 16, S_IFBLK},
+	{"r", 17, S_IFBLK},
+	{"s", 18, S_IFBLK},
+	{"t", 19, S_IFBLK},
+	{"u", 20, S_IFBLK},
+#endif			/* defined(_FIRMWARE_NEEDS_FDISK) */
+	{"a,raw", 0, S_IFCHR},
+	{"b,raw", 1, S_IFCHR},
+	{"c,raw", 2, S_IFCHR},
+	{"d,raw", 3, S_IFCHR},
+	{"e,raw", 4, S_IFCHR},
+	{"f,raw", 5, S_IFCHR},
+	{"g,raw", 6, S_IFCHR},
+	{"wd,raw", 7, S_IFCHR},
+#if defined(_FIRMWARE_NEEDS_FDISK)
+	{"q,raw", 16, S_IFCHR},
+	{"r,raw", 17, S_IFCHR},
+	{"s,raw", 18, S_IFCHR},
+	{"t,raw", 19, S_IFCHR},
+	{"u,raw", 20, S_IFCHR},
+#endif			/* defined(_FIRMWARE_NEEDS_FDISK) */
+	{0}
+};
+
+
+
+extern struct mod_ops mod_miscops;
+
+/*
+ * Global buffer and mutex for debug logging
+ */
+static char	cmlb_log_buffer[1024];
+static kmutex_t	cmlb_log_mutex;
+
+
+struct cmlb_lun *cmlb_debug_un = NULL;
+uint_t cmlb_level_mask = 0x0;
+
+int cmlb_rot_delay = 4;	/* default rotational delay */
+
+static struct modlmisc modlmisc = {
+	&mod_miscops,   /* Type of module */
+	"Common Labeling module %I%"
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlmisc, NULL
+};
+
+/* Local function prototypes */
+static dev_t cmlb_make_device(struct cmlb_lun *un);
+static int cmlb_validate_geometry(struct cmlb_lun *un, int forcerevalid);
+static void cmlb_resync_geom_caches(struct cmlb_lun *un, diskaddr_t capacity);
+static int cmlb_read_fdisk(struct cmlb_lun *un, diskaddr_t capacity);
+static void cmlb_swap_efi_gpt(efi_gpt_t *e);
+static void cmlb_swap_efi_gpe(int nparts, efi_gpe_t *p);
+static int cmlb_validate_efi(efi_gpt_t *labp);
+static int cmlb_use_efi(struct cmlb_lun *un, diskaddr_t capacity);
+static void cmlb_build_default_label(struct cmlb_lun *un);
+static int  cmlb_uselabel(struct cmlb_lun *un,  struct dk_label *l);
+static void cmlb_build_user_vtoc(struct cmlb_lun *un, struct vtoc *user_vtoc);
+static int cmlb_build_label_vtoc(struct cmlb_lun *un, struct vtoc *user_vtoc);
+static int cmlb_write_label(struct cmlb_lun *un);
+static int cmlb_set_vtoc(struct cmlb_lun *un, struct dk_label *dkl);
+static void cmlb_clear_efi(struct cmlb_lun *un);
+static void cmlb_clear_vtoc(struct cmlb_lun *un);
+static void cmlb_setup_default_geometry(struct cmlb_lun *un);
+static int cmlb_create_minor_nodes(struct cmlb_lun *un);
+static int cmlb_check_update_blockcount(struct cmlb_lun *un);
+
+#if defined(__i386) || defined(__amd64)
+static int cmlb_update_fdisk_and_vtoc(struct cmlb_lun *un);
+#endif
+
+#if defined(_FIRMWARE_NEEDS_FDISK)
+static int  cmlb_has_max_chs_vals(struct ipart *fdp);
+#endif
+
+#if defined(_SUNOS_VTOC_16)
+static void cmlb_convert_geometry(diskaddr_t capacity, struct dk_geom *un_g);
+#endif
+
+static int cmlb_dkio_get_geometry(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_set_geometry(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_get_partition(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_set_partition(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_get_efi(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_set_efi(struct cmlb_lun *un, dev_t dev, caddr_t arg,
+    int flag);
+static int cmlb_dkio_get_vtoc(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_set_vtoc(struct cmlb_lun *un, dev_t dev, caddr_t arg,
+    int flag);
+static int cmlb_dkio_get_mboot(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_set_mboot(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_partition(struct cmlb_lun *un, caddr_t arg, int flag);
+
+#if defined(__i386) || defined(__amd64)
+static int cmlb_dkio_get_virtgeom(struct cmlb_lun *un, caddr_t arg, int flag);
+static int cmlb_dkio_get_phygeom(struct cmlb_lun *un, caddr_t  arg, int flag);
+static int cmlb_dkio_partinfo(struct cmlb_lun *un, dev_t dev, caddr_t arg,
+    int flag);
+#endif
+
+static void cmlb_dbg(uint_t comp, struct cmlb_lun *un, const char *fmt, ...);
+static void cmlb_v_log(dev_info_t *dev, char *label, uint_t level,
+    const char *fmt, va_list ap);
+static void cmlb_log(dev_info_t *dev, char *label, uint_t level,
+    const char *fmt, ...);
+
+int
+_init(void)
+{
+	mutex_init(&cmlb_log_mutex, NULL, MUTEX_DRIVER, NULL);
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int err;
+
+	if ((err = mod_remove(&modlinkage)) != 0) {
+		return (err);
+	}
+
+	mutex_destroy(&cmlb_log_mutex);
+	return (err);
+}
+
+/*
+ * cmlb_dbg is used for debugging to log additional info
+ * Level of output is controlled via cmlb_level_mask setting.
+ */
+static void
+cmlb_dbg(uint_t comp, struct cmlb_lun *un, const char *fmt, ...)
+{
+	va_list		ap;
+	dev_info_t	*dev;
+	uint_t		level_mask = 0;
+
+	ASSERT(un != NULL);
+	dev = CMLB_DEVINFO(un);
+	ASSERT(dev != NULL);
+	/*
+	 * Filter messages based on the global component and level masks,
+	 * also print if un matches the value of cmlb_debug_un, or if
+	 * cmlb_debug_un is set to NULL.
+	 */
+	if (comp & CMLB_TRACE)
+		level_mask |= CMLB_LOGMASK_TRACE;
+
+	if (comp & CMLB_INFO)
+		level_mask |= CMLB_LOGMASK_INFO;
+
+	if (comp & CMLB_ERROR)
+		level_mask |= CMLB_LOGMASK_ERROR;
+
+	if ((cmlb_level_mask & level_mask) &&
+	    ((cmlb_debug_un == NULL) || (cmlb_debug_un == un))) {
+		va_start(ap, fmt);
+		cmlb_v_log(dev, CMLB_LABEL(un), CE_CONT, fmt, ap);
+		va_end(ap);
+	}
+}
+
+/*
+ * cmlb_log is basically a duplicate of scsi_log. It is redefined here
+ * so that this module does not depend on scsi module.
+ */
+static void
+cmlb_log(dev_info_t *dev, char *label, uint_t level, const char *fmt, ...)
+{
+	va_list		ap;
+
+	va_start(ap, fmt);
+	cmlb_v_log(dev, label, level, fmt, ap);
+	va_end(ap);
+}
+
+static void
+cmlb_v_log(dev_info_t *dev, char *label, uint_t level, const char *fmt,
+    va_list ap)
+{
+	static char 	name[256];
+	int 		log_only = 0;
+	int 		boot_only = 0;
+	int 		console_only = 0;
+
+	mutex_enter(&cmlb_log_mutex);
+
+	if (dev) {
+		if (level == CE_PANIC || level == CE_WARN ||
+		    level == CE_NOTE) {
+			(void) sprintf(name, "%s (%s%d):\n",
+			    ddi_pathname(dev, cmlb_log_buffer),
+			    label, ddi_get_instance(dev));
+		} else {
+			name[0] = '\0';
+		}
+	} else {
+		(void) sprintf(name, "%s:", label);
+	}
+
+	(void) vsprintf(cmlb_log_buffer, fmt, ap);
+
+	switch (cmlb_log_buffer[0]) {
+	case '!':
+		log_only = 1;
+		break;
+	case '?':
+		boot_only = 1;
+		break;
+	case '^':
+		console_only = 1;
+		break;
+	}
+
+	switch (level) {
+	case CE_NOTE:
+		level = CE_CONT;
+		/* FALLTHROUGH */
+	case CE_CONT:
+	case CE_WARN:
+	case CE_PANIC:
+		if (boot_only) {
+			cmn_err(level, "?%s\t%s", name, &cmlb_log_buffer[1]);
+		} else if (console_only) {
+			cmn_err(level, "^%s\t%s", name, &cmlb_log_buffer[1]);
+		} else if (log_only) {
+			cmn_err(level, "!%s\t%s", name, &cmlb_log_buffer[1]);
+		} else {
+			cmn_err(level, "%s\t%s", name, cmlb_log_buffer);
+		}
+		break;
+	case CE_IGNORE:
+		break;
+	default:
+		cmn_err(CE_CONT, "^DEBUG: %s\t%s", name, cmlb_log_buffer);
+		break;
+	}
+	mutex_exit(&cmlb_log_mutex);
+}
+
+
+/*
+ * cmlb_alloc_handle:
+ *
+ *	Allocates a handle.
+ *
+ * Arguments:
+ *	cmlbhandlep	pointer to handle
+ *
+ * Notes:
+ *	Allocates a handle and stores the allocated handle in the area
+ *	pointed to by cmlbhandlep
+ *
+ * Context:
+ *	Kernel thread only (can sleep).
+ */
+void
+cmlb_alloc_handle(cmlb_handle_t *cmlbhandlep)
+{
+	struct cmlb_lun 	*un;
+
+	un = kmem_zalloc(sizeof (struct cmlb_lun), KM_SLEEP);
+	ASSERT(cmlbhandlep != NULL);
+
+	un->un_state = CMLB_INITED;
+	un->un_def_labeltype = CMLB_LABEL_UNDEF;
+	mutex_init(CMLB_MUTEX(un), NULL, MUTEX_DRIVER, NULL);
+
+	*cmlbhandlep = (cmlb_handle_t)(un);
+}
+
+/*
+ * cmlb_free_handle
+ *
+ *	Frees handle.
+ *
+ * Arguments:
+ *	cmlbhandlep	pointer to handle
+ */
+void
+cmlb_free_handle(cmlb_handle_t *cmlbhandlep)
+{
+	struct cmlb_lun 	*un;
+
+	un = (struct cmlb_lun *)*cmlbhandlep;
+	if (un != NULL) {
+		mutex_destroy(CMLB_MUTEX(un));
+		kmem_free(un, sizeof (struct cmlb_lun));
+	}
+
+}
+
+/*
+ * cmlb_attach:
+ *
+ *	Attach handle to device, create minor nodes for device.
+ *
+ * Arguments:
+ * 	devi		pointer to device's dev_info structure.
+ * 	tgopsp		pointer to array of functions cmlb can use to callback
+ *			to target driver.
+ *
+ *	device_type	Peripheral device type as defined in
+ *			scsi/generic/inquiry.h
+ *
+ *	is_removable	whether or not device is removable.
+ *			0 non-removable, 1 removable.
+ *
+ *	node_type	minor node type (as used by ddi_create_minor_node)
+ *
+ *	alter_behavior
+ *			bit flags:
+ *
+ *			CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT: create
+ *			an alternate slice for the default label, if
+ *			device type is DTYPE_DIRECT an architectures default
+ *			label type is VTOC16.
+ *			Otherwise alternate slice will no be created.
+ *
+ *
+ *			CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8: report a default
+ *			geometry and label for DKIOCGGEOM and DKIOCGVTOC
+ *			on architecture with VTOC8 label types.
+ *
+ *
+ *	cmlbhandle	cmlb handle associated with device
+ *
+ * Notes:
+ *	Assumes a default label based on capacity for non-removable devices.
+ *	If capacity > 1TB, EFI is assumed otherwise VTOC (default VTOC
+ *	for the architecture).
+ *
+ *	For removable devices, default label type is assumed to be VTOC
+ *	type. Create minor nodes based on a default label type.
+ *	Label on the media is not validated.
+ *	minor number consists of:
+ *		if _SUNOS_VTOC_8 is defined
+ *			lowest 3 bits is taken as partition number
+ *			the rest is instance number
+ *		if _SUNOS_VTOC_16 is defined
+ *			lowest 6 bits is taken as partition number
+ *			the rest is instance number
+ *
+ *
+ * Return values:
+ *	0 	Success
+ * 	ENXIO 	creating minor nodes failed.
+ */
+int
+cmlb_attach(dev_info_t *devi, cmlb_tg_ops_t *tgopsp, int device_type,
+    int is_removable, char *node_type, int alter_behavior,
+    cmlb_handle_t cmlbhandle)
+{
+
+	struct cmlb_lun	*un = (struct cmlb_lun *)cmlbhandle;
+	diskaddr_t	cap;
+	int		status;
+
+	mutex_enter(CMLB_MUTEX(un));
+
+	CMLB_DEVINFO(un) = devi;
+	un->cmlb_tg_ops = tgopsp;
+	un->un_device_type = device_type;
+	un->un_is_removable = is_removable;
+	un->un_node_type = node_type;
+	un->un_sys_blocksize = DEV_BSIZE;
+	un->un_f_geometry_is_valid = FALSE;
+	un->un_def_labeltype = CMLB_LABEL_VTOC;
+	un->un_alter_behavior = alter_behavior;
+
+	if (is_removable != 0) {
+		mutex_exit(CMLB_MUTEX(un));
+		status = DK_TG_GETCAP(un, &cap);
+		mutex_enter(CMLB_MUTEX(un));
+		if (status == 0 && cap > DK_MAX_BLOCKS) {
+			/* set default EFI if > 1TB */
+			un->un_def_labeltype = CMLB_LABEL_EFI;
+		}
+	}
+
+	/* create minor nodes based on default label type */
+	un->un_last_labeltype = CMLB_LABEL_UNDEF;
+	un->un_cur_labeltype = CMLB_LABEL_UNDEF;
+
+	if (cmlb_create_minor_nodes(un) != 0) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (ENXIO);
+	}
+
+	un->un_state = CMLB_ATTACHED;
+
+	mutex_exit(CMLB_MUTEX(un));
+	return (0);
+}
+
+/*
+ * cmlb_detach:
+ *
+ * Invalidate in-core labeling data and remove all minor nodes for
+ * the device associate with handle.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ */
+void
+cmlb_detach(cmlb_handle_t cmlbhandle)
+{
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+
+	mutex_enter(CMLB_MUTEX(un));
+	un->un_def_labeltype = CMLB_LABEL_UNDEF;
+	un->un_f_geometry_is_valid = FALSE;
+	ddi_remove_minor_node(CMLB_DEVINFO(un), NULL);
+	un->un_state = CMLB_INITED;
+	mutex_exit(CMLB_MUTEX(un));
+}
+
+/*
+ * cmlb_validate:
+ *
+ *	Validates label.
+ *
+ * Arguments
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ * Notes:
+ *	If new label type is different from the current, adjust minor nodes
+ *	accordingly.
+ *
+ * Return values:
+ *	0		success
+ *			Note: having fdisk but no solaris partition is assumed
+ *			success.
+ *
+ *	ENOMEM		memory allocation failed
+ *	EIO		i/o errors during read or get capacity
+ * 	EACCESS		reservation conflicts
+ * 	EINVAL		label was corrupt, or no default label was assumed
+ *	ENXIO		invalid handle
+ */
+int
+cmlb_validate(cmlb_handle_t cmlbhandle)
+{
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+	int 		rval;
+	int  		ret = 0;
+
+	/*
+	 * Temp work-around checking un for NULL since there is a bug
+	 * in sd_detach calling this routine from taskq_dispatch
+	 * inited function.
+	 */
+	if (un == NULL)
+		return (ENXIO);
+
+	ASSERT(un != NULL);
+
+	mutex_enter(CMLB_MUTEX(un));
+	if (un->un_state < CMLB_ATTACHED) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (ENXIO);
+	}
+
+	rval = cmlb_validate_geometry((struct cmlb_lun *)cmlbhandle, 0);
+
+	if (rval == ENOTSUP) {
+		if (un->un_f_geometry_is_valid == TRUE) {
+			un->un_cur_labeltype = CMLB_LABEL_EFI;
+			ret = 0;
+		} else {
+			ret = EINVAL;
+		}
+	} else {
+		ret = rval;
+		if (ret == 0)
+			un->un_cur_labeltype = CMLB_LABEL_VTOC;
+	}
+
+	if (ret == 0)
+		(void) cmlb_create_minor_nodes(un);
+
+	mutex_exit(CMLB_MUTEX(un));
+	return (ret);
+}
+
+/*
+ * cmlb_invalidate:
+ *	Invalidate in core label data
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ */
+void
+cmlb_invalidate(cmlb_handle_t cmlbhandle)
+{
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+
+	if (un == NULL)
+		return;
+
+	mutex_enter(CMLB_MUTEX(un));
+	un->un_f_geometry_is_valid = FALSE;
+	mutex_exit(CMLB_MUTEX(un));
+}
+
+/*
+ * cmlb_close:
+ *
+ * Close the device, revert to a default label minor node for the device,
+ * if it is removable.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ * Return values:
+ *	0	Success
+ * 	ENXIO	Re-creating minor node failed.
+ */
+int
+cmlb_close(cmlb_handle_t cmlbhandle)
+{
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+
+	mutex_enter(CMLB_MUTEX(un));
+	un->un_f_geometry_is_valid = FALSE;
+
+	/* revert to default minor node for this device */
+	if (ISREMOVABLE(un)) {
+		un->un_cur_labeltype = CMLB_LABEL_UNDEF;
+		(void) cmlb_create_minor_nodes(un);
+	}
+
+	mutex_exit(CMLB_MUTEX(un));
+	return (0);
+}
+
+/*
+ * cmlb_get_devid_block:
+ *	 get the block number where device id is stored.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *	devidblockp	pointer to block number.
+ *
+ * Notes:
+ *	It stores the block number of device id in the area pointed to
+ *	by devidblockp.
+ * 	with the block number of device id.
+ *
+ * Return values:
+ *	0	success
+ *	EINVAL 	device id does not apply to current label type.
+ */
+int
+cmlb_get_devid_block(cmlb_handle_t cmlbhandle, diskaddr_t *devidblockp)
+{
+	daddr_t			spc, blk, head, cyl;
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+
+	mutex_enter(CMLB_MUTEX(un));
+	if (un->un_state < CMLB_ATTACHED) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EINVAL);
+	}
+
+	if (un->un_blockcount <= DK_MAX_BLOCKS) {
+		/* this geometry doesn't allow us to write a devid */
+		if (un->un_g.dkg_acyl < 2) {
+			mutex_exit(CMLB_MUTEX(un));
+			return (EINVAL);
+		}
+
+		/*
+		 * Subtract 2 guarantees that the next to last cylinder
+		 * is used
+		 */
+		cyl  = un->un_g.dkg_ncyl  + un->un_g.dkg_acyl - 2;
+		spc  = un->un_g.dkg_nhead * un->un_g.dkg_nsect;
+		head = un->un_g.dkg_nhead - 1;
+		blk  = (cyl * (spc - un->un_g.dkg_apc)) +
+		    (head * un->un_g.dkg_nsect) + 1;
+	} else {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EINVAL);
+	}
+	*devidblockp = blk;
+	mutex_exit(CMLB_MUTEX(un));
+	return (0);
+}
+
+/*
+ * cmlb_partinfo:
+ *	Get partition info for specified partition number.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *	part		partition number
+ *	nblocksp	pointer to number of blocks
+ *	startblockp	pointer to starting block
+ *	partnamep	pointer to name of partition
+ *	tagp		pointer to tag info
+ *
+ *
+ * Notes:
+ *	If in-core label is not valid, this functions tries to revalidate
+ *	the label. If label is valid, it stores the total number of blocks
+ *	in this partition in the area pointed to by nblocksp, starting
+ *	block number in area pointed to by startblockp,  pointer to partition
+ *	name in area pointed to by partnamep, and tag value in area
+ *	pointed by tagp.
+ *	For EFI labels, tag value will be set to 0.
+ *
+ *	For all nblocksp, startblockp and partnamep, tagp, a value of NULL
+ *	indicates the corresponding info is not requested.
+ *
+ *
+ * Return values:
+ *	0	success
+ *	EINVAL  no valid label or requested partition number is invalid.
+ *
+ */
+int
+cmlb_partinfo(cmlb_handle_t cmlbhandle, int part, diskaddr_t *nblocksp,
+    diskaddr_t *startblockp, char **partnamep, uint16_t *tagp)
+{
+
+	struct cmlb_lun *un = (struct cmlb_lun *)cmlbhandle;
+	int rval;
+
+	ASSERT(un != NULL);
+	mutex_enter(CMLB_MUTEX(un));
+	if (un->un_state < CMLB_ATTACHED) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EINVAL);
+	}
+
+	if (part  < 0 || part >= MAXPART) {
+		rval = EINVAL;
+	} else {
+		(void) cmlb_validate_geometry((struct cmlb_lun *)un, 0);
+		if ((un->un_f_geometry_is_valid == FALSE) ||
+		    (part < NDKMAP && un->un_solaris_size == 0)) {
+			rval = EINVAL;
+		} else {
+			if (startblockp != NULL)
+				*startblockp = (diskaddr_t)un->un_offset[part];
+
+			if (nblocksp != NULL)
+				*nblocksp = (diskaddr_t)
+				    un->un_map[part].dkl_nblk;
+
+			if (tagp != NULL)
+				if (un->un_cur_labeltype == CMLB_LABEL_EFI)
+					*tagp = V_UNASSIGNED;
+				else
+					*tagp = un->un_vtoc.v_part[part].p_tag;
+			rval = 0;
+		}
+
+		/* consistent with behavior of sd for getting minor name */
+		if (partnamep != NULL)
+			*partnamep = dk_minor_data[part].name;
+
+	}
+
+	mutex_exit(CMLB_MUTEX(un));
+	return (rval);
+}
+
+/* ARGSUSED */
+int
+cmlb_ioctl(cmlb_handle_t cmlbhandle, dev_t dev, int cmd, intptr_t arg,
+    int flag, cred_t *cred_p, int *rval_p)
+{
+
+	int err;
+	struct cmlb_lun *un;
+
+	un = (struct cmlb_lun *)cmlbhandle;
+
+	ASSERT(un != NULL);
+
+	mutex_enter(CMLB_MUTEX(un));
+	if (un->un_state < CMLB_ATTACHED) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EIO);
+	}
+
+
+	if ((cmlb_check_update_blockcount(un) == 0) &&
+	    (un->un_blockcount > DK_MAX_BLOCKS)) {
+		switch (cmd) {
+		case DKIOCGAPART:
+		case DKIOCGGEOM:
+		case DKIOCSGEOM:
+		case DKIOCGVTOC:
+		case DKIOCSVTOC:
+		case DKIOCSAPART:
+		case DKIOCG_PHYGEOM:
+		case DKIOCG_VIRTGEOM:
+			mutex_exit(CMLB_MUTEX(un));
+			return (ENOTSUP);
+		}
+	}
+
+	switch (cmd) {
+		case DKIOCSVTOC:
+		case DKIOCSETEFI:
+		case DKIOCSMBOOT:
+			break;
+		default:
+			(void) cmlb_validate_geometry(un, 0);
+			if ((un->un_f_geometry_is_valid == TRUE) &&
+			    (un->un_solaris_size > 0)) {
+			/*
+			 * the "geometry_is_valid" flag could be true if we
+			 * have an fdisk table but no Solaris partition
+			 */
+			if (un->un_vtoc.v_sanity != VTOC_SANE) {
+				/* it is EFI, so return ENOTSUP for these */
+				switch (cmd) {
+				case DKIOCGAPART:
+				case DKIOCGGEOM:
+				case DKIOCGVTOC:
+				case DKIOCSVTOC:
+				case DKIOCSAPART:
+					mutex_exit(CMLB_MUTEX(un));
+					return (ENOTSUP);
+				}
+			}
+		}
+	}
+
+	mutex_exit(CMLB_MUTEX(un));
+
+	switch (cmd) {
+	case DKIOCGGEOM:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCGGEOM\n");
+		err = cmlb_dkio_get_geometry(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCSGEOM:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCSGEOM\n");
+		err = cmlb_dkio_set_geometry(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCGAPART:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCGAPART\n");
+		err = cmlb_dkio_get_partition(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCSAPART:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCSAPART\n");
+		err = cmlb_dkio_set_partition(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCGVTOC:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCGVTOC\n");
+		err = cmlb_dkio_get_vtoc(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCGETEFI:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCGETEFI\n");
+		err = cmlb_dkio_get_efi(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCPARTITION:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCPARTITION\n");
+		err = cmlb_dkio_partition(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCSVTOC:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCSVTOC\n");
+		err = cmlb_dkio_set_vtoc(un, dev, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCSETEFI:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCSETEFI\n");
+		err = cmlb_dkio_set_efi(un, dev, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCGMBOOT:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCGMBOOT\n");
+		err = cmlb_dkio_get_mboot(un, (caddr_t)arg, flag);
+		break;
+
+	case DKIOCSMBOOT:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCSMBOOT\n");
+		err = cmlb_dkio_set_mboot(un, (caddr_t)arg, flag);
+		break;
+	case DKIOCG_PHYGEOM:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCG_PHYGEOM\n");
+#if defined(__i386) || defined(__amd64)
+		err = cmlb_dkio_get_phygeom(un, (caddr_t)arg, flag);
+#else
+		err = ENOTTY;
+#endif
+		break;
+	case DKIOCG_VIRTGEOM:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCG_VIRTGEOM\n");
+#if defined(__i386) || defined(__amd64)
+		err = cmlb_dkio_get_virtgeom(un, (caddr_t)arg, flag);
+#else
+		err = ENOTTY;
+#endif
+		break;
+	case DKIOCPARTINFO:
+		cmlb_dbg(CMLB_TRACE, un, "DKIOCPARTINFO");
+#if defined(__i386) || defined(__amd64)
+		err = cmlb_dkio_partinfo(un, dev, (caddr_t)arg, flag);
+#else
+		err = ENOTTY;
+#endif
+		break;
+
+	default:
+		err = ENOTTY;
+
+	}
+	return (err);
+}
+
+dev_t
+cmlb_make_device(struct cmlb_lun *un)
+{
+	return (makedevice(ddi_name_to_major(ddi_get_name(CMLB_DEVINFO(un))),
+	    ddi_get_instance(CMLB_DEVINFO(un)) << CMLBUNIT_SHIFT));
+}
+
+/*
+ * Function: cmlb_check_update_blockcount
+ *
+ * Description: If current capacity value is invalid, obtains the
+ *		current capacity from target driver.
+ *
+ * Return Code: 0	success
+ *		EIO	failure
+ */
+static int
+cmlb_check_update_blockcount(struct cmlb_lun *un)
+{
+	int status;
+	diskaddr_t capacity;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	if (un->un_f_geometry_is_valid == FALSE)  {
+		mutex_exit(CMLB_MUTEX(un));
+		status = DK_TG_GETCAP(un, &capacity);
+		mutex_enter(CMLB_MUTEX(un));
+		if (status == 0 && capacity != 0) {
+			un->un_blockcount = capacity;
+			return (0);
+		} else
+			return (EIO);
+	} else
+		return (0);
+}
+
+/*
+ *    Function: cmlb_create_minor_nodes
+ *
+ * Description: Create or adjust the minor device nodes for the instance.
+ * 		Minor nodes are created based on default label type,
+ *		current label type and last label type we created
+ *		minor nodes based on.
+ *
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return Code: 0 success
+ *		ENXIO	failure.
+ *
+ *     Context: Kernel thread context
+ */
+static int
+cmlb_create_minor_nodes(struct cmlb_lun *un)
+{
+	struct driver_minor_data	*dmdp;
+	int				instance;
+	char				name[48];
+	cmlb_label_t			newlabeltype;
+
+	ASSERT(un != NULL);
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+
+	/* check the most common case */
+	if (un->un_cur_labeltype != CMLB_LABEL_UNDEF &&
+	    un->un_last_labeltype == un->un_cur_labeltype) {
+		/* do nothing */
+		return (0);
+	}
+
+	if (un->un_def_labeltype == CMLB_LABEL_UNDEF) {
+		/* we should never get here */
+		return (ENXIO);
+	}
+
+	if (un->un_last_labeltype == CMLB_LABEL_UNDEF) {
+		/* first time during attach */
+		newlabeltype = un->un_def_labeltype;
+
+		instance = ddi_get_instance(CMLB_DEVINFO(un));
+
+		/* Create all the minor nodes for this target. */
+		dmdp = (newlabeltype == CMLB_LABEL_EFI) ? dk_minor_data_efi :
+		    dk_minor_data;
+		while (dmdp->name != NULL) {
+
+			(void) sprintf(name, "%s", dmdp->name);
+
+			if (ddi_create_minor_node(CMLB_DEVINFO(un), name,
+			    dmdp->type,
+			    (instance << CMLBUNIT_SHIFT) | dmdp->minor,
+			    un->un_node_type, NULL) == DDI_FAILURE) {
+				/*
+				 * Clean up any nodes that may have been
+				 * created, in case this fails in the middle
+				 * of the loop.
+				 */
+				ddi_remove_minor_node(CMLB_DEVINFO(un), NULL);
+				return (ENXIO);
+			}
+			dmdp++;
+		}
+		un->un_last_labeltype = newlabeltype;
+		return (0);
+	}
+
+	/* Not first time  */
+	if (un->un_cur_labeltype == CMLB_LABEL_UNDEF) {
+		if (un->un_last_labeltype != un->un_def_labeltype) {
+			/* close time, revert to default. */
+			newlabeltype = un->un_def_labeltype;
+		} else {
+			/*
+			 * do nothing since the type for which we last created
+			 * nodes matches the default
+			 */
+			return (0);
+		}
+	} else {
+		if (un->un_cur_labeltype != un->un_last_labeltype) {
+			/* We are not closing, use current label type */
+			newlabeltype = un->un_cur_labeltype;
+		} else {
+			/*
+			 * do nothing since the type for which we last created
+			 * nodes matches the current label type
+			 */
+			return (0);
+		}
+	}
+
+	instance = ddi_get_instance(CMLB_DEVINFO(un));
+
+	/*
+	 * Currently we only fix up the s7 node when we are switching
+	 * label types from or to EFI. This is consistent with
+	 * current behavior of sd.
+	 */
+	if (newlabeltype == CMLB_LABEL_EFI &&
+	    un->un_last_labeltype != CMLB_LABEL_EFI) {
+		/* from vtoc to EFI */
+		ddi_remove_minor_node(CMLB_DEVINFO(un), "h");
+		ddi_remove_minor_node(CMLB_DEVINFO(un), "h,raw");
+		(void) ddi_create_minor_node(CMLB_DEVINFO(un), "wd",
+		    S_IFBLK, (instance << CMLBUNIT_SHIFT) | WD_NODE,
+		    un->un_node_type, NULL);
+		(void) ddi_create_minor_node(CMLB_DEVINFO(un), "wd,raw",
+		    S_IFCHR, (instance << CMLBUNIT_SHIFT) | WD_NODE,
+		    un->un_node_type, NULL);
+	} else {
+		/* from efi to vtoc */
+		ddi_remove_minor_node(CMLB_DEVINFO(un), "wd");
+		ddi_remove_minor_node(CMLB_DEVINFO(un), "wd,raw");
+		(void) ddi_create_minor_node(CMLB_DEVINFO(un), "h",
+		    S_IFBLK, (instance << CMLBUNIT_SHIFT) | WD_NODE,
+		    un->un_node_type, NULL);
+		(void) ddi_create_minor_node(CMLB_DEVINFO(un), "h,raw",
+		    S_IFCHR, (instance << CMLBUNIT_SHIFT) | WD_NODE,
+		    un->un_node_type, NULL);
+	}
+
+	un->un_last_labeltype = newlabeltype;
+	return (0);
+}
+
+/*
+ *    Function: cmlb_validate_geometry
+ *
+ * Description: Read the label from the disk (if present). Update the unit's
+ *		geometry and vtoc information from the data in the label.
+ *		Verify that the label is valid.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return Code: 0 - Successful completion
+ *		EINVAL  - Invalid value in un->un_tgt_blocksize or
+ *			  un->un_blockcount; or label on disk is corrupted
+ *			  or unreadable.
+ *		EACCES  - Reservation conflict at the device.
+ *		ENOMEM  - Resource allocation error
+ *		ENOTSUP - geometry not applicable
+ *
+ *     Context: Kernel thread only (can sleep).
+ */
+static int
+cmlb_validate_geometry(struct cmlb_lun *un, int forcerevalid)
+{
+	int		label_error = 0;
+	diskaddr_t	capacity;
+	int		count;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	if ((un->un_f_geometry_is_valid == TRUE) && (forcerevalid == 0)) {
+		if (un->un_cur_labeltype == CMLB_LABEL_EFI)
+			return (ENOTSUP);
+		return (0);
+	}
+
+	if (cmlb_check_update_blockcount(un) != 0)
+		return (EIO);
+
+	capacity = un->un_blockcount;
+
+#if defined(_SUNOS_VTOC_16)
+	/*
+	 * Set up the "whole disk" fdisk partition; this should always
+	 * exist, regardless of whether the disk contains an fdisk table
+	 * or vtoc.
+	 */
+	un->un_map[P0_RAW_DISK].dkl_cylno = 0;
+	/*
+	 * note if capacity > uint32_max we should be using efi,
+	 * and not use p0, so the truncation does not matter.
+	 */
+	un->un_map[P0_RAW_DISK].dkl_nblk  = capacity;
+#endif
+	/*
+	 * Refresh the logical and physical geometry caches.
+	 * (data from MODE SENSE format/rigid disk geometry pages,
+	 * and scsi_ifgetcap("geometry").
+	 */
+	cmlb_resync_geom_caches(un, capacity);
+
+	label_error = cmlb_use_efi(un, capacity);
+	if (label_error == 0) {
+
+		/* found a valid EFI label */
+		cmlb_dbg(CMLB_TRACE, un,
+		    "cmlb_validate_geometry: found EFI label\n");
+		/*
+		 * solaris_size and geometry_is_valid are set in
+		 * cmlb_use_efi
+		 */
+		return (ENOTSUP);
+	}
+
+	/* NO EFI label found */
+
+	if (capacity > DK_MAX_BLOCKS) {
+		if (label_error == ESRCH) {
+			/*
+			 * they've configured a LUN over 1TB, but used
+			 * format.dat to restrict format's view of the
+			 * capacity to be under 1TB
+			 */
+			/* i.e > 1Tb with a VTOC < 1TB */
+
+			cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_WARN,
+			    "is >1TB and has a VTOC label: use format(1M) to "
+			    "either decrease the");
+			cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_CONT,
+			    "size to be < 1TB or relabel the disk with an EFI "
+			    "label");
+		} else {
+			/* unlabeled disk over 1TB */
+			return (ENOTSUP);
+		}
+	}
+
+	label_error = 0;
+
+	/*
+	 * at this point it is either labeled with a VTOC or it is
+	 * under 1TB
+	 */
+
+	/*
+	 * Only DIRECT ACCESS devices will have Sun labels.
+	 * CD's supposedly have a Sun label, too
+	 */
+	if (un->un_device_type == DTYPE_DIRECT || ISREMOVABLE(un)) {
+		struct	dk_label *dkl;
+		offset_t label_addr;
+		int	rval;
+		size_t	buffer_size;
+
+		/*
+		 * Note: This will set up un->un_solaris_size and
+		 * un->un_solaris_offset.
+		 */
+		rval = cmlb_read_fdisk(un, capacity);
+		if (rval != 0) {
+			ASSERT(mutex_owned(CMLB_MUTEX(un)));
+			return (rval);
+		}
+
+		if (un->un_solaris_size <= DK_LABEL_LOC) {
+			/*
+			 * Found fdisk table but no Solaris partition entry,
+			 * so don't call cmlb_uselabel() and don't create
+			 * a default label.
+			 */
+			label_error = 0;
+			un->un_f_geometry_is_valid = TRUE;
+			goto no_solaris_partition;
+		}
+
+		label_addr = (daddr_t)(un->un_solaris_offset + DK_LABEL_LOC);
+
+		buffer_size = sizeof (struct dk_label);
+
+		cmlb_dbg(CMLB_TRACE, un, "cmlb_validate_geometry: "
+		    "label_addr: 0x%x allocation size: 0x%x\n",
+		    label_addr, buffer_size);
+
+		if ((dkl = kmem_zalloc(buffer_size, KM_NOSLEEP)) == NULL)
+			return (ENOMEM);
+
+		mutex_exit(CMLB_MUTEX(un));
+		rval = DK_TG_READ(un, dkl, label_addr, buffer_size);
+		mutex_enter(CMLB_MUTEX(un));
+
+		switch (rval) {
+		case 0:
+			/*
+			 * cmlb_uselabel will establish that the geometry
+			 * is valid.
+			 */
+			if (cmlb_uselabel(un,
+			    (struct dk_label *)(uintptr_t)dkl) !=
+			    CMLB_LABEL_IS_VALID) {
+				label_error = EINVAL;
+			} else
+				un->un_vtoc_label_is_from_media = 1;
+			break;
+		case EACCES:
+			label_error = EACCES;
+			break;
+		default:
+			label_error = EINVAL;
+			break;
+		}
+
+		kmem_free(dkl, buffer_size);
+	}
+
+	/*
+	 * If a valid label was not found, AND if no reservation conflict
+	 * was detected, then go ahead and create a default label (4069506).
+	 *
+	 * Note: currently, for VTOC_8 devices, the default label is created
+	 * for removables only.  For VTOC_16 devices, the default label will
+	 * be created for both removables and non-removables alike.
+	 * (see cmlb_build_default_label)
+	 */
+#if defined(_SUNOS_VTOC_8)
+	if (ISREMOVABLE(un) && (label_error != EACCES)) {
+#elif defined(_SUNOS_VTOC_16)
+	if (label_error != EACCES) {
+#endif
+		if (un->un_f_geometry_is_valid == FALSE) {
+			cmlb_build_default_label(un);
+		}
+		label_error = 0;
+	}
+
+no_solaris_partition:
+
+#if defined(_SUNOS_VTOC_16)
+	/*
+	 * If we have valid geometry, set up the remaining fdisk partitions.
+	 * Note that dkl_cylno is not used for the fdisk map entries, so
+	 * we set it to an entirely bogus value.
+	 */
+	for (count = 0; count < FD_NUMPART; count++) {
+		un->un_map[FDISK_P1 + count].dkl_cylno = -1;
+		un->un_map[FDISK_P1 + count].dkl_nblk =
+		    un->un_fmap[count].fmap_nblk;
+
+		un->un_offset[FDISK_P1 + count] =
+		    un->un_fmap[count].fmap_start;
+	}
+#endif
+
+	for (count = 0; count < NDKMAP; count++) {
+#if defined(_SUNOS_VTOC_8)
+		struct dk_map *lp  = &un->un_map[count];
+		un->un_offset[count] =
+		    un->un_g.dkg_nhead * un->un_g.dkg_nsect * lp->dkl_cylno;
+#elif defined(_SUNOS_VTOC_16)
+		struct dkl_partition *vp = &un->un_vtoc.v_part[count];
+
+		un->un_offset[count] = vp->p_start + un->un_solaris_offset;
+#else
+#error "No VTOC format defined."
+#endif
+	}
+
+	return (label_error);
+}
+
+#if defined(_SUNOS_VTOC_16)
+/*
+ * Macro: MAX_BLKS
+ *
+ *	This macro is used for table entries where we need to have the largest
+ *	possible sector value for that head & SPT (sectors per track)
+ *	combination.  Other entries for some smaller disk sizes are set by
+ *	convention to match those used by X86 BIOS usage.
+ */
+#define	MAX_BLKS(heads, spt)	UINT16_MAX * heads * spt, heads, spt
+
+/*
+ *    Function: cmlb_convert_geometry
+ *
+ * Description: Convert physical geometry into a dk_geom structure. In
+ *		other words, make sure we don't wrap 16-bit values.
+ *		e.g. converting from geom_cache to dk_geom
+ *
+ *     Context: Kernel thread only
+ */
+static void
+cmlb_convert_geometry(diskaddr_t capacity, struct dk_geom *un_g)
+{
+	int i;
+	static const struct chs_values {
+		uint_t max_cap;		/* Max Capacity for this HS. */
+		uint_t nhead;		/* Heads to use. */
+		uint_t nsect;		/* SPT to use. */
+	} CHS_values[] = {
+		{0x00200000,  64, 32},		/* 1GB or smaller disk. */
+		{0x01000000, 128, 32},		/* 8GB or smaller disk. */
+		{MAX_BLKS(255,  63)},		/* 502.02GB or smaller disk. */
+		{MAX_BLKS(255, 126)},		/* .98TB or smaller disk. */
+		{DK_MAX_BLOCKS, 255, 189}	/* Max size is just under 1TB */
+	};
+
+	/* Unlabeled SCSI floppy device */
+	if (capacity <= 0x1000) {
+		un_g->dkg_nhead = 2;
+		un_g->dkg_ncyl = 80;
+		un_g->dkg_nsect = capacity / (un_g->dkg_nhead * un_g->dkg_ncyl);
+		return;
+	}
+
+	/*
+	 * For all devices we calculate cylinders using the
+	 * heads and sectors we assign based on capacity of the
+	 * device.  The table is designed to be compatible with the
+	 * way other operating systems lay out fdisk tables for X86
+	 * and to insure that the cylinders never exceed 65535 to
+	 * prevent problems with X86 ioctls that report geometry.
+	 * We use SPT that are multiples of 63, since other OSes that
+	 * are not limited to 16-bits for cylinders stop at 63 SPT
+	 * we make do by using multiples of 63 SPT.
+	 *
+	 * Note than capacities greater than or equal to 1TB will simply
+	 * get the largest geometry from the table. This should be okay
+	 * since disks this large shouldn't be using CHS values anyway.
+	 */
+	for (i = 0; CHS_values[i].max_cap < capacity &&
+	    CHS_values[i].max_cap != DK_MAX_BLOCKS; i++)
+		;
+
+	un_g->dkg_nhead = CHS_values[i].nhead;
+	un_g->dkg_nsect = CHS_values[i].nsect;
+}
+#endif
+
+/*
+ *    Function: cmlb_resync_geom_caches
+ *
+ * Description: (Re)initialize both geometry caches: the virtual geometry
+ *            information is extracted from the HBA (the "geometry"
+ *            capability), and the physical geometry cache data is
+ *            generated by issuing MODE SENSE commands.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *		capacity - disk capacity in #blocks
+ *
+ *     Context: Kernel thread only (can sleep).
+ */
+static void
+cmlb_resync_geom_caches(struct cmlb_lun *un, diskaddr_t capacity)
+{
+	struct	cmlb_geom 	pgeom;
+	struct	cmlb_geom	lgeom;
+	struct 	cmlb_geom	*pgeomp = &pgeom;
+	unsigned short 		nhead;
+	unsigned short 		nsect;
+	int 			spc;
+	int			ret;
+
+	ASSERT(un != NULL);
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/*
+	 * Ask the controller for its logical geometry.
+	 * Note: if the HBA does not support scsi_ifgetcap("geometry"),
+	 * then the lgeom cache will be invalid.
+	 */
+	mutex_exit(CMLB_MUTEX(un));
+	bzero(&lgeom, sizeof (struct cmlb_geom));
+	ret = DK_TG_GETVIRTGEOM(un, &lgeom);
+	mutex_enter(CMLB_MUTEX(un));
+
+	bcopy(&lgeom, &un->un_lgeom, sizeof (un->un_lgeom));
+
+	/*
+	 * Initialize the pgeom cache from lgeom, so that if MODE SENSE
+	 * doesn't work, DKIOCG_PHYSGEOM can return reasonable values.
+	 */
+	if (ret != 0 || un->un_lgeom.g_nsect == 0 ||
+	    un->un_lgeom.g_nhead == 0) {
+		/*
+		 * Note: Perhaps this needs to be more adaptive? The rationale
+		 * is that, if there's no HBA geometry from the HBA driver, any
+		 * guess is good, since this is the physical geometry. If MODE
+		 * SENSE fails this gives a max cylinder size for non-LBA access
+		 */
+		nhead = 255;
+		nsect = 63;
+	} else {
+		nhead = un->un_lgeom.g_nhead;
+		nsect = un->un_lgeom.g_nsect;
+	}
+
+	if (ISCD(un)) {
+		pgeomp->g_nhead = 1;
+		pgeomp->g_nsect = nsect * nhead;
+	} else {
+		pgeomp->g_nhead = nhead;
+		pgeomp->g_nsect = nsect;
+	}
+
+	spc = pgeomp->g_nhead * pgeomp->g_nsect;
+	pgeomp->g_capacity = capacity;
+	pgeomp->g_ncyl = pgeomp->g_capacity / spc;
+	pgeomp->g_acyl = 0;
+
+	/*
+	 * Retrieve fresh geometry data from the hardware, stash it
+	 * here temporarily before we rebuild the incore label.
+	 *
+	 * We want to use the MODE SENSE commands to derive the
+	 * physical geometry of the device, but if either command
+	 * fails, the logical geometry is used as the fallback for
+	 * disk label geometry.
+	 */
+
+	mutex_exit(CMLB_MUTEX(un));
+	(void) DK_TG_GETPHYGEOM(un,  pgeomp);
+	mutex_enter(CMLB_MUTEX(un));
+
+	/*
+	 * Now update the real copy while holding the mutex. This
+	 * way the global copy is never in an inconsistent state.
+	 */
+	bcopy(pgeomp, &un->un_pgeom,  sizeof (un->un_pgeom));
+
+	cmlb_dbg(CMLB_INFO, un, "cmlb_resync_geom_caches: "
+	    "(cached from lgeom)\n");
+	cmlb_dbg(CMLB_INFO,  un,
+	    "   ncyl: %ld; acyl: %d; nhead: %d; nsect: %d\n",
+	    un->un_pgeom.g_ncyl, un->un_pgeom.g_acyl,
+	    un->un_pgeom.g_nhead, un->un_pgeom.g_nsect);
+	cmlb_dbg(CMLB_INFO,  un, "   lbasize: %d; capacity: %ld; "
+	    "intrlv: %d; rpm: %d\n", un->un_pgeom.g_secsize,
+	    un->un_pgeom.g_capacity, un->un_pgeom.g_intrlv,
+	    un->un_pgeom.g_rpm);
+}
+
+
+/*
+ *    Function: cmlb_read_fdisk
+ *
+ * Description: utility routine to read the fdisk table.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return Code: 0 for success (includes not reading for no_fdisk_present case
+ *		errnos from tg_rw if failed to read the first block.
+ *
+ *     Context: Kernel thread only (can sleep).
+ */
+/* ARGSUSED */
+static int
+cmlb_read_fdisk(struct cmlb_lun *un, diskaddr_t capacity)
+{
+#if defined(_NO_FDISK_PRESENT)
+
+	un->un_solaris_offset = 0;
+	un->un_solaris_size = capacity;
+	bzero(un->un_fmap, sizeof (struct fmap) * FD_NUMPART);
+	return (0);
+
+#elif defined(_FIRMWARE_NEEDS_FDISK)
+
+	struct ipart	*fdp;
+	struct mboot	*mbp;
+	struct ipart	fdisk[FD_NUMPART];
+	int		i;
+	char		sigbuf[2];
+	caddr_t		bufp;
+	int		uidx;
+	int 		rval;
+	int		lba = 0;
+	uint_t		solaris_offset;	/* offset to solaris part. */
+	daddr_t		solaris_size;	/* size of solaris partition */
+	uint32_t	blocksize;
+
+	ASSERT(un != NULL);
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/*
+	 * Start off assuming no fdisk table
+	 */
+	solaris_offset = 0;
+	solaris_size   = capacity;
+
+	blocksize = 512;
+
+	bufp = kmem_zalloc(blocksize, KM_SLEEP);
+
+	mutex_exit(CMLB_MUTEX(un));
+	rval = DK_TG_READ(un,  bufp, 0, blocksize);
+	mutex_enter(CMLB_MUTEX(un));
+
+	if (rval != 0) {
+		cmlb_dbg(CMLB_ERROR,  un,
+		    "cmlb_read_fdisk: fdisk read err\n");
+		kmem_free(bufp, blocksize);
+		return (rval);
+	}
+
+	mbp = (struct mboot *)bufp;
+
+	/*
+	 * The fdisk table does not begin on a 4-byte boundary within the
+	 * master boot record, so we copy it to an aligned structure to avoid
+	 * alignment exceptions on some processors.
+	 */
+	bcopy(&mbp->parts[0], fdisk, sizeof (fdisk));
+
+	/*
+	 * Check for lba support before verifying sig; sig might not be
+	 * there, say on a blank disk, but the max_chs mark may still
+	 * be present.
+	 *
+	 * Note: LBA support and BEFs are an x86-only concept but this
+	 * code should work OK on SPARC as well.
+	 */
+
+	/*
+	 * First, check for lba-access-ok on root node (or prom root node)
+	 * if present there, don't need to search fdisk table.
+	 */
+	if (ddi_getprop(DDI_DEV_T_ANY, ddi_root_node(), 0,
+	    "lba-access-ok", 0) != 0) {
+		/* All drives do LBA; don't search fdisk table */
+		lba = 1;
+	} else {
+		/* Okay, look for mark in fdisk table */
+		for (fdp = fdisk, i = 0; i < FD_NUMPART; i++, fdp++) {
+			/* accumulate "lba" value from all partitions */
+			lba = (lba || cmlb_has_max_chs_vals(fdp));
+		}
+	}
+
+	/*
+	 * Next, look for 'no-bef-lba-access' prop on parent.
+	 * Its presence means the realmode driver doesn't support
+	 * LBA, so the target driver shouldn't advertise it as ok.
+	 * This should be a temporary condition; one day all
+	 * BEFs should support the LBA access functions.
+	 */
+	if ((lba != 0) && (ddi_getprop(DDI_DEV_T_ANY,
+	    ddi_get_parent(CMLB_DEVINFO(un)), DDI_PROP_DONTPASS,
+	    "no-bef-lba-access", 0) != 0)) {
+		/* BEF doesn't support LBA; don't advertise it as ok */
+		lba = 0;
+	}
+
+	if (lba != 0) {
+		dev_t dev = cmlb_make_device(un);
+
+		if (ddi_getprop(dev, CMLB_DEVINFO(un), DDI_PROP_DONTPASS,
+		    "lba-access-ok", 0) == 0) {
+			/* not found; create it */
+			if (ddi_prop_create(dev, CMLB_DEVINFO(un), 0,
+			    "lba-access-ok", (caddr_t)NULL, 0) !=
+			    DDI_PROP_SUCCESS) {
+				cmlb_dbg(CMLB_ERROR,  un,
+				    "cmlb_read_fdisk: Can't create lba "
+				    "property for instance %d\n",
+				    ddi_get_instance(CMLB_DEVINFO(un)));
+			}
+		}
+	}
+
+	bcopy(&mbp->signature, sigbuf, sizeof (sigbuf));
+
+	/*
+	 * Endian-independent signature check
+	 */
+	if (((sigbuf[1] & 0xFF) != ((MBB_MAGIC >> 8) & 0xFF)) ||
+	    (sigbuf[0] != (MBB_MAGIC & 0xFF))) {
+		cmlb_dbg(CMLB_ERROR,  un,
+		    "cmlb_read_fdisk: no fdisk\n");
+		bzero(un->un_fmap, sizeof (struct fmap) * FD_NUMPART);
+		goto done;
+	}
+
+#ifdef CMLBDEBUG
+	if (cmlb_level_mask & SD_LOGMASK_INFO) {
+		fdp = fdisk;
+		cmlb_dbg(CMLB_INFO,  un, "cmlb_read_fdisk:\n");
+		cmlb_dbg(CMLB_INFO,  un, "         relsect    "
+		    "numsect         sysid       bootid\n");
+		for (i = 0; i < FD_NUMPART; i++, fdp++) {
+			cmlb_dbg(CMLB_INFO,  un,
+			    "    %d:  %8d   %8d     0x%08x     0x%08x\n",
+			    i, fdp->relsect, fdp->numsect,
+			    fdp->systid, fdp->bootid);
+		}
+	}
+#endif
+
+	/*
+	 * Try to find the unix partition
+	 */
+	uidx = -1;
+	solaris_offset = 0;
+	solaris_size   = 0;
+
+	for (fdp = fdisk, i = 0; i < FD_NUMPART; i++, fdp++) {
+		int	relsect;
+		int	numsect;
+
+		if (fdp->numsect == 0) {
+			un->un_fmap[i].fmap_start = 0;
+			un->un_fmap[i].fmap_nblk  = 0;
+			continue;
+		}
+
+		/*
+		 * Data in the fdisk table is little-endian.
+		 */
+		relsect = LE_32(fdp->relsect);
+		numsect = LE_32(fdp->numsect);
+
+		un->un_fmap[i].fmap_start = relsect;
+		un->un_fmap[i].fmap_nblk  = numsect;
+
+		if (fdp->systid != SUNIXOS &&
+		    fdp->systid != SUNIXOS2 &&
+		    fdp->systid != EFI_PMBR) {
+			continue;
+		}
+
+		/*
+		 * use the last active solaris partition id found
+		 * (there should only be 1 active partition id)
+		 *
+		 * if there are no active solaris partition id
+		 * then use the first inactive solaris partition id
+		 */
+		if ((uidx == -1) || (fdp->bootid == ACTIVE)) {
+			uidx = i;
+			solaris_offset = relsect;
+			solaris_size   = numsect;
+		}
+	}
+
+	cmlb_dbg(CMLB_INFO,  un, "fdisk 0x%x 0x%lx",
+	    un->un_solaris_offset, un->un_solaris_size);
+done:
+
+	/*
+	 * Clear the VTOC info, only if the Solaris partition entry
+	 * has moved, changed size, been deleted, or if the size of
+	 * the partition is too small to even fit the label sector.
+	 */
+	if ((un->un_solaris_offset != solaris_offset) ||
+	    (un->un_solaris_size != solaris_size) ||
+	    solaris_size <= DK_LABEL_LOC) {
+		cmlb_dbg(CMLB_INFO,  un, "fdisk moved 0x%x 0x%lx",
+			solaris_offset, solaris_size);
+		bzero(&un->un_g, sizeof (struct dk_geom));
+		bzero(&un->un_vtoc, sizeof (struct dk_vtoc));
+		bzero(&un->un_map, NDKMAP * (sizeof (struct dk_map)));
+		un->un_f_geometry_is_valid = FALSE;
+	}
+	un->un_solaris_offset = solaris_offset;
+	un->un_solaris_size = solaris_size;
+	kmem_free(bufp, blocksize);
+	return (rval);
+
+#else	/* #elif defined(_FIRMWARE_NEEDS_FDISK) */
+#error "fdisk table presence undetermined for this platform."
+#endif	/* #if defined(_NO_FDISK_PRESENT) */
+}
+
+static void
+cmlb_swap_efi_gpt(efi_gpt_t *e)
+{
+	_NOTE(ASSUMING_PROTECTED(*e))
+	e->efi_gpt_Signature = LE_64(e->efi_gpt_Signature);
+	e->efi_gpt_Revision = LE_32(e->efi_gpt_Revision);
+	e->efi_gpt_HeaderSize = LE_32(e->efi_gpt_HeaderSize);
+	e->efi_gpt_HeaderCRC32 = LE_32(e->efi_gpt_HeaderCRC32);
+	e->efi_gpt_MyLBA = LE_64(e->efi_gpt_MyLBA);
+	e->efi_gpt_AlternateLBA = LE_64(e->efi_gpt_AlternateLBA);
+	e->efi_gpt_FirstUsableLBA = LE_64(e->efi_gpt_FirstUsableLBA);
+	e->efi_gpt_LastUsableLBA = LE_64(e->efi_gpt_LastUsableLBA);
+	UUID_LE_CONVERT(e->efi_gpt_DiskGUID, e->efi_gpt_DiskGUID);
+	e->efi_gpt_PartitionEntryLBA = LE_64(e->efi_gpt_PartitionEntryLBA);
+	e->efi_gpt_NumberOfPartitionEntries =
+	    LE_32(e->efi_gpt_NumberOfPartitionEntries);
+	e->efi_gpt_SizeOfPartitionEntry =
+	    LE_32(e->efi_gpt_SizeOfPartitionEntry);
+	e->efi_gpt_PartitionEntryArrayCRC32 =
+	    LE_32(e->efi_gpt_PartitionEntryArrayCRC32);
+}
+
+static void
+cmlb_swap_efi_gpe(int nparts, efi_gpe_t *p)
+{
+	int i;
+
+	_NOTE(ASSUMING_PROTECTED(*p))
+	for (i = 0; i < nparts; i++) {
+		UUID_LE_CONVERT(p[i].efi_gpe_PartitionTypeGUID,
+		    p[i].efi_gpe_PartitionTypeGUID);
+		p[i].efi_gpe_StartingLBA = LE_64(p[i].efi_gpe_StartingLBA);
+		p[i].efi_gpe_EndingLBA = LE_64(p[i].efi_gpe_EndingLBA);
+		/* PartitionAttrs */
+	}
+}
+
+static int
+cmlb_validate_efi(efi_gpt_t *labp)
+{
+	if (labp->efi_gpt_Signature != EFI_SIGNATURE)
+		return (EINVAL);
+	/* at least 96 bytes in this version of the spec. */
+	if (sizeof (efi_gpt_t) - sizeof (labp->efi_gpt_Reserved2) >
+	    labp->efi_gpt_HeaderSize)
+		return (EINVAL);
+	/* this should be 128 bytes */
+	if (labp->efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t))
+		return (EINVAL);
+	return (0);
+}
+
+static int
+cmlb_use_efi(struct cmlb_lun *un, diskaddr_t capacity)
+{
+	int		i;
+	int		rval = 0;
+	efi_gpe_t	*partitions;
+	uchar_t		*buf;
+	uint_t		lbasize;	/* is really how much to read */
+	diskaddr_t	cap;
+	uint_t		nparts;
+	diskaddr_t	gpe_lba;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	lbasize = un->un_sys_blocksize;
+
+	buf = kmem_zalloc(EFI_MIN_ARRAY_SIZE, KM_SLEEP);
+	mutex_exit(CMLB_MUTEX(un));
+
+	rval = DK_TG_READ(un, buf, 0, lbasize);
+	if (rval) {
+		goto done_err;
+	}
+	if (((struct dk_label *)buf)->dkl_magic == DKL_MAGIC) {
+		/* not ours */
+		rval = ESRCH;
+		goto done_err;
+	}
+
+	rval = DK_TG_READ(un, buf, 1, lbasize);
+	if (rval) {
+		goto done_err;
+	}
+	cmlb_swap_efi_gpt((efi_gpt_t *)buf);
+
+	if ((rval = cmlb_validate_efi((efi_gpt_t *)buf)) != 0) {
+		/*
+		 * Couldn't read the primary, try the backup.  Our
+		 * capacity at this point could be based on CHS, so
+		 * check what the device reports.
+		 */
+		rval = DK_TG_GETCAP(un, &cap);
+
+		if (rval) {
+			goto done_err;
+		}
+		if ((rval = DK_TG_READ(un, buf, cap - 1, lbasize)) != 0) {
+			goto done_err;
+		}
+		cmlb_swap_efi_gpt((efi_gpt_t *)buf);
+		if ((rval = cmlb_validate_efi((efi_gpt_t *)buf)) != 0)
+			goto done_err;
+		cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_WARN,
+		    "primary label corrupt; using backup\n");
+	}
+
+	nparts = ((efi_gpt_t *)buf)->efi_gpt_NumberOfPartitionEntries;
+	gpe_lba = ((efi_gpt_t *)buf)->efi_gpt_PartitionEntryLBA;
+
+	rval = DK_TG_READ(un, buf, gpe_lba, EFI_MIN_ARRAY_SIZE);
+	if (rval) {
+		goto done_err;
+	}
+	partitions = (efi_gpe_t *)buf;
+
+	if (nparts > MAXPART) {
+		nparts = MAXPART;
+	}
+	cmlb_swap_efi_gpe(nparts, partitions);
+
+	mutex_enter(CMLB_MUTEX(un));
+
+	/* Fill in partition table. */
+	for (i = 0; i < nparts; i++) {
+		if (partitions->efi_gpe_StartingLBA != 0 ||
+		    partitions->efi_gpe_EndingLBA != 0) {
+			un->un_map[i].dkl_cylno =
+			    partitions->efi_gpe_StartingLBA;
+			un->un_map[i].dkl_nblk =
+			    partitions->efi_gpe_EndingLBA -
+			    partitions->efi_gpe_StartingLBA + 1;
+			un->un_offset[i] =
+			    partitions->efi_gpe_StartingLBA;
+		}
+		if (i == WD_NODE) {
+			/*
+			 * minor number 7 corresponds to the whole disk
+			 */
+			un->un_map[i].dkl_cylno = 0;
+			un->un_map[i].dkl_nblk = capacity;
+			un->un_offset[i] = 0;
+		}
+		partitions++;
+	}
+	un->un_solaris_offset = 0;
+	un->un_solaris_size = capacity;
+	un->un_f_geometry_is_valid = TRUE;
+	kmem_free(buf, EFI_MIN_ARRAY_SIZE);
+	return (0);
+
+done_err:
+	kmem_free(buf, EFI_MIN_ARRAY_SIZE);
+	mutex_enter(CMLB_MUTEX(un));
+	/*
+	 * if we didn't find something that could look like a VTOC
+	 * and the disk is over 1TB, we know there isn't a valid label.
+	 * Otherwise let cmlb_uselabel decide what to do.  We only
+	 * want to invalidate this if we're certain the label isn't
+	 * valid because cmlb_prop_op will now fail, which in turn
+	 * causes things like opens and stats on the partition to fail.
+	 */
+	if ((capacity > DK_MAX_BLOCKS) && (rval != ESRCH)) {
+		un->un_f_geometry_is_valid = FALSE;
+	}
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_uselabel
+ *
+ * Description: Validate the disk label and update the relevant data (geometry,
+ *		partition, vtoc, and capacity data) in the cmlb_lun struct.
+ *		Marks the geometry of the unit as being valid.
+ *
+ *   Arguments: un: unit struct.
+ *		dk_label: disk label
+ *
+ * Return Code: CMLB_LABEL_IS_VALID: Label read from disk is OK; geometry,
+ *		partition, vtoc, and capacity data are good.
+ *
+ *		CMLB_LABEL_IS_INVALID: Magic number or checksum error in the
+ *		label; or computed capacity does not jibe with capacity
+ *		reported from the READ CAPACITY command.
+ *
+ *     Context: Kernel thread only (can sleep).
+ */
+static int
+cmlb_uselabel(struct cmlb_lun *un, struct dk_label *labp)
+{
+	short		*sp;
+	short		sum;
+	short		count;
+	int		label_error = CMLB_LABEL_IS_VALID;
+	int		i;
+	diskaddr_t	label_capacity;
+	int		part_end;
+	diskaddr_t	track_capacity;
+#if defined(_SUNOS_VTOC_16)
+	struct	dkl_partition	*vpartp;
+#endif
+	ASSERT(un != NULL);
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/* Validate the magic number of the label. */
+	if (labp->dkl_magic != DKL_MAGIC) {
+#if defined(__sparc)
+		if (!ISREMOVABLE(un)) {
+			cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_WARN,
+			    "Corrupt label; wrong magic number\n");
+		}
+#endif
+		return (CMLB_LABEL_IS_INVALID);
+	}
+
+	/* Validate the checksum of the label. */
+	sp  = (short *)labp;
+	sum = 0;
+	count = sizeof (struct dk_label) / sizeof (short);
+	while (count--)	 {
+		sum ^= *sp++;
+	}
+
+	if (sum != 0) {
+#if defined(_SUNOS_VTOC_16)
+		if (!ISCD(un)) {
+#elif defined(_SUNOS_VTOC_8)
+		if (!ISREMOVABLE(un)) {
+#endif
+			cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_WARN,
+			    "Corrupt label - label checksum failed\n");
+		}
+		return (CMLB_LABEL_IS_INVALID);
+	}
+
+
+	/*
+	 * Fill in geometry structure with data from label.
+	 */
+	bzero(&un->un_g, sizeof (struct dk_geom));
+	un->un_g.dkg_ncyl   = labp->dkl_ncyl;
+	un->un_g.dkg_acyl   = labp->dkl_acyl;
+	un->un_g.dkg_bcyl   = 0;
+	un->un_g.dkg_nhead  = labp->dkl_nhead;
+	un->un_g.dkg_nsect  = labp->dkl_nsect;
+	un->un_g.dkg_intrlv = labp->dkl_intrlv;
+
+#if defined(_SUNOS_VTOC_8)
+	un->un_g.dkg_gap1   = labp->dkl_gap1;
+	un->un_g.dkg_gap2   = labp->dkl_gap2;
+	un->un_g.dkg_bhead  = labp->dkl_bhead;
+#endif
+#if defined(_SUNOS_VTOC_16)
+	un->un_dkg_skew = labp->dkl_skew;
+#endif
+
+#if defined(__i386) || defined(__amd64)
+	un->un_g.dkg_apc = labp->dkl_apc;
+#endif
+
+	/*
+	 * Currently we rely on the values in the label being accurate. If
+	 * dkl_rpm or dkl_pcly are zero in the label, use a default value.
+	 *
+	 * Note: In the future a MODE SENSE may be used to retrieve this data,
+	 * although this command is optional in SCSI-2.
+	 */
+	un->un_g.dkg_rpm  = (labp->dkl_rpm  != 0) ? labp->dkl_rpm  : 3600;
+	un->un_g.dkg_pcyl = (labp->dkl_pcyl != 0) ? labp->dkl_pcyl :
+	    (un->un_g.dkg_ncyl + un->un_g.dkg_acyl);
+
+	/*
+	 * The Read and Write reinstruct values may not be valid
+	 * for older disks.
+	 */
+	un->un_g.dkg_read_reinstruct  = labp->dkl_read_reinstruct;
+	un->un_g.dkg_write_reinstruct = labp->dkl_write_reinstruct;
+
+	/* Fill in partition table. */
+#if defined(_SUNOS_VTOC_8)
+	for (i = 0; i < NDKMAP; i++) {
+		un->un_map[i].dkl_cylno = labp->dkl_map[i].dkl_cylno;
+		un->un_map[i].dkl_nblk  = labp->dkl_map[i].dkl_nblk;
+	}
+#endif
+#if  defined(_SUNOS_VTOC_16)
+	vpartp		= labp->dkl_vtoc.v_part;
+	track_capacity	= labp->dkl_nhead * labp->dkl_nsect;
+
+	for (i = 0; i < NDKMAP; i++, vpartp++) {
+		un->un_map[i].dkl_cylno = vpartp->p_start / track_capacity;
+		un->un_map[i].dkl_nblk  = vpartp->p_size;
+	}
+#endif
+
+	/* Fill in VTOC Structure. */
+	bcopy(&labp->dkl_vtoc, &un->un_vtoc, sizeof (struct dk_vtoc));
+#if defined(_SUNOS_VTOC_8)
+	/*
+	 * The 8-slice vtoc does not include the ascii label; save it into
+	 * the device's soft state structure here.
+	 */
+	bcopy(labp->dkl_asciilabel, un->un_asciilabel, LEN_DKL_ASCII);
+#endif
+
+	/* Mark the geometry as valid. */
+	un->un_f_geometry_is_valid = TRUE;
+
+	/* Now look for a valid capacity. */
+	track_capacity	= (un->un_g.dkg_nhead * un->un_g.dkg_nsect);
+	label_capacity	= (un->un_g.dkg_ncyl  * track_capacity);
+
+	if (un->un_g.dkg_acyl) {
+#if defined(__i386) || defined(__amd64)
+		/* we may have > 1 alts cylinder */
+		label_capacity += (track_capacity * un->un_g.dkg_acyl);
+#else
+		label_capacity += track_capacity;
+#endif
+	}
+
+	/*
+	 * if we got invalidated when mutex exit and entered again,
+	 * if blockcount different than when we came in, need to
+	 * retry from beginning of cmlb_validate_geometry.
+	 * revisit this on next phase of utilizing this for
+	 * sd.
+	 */
+
+	if (label_capacity <= un->un_blockcount) {
+#if defined(_SUNOS_VTOC_8)
+		/*
+		 * We can't let this happen on drives that are subdivided
+		 * into logical disks (i.e., that have an fdisk table).
+		 * The un_blockcount field should always hold the full media
+		 * size in sectors, period.  This code would overwrite
+		 * un_blockcount with the size of the Solaris fdisk partition.
+		 */
+		cmlb_dbg(CMLB_ERROR,  un,
+		    "cmlb_uselabel: Label %d blocks; Drive %d blocks\n",
+		    label_capacity, un->un_blockcount);
+		un->un_solaris_size = label_capacity;
+
+#endif	/* defined(_SUNOS_VTOC_8) */
+		goto done;
+	}
+
+	if (ISCD(un)) {
+		/* For CDROMs, we trust that the data in the label is OK. */
+#if defined(_SUNOS_VTOC_8)
+		for (i = 0; i < NDKMAP; i++) {
+			part_end = labp->dkl_nhead * labp->dkl_nsect *
+			    labp->dkl_map[i].dkl_cylno +
+			    labp->dkl_map[i].dkl_nblk  - 1;
+
+			if ((labp->dkl_map[i].dkl_nblk) &&
+			    (part_end > un->un_blockcount)) {
+				un->un_f_geometry_is_valid = FALSE;
+				break;
+			}
+		}
+#endif
+#if defined(_SUNOS_VTOC_16)
+		vpartp = &(labp->dkl_vtoc.v_part[0]);
+		for (i = 0; i < NDKMAP; i++, vpartp++) {
+			part_end = vpartp->p_start + vpartp->p_size;
+			if ((vpartp->p_size > 0) &&
+			    (part_end > un->un_blockcount)) {
+				un->un_f_geometry_is_valid = FALSE;
+				break;
+			}
+		}
+#endif
+	} else {
+		/* label_capacity > un->un_blockcount */
+		cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_WARN,
+		    "Corrupt label - bad geometry\n");
+		cmlb_log(CMLB_DEVINFO(un), CMLB_LABEL(un), CE_CONT,
+		    "Label says %llu blocks; Drive says %llu blocks\n",
+		    label_capacity, un->un_blockcount);
+		un->un_f_geometry_is_valid = FALSE;
+		label_error = CMLB_LABEL_IS_INVALID;
+	}
+
+done:
+
+	cmlb_dbg(CMLB_INFO,  un, "cmlb_uselabel: (label geometry)\n");
+	cmlb_dbg(CMLB_INFO,  un,
+	    "   ncyl: %d; acyl: %d; nhead: %d; nsect: %d\n",
+	    un->un_g.dkg_ncyl,  un->un_g.dkg_acyl,
+	    un->un_g.dkg_nhead, un->un_g.dkg_nsect);
+
+	cmlb_dbg(CMLB_INFO,  un,
+	    "   label_capacity: %d; intrlv: %d; rpm: %d\n",
+	    un->un_blockcount, un->un_g.dkg_intrlv, un->un_g.dkg_rpm);
+	cmlb_dbg(CMLB_INFO,  un, "   wrt_reinstr: %d; rd_reinstr: %d\n",
+	    un->un_g.dkg_write_reinstruct, un->un_g.dkg_read_reinstruct);
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	return (label_error);
+}
+
+
+/*
+ *    Function: cmlb_build_default_label
+ *
+ * Description: Generate a default label for those devices that do not have
+ *		one, e.g., new media, removable cartridges, etc..
+ *
+ *     Context: Kernel thread only
+ */
+static void
+cmlb_build_default_label(struct cmlb_lun *un)
+{
+#if defined(_SUNOS_VTOC_16)
+	uint_t	phys_spc;
+	uint_t	disksize;
+	struct  dk_geom un_g;
+#endif
+
+	ASSERT(un != NULL);
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+#if defined(_SUNOS_VTOC_8)
+	/*
+	 * Note: This is a legacy check for non-removable devices on VTOC_8
+	 * only. This may be a valid check for VTOC_16 as well.
+	 */
+	if (!ISREMOVABLE(un)) {
+		return;
+	}
+#endif
+
+	bzero(&un->un_g, sizeof (struct dk_geom));
+	bzero(&un->un_vtoc, sizeof (struct dk_vtoc));
+	bzero(&un->un_map, NDKMAP * (sizeof (struct dk_map)));
+
+#if defined(_SUNOS_VTOC_8)
+
+	/*
+	 * It's a REMOVABLE media, therefore no label (on sparc, anyway).
+	 * But it is still necessary to set up various geometry information,
+	 * and we are doing this here.
+	 */
+
+	/*
+	 * For the rpm, we use the minimum for the disk.  For the head, cyl,
+	 * and number of sector per track, if the capacity <= 1GB, head = 64,
+	 * sect = 32.  else head = 255, sect 63 Note: the capacity should be
+	 * equal to C*H*S values.  This will cause some truncation of size due
+	 * to round off errors. For CD-ROMs, this truncation can have adverse
+	 * side effects, so returning ncyl and nhead as 1. The nsect will
+	 * overflow for most of CD-ROMs as nsect is of type ushort. (4190569)
+	 */
+	un->un_solaris_size = un->un_blockcount;
+	if (ISCD(un)) {
+		tg_attribute_t tgattribute;
+		int is_writable;
+		/*
+		 * Preserve the old behavior for non-writable
+		 * medias. Since dkg_nsect is a ushort, it
+		 * will lose bits as cdroms have more than
+		 * 65536 sectors. So if we recalculate
+		 * capacity, it will become much shorter.
+		 * But the dkg_* information is not
+		 * used for CDROMs so it is OK. But for
+		 * Writable CDs we need this information
+		 * to be valid (for newfs say). So we
+		 * make nsect and nhead > 1 that way
+		 * nsect can still stay within ushort limit
+		 * without losing any bits.
+		 */
+
+		bzero(&tgattribute, sizeof (tg_attribute_t));
+
+		mutex_exit(CMLB_MUTEX(un));
+		is_writable = (DK_TG_GETATTRIBUTE(un, &tgattribute) == 0) ?
+		    tgattribute.media_is_writable : 1;
+		mutex_enter(CMLB_MUTEX(un));
+
+		if (is_writable) {
+			un->un_g.dkg_nhead = 64;
+			un->un_g.dkg_nsect = 32;
+			un->un_g.dkg_ncyl = un->un_blockcount / (64 * 32);
+			un->un_solaris_size = un->un_g.dkg_ncyl *
+			    un->un_g.dkg_nhead * un->un_g.dkg_nsect;
+		} else {
+			un->un_g.dkg_ncyl  = 1;
+			un->un_g.dkg_nhead = 1;
+			un->un_g.dkg_nsect = un->un_blockcount;
+		}
+	} else {
+		if (un->un_blockcount <= 0x1000) {
+			/* unlabeled SCSI floppy device */
+			un->un_g.dkg_nhead = 2;
+			un->un_g.dkg_ncyl = 80;
+			un->un_g.dkg_nsect = un->un_blockcount / (2 * 80);
+		} else if (un->un_blockcount <= 0x200000) {
+			un->un_g.dkg_nhead = 64;
+			un->un_g.dkg_nsect = 32;
+			un->un_g.dkg_ncyl  = un->un_blockcount / (64 * 32);
+		} else {
+			un->un_g.dkg_nhead = 255;
+			un->un_g.dkg_nsect = 63;
+			un->un_g.dkg_ncyl  = un->un_blockcount / (255 * 63);
+		}
+		un->un_solaris_size =
+		    un->un_g.dkg_ncyl * un->un_g.dkg_nhead * un->un_g.dkg_nsect;
+
+	}
+
+	un->un_g.dkg_acyl	= 0;
+	un->un_g.dkg_bcyl	= 0;
+	un->un_g.dkg_rpm	= 200;
+	un->un_asciilabel[0]	= '\0';
+	un->un_g.dkg_pcyl	= un->un_g.dkg_ncyl;
+
+	un->un_map[0].dkl_cylno = 0;
+	un->un_map[0].dkl_nblk  = un->un_solaris_size;
+
+	un->un_map[2].dkl_cylno = 0;
+	un->un_map[2].dkl_nblk  = un->un_solaris_size;
+
+#elif defined(_SUNOS_VTOC_16)
+
+	if (un->un_solaris_size == 0) {
+		/*
+		 * Got fdisk table but no solaris entry therefore
+		 * don't create a default label
+		 */
+		un->un_f_geometry_is_valid = TRUE;
+		return;
+	}
+
+	/*
+	 * For CDs we continue to use the physical geometry to calculate
+	 * number of cylinders. All other devices must convert the
+	 * physical geometry (cmlb_geom) to values that will fit
+	 * in a dk_geom structure.
+	 */
+	if (ISCD(un)) {
+		phys_spc = un->un_pgeom.g_nhead * un->un_pgeom.g_nsect;
+	} else {
+		/* Convert physical geometry to disk geometry */
+		bzero(&un_g, sizeof (struct dk_geom));
+		cmlb_convert_geometry(un->un_blockcount, &un_g);
+		bcopy(&un_g, &un->un_g, sizeof (un->un_g));
+		phys_spc = un->un_g.dkg_nhead * un->un_g.dkg_nsect;
+	}
+
+	un->un_g.dkg_pcyl = un->un_solaris_size / phys_spc;
+	un->un_g.dkg_acyl = DK_ACYL;
+	un->un_g.dkg_ncyl = un->un_g.dkg_pcyl - DK_ACYL;
+	disksize = un->un_g.dkg_ncyl * phys_spc;
+
+	if (ISCD(un)) {
+		/*
+		 * CD's don't use the "heads * sectors * cyls"-type of
+		 * geometry, but instead use the entire capacity of the media.
+		 */
+		disksize = un->un_solaris_size;
+		un->un_g.dkg_nhead = 1;
+		un->un_g.dkg_nsect = 1;
+		un->un_g.dkg_rpm =
+		    (un->un_pgeom.g_rpm == 0) ? 200 : un->un_pgeom.g_rpm;
+
+		un->un_vtoc.v_part[0].p_start = 0;
+		un->un_vtoc.v_part[0].p_size  = disksize;
+		un->un_vtoc.v_part[0].p_tag   = V_BACKUP;
+		un->un_vtoc.v_part[0].p_flag  = V_UNMNT;
+
+		un->un_map[0].dkl_cylno = 0;
+		un->un_map[0].dkl_nblk  = disksize;
+		un->un_offset[0] = 0;
+
+	} else {
+		/*
+		 * Hard disks and removable media cartridges
+		 */
+		un->un_g.dkg_rpm =
+		    (un->un_pgeom.g_rpm == 0) ? 3600: un->un_pgeom.g_rpm;
+		un->un_vtoc.v_sectorsz = un->un_sys_blocksize;
+
+		/* Add boot slice */
+		un->un_vtoc.v_part[8].p_start = 0;
+		un->un_vtoc.v_part[8].p_size  = phys_spc;
+		un->un_vtoc.v_part[8].p_tag   = V_BOOT;
+		un->un_vtoc.v_part[8].p_flag  = V_UNMNT;
+
+		un->un_map[8].dkl_cylno = 0;
+		un->un_map[8].dkl_nblk  = phys_spc;
+		un->un_offset[8] = 0;
+
+		if ((un->un_alter_behavior &
+		    CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT) &&
+		    un->un_device_type == DTYPE_DIRECT) {
+			un->un_vtoc.v_part[9].p_start = phys_spc;
+			un->un_vtoc.v_part[9].p_size  = 2 * phys_spc;
+			un->un_vtoc.v_part[9].p_tag   = V_ALTSCTR;
+			un->un_vtoc.v_part[9].p_flag  = 0;
+
+			un->un_map[9].dkl_cylno = 1;
+			un->un_map[9].dkl_nblk  = 2 * phys_spc;
+			un->un_offset[9] = phys_spc;
+		}
+	}
+
+	un->un_g.dkg_apc = 0;
+	un->un_vtoc.v_nparts = V_NUMPAR;
+	un->un_vtoc.v_version = V_VERSION;
+
+	/* Add backup slice */
+	un->un_vtoc.v_part[2].p_start = 0;
+	un->un_vtoc.v_part[2].p_size  = disksize;
+	un->un_vtoc.v_part[2].p_tag   = V_BACKUP;
+	un->un_vtoc.v_part[2].p_flag  = V_UNMNT;
+
+	un->un_map[2].dkl_cylno = 0;
+	un->un_map[2].dkl_nblk  = disksize;
+	un->un_offset[2] = 0;
+
+	(void) sprintf(un->un_vtoc.v_asciilabel, "DEFAULT cyl %d alt %d"
+	    " hd %d sec %d", un->un_g.dkg_ncyl, un->un_g.dkg_acyl,
+	    un->un_g.dkg_nhead, un->un_g.dkg_nsect);
+
+#else
+#error "No VTOC format defined."
+#endif
+
+	un->un_g.dkg_read_reinstruct  = 0;
+	un->un_g.dkg_write_reinstruct = 0;
+
+	un->un_g.dkg_intrlv = 1;
+
+	un->un_vtoc.v_sanity  = VTOC_SANE;
+
+	un->un_f_geometry_is_valid = TRUE;
+	un->un_vtoc_label_is_from_media = 0;
+
+	cmlb_dbg(CMLB_INFO,  un,
+	    "cmlb_build_default_label: Default label created: "
+	    "cyl: %d\tacyl: %d\tnhead: %d\tnsect: %d\tcap: %d\n",
+	    un->un_g.dkg_ncyl, un->un_g.dkg_acyl, un->un_g.dkg_nhead,
+	    un->un_g.dkg_nsect, un->un_blockcount);
+}
+
+
+#if defined(_FIRMWARE_NEEDS_FDISK)
+/*
+ * Max CHS values, as they are encoded into bytes, for 1022/254/63
+ */
+#define	LBA_MAX_SECT	(63 | ((1022 & 0x300) >> 2))
+#define	LBA_MAX_CYL	(1022 & 0xFF)
+#define	LBA_MAX_HEAD	(254)
+
+
+/*
+ *    Function: cmlb_has_max_chs_vals
+ *
+ * Description: Return TRUE if Cylinder-Head-Sector values are all at maximum.
+ *
+ *   Arguments: fdp - ptr to CHS info
+ *
+ * Return Code: True or false
+ *
+ *     Context: Any.
+ */
+static int
+cmlb_has_max_chs_vals(struct ipart *fdp)
+{
+	return ((fdp->begcyl  == LBA_MAX_CYL)	&&
+	    (fdp->beghead == LBA_MAX_HEAD)	&&
+	    (fdp->begsect == LBA_MAX_SECT)	&&
+	    (fdp->endcyl  == LBA_MAX_CYL)	&&
+	    (fdp->endhead == LBA_MAX_HEAD)	&&
+	    (fdp->endsect == LBA_MAX_SECT));
+}
+#endif
+
+/*
+ *    Function: cmlb_dkio_get_geometry
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to get the device geometry (DKIOCGGEOM).
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided dk_geom structure specifying
+ *			the controller's notion of the current geometry.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EFAULT
+ *		ENXIO
+ *		EIO
+ */
+static int
+cmlb_dkio_get_geometry(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct dk_geom	*tmp_geom = NULL;
+	int		rval = 0;
+
+	/*
+	 * cmlb_validate_geometry does not spin a disk up
+	 * if it was spun down. We need to make sure it
+	 * is ready.
+	 */
+	mutex_enter(CMLB_MUTEX(un));
+	rval = cmlb_validate_geometry(un, 1);
+#if defined(_SUNOS_VTOC_8)
+	if (rval == EINVAL &&
+	    un->un_alter_behavior & CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8) {
+		/*
+		 * This is to return a default label geometry even when we
+		 * do not really assume a default label for the device.
+		 * dad driver utilizes this.
+		 */
+		if (un->un_blockcount <= DK_MAX_BLOCKS) {
+			cmlb_setup_default_geometry(un);
+			rval = 0;
+		}
+	}
+#endif
+	if (rval) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (rval);
+	}
+
+#if defined(__i386) || defined(__amd64)
+	if (un->un_solaris_size == 0) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EIO);
+	}
+#endif
+
+	/*
+	 * Make a local copy of the soft state geometry to avoid some potential
+	 * race conditions associated with holding the mutex and updating the
+	 * write_reinstruct value
+	 */
+	tmp_geom = kmem_zalloc(sizeof (struct dk_geom), KM_SLEEP);
+	bcopy(&un->un_g, tmp_geom, sizeof (struct dk_geom));
+
+	if (tmp_geom->dkg_write_reinstruct == 0) {
+		tmp_geom->dkg_write_reinstruct =
+		    (int)((int)(tmp_geom->dkg_nsect * tmp_geom->dkg_rpm *
+		    cmlb_rot_delay) / (int)60000);
+	}
+	mutex_exit(CMLB_MUTEX(un));
+
+	rval = ddi_copyout(tmp_geom, (void *)arg, sizeof (struct dk_geom),
+	    flag);
+	if (rval != 0) {
+		rval = EFAULT;
+	}
+
+	kmem_free(tmp_geom, sizeof (struct dk_geom));
+	return (rval);
+
+}
+
+
+/*
+ *    Function: cmlb_dkio_set_geometry
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to set the device geometry (DKIOCSGEOM). The actual
+ *		device geometry is not updated, just the driver "notion" of it.
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided dk_geom structure used to set
+ *			the controller's notion of the current geometry.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EFAULT
+ *		ENXIO
+ *		EIO
+ */
+static int
+cmlb_dkio_set_geometry(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct dk_geom	*tmp_geom;
+	struct dk_map	*lp;
+	int		rval = 0;
+	int		i;
+
+
+#if defined(__i386) || defined(__amd64)
+	if (un->un_solaris_size == 0) {
+		return (EIO);
+	}
+#endif
+	/*
+	 * We need to copy the user specified geometry into local
+	 * storage and then update the softstate. We don't want to hold
+	 * the mutex and copyin directly from the user to the soft state
+	 */
+	tmp_geom = (struct dk_geom *)
+	    kmem_zalloc(sizeof (struct dk_geom), KM_SLEEP);
+	rval = ddi_copyin(arg, tmp_geom, sizeof (struct dk_geom), flag);
+	if (rval != 0) {
+		kmem_free(tmp_geom, sizeof (struct dk_geom));
+		return (EFAULT);
+	}
+
+	mutex_enter(CMLB_MUTEX(un));
+	bcopy(tmp_geom, &un->un_g, sizeof (struct dk_geom));
+	for (i = 0; i < NDKMAP; i++) {
+		lp  = &un->un_map[i];
+		un->un_offset[i] =
+		    un->un_g.dkg_nhead * un->un_g.dkg_nsect * lp->dkl_cylno;
+#if defined(__i386) || defined(__amd64)
+		un->un_offset[i] += un->un_solaris_offset;
+#endif
+	}
+	un->un_f_geometry_is_valid = FALSE;
+	mutex_exit(CMLB_MUTEX(un));
+	kmem_free(tmp_geom, sizeof (struct dk_geom));
+
+	return (rval);
+}
+
+/*
+ *    Function: cmlb_dkio_get_partition
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to get the partition table (DKIOCGAPART).
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided dk_allmap structure specifying
+ *			the controller's notion of the current partition table.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EFAULT
+ *		ENXIO
+ *		EIO
+ */
+static int
+cmlb_dkio_get_partition(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	int		rval = 0;
+	int		size;
+
+	/*
+	 * Make sure the geometry is valid before getting the partition
+	 * information.
+	 */
+	mutex_enter(CMLB_MUTEX(un));
+	if ((rval = cmlb_validate_geometry(un, 1)) != 0) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (rval);
+	}
+	mutex_exit(CMLB_MUTEX(un));
+
+#if defined(__i386) || defined(__amd64)
+	if (un->un_solaris_size == 0) {
+		return (EIO);
+	}
+#endif
+
+#ifdef _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(flag & FMODELS)) {
+	case DDI_MODEL_ILP32: {
+		struct dk_map32 dk_map32[NDKMAP];
+		int		i;
+
+		for (i = 0; i < NDKMAP; i++) {
+			dk_map32[i].dkl_cylno = un->un_map[i].dkl_cylno;
+			dk_map32[i].dkl_nblk  = un->un_map[i].dkl_nblk;
+		}
+		size = NDKMAP * sizeof (struct dk_map32);
+		rval = ddi_copyout(dk_map32, (void *)arg, size, flag);
+		if (rval != 0) {
+			rval = EFAULT;
+		}
+		break;
+	}
+	case DDI_MODEL_NONE:
+		size = NDKMAP * sizeof (struct dk_map);
+		rval = ddi_copyout(un->un_map, (void *)arg, size, flag);
+		if (rval != 0) {
+			rval = EFAULT;
+		}
+		break;
+	}
+#else /* ! _MULTI_DATAMODEL */
+	size = NDKMAP * sizeof (struct dk_map);
+	rval = ddi_copyout(un->un_map, (void *)arg, size, flag);
+	if (rval != 0) {
+		rval = EFAULT;
+	}
+#endif /* _MULTI_DATAMODEL */
+	return (rval);
+}
+
+/*
+ *    Function: cmlb_dkio_set_partition
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to set the partition table (DKIOCSAPART). The actual
+ *		device partition is not updated.
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided dk_allmap structure used to set
+ *			the controller's notion of the partition table.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EINVAL
+ *		EFAULT
+ *		ENXIO
+ *		EIO
+ */
+static int
+cmlb_dkio_set_partition(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct dk_map	dk_map[NDKMAP];
+	struct dk_map	*lp;
+	int		rval = 0;
+	int		size;
+	int		i;
+#if defined(_SUNOS_VTOC_16)
+	struct dkl_partition	*vp;
+#endif
+
+	/*
+	 * Set the map for all logical partitions.  We lock
+	 * the priority just to make sure an interrupt doesn't
+	 * come in while the map is half updated.
+	 */
+	_NOTE(DATA_READABLE_WITHOUT_LOCK(cmlb_lun::un_solaris_size))
+	mutex_enter(CMLB_MUTEX(un));
+
+	if (un->un_blockcount > DK_MAX_BLOCKS) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (ENOTSUP);
+	}
+	mutex_exit(CMLB_MUTEX(un));
+	if (un->un_solaris_size == 0) {
+		return (EIO);
+	}
+
+#ifdef _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(flag & FMODELS)) {
+	case DDI_MODEL_ILP32: {
+		struct dk_map32 dk_map32[NDKMAP];
+
+		size = NDKMAP * sizeof (struct dk_map32);
+		rval = ddi_copyin((void *)arg, dk_map32, size, flag);
+		if (rval != 0) {
+			return (EFAULT);
+		}
+		for (i = 0; i < NDKMAP; i++) {
+			dk_map[i].dkl_cylno = dk_map32[i].dkl_cylno;
+			dk_map[i].dkl_nblk  = dk_map32[i].dkl_nblk;
+		}
+		break;
+	}
+	case DDI_MODEL_NONE:
+		size = NDKMAP * sizeof (struct dk_map);
+		rval = ddi_copyin((void *)arg, dk_map, size, flag);
+		if (rval != 0) {
+			return (EFAULT);
+		}
+		break;
+	}
+#else /* ! _MULTI_DATAMODEL */
+	size = NDKMAP * sizeof (struct dk_map);
+	rval = ddi_copyin((void *)arg, dk_map, size, flag);
+	if (rval != 0) {
+		return (EFAULT);
+	}
+#endif /* _MULTI_DATAMODEL */
+
+	mutex_enter(CMLB_MUTEX(un));
+	/* Note: The size used in this bcopy is set based upon the data model */
+	bcopy(dk_map, un->un_map, size);
+#if defined(_SUNOS_VTOC_16)
+	vp = (struct dkl_partition *)&(un->un_vtoc);
+#endif	/* defined(_SUNOS_VTOC_16) */
+	for (i = 0; i < NDKMAP; i++) {
+		lp  = &un->un_map[i];
+		un->un_offset[i] =
+		    un->un_g.dkg_nhead * un->un_g.dkg_nsect * lp->dkl_cylno;
+#if defined(_SUNOS_VTOC_16)
+		vp->p_start = un->un_offset[i];
+		vp->p_size = lp->dkl_nblk;
+		vp++;
+#endif	/* defined(_SUNOS_VTOC_16) */
+#if defined(__i386) || defined(__amd64)
+		un->un_offset[i] += un->un_solaris_offset;
+#endif
+	}
+	mutex_exit(CMLB_MUTEX(un));
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_dkio_get_vtoc
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to get the current volume table of contents
+ *		(DKIOCGVTOC).
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided vtoc structure specifying
+ *			the current vtoc.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EFAULT
+ *		ENXIO
+ *		EIO
+ */
+static int
+cmlb_dkio_get_vtoc(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+#if defined(_SUNOS_VTOC_8)
+	struct vtoc	user_vtoc;
+#endif	/* defined(_SUNOS_VTOC_8) */
+	int		rval = 0;
+
+	mutex_enter(CMLB_MUTEX(un));
+	rval = cmlb_validate_geometry(un, 1);
+
+#if defined(_SUNOS_VTOC_8)
+	if (rval == EINVAL &&
+	    (un->un_alter_behavior & CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8)) {
+		/*
+		 * This is to return a default label even when we do not
+		 * really assume a default label for the device.
+		 * dad driver utilizes this.
+		 */
+		if (un->un_blockcount <= DK_MAX_BLOCKS) {
+			cmlb_setup_default_geometry(un);
+			rval = 0;
+		}
+	}
+#endif
+	if (rval) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (rval);
+	}
+
+#if defined(_SUNOS_VTOC_8)
+	cmlb_build_user_vtoc(un, &user_vtoc);
+	mutex_exit(CMLB_MUTEX(un));
+
+#ifdef _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(flag & FMODELS)) {
+	case DDI_MODEL_ILP32: {
+		struct vtoc32 user_vtoc32;
+
+		vtoctovtoc32(user_vtoc, user_vtoc32);
+		if (ddi_copyout(&user_vtoc32, (void *)arg,
+		    sizeof (struct vtoc32), flag)) {
+			return (EFAULT);
+		}
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyout(&user_vtoc, (void *)arg,
+		    sizeof (struct vtoc), flag)) {
+			return (EFAULT);
+		}
+		break;
+	}
+#else /* ! _MULTI_DATAMODEL */
+	if (ddi_copyout(&user_vtoc, (void *)arg, sizeof (struct vtoc), flag)) {
+		return (EFAULT);
+	}
+#endif /* _MULTI_DATAMODEL */
+
+#elif defined(_SUNOS_VTOC_16)
+	mutex_exit(CMLB_MUTEX(un));
+
+#ifdef _MULTI_DATAMODEL
+	/*
+	 * The un_vtoc structure is a "struct dk_vtoc"  which is always
+	 * 32-bit to maintain compatibility with existing on-disk
+	 * structures.  Thus, we need to convert the structure when copying
+	 * it out to a datamodel-dependent "struct vtoc" in a 64-bit
+	 * program.  If the target is a 32-bit program, then no conversion
+	 * is necessary.
+	 */
+	/* LINTED: logical expression always true: op "||" */
+	ASSERT(sizeof (un->un_vtoc) == sizeof (struct vtoc32));
+	switch (ddi_model_convert_from(flag & FMODELS)) {
+	case DDI_MODEL_ILP32:
+		if (ddi_copyout(&(un->un_vtoc), (void *)arg,
+		    sizeof (un->un_vtoc), flag)) {
+			return (EFAULT);
+		}
+		break;
+
+	case DDI_MODEL_NONE: {
+		struct vtoc user_vtoc;
+
+		vtoc32tovtoc(un->un_vtoc, user_vtoc);
+		if (ddi_copyout(&user_vtoc, (void *)arg,
+		    sizeof (struct vtoc), flag)) {
+			return (EFAULT);
+		}
+		break;
+	}
+	}
+#else /* ! _MULTI_DATAMODEL */
+	if (ddi_copyout(&(un->un_vtoc), (void *)arg, sizeof (un->un_vtoc),
+	    flag)) {
+		return (EFAULT);
+	}
+#endif /* _MULTI_DATAMODEL */
+#else
+#error "No VTOC format defined."
+#endif
+
+	return (rval);
+}
+
+static int
+cmlb_dkio_get_efi(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	dk_efi_t	user_efi;
+	int		rval = 0;
+	void		*buffer;
+
+	if (ddi_copyin(arg, &user_efi, sizeof (dk_efi_t), flag))
+		return (EFAULT);
+
+	user_efi.dki_data = (void *)(uintptr_t)user_efi.dki_data_64;
+
+	buffer = kmem_alloc(user_efi.dki_length, KM_SLEEP);
+	rval = DK_TG_READ(un, buffer, user_efi.dki_lba, user_efi.dki_length);
+	if (rval == 0 && ddi_copyout(buffer, user_efi.dki_data,
+	    user_efi.dki_length, flag) != 0)
+		rval = EFAULT;
+
+	kmem_free(buffer, user_efi.dki_length);
+	return (rval);
+}
+
+/*
+ *    Function: cmlb_build_user_vtoc
+ *
+ * Description: This routine populates a pass by reference variable with the
+ *		current volume table of contents.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *		user_vtoc - pointer to vtoc structure to be populated
+ */
+static void
+cmlb_build_user_vtoc(struct cmlb_lun *un, struct vtoc *user_vtoc)
+{
+	struct dk_map2		*lpart;
+	struct dk_map		*lmap;
+	struct partition	*vpart;
+	int			nblks;
+	int			i;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/*
+	 * Return vtoc structure fields in the provided VTOC area, addressed
+	 * by *vtoc.
+	 */
+	bzero(user_vtoc, sizeof (struct vtoc));
+	user_vtoc->v_bootinfo[0] = un->un_vtoc.v_bootinfo[0];
+	user_vtoc->v_bootinfo[1] = un->un_vtoc.v_bootinfo[1];
+	user_vtoc->v_bootinfo[2] = un->un_vtoc.v_bootinfo[2];
+	user_vtoc->v_sanity	= VTOC_SANE;
+	user_vtoc->v_version	= un->un_vtoc.v_version;
+	bcopy(un->un_vtoc.v_volume, user_vtoc->v_volume, LEN_DKL_VVOL);
+	user_vtoc->v_sectorsz = un->un_sys_blocksize;
+	user_vtoc->v_nparts = un->un_vtoc.v_nparts;
+
+	for (i = 0; i < 10; i++)
+		user_vtoc->v_reserved[i] = un->un_vtoc.v_reserved[i];
+
+	/*
+	 * Convert partitioning information.
+	 *
+	 * Note the conversion from starting cylinder number
+	 * to starting sector number.
+	 */
+	lmap = un->un_map;
+	lpart = (struct dk_map2 *)un->un_vtoc.v_part;
+	vpart = user_vtoc->v_part;
+
+	nblks = un->un_g.dkg_nsect * un->un_g.dkg_nhead;
+
+	for (i = 0; i < V_NUMPAR; i++) {
+		vpart->p_tag	= lpart->p_tag;
+		vpart->p_flag	= lpart->p_flag;
+		vpart->p_start	= lmap->dkl_cylno * nblks;
+		vpart->p_size	= lmap->dkl_nblk;
+		lmap++;
+		lpart++;
+		vpart++;
+
+		/* (4364927) */
+		user_vtoc->timestamp[i] = (time_t)un->un_vtoc.v_timestamp[i];
+	}
+
+	bcopy(un->un_asciilabel, user_vtoc->v_asciilabel, LEN_DKL_ASCII);
+}
+
+static int
+cmlb_dkio_partition(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct partition64	p64;
+	int			rval = 0;
+	uint_t			nparts;
+	efi_gpe_t		*partitions;
+	efi_gpt_t		*buffer;
+	diskaddr_t		gpe_lba;
+
+	if (ddi_copyin((const void *)arg, &p64,
+	    sizeof (struct partition64), flag)) {
+		return (EFAULT);
+	}
+
+	buffer = kmem_alloc(EFI_MIN_ARRAY_SIZE, KM_SLEEP);
+	rval = DK_TG_READ(un, buffer, 1, DEV_BSIZE);
+	if (rval != 0)
+		goto done_error;
+
+	cmlb_swap_efi_gpt(buffer);
+
+	if ((rval = cmlb_validate_efi(buffer)) != 0)
+		goto done_error;
+
+	nparts = buffer->efi_gpt_NumberOfPartitionEntries;
+	gpe_lba = buffer->efi_gpt_PartitionEntryLBA;
+	if (p64.p_partno > nparts) {
+		/* couldn't find it */
+		rval = ESRCH;
+		goto done_error;
+	}
+	/*
+	 * if we're dealing with a partition that's out of the normal
+	 * 16K block, adjust accordingly
+	 */
+	gpe_lba += p64.p_partno / sizeof (efi_gpe_t);
+	rval = DK_TG_READ(un, buffer, gpe_lba, EFI_MIN_ARRAY_SIZE);
+
+	if (rval) {
+		goto done_error;
+	}
+	partitions = (efi_gpe_t *)buffer;
+
+	cmlb_swap_efi_gpe(nparts, partitions);
+
+	partitions += p64.p_partno;
+	bcopy(&partitions->efi_gpe_PartitionTypeGUID, &p64.p_type,
+	    sizeof (struct uuid));
+	p64.p_start = partitions->efi_gpe_StartingLBA;
+	p64.p_size = partitions->efi_gpe_EndingLBA -
+			p64.p_start + 1;
+
+	if (ddi_copyout(&p64, (void *)arg, sizeof (struct partition64), flag))
+		rval = EFAULT;
+
+done_error:
+	kmem_free(buffer, EFI_MIN_ARRAY_SIZE);
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_dkio_set_vtoc
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to set the current volume table of contents
+ *		(DKIOCSVTOC).
+ *
+ *   Arguments: dev  - the device number
+ *		arg  - pointer to user provided vtoc structure used to set the
+ *			current vtoc.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EFAULT
+ *		ENXIO
+ *		EINVAL
+ *		ENOTSUP
+ */
+static int
+cmlb_dkio_set_vtoc(struct cmlb_lun *un, dev_t dev, caddr_t arg, int flag)
+{
+	struct vtoc	user_vtoc;
+	int		rval = 0;
+
+#ifdef _MULTI_DATAMODEL
+	switch (ddi_model_convert_from(flag & FMODELS)) {
+	case DDI_MODEL_ILP32: {
+		struct vtoc32 user_vtoc32;
+
+		if (ddi_copyin((const void *)arg, &user_vtoc32,
+		    sizeof (struct vtoc32), flag)) {
+			return (EFAULT);
+		}
+		vtoc32tovtoc(user_vtoc32, user_vtoc);
+		break;
+	}
+
+	case DDI_MODEL_NONE:
+		if (ddi_copyin((const void *)arg, &user_vtoc,
+		    sizeof (struct vtoc), flag)) {
+			return (EFAULT);
+		}
+		break;
+	}
+#else /* ! _MULTI_DATAMODEL */
+	if (ddi_copyin((const void *)arg, &user_vtoc,
+	    sizeof (struct vtoc), flag)) {
+		return (EFAULT);
+	}
+#endif /* _MULTI_DATAMODEL */
+
+	mutex_enter(CMLB_MUTEX(un));
+	if (un->un_blockcount > DK_MAX_BLOCKS) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (ENOTSUP);
+	}
+	if (un->un_g.dkg_ncyl == 0) {
+		mutex_exit(CMLB_MUTEX(un));
+		return (EINVAL);
+	}
+
+	mutex_exit(CMLB_MUTEX(un));
+	cmlb_clear_efi(un);
+	ddi_remove_minor_node(CMLB_DEVINFO(un), "wd");
+	ddi_remove_minor_node(CMLB_DEVINFO(un), "wd,raw");
+	(void) ddi_create_minor_node(CMLB_DEVINFO(un), "h",
+	    S_IFBLK, (CMLBUNIT(dev) << CMLBUNIT_SHIFT) | WD_NODE,
+	    un->un_node_type, NULL);
+	(void) ddi_create_minor_node(CMLB_DEVINFO(un), "h,raw",
+	    S_IFCHR, (CMLBUNIT(dev) << CMLBUNIT_SHIFT) | WD_NODE,
+	    un->un_node_type, NULL);
+	mutex_enter(CMLB_MUTEX(un));
+
+	if ((rval = cmlb_build_label_vtoc(un, &user_vtoc)) == 0) {
+		if ((rval = cmlb_write_label(un)) == 0) {
+			if (cmlb_validate_geometry(un, 1) != 0) {
+				cmlb_dbg(CMLB_ERROR, un,
+				    "cmlb_dkio_set_vtoc: "
+				    "Failed validate geometry\n");
+			}
+		}
+	}
+	mutex_exit(CMLB_MUTEX(un));
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_build_label_vtoc
+ *
+ * Description: This routine updates the driver soft state current volume table
+ *		of contents based on a user specified vtoc.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *		user_vtoc - pointer to vtoc structure specifying vtoc to be used
+ *			    to update the driver soft state.
+ *
+ * Return Code: 0
+ *		EINVAL
+ */
+static int
+cmlb_build_label_vtoc(struct cmlb_lun *un, struct vtoc *user_vtoc)
+{
+	struct dk_map		*lmap;
+	struct partition	*vpart;
+	int			nblks;
+#if defined(_SUNOS_VTOC_8)
+	int			ncyl;
+	struct dk_map2		*lpart;
+#endif	/* defined(_SUNOS_VTOC_8) */
+	int			i;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/* Sanity-check the vtoc */
+	if (user_vtoc->v_sanity != VTOC_SANE ||
+	    user_vtoc->v_sectorsz != un->un_sys_blocksize ||
+	    user_vtoc->v_nparts != V_NUMPAR) {
+		cmlb_dbg(CMLB_INFO,  un,
+		    "cmlb_build_label_vtoc: vtoc not valid\n");
+		return (EINVAL);
+	}
+
+	nblks = un->un_g.dkg_nsect * un->un_g.dkg_nhead;
+	if (nblks == 0) {
+		cmlb_dbg(CMLB_INFO,  un,
+		    "cmlb_build_label_vtoc: geom nblks is 0\n");
+		return (EINVAL);
+	}
+
+#if defined(_SUNOS_VTOC_8)
+	vpart = user_vtoc->v_part;
+	for (i = 0; i < V_NUMPAR; i++) {
+		if ((vpart->p_start % nblks) != 0) {
+			cmlb_dbg(CMLB_INFO,  un,
+			    "cmlb_build_label_vtoc: p_start not multiply of"
+			    "nblks part %d p_start %d nblks %d\n", i,
+			    vpart->p_start, nblks);
+			return (EINVAL);
+		}
+		ncyl = vpart->p_start / nblks;
+		ncyl += vpart->p_size / nblks;
+		if ((vpart->p_size % nblks) != 0) {
+			ncyl++;
+		}
+		if (ncyl > (int)un->un_g.dkg_ncyl) {
+			cmlb_dbg(CMLB_INFO,  un,
+			    "cmlb_build_label_vtoc: ncyl %d  > dkg_ncyl %d"
+			    "p_size %ld p_start %ld nblks %d  part number %d"
+			    "tag %d\n",
+			    ncyl, un->un_g.dkg_ncyl, vpart->p_size,
+			    vpart->p_start, nblks,
+			    i, vpart->p_tag);
+
+			return (EINVAL);
+		}
+		vpart++;
+	}
+#endif	/* defined(_SUNOS_VTOC_8) */
+
+	/* Put appropriate vtoc structure fields into the disk label */
+#if defined(_SUNOS_VTOC_16)
+	/*
+	 * The vtoc is always a 32bit data structure to maintain the
+	 * on-disk format. Convert "in place" instead of doing bcopy.
+	 */
+	vtoctovtoc32((*user_vtoc), (*((struct vtoc32 *)&(un->un_vtoc))));
+
+	/*
+	 * in the 16-slice vtoc, starting sectors are expressed in
+	 * numbers *relative* to the start of the Solaris fdisk partition.
+	 */
+	lmap = un->un_map;
+	vpart = user_vtoc->v_part;
+
+	for (i = 0; i < (int)user_vtoc->v_nparts; i++, lmap++, vpart++) {
+		lmap->dkl_cylno = vpart->p_start / nblks;
+		lmap->dkl_nblk = vpart->p_size;
+	}
+
+#elif defined(_SUNOS_VTOC_8)
+
+	un->un_vtoc.v_bootinfo[0] = (uint32_t)user_vtoc->v_bootinfo[0];
+	un->un_vtoc.v_bootinfo[1] = (uint32_t)user_vtoc->v_bootinfo[1];
+	un->un_vtoc.v_bootinfo[2] = (uint32_t)user_vtoc->v_bootinfo[2];
+
+	un->un_vtoc.v_sanity = (uint32_t)user_vtoc->v_sanity;
+	un->un_vtoc.v_version = (uint32_t)user_vtoc->v_version;
+
+	bcopy(user_vtoc->v_volume, un->un_vtoc.v_volume, LEN_DKL_VVOL);
+
+	un->un_vtoc.v_nparts = user_vtoc->v_nparts;
+
+	for (i = 0; i < 10; i++)
+		un->un_vtoc.v_reserved[i] =  user_vtoc->v_reserved[i];
+
+	/*
+	 * Note the conversion from starting sector number
+	 * to starting cylinder number.
+	 * Return error if division results in a remainder.
+	 */
+	lmap = un->un_map;
+	lpart = un->un_vtoc.v_part;
+	vpart = user_vtoc->v_part;
+
+	for (i = 0; i < (int)user_vtoc->v_nparts; i++) {
+		lpart->p_tag  = vpart->p_tag;
+		lpart->p_flag = vpart->p_flag;
+		lmap->dkl_cylno = vpart->p_start / nblks;
+		lmap->dkl_nblk = vpart->p_size;
+
+		lmap++;
+		lpart++;
+		vpart++;
+
+		/* (4387723) */
+#ifdef _LP64
+		if (user_vtoc->timestamp[i] > TIME32_MAX) {
+			un->un_vtoc.v_timestamp[i] = TIME32_MAX;
+		} else {
+			un->un_vtoc.v_timestamp[i] = user_vtoc->timestamp[i];
+		}
+#else
+		un->un_vtoc.v_timestamp[i] = user_vtoc->timestamp[i];
+#endif
+	}
+
+	bcopy(user_vtoc->v_asciilabel, un->un_asciilabel, LEN_DKL_ASCII);
+#else
+#error "No VTOC format defined."
+#endif
+	return (0);
+}
+
+/*
+ *    Function: cmlb_clear_efi
+ *
+ * Description: This routine clears all EFI labels.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return Code: void
+ */
+static void
+cmlb_clear_efi(struct cmlb_lun *un)
+{
+	efi_gpt_t	*gpt;
+	diskaddr_t	cap;
+	int		rval;
+
+	ASSERT(!mutex_owned(CMLB_MUTEX(un)));
+
+	gpt = kmem_alloc(sizeof (efi_gpt_t), KM_SLEEP);
+
+	if (DK_TG_READ(un, gpt, 1, DEV_BSIZE) != 0) {
+		goto done;
+	}
+
+	cmlb_swap_efi_gpt(gpt);
+	rval = cmlb_validate_efi(gpt);
+	if (rval == 0) {
+		/* clear primary */
+		bzero(gpt, sizeof (efi_gpt_t));
+		if (rval = DK_TG_WRITE(un, gpt, 1, EFI_LABEL_SIZE)) {
+			cmlb_dbg(CMLB_INFO,  un,
+				"cmlb_clear_efi: clear primary label failed\n");
+		}
+	}
+	/* the backup */
+	rval = DK_TG_GETCAP(un, &cap);
+	if (rval) {
+		goto done;
+	}
+
+	if ((rval = DK_TG_READ(un, gpt, cap - 1, EFI_LABEL_SIZE)) != 0) {
+		goto done;
+	}
+	cmlb_swap_efi_gpt(gpt);
+	rval = cmlb_validate_efi(gpt);
+	if (rval == 0) {
+		/* clear backup */
+		cmlb_dbg(CMLB_TRACE,  un,
+		    "cmlb_clear_efi clear backup@%lu\n", cap - 1);
+		bzero(gpt, sizeof (efi_gpt_t));
+		if ((rval = DK_TG_WRITE(un, gpt, cap - 1, EFI_LABEL_SIZE))) {
+			cmlb_dbg(CMLB_INFO,  un,
+				"cmlb_clear_efi: clear backup label failed\n");
+		}
+	}
+
+done:
+	kmem_free(gpt, sizeof (efi_gpt_t));
+}
+
+/*
+ *    Function: cmlb_set_vtoc
+ *
+ * Description: This routine writes data to the appropriate positions
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *              dkl  - the data to be written
+ *
+ * Return: void
+ */
+static int
+cmlb_set_vtoc(struct cmlb_lun *un, struct dk_label *dkl)
+{
+	uint_t	label_addr;
+	int	sec;
+	int	blk;
+	int	head;
+	int	cyl;
+	int	rval;
+
+#if defined(__i386) || defined(__amd64)
+	label_addr = un->un_solaris_offset + DK_LABEL_LOC;
+#else
+	/* Write the primary label at block 0 of the solaris partition. */
+	label_addr = 0;
+#endif
+
+	rval = DK_TG_WRITE(un, dkl, label_addr, un->un_sys_blocksize);
+
+	if (rval != 0) {
+		return (rval);
+	}
+
+	/*
+	 * Calculate where the backup labels go.  They are always on
+	 * the last alternate cylinder, but some older drives put them
+	 * on head 2 instead of the last head.	They are always on the
+	 * first 5 odd sectors of the appropriate track.
+	 *
+	 * We have no choice at this point, but to believe that the
+	 * disk label is valid.	 Use the geometry of the disk
+	 * as described in the label.
+	 */
+	cyl  = dkl->dkl_ncyl  + dkl->dkl_acyl - 1;
+	head = dkl->dkl_nhead - 1;
+
+	/*
+	 * Write and verify the backup labels. Make sure we don't try to
+	 * write past the last cylinder.
+	 */
+	for (sec = 1; ((sec < 5 * 2 + 1) && (sec < dkl->dkl_nsect)); sec += 2) {
+		blk = (daddr_t)(
+		    (cyl * ((dkl->dkl_nhead * dkl->dkl_nsect) - dkl->dkl_apc)) +
+		    (head * dkl->dkl_nsect) + sec);
+#if defined(__i386) || defined(__amd64)
+		blk += un->un_solaris_offset;
+#endif
+		rval = DK_TG_WRITE(un, dkl, blk, un->un_sys_blocksize);
+		cmlb_dbg(CMLB_INFO,  un,
+		"cmlb_set_vtoc: wrote backup label %d\n", blk);
+		if (rval != 0) {
+			goto exit;
+		}
+	}
+exit:
+	return (rval);
+}
+
+/*
+ *    Function: cmlb_clear_vtoc
+ *
+ * Description: This routine clears out the VTOC labels.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return: void
+ */
+static void
+cmlb_clear_vtoc(struct cmlb_lun *un)
+{
+	struct dk_label		*dkl;
+
+	mutex_exit(CMLB_MUTEX(un));
+	dkl = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
+	mutex_enter(CMLB_MUTEX(un));
+	/*
+	 * cmlb_set_vtoc uses these fields in order to figure out
+	 * where to overwrite the backup labels
+	 */
+	dkl->dkl_apc    = un->un_g.dkg_apc;
+	dkl->dkl_ncyl   = un->un_g.dkg_ncyl;
+	dkl->dkl_acyl   = un->un_g.dkg_acyl;
+	dkl->dkl_nhead  = un->un_g.dkg_nhead;
+	dkl->dkl_nsect  = un->un_g.dkg_nsect;
+	mutex_exit(CMLB_MUTEX(un));
+	(void) cmlb_set_vtoc(un, dkl);
+	kmem_free(dkl, sizeof (struct dk_label));
+
+	mutex_enter(CMLB_MUTEX(un));
+}
+
+/*
+ *    Function: cmlb_write_label
+ *
+ * Description: This routine will validate and write the driver soft state vtoc
+ *		contents to the device.
+ *
+ *   Arguments: un	cmlb handle
+ *
+ * Return Code: the code returned by cmlb_send_scsi_cmd()
+ *		0
+ *		EINVAL
+ *		ENXIO
+ *		ENOMEM
+ */
+static int
+cmlb_write_label(struct cmlb_lun *un)
+{
+	struct dk_label	*dkl;
+	short		sum;
+	short		*sp;
+	int		i;
+	int		rval;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+	mutex_exit(CMLB_MUTEX(un));
+	dkl = kmem_zalloc(sizeof (struct dk_label), KM_SLEEP);
+	mutex_enter(CMLB_MUTEX(un));
+
+	bcopy(&un->un_vtoc, &dkl->dkl_vtoc, sizeof (struct dk_vtoc));
+	dkl->dkl_rpm	= un->un_g.dkg_rpm;
+	dkl->dkl_pcyl	= un->un_g.dkg_pcyl;
+	dkl->dkl_apc	= un->un_g.dkg_apc;
+	dkl->dkl_intrlv = un->un_g.dkg_intrlv;
+	dkl->dkl_ncyl	= un->un_g.dkg_ncyl;
+	dkl->dkl_acyl	= un->un_g.dkg_acyl;
+	dkl->dkl_nhead	= un->un_g.dkg_nhead;
+	dkl->dkl_nsect	= un->un_g.dkg_nsect;
+
+#if defined(_SUNOS_VTOC_8)
+	dkl->dkl_obs1	= un->un_g.dkg_obs1;
+	dkl->dkl_obs2	= un->un_g.dkg_obs2;
+	dkl->dkl_obs3	= un->un_g.dkg_obs3;
+	for (i = 0; i < NDKMAP; i++) {
+		dkl->dkl_map[i].dkl_cylno = un->un_map[i].dkl_cylno;
+		dkl->dkl_map[i].dkl_nblk  = un->un_map[i].dkl_nblk;
+	}
+	bcopy(un->un_asciilabel, dkl->dkl_asciilabel, LEN_DKL_ASCII);
+#elif defined(_SUNOS_VTOC_16)
+	dkl->dkl_skew	= un->un_dkg_skew;
+#else
+#error "No VTOC format defined."
+#endif
+
+	dkl->dkl_magic			= DKL_MAGIC;
+	dkl->dkl_write_reinstruct	= un->un_g.dkg_write_reinstruct;
+	dkl->dkl_read_reinstruct	= un->un_g.dkg_read_reinstruct;
+
+	/* Construct checksum for the new disk label */
+	sum = 0;
+	sp = (short *)dkl;
+	i = sizeof (struct dk_label) / sizeof (short);
+	while (i--) {
+		sum ^= *sp++;
+	}
+	dkl->dkl_cksum = sum;
+
+	mutex_exit(CMLB_MUTEX(un));
+
+	rval = cmlb_set_vtoc(un, dkl);
+exit:
+	kmem_free(dkl, sizeof (struct dk_label));
+	mutex_enter(CMLB_MUTEX(un));
+	return (rval);
+}
+
+static int
+cmlb_dkio_set_efi(struct cmlb_lun *un, dev_t dev, caddr_t arg, int flag)
+{
+	dk_efi_t	user_efi;
+	int		rval = 0;
+	void		*buffer;
+
+	if (ddi_copyin(arg, &user_efi, sizeof (dk_efi_t), flag))
+		return (EFAULT);
+
+	user_efi.dki_data = (void *)(uintptr_t)user_efi.dki_data_64;
+
+	buffer = kmem_alloc(user_efi.dki_length, KM_SLEEP);
+	if (ddi_copyin(user_efi.dki_data, buffer, user_efi.dki_length, flag)) {
+		rval = EFAULT;
+	} else {
+		/*
+		 * let's clear the vtoc labels and clear the softstate
+		 * vtoc.
+		 */
+		mutex_enter(CMLB_MUTEX(un));
+		if (un->un_vtoc.v_sanity == VTOC_SANE) {
+			cmlb_dbg(CMLB_TRACE,  un,
+				"cmlb_dkio_set_efi: CLEAR VTOC\n");
+			if (un->un_vtoc_label_is_from_media)
+				cmlb_clear_vtoc(un);
+			bzero(&un->un_vtoc, sizeof (struct dk_vtoc));
+			mutex_exit(CMLB_MUTEX(un));
+			ddi_remove_minor_node(CMLB_DEVINFO(un), "h");
+			ddi_remove_minor_node(CMLB_DEVINFO(un), "h,raw");
+			(void) ddi_create_minor_node(CMLB_DEVINFO(un), "wd",
+			    S_IFBLK,
+			    (CMLBUNIT(dev) << CMLBUNIT_SHIFT) | WD_NODE,
+			    un->un_node_type, NULL);
+			(void) ddi_create_minor_node(CMLB_DEVINFO(un), "wd,raw",
+			    S_IFCHR,
+			    (CMLBUNIT(dev) << CMLBUNIT_SHIFT) | WD_NODE,
+			    un->un_node_type, NULL);
+		} else
+			mutex_exit(CMLB_MUTEX(un));
+		rval = DK_TG_WRITE(un, buffer, user_efi.dki_lba,
+		    user_efi.dki_length);
+		if (rval == 0) {
+			mutex_enter(CMLB_MUTEX(un));
+			un->un_f_geometry_is_valid = FALSE;
+			mutex_exit(CMLB_MUTEX(un));
+		}
+	}
+	kmem_free(buffer, user_efi.dki_length);
+	return (rval);
+}
+
+/*
+ *    Function: cmlb_dkio_get_mboot
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to get the current device mboot (DKIOCGMBOOT)
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided mboot structure specifying
+ *			the current mboot.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EINVAL
+ *		EFAULT
+ *		ENXIO
+ */
+static int
+cmlb_dkio_get_mboot(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct mboot	*mboot;
+	int		rval;
+	size_t		buffer_size;
+
+
+#if defined(_SUNOS_VTOC_8)
+	if ((!ISREMOVABLE(un)) || (arg == NULL)) {
+#elif defined(_SUNOS_VTOC_16)
+	if (arg == NULL) {
+#endif
+		return (EINVAL);
+	}
+
+	/*
+	 * Read the mboot block, located at absolute block 0 on the target.
+	 */
+	buffer_size = sizeof (struct mboot);
+
+	cmlb_dbg(CMLB_TRACE,  un,
+	    "cmlb_dkio_get_mboot: allocation size: 0x%x\n", buffer_size);
+
+	mboot = kmem_zalloc(buffer_size, KM_SLEEP);
+	if ((rval = DK_TG_READ(un, mboot, 0, buffer_size)) == 0) {
+		if (ddi_copyout(mboot, (void *)arg,
+		    sizeof (struct mboot), flag) != 0) {
+			rval = EFAULT;
+		}
+	}
+	kmem_free(mboot, buffer_size);
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_dkio_set_mboot
+ *
+ * Description: This routine is the driver entry point for handling user
+ *		requests to validate and set the device master boot
+ *		(DKIOCSMBOOT).
+ *
+ *   Arguments:
+ *		arg  - pointer to user provided mboot structure used to set the
+ *			master boot.
+ *		flag - this argument is a pass through to ddi_copyxxx()
+ *		       directly from the mode argument of ioctl().
+ *
+ * Return Code: 0
+ *		EINVAL
+ *		EFAULT
+ *		ENXIO
+ */
+static int
+cmlb_dkio_set_mboot(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	struct mboot	*mboot = NULL;
+	int		rval;
+	ushort_t	magic;
+
+
+	ASSERT(!mutex_owned(CMLB_MUTEX(un)));
+
+#if defined(_SUNOS_VTOC_8)
+	if (!ISREMOVABLE(un)) {
+		return (EINVAL);
+	}
+#endif
+
+	if (arg == NULL) {
+		return (EINVAL);
+	}
+
+	mboot = kmem_zalloc(sizeof (struct mboot), KM_SLEEP);
+
+	if (ddi_copyin((const void *)arg, mboot,
+	    sizeof (struct mboot), flag) != 0) {
+		kmem_free(mboot, (size_t)(sizeof (struct mboot)));
+		return (EFAULT);
+	}
+
+	/* Is this really a master boot record? */
+	magic = LE_16(mboot->signature);
+	if (magic != MBB_MAGIC) {
+		kmem_free(mboot, (size_t)(sizeof (struct mboot)));
+		return (EINVAL);
+	}
+
+	rval = DK_TG_WRITE(un, mboot, 0, un->un_sys_blocksize);
+
+	mutex_enter(CMLB_MUTEX(un));
+#if defined(__i386) || defined(__amd64)
+	if (rval == 0) {
+		/*
+		 * mboot has been written successfully.
+		 * update the fdisk and vtoc tables in memory
+		 */
+		rval = cmlb_update_fdisk_and_vtoc(un);
+		if ((un->un_f_geometry_is_valid == FALSE) || (rval != 0)) {
+			mutex_exit(CMLB_MUTEX(un));
+			kmem_free(mboot, (size_t)(sizeof (struct mboot)));
+			return (rval);
+		}
+	}
+#else
+	if (rval == 0) {
+		/*
+		 * mboot has been written successfully.
+		 * set up the default geometry and VTOC
+		 */
+		if (un->un_blockcount <= DK_MAX_BLOCKS)
+			cmlb_setup_default_geometry(un);
+	}
+#endif
+	mutex_exit(CMLB_MUTEX(un));
+	kmem_free(mboot, (size_t)(sizeof (struct mboot)));
+	return (rval);
+}
+
+
+/*
+ *    Function: cmlb_setup_default_geometry
+ *
+ * Description: This local utility routine sets the default geometry as part of
+ *		setting the device mboot.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Note: This may be redundant with cmlb_build_default_label.
+ */
+static void
+cmlb_setup_default_geometry(struct cmlb_lun *un)
+{
+	struct cmlb_geom	pgeom;
+	struct cmlb_geom	*pgeomp = &pgeom;
+	int			ret;
+	int			geom_base_cap = 1;
+
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	/* zero out the soft state geometry and partition table. */
+	bzero(&un->un_g, sizeof (struct dk_geom));
+	bzero(&un->un_vtoc, sizeof (struct dk_vtoc));
+	bzero(un->un_map, NDKMAP * (sizeof (struct dk_map)));
+
+	/*
+	 * For the rpm, we use the minimum for the disk.
+	 * For the head, cyl and number of sector per track,
+	 * if the capacity <= 1GB, head = 64, sect = 32.
+	 * else head = 255, sect 63
+	 * Note: the capacity should be equal to C*H*S values.
+	 * This will cause some truncation of size due to
+	 * round off errors. For CD-ROMs, this truncation can
+	 * have adverse side effects, so returning ncyl and
+	 * nhead as 1. The nsect will overflow for most of
+	 * CD-ROMs as nsect is of type ushort.
+	 */
+	if (un->un_alter_behavior & CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8) {
+		/*
+		 * newfs currently can not handle 255 ntracks for SPARC
+		 * so get the geometry from target driver instead of coming up
+		 * with one based on capacity.
+		 */
+		mutex_exit(CMLB_MUTEX(un));
+		ret = DK_TG_GETPHYGEOM(un, pgeomp);
+		mutex_enter(CMLB_MUTEX(un));
+
+		if (ret  == 0) {
+			geom_base_cap = 0;
+		} else {
+			cmlb_dbg(CMLB_ERROR,  un,
+			    "cmlb_setup_default_geometry: "
+			    "tg_getphygeom failed %d\n", ret);
+
+			/* do default setting, geometry based on capacity */
+		}
+	}
+
+	if (geom_base_cap) {
+		if (ISCD(un)) {
+			un->un_g.dkg_ncyl = 1;
+			un->un_g.dkg_nhead = 1;
+			un->un_g.dkg_nsect = un->un_blockcount;
+		} else if (un->un_blockcount <= 0x1000) {
+			/* Needed for unlabeled SCSI floppies. */
+			un->un_g.dkg_nhead = 2;
+			un->un_g.dkg_ncyl = 80;
+			un->un_g.dkg_pcyl = 80;
+			un->un_g.dkg_nsect = un->un_blockcount / (2 * 80);
+		} else if (un->un_blockcount <= 0x200000) {
+			un->un_g.dkg_nhead = 64;
+			un->un_g.dkg_nsect = 32;
+			un->un_g.dkg_ncyl = un->un_blockcount / (64 * 32);
+		} else {
+			un->un_g.dkg_nhead = 255;
+			un->un_g.dkg_nsect = 63;
+			un->un_g.dkg_ncyl = un->un_blockcount / (255 * 63);
+		}
+
+		un->un_g.dkg_acyl = 0;
+		un->un_g.dkg_bcyl = 0;
+		un->un_g.dkg_intrlv = 1;
+		un->un_g.dkg_rpm = 200;
+		if (un->un_g.dkg_pcyl == 0)
+			un->un_g.dkg_pcyl = un->un_g.dkg_ncyl +
+			    un->un_g.dkg_acyl;
+	} else {
+		un->un_g.dkg_ncyl = (short)pgeomp->g_ncyl;
+		un->un_g.dkg_acyl = pgeomp->g_acyl;
+		un->un_g.dkg_nhead = pgeomp->g_nhead;
+		un->un_g.dkg_nsect = pgeomp->g_nsect;
+		un->un_g.dkg_intrlv = pgeomp->g_intrlv;
+		un->un_g.dkg_rpm = pgeomp->g_rpm;
+		un->un_g.dkg_pcyl = un->un_g.dkg_ncyl + un->un_g.dkg_acyl;
+	}
+
+	un->un_g.dkg_read_reinstruct = 0;
+	un->un_g.dkg_write_reinstruct = 0;
+	un->un_solaris_size = un->un_g.dkg_ncyl *
+	    un->un_g.dkg_nhead * un->un_g.dkg_nsect;
+
+	un->un_map['a'-'a'].dkl_cylno = 0;
+	un->un_map['a'-'a'].dkl_nblk = un->un_solaris_size;
+
+	un->un_map['c'-'a'].dkl_cylno = 0;
+	un->un_map['c'-'a'].dkl_nblk = un->un_solaris_size;
+
+	un->un_vtoc.v_part[2].p_tag   = V_BACKUP;
+	un->un_vtoc.v_part[2].p_flag  = V_UNMNT;
+	un->un_vtoc.v_nparts = V_NUMPAR;
+	un->un_vtoc.v_version = V_VERSION;
+	(void) sprintf((char *)un->un_asciilabel, "DEFAULT cyl %d alt %d"
+	    " hd %d sec %d", un->un_g.dkg_ncyl, un->un_g.dkg_acyl,
+	    un->un_g.dkg_nhead, un->un_g.dkg_nsect);
+
+	un->un_f_geometry_is_valid = FALSE;
+}
+
+
+#if defined(__i386) || defined(__amd64)
+/*
+ *    Function: cmlb_update_fdisk_and_vtoc
+ *
+ * Description: This local utility routine updates the device fdisk and vtoc
+ *		as part of setting the device mboot.
+ *
+ *   Arguments: un - driver soft state (unit) structure
+ *
+ * Return Code: 0 for success or errno-type return code.
+ *
+ *    Note:x86: This looks like a duplicate of cmlb_validate_geometry(), but
+ *		these did exist separately in x86 sd.c.
+ */
+static int
+cmlb_update_fdisk_and_vtoc(struct cmlb_lun *un)
+{
+	int		count;
+	int		label_rc = 0;
+	int		fdisk_rval;
+	diskaddr_t	capacity;
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+
+	if (cmlb_check_update_blockcount(un) != 0)
+		return (EINVAL);
+
+#if defined(_SUNOS_VTOC_16)
+	/*
+	 * Set up the "whole disk" fdisk partition; this should always
+	 * exist, regardless of whether the disk contains an fdisk table
+	 * or vtoc.
+	 */
+	un->un_map[P0_RAW_DISK].dkl_cylno = 0;
+	un->un_map[P0_RAW_DISK].dkl_nblk = un->un_blockcount;
+#endif	/* defined(_SUNOS_VTOC_16) */
+
+	/*
+	 * copy the lbasize and capacity so that if they're
+	 * reset while we're not holding the CMLB_MUTEX(un), we will
+	 * continue to use valid values after the CMLB_MUTEX(un) is
+	 * reacquired.
+	 */
+	capacity = un->un_blockcount;
+
+	/*
+	 * refresh the logical and physical geometry caches.
+	 * (data from mode sense format/rigid disk geometry pages,
+	 * and scsi_ifgetcap("geometry").
+	 */
+	cmlb_resync_geom_caches(un, capacity);
+
+	/*
+	 * Only DIRECT ACCESS devices will have Sun labels.
+	 * CD's supposedly have a Sun label, too
+	 */
+	if (un->un_device_type == DTYPE_DIRECT || ISREMOVABLE(un)) {
+		fdisk_rval = cmlb_read_fdisk(un, capacity);
+		if (fdisk_rval != 0) {
+			ASSERT(mutex_owned(CMLB_MUTEX(un)));
+			return (fdisk_rval);
+		}
+
+		if (un->un_solaris_size <= DK_LABEL_LOC) {
+			/*
+			 * Found fdisk table but no Solaris partition entry,
+			 * so don't call cmlb_uselabel() and don't create
+			 * a default label.
+			 */
+			label_rc = 0;
+			un->un_f_geometry_is_valid = TRUE;
+			goto no_solaris_partition;
+		}
+	} else if (capacity < 0) {
+		ASSERT(mutex_owned(CMLB_MUTEX(un)));
+		return (EINVAL);
+	}
+
+	/*
+	 * For Removable media We reach here if we have found a
+	 * SOLARIS PARTITION.
+	 * If un_f_geometry_is_valid is FALSE it indicates that the SOLARIS
+	 * PARTITION has changed from the previous one, hence we will setup a
+	 * default VTOC in this case.
+	 */
+	if (un->un_f_geometry_is_valid == FALSE) {
+		/* if we get here it is writable */
+		/* we are called from SMBOOT, and after a write of fdisk */
+		cmlb_build_default_label(un);
+		label_rc = 0;
+	}
+
+no_solaris_partition:
+
+#if defined(_SUNOS_VTOC_16)
+	/*
+	 * If we have valid geometry, set up the remaining fdisk partitions.
+	 * Note that dkl_cylno is not used for the fdisk map entries, so
+	 * we set it to an entirely bogus value.
+	 */
+	for (count = 0; count < FD_NUMPART; count++) {
+		un->un_map[FDISK_P1 + count].dkl_cylno = -1;
+		un->un_map[FDISK_P1 + count].dkl_nblk =
+		    un->un_fmap[count].fmap_nblk;
+		un->un_offset[FDISK_P1 + count] =
+		    un->un_fmap[count].fmap_start;
+	}
+#endif
+
+	for (count = 0; count < NDKMAP; count++) {
+#if defined(_SUNOS_VTOC_8)
+		struct dk_map *lp  = &un->un_map[count];
+		un->un_offset[count] =
+		    un->un_g.dkg_nhead * un->un_g.dkg_nsect * lp->dkl_cylno;
+#elif defined(_SUNOS_VTOC_16)
+		struct dkl_partition *vp = &un->un_vtoc.v_part[count];
+		un->un_offset[count] = vp->p_start + un->un_solaris_offset;
+#else
+#error "No VTOC format defined."
+#endif
+	}
+
+	ASSERT(mutex_owned(CMLB_MUTEX(un)));
+	return (label_rc);
+}
+#endif
+
+#if defined(__i386) || defined(__amd64)
+static int
+cmlb_dkio_get_virtgeom(struct cmlb_lun *un, caddr_t arg, int flag)
+{
+	int err = 0;
+
+	/* Return the driver's notion of the media's logical geometry */
+	struct dk_geom	disk_geom;
+	struct dk_geom	*dkgp = &disk_geom;
+
+	mutex_enter(CMLB_MUTEX(un));
+	/*
+	 * If there is no HBA geometry available, or
+	 * if the HBA returned us something that doesn't
+	 * really fit into an Int 13/function 8 geometry
+	 * result, just fail the ioctl.  See PSARC 1998/313.
+	 */
+	if (un->un_lgeom.g_nhead == 0 ||
+	    un->un_lgeom.g_nsect == 0 ||
+	    un->un_lgeom.g_ncyl > 1024) {
+		mutex_exit(CMLB_MUTEX(un));
+		err = EINVAL;
+	} else {
+		dkgp->dkg_ncyl	= un->un_lgeom.g_ncyl;
+		dkgp->dkg_acyl	= un->un_lgeom.g_acyl;
+		dkgp->dkg_pcyl	= dkgp->dkg_ncyl + dkgp->dkg_acyl;
+		dkgp->dkg_nhead	= un->un_lgeom.g_nhead;
+		dkgp->dkg_nsect	= un->un_lgeom.g_nsect;
+
+		if (ddi_copyout(dkgp, (void *)arg,
+		    sizeof (struct dk_geom), flag)) {
+			mutex_exit(CMLB_MUTEX(un));
+			err = EFAULT;
+		} else {
+			mutex_exit(CMLB_MUTEX(un));
+			err = 0;
+		}
+	}
+	return (err);
+}
+#endif
+
+#if defined(__i386) || defined(__amd64)
+static int
+cmlb_dkio_get_phygeom(struct cmlb_lun *un, caddr_t  arg, int flag)
+{
+	int err = 0;
+
+
+	/* Return the driver's notion of the media physical geometry */
+	struct dk_geom	disk_geom;
+	struct dk_geom	*dkgp = &disk_geom;
+
+	mutex_enter(CMLB_MUTEX(un));
+
+	if (un->un_g.dkg_nhead != 0 &&
+	    un->un_g.dkg_nsect != 0) {
+		/*
+		 * We succeeded in getting a geometry, but
+		 * right now it is being reported as just the
+		 * Solaris fdisk partition, just like for
+		 * DKIOCGGEOM. We need to change that to be
+		 * correct for the entire disk now.
+		 */
+		bcopy(&un->un_g, dkgp, sizeof (*dkgp));
+		dkgp->dkg_acyl = 0;
+		dkgp->dkg_ncyl = un->un_blockcount /
+		    (dkgp->dkg_nhead * dkgp->dkg_nsect);
+	} else {
+		bzero(dkgp, sizeof (struct dk_geom));
+		/*
+		 * This disk does not have a Solaris VTOC
+		 * so we must present a physical geometry
+		 * that will remain consistent regardless
+		 * of how the disk is used. This will ensure
+		 * that the geometry does not change regardless
+		 * of the fdisk partition type (ie. EFI, FAT32,
+		 * Solaris, etc).
+		 */
+		if (ISCD(un)) {
+			dkgp->dkg_nhead = un->un_pgeom.g_nhead;
+			dkgp->dkg_nsect = un->un_pgeom.g_nsect;
+			dkgp->dkg_ncyl = un->un_pgeom.g_ncyl;
+			dkgp->dkg_acyl = un->un_pgeom.g_acyl;
+		} else {
+			cmlb_convert_geometry(un->un_blockcount, dkgp);
+			dkgp->dkg_acyl = 0;
+			dkgp->dkg_ncyl = un->un_blockcount /
+			    (dkgp->dkg_nhead * dkgp->dkg_nsect);
+		}
+	}
+	dkgp->dkg_pcyl = dkgp->dkg_ncyl + dkgp->dkg_acyl;
+
+	if (ddi_copyout(dkgp, (void *)arg,
+	    sizeof (struct dk_geom), flag)) {
+		mutex_exit(CMLB_MUTEX(un));
+		err = EFAULT;
+	} else {
+		mutex_exit(CMLB_MUTEX(un));
+		err = 0;
+	}
+	return (err);
+}
+#endif
+
+#if defined(__i386) || defined(__amd64)
+static int
+cmlb_dkio_partinfo(struct cmlb_lun *un, dev_t dev, caddr_t  arg, int flag)
+{
+	int err = 0;
+
+	/*
+	 * Return parameters describing the selected disk slice.
+	 * Note: this ioctl is for the intel platform only
+	 */
+	int part;
+
+	part = CMLBPART(dev);
+
+	/* don't check un_solaris_size for pN */
+	if (part < P0_RAW_DISK && un->un_solaris_size == 0) {
+		err = EIO;
+	} else {
+		struct part_info p;
+
+		p.p_start = (daddr_t)un->un_offset[part];
+		p.p_length = (int)un->un_map[part].dkl_nblk;
+#ifdef _MULTI_DATAMODEL
+		switch (ddi_model_convert_from(flag & FMODELS)) {
+		case DDI_MODEL_ILP32:
+		{
+			struct part_info32 p32;
+
+			p32.p_start = (daddr32_t)p.p_start;
+			p32.p_length = p.p_length;
+			if (ddi_copyout(&p32, (void *)arg,
+			    sizeof (p32), flag))
+				err = EFAULT;
+			break;
+		}
+
+		case DDI_MODEL_NONE:
+		{
+			if (ddi_copyout(&p, (void *)arg, sizeof (p),
+			    flag))
+				err = EFAULT;
+			break;
+		}
+		}
+#else /* ! _MULTI_DATAMODEL */
+		if (ddi_copyout(&p, (void *)arg, sizeof (p), flag))
+			err = EFAULT;
+#endif /* _MULTI_DATAMODEL */
+	}
+	return (err);
+}
+#endif
--- a/usr/src/uts/common/io/dktp/disk/cmdk.c	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/common/io/dktp/disk/cmdk.c	Fri Oct 28 23:47:40 2005 -0700
@@ -37,15 +37,17 @@
 #include <sys/dktp/tgdk.h>
 #include <sys/dktp/dadk.h>
 #include <sys/dktp/bbh.h>
+#include <sys/dktp/altsctr.h>
+#include <sys/dktp/cmdk.h>
 
-#include <sys/dktp/cmdk.h>
 #include <sys/stat.h>
 #include <sys/vtoc.h>
 #include <sys/file.h>
 #include <sys/dktp/dadkio.h>
-#include <sys/dktp/dklb.h>
 #include <sys/aio_req.h>
 
+#include <sys/cmlb.h>
+
 /*
  * Local Static Data
  */
@@ -90,82 +92,25 @@
  */
 static int	cmdk_indump;
 
-static struct driver_minor_data {
-	char    *name;
-	int	minor;
-	int	type;
-} cmdk_minor_data[] = {
-	{"a", 0, S_IFBLK},
-	{"b", 1, S_IFBLK},
-	{"c", 2, S_IFBLK},
-	{"d", 3, S_IFBLK},
-	{"e", 4, S_IFBLK},
-	{"f", 5, S_IFBLK},
-	{"g", 6, S_IFBLK},
-	{"h", 7, S_IFBLK},
-	{"i", 8, S_IFBLK},
-	{"j", 9, S_IFBLK},
-	{"k", 10, S_IFBLK},
-	{"l", 11, S_IFBLK},
-	{"m", 12, S_IFBLK},
-	{"n", 13, S_IFBLK},
-	{"o", 14, S_IFBLK},
-	{"p", 15, S_IFBLK},
-	{"q", 16, S_IFBLK},
-	{"r", 17, S_IFBLK},
-	{"s", 18, S_IFBLK},
-	{"t", 19, S_IFBLK},
-	{"u", 20, S_IFBLK},
-	{"a,raw", 0, S_IFCHR},
-	{"b,raw", 1, S_IFCHR},
-	{"c,raw", 2, S_IFCHR},
-	{"d,raw", 3, S_IFCHR},
-	{"e,raw", 4, S_IFCHR},
-	{"f,raw", 5, S_IFCHR},
-	{"g,raw", 6, S_IFCHR},
-	{"h,raw", 7, S_IFCHR},
-	{"i,raw", 8, S_IFCHR},
-	{"j,raw", 9, S_IFCHR},
-	{"k,raw", 10, S_IFCHR},
-	{"l,raw", 11, S_IFCHR},
-	{"m,raw", 12, S_IFCHR},
-	{"n,raw", 13, S_IFCHR},
-	{"o,raw", 14, S_IFCHR},
-	{"p,raw", 15, S_IFCHR},
-	{"q,raw", 16, S_IFCHR},
-	{"r,raw", 17, S_IFCHR},
-	{"s,raw", 18, S_IFCHR},
-	{"t,raw", 19, S_IFCHR},
-	{"u,raw", 20, S_IFCHR},
-	{0}
-};
-
 /*
  * Local Function Prototypes
  */
-static int cmdk_reopen(struct cmdk *dkp);
 static int cmdk_create_obj(dev_info_t *dip, struct cmdk *dkp);
 static void cmdk_destroy_obj(dev_info_t *dip, struct cmdk *dkp);
-static int cmdk_create_lbobj(dev_info_t *dip, struct cmdk *dkp);
-static void cmdk_destroy_lbobj(dev_info_t *dip, struct cmdk *dkp, int unload);
 static void cmdkmin(struct buf *bp);
 static int cmdkrw(dev_t dev, struct uio *uio, int flag);
 static int cmdkarw(dev_t dev, struct aio_req *aio, int flag);
-static int cmdk_part_info(struct cmdk *dkp, int force, daddr_t *startp,
-    long *countp, int part);
-static void cmdk_part_info_init(struct cmdk *dkp);
-static void cmdk_part_info_fini(struct cmdk *dkp);
-
-#ifdef	NOT_USED
-static void cmdk_devstatus(struct cmdk *dkp);
-#endif	/* NOT_USED */
 
 /*
  * Bad Block Handling Functions Prototypes
  */
+static void cmdk_bbh_reopen(struct cmdk *dkp);
 static opaque_t cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp);
 static bbh_cookie_t cmdk_bbh_htoc(opaque_t bbh_data, opaque_t handle);
 static void cmdk_bbh_freehandle(opaque_t bbh_data, opaque_t handle);
+static void cmdk_bbh_close(struct cmdk *dkp);
+static void cmdk_bbh_setalts_idx(struct cmdk *dkp);
+static int cmdk_bbh_bsearch(struct alts_ent *buf, int cnt, daddr32_t key);
 
 static struct bbh_objops cmdk_bbh_ops = {
 	nulldev,
@@ -176,11 +121,6 @@
 	0, 0
 };
 
-static struct bbh_obj cmdk_bbh_obj = {
-	NULL,
-	&cmdk_bbh_ops
-};
-
 static int cmdkopen(dev_t *dev_p, int flag, int otyp, cred_t *credp);
 static int cmdkclose(dev_t dev, int flag, int otyp, cred_t *credp);
 static int cmdkstrategy(struct buf *bp);
@@ -194,10 +134,6 @@
 static int cmdkawrite(dev_t dev, struct aio_req *aio, cred_t *credp);
 
 /*
- * Configuration Data
- */
-
-/*
  * Device driver ops vector
  */
 
@@ -258,6 +194,30 @@
 	MODREV_1, (void *)&modldrv, NULL
 };
 
+/* Function prototypes for cmlb callbacks */
+
+static int cmdk_lb_rdwr(dev_info_t *dip, uchar_t cmd, void *bufaddr,
+    diskaddr_t start, size_t length);
+static int cmdk_lb_getphygeom(dev_info_t *dip,  cmlb_geom_t *phygeomp);
+static int cmdk_lb_getvirtgeom(dev_info_t *dip,  cmlb_geom_t *virtgeomp);
+static int cmdk_lb_getcapacity(dev_info_t *dip, diskaddr_t *capp);
+static int cmdk_lb_getattribute(dev_info_t *dip, tg_attribute_t *tgattribute);
+
+static void cmdk_devid_setup(struct cmdk *dkp);
+static int cmdk_devid_modser(struct cmdk *dkp);
+static int cmdk_get_modser(struct cmdk *dkp, int ioccmd, char *buf, int len);
+static int cmdk_devid_fabricate(struct cmdk *dkp);
+static int cmdk_devid_read(struct cmdk *dkp);
+
+static cmlb_tg_ops_t cmdk_lb_ops = {
+	TG_DK_OPS_VERSION_0,
+	cmdk_lb_rdwr,
+	cmdk_lb_getphygeom,
+	cmdk_lb_getvirtgeom,
+	cmdk_lb_getcapacity,
+	cmdk_lb_getattribute
+};
+
 int
 _init(void)
 {
@@ -305,27 +265,6 @@
 	return (mod_info(&modlinkage, modinfop));
 }
 
-/*	pseudo BBH functions						*/
-/*ARGSUSED*/
-static opaque_t
-cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp)
-{
-	return (NULL);
-}
-
-/*ARGSUSED*/
-static bbh_cookie_t
-cmdk_bbh_htoc(opaque_t bbh_data, opaque_t handle)
-{
-	return (NULL);
-}
-
-/*ARGSUSED*/
-static void
-cmdk_bbh_freehandle(opaque_t bbh_data, opaque_t handle)
-{
-}
-
 /*
  * Autoconfiguration Routines
  */
@@ -345,26 +284,34 @@
 	    ((dkp = ddi_get_soft_state(cmdk_state, instance)) == NULL))
 		return (DDI_PROBE_PARTIAL);
 
+	mutex_init(&dkp->dk_mutex, NULL, MUTEX_DRIVER, NULL);
+	rw_init(&dkp->dk_bbh_mutex, NULL, RW_DRIVER, NULL);
 	dkp->dk_dip = dip;
+	mutex_enter(&dkp->dk_mutex);
 
-	/* for property create inside DKLB_*() */
 	dkp->dk_dev = makedevice(ddi_driver_major(dip),
 	    ddi_get_instance(dip) << CMDK_UNITSHF);
 
+	/* linkage to dadk and strategy */
 	if (cmdk_create_obj(dip, dkp) != DDI_SUCCESS) {
+		mutex_exit(&dkp->dk_mutex);
+		mutex_destroy(&dkp->dk_mutex);
+		rw_destroy(&dkp->dk_bbh_mutex);
 		ddi_soft_state_free(cmdk_state, instance);
 		return (DDI_PROBE_PARTIAL);
 	}
 
 	status = dadk_probe(DKTP_DATA, KM_NOSLEEP);
 	if (status != DDI_PROBE_SUCCESS) {
-		cmdk_destroy_obj(dip, dkp);
+		cmdk_destroy_obj(dip, dkp);	/* dadk/strategy linkage  */
+		mutex_exit(&dkp->dk_mutex);
+		mutex_destroy(&dkp->dk_mutex);
+		rw_destroy(&dkp->dk_bbh_mutex);
 		ddi_soft_state_free(cmdk_state, instance);
 		return (status);
 	}
 
-	sema_init(&dkp->dk_semoclose, 1, NULL, SEMA_DRIVER, NULL);
-
+	mutex_exit(&dkp->dk_mutex);
 #ifdef CMDK_DEBUG
 	if (cmdk_debug & DENT)
 		PRF("cmdkprobe: instance= %d name= `%s`\n",
@@ -376,12 +323,9 @@
 static int
 cmdkattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 {
-	int 	instance;
-	long	start, count;
-	struct 	driver_minor_data *dmdp;
-	struct	cmdk *dkp;
-	char 	*node_type;
-	char	name[48];
+	int 		instance;
+	struct		cmdk *dkp;
+	char 		*node_type;
 
 	if (cmd != DDI_ATTACH)
 		return (DDI_FAILURE);
@@ -390,32 +334,53 @@
 	if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
 		return (DDI_FAILURE);
 
+	mutex_enter(&dkp->dk_mutex);
+
 	/* dadk_attach is an empty function that only returns SUCCESS */
 	(void) dadk_attach(DKTP_DATA);
 
-	node_type = DKTP_EXT->tg_nodetype;
-	for (dmdp = cmdk_minor_data; dmdp->name != NULL; dmdp++) {
-		minor_t minor_num = (instance << CMDK_UNITSHF) | dmdp->minor;
+	node_type = (DKTP_EXT->tg_nodetype);
+
+	/*
+	 * this open allows cmlb to read the device
+	 * and determine the label types
+	 * so that cmlb can create minor nodes for device
+	 */
 
-		(void) sprintf(name, "%s", dmdp->name);
-		if (ddi_create_minor_node(dip, name, dmdp->type, minor_num,
-		    node_type, NULL) == DDI_FAILURE) {
+	/* open the target disk	 */
+	if (dadk_open(DKTP_DATA, 0) != DDI_SUCCESS)
+		goto fail2;
+
+	/* mark as having opened target */
+	dkp->dk_flag |= CMDK_TGDK_OPEN;
+
+	cmlb_alloc_handle((cmlb_handle_t *)&dkp->dk_cmlbhandle);
 
-			cmdk_destroy_obj(dip, dkp);
-
-			sema_destroy(&dkp->dk_semoclose);
-			ddi_soft_state_free(cmdk_state, instance);
+	if (cmlb_attach(dip,
+	    &cmdk_lb_ops,
+	    DTYPE_DIRECT,		/* device_type */
+	    0,				/* removable */
+	    node_type,
+	    CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT,	/* alter_behaviour */
+	    dkp->dk_cmlbhandle) != 0)
+		goto fail1;
 
-			ddi_remove_minor_node(dip, NULL);
-			ddi_prop_remove_all(dip);
-			return (DDI_FAILURE);
-		}
-	}
+	/* Calling validate will create minor nodes according to disk label */
+	(void) cmlb_validate(dkp->dk_cmlbhandle);
+
+	/* set bbh (Bad Block Handling) */
+	cmdk_bbh_reopen(dkp);
+
+	/* setup devid string */
+	cmdk_devid_setup(dkp);
+
 	mutex_enter(&cmdk_attach_mutex);
 	if (instance > cmdk_max_instance)
 		cmdk_max_instance = instance;
 	mutex_exit(&cmdk_attach_mutex);
 
+	mutex_exit(&dkp->dk_mutex);
+
 	/*
 	 * Add a zero-length attribute to tell the world we support
 	 * kernel ioctls (for layered drivers)
@@ -424,12 +389,18 @@
 	    DDI_KERNEL_IOCTL, NULL, 0);
 	ddi_report_dev(dip);
 
-	cmdk_part_info_init(dkp);
+	return (DDI_SUCCESS);
 
-	/* Need to open the label and register devid early */
-	(void) cmdk_part_info(dkp, TRUE, &start, &count, PARTITION0_INDEX);
-
-	return (DDI_SUCCESS);
+fail1:
+	cmlb_free_handle(&dkp->dk_cmlbhandle);
+	(void) dadk_close(DKTP_DATA);
+fail2:
+	cmdk_destroy_obj(dip, dkp);
+	rw_destroy(&dkp->dk_bbh_mutex);
+	mutex_exit(&dkp->dk_mutex);
+	mutex_destroy(&dkp->dk_mutex);
+	ddi_soft_state_free(cmdk_state, instance);
+	return (DDI_FAILURE);
 }
 
 
@@ -453,6 +424,7 @@
 	max_instance = cmdk_max_instance;
 	mutex_exit(&cmdk_attach_mutex);
 
+	/* check if any instance of driver is open */
 	for (instance = 0; instance < max_instance; instance++) {
 		dkp = ddi_get_soft_state(cmdk_state, instance);
 		if (!dkp)
@@ -465,6 +437,8 @@
 	if (!(dkp = ddi_get_soft_state(cmdk_state, instance)))
 		return (DDI_SUCCESS);
 
+	mutex_enter(&dkp->dk_mutex);
+
 	/*
 	 * The cmdk_part_info call at the end of cmdkattach may have
 	 * caused cmdk_reopen to do a TGDK_OPEN, make sure we close on
@@ -475,15 +449,16 @@
 		(void) dadk_close(DKTP_DATA);
 	}
 
-	cmdk_part_info_fini(dkp);
-	cmdk_destroy_lbobj(dip, dkp, 1);
-	cmdk_destroy_obj(dip, dkp);
+	cmlb_detach(dkp->dk_cmlbhandle);
+	cmlb_free_handle(&dkp->dk_cmlbhandle);
+	ddi_prop_remove_all(dip);
 
-	sema_destroy(&dkp->dk_semoclose);
+	cmdk_destroy_obj(dip, dkp);	/* dadk/strategy linkage  */
+	mutex_exit(&dkp->dk_mutex);
+	mutex_destroy(&dkp->dk_mutex);
+	rw_destroy(&dkp->dk_bbh_mutex);
 	ddi_soft_state_free(cmdk_state, instance);
 
-	ddi_prop_remove_all(dip);
-	ddi_remove_minor_node(dip, NULL);
 	return (DDI_SUCCESS);
 }
 
@@ -497,7 +472,6 @@
 #ifdef lint
 	dip = dip;	/* no one ever uses this */
 #endif
-
 #ifdef CMDK_DEBUG
 	if (cmdk_debug & DENT)
 		PRF("cmdkinfo: call\n");
@@ -523,39 +497,40 @@
 cmdk_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op, int mod_flags,
     char *name, caddr_t valuep, int *lengthp)
 {
-	int		instance = ddi_get_instance(dip);
 	struct	cmdk	*dkp;
-	long		lblocks;
-	uint64_t	nblocks64;
-	daddr_t		p_lblksrt;
+	diskaddr_t	p_lblksrt;
+	diskaddr_t	p_lblkcnt;
 
 #ifdef CMDK_DEBUG
 	if (cmdk_debug & DENT)
 		PRF("cmdk_prop_op: call\n");
 #endif
 
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+
 	/*
 	 * Our dynamic properties are all device specific and size oriented.
 	 * Requests issued under conditions where size is valid are passed
 	 * to ddi_prop_op_nblocks with the size information, otherwise the
 	 * request is passed to ddi_prop_op. Size depends on valid label.
 	 */
-	dkp = ddi_get_soft_state(cmdk_state, instance);
-	if ((dev == DDI_DEV_T_ANY) || (dkp == NULL) ||
-	    !(dkp->dk_flag & CMDK_VALID_LABEL)) {
-pass:		return (ddi_prop_op(dev, dip, prop_op, mod_flags,
-		    name, valuep, lengthp));
-	} else {
-		/* force re-read of MBR and label and partition info */
-		if (!cmdk_part_info(dkp, TRUE, &p_lblksrt,
-		    &lblocks, CMDKPART(dev)))
-			goto pass;
+	if ((dev != DDI_DEV_T_ANY) && (dkp != NULL)) {
+		if (!cmlb_partinfo(
+		    dkp->dk_cmlbhandle,
+		    CMDKPART(dev),
+		    &p_lblkcnt,
+		    &p_lblksrt,
+		    NULL,
+		    NULL))
+			return (ddi_prop_op_nblocks(dev, dip,
+			    prop_op, mod_flags,
+			    name, valuep, lengthp,
+			    (uint64_t)p_lblkcnt));
+	}
 
-		/* get nblocks value */
-		nblocks64 = (ulong_t)lblocks;
-		return (ddi_prop_op_nblocks(dev, dip, prop_op, mod_flags,
-		    name, valuep, lengthp, nblocks64));
-	}
+	return (ddi_prop_op(dev, dip,
+	    prop_op, mod_flags,
+	    name, valuep, lengthp));
 }
 
 /*
@@ -566,8 +541,8 @@
 {
 	int 		instance;
 	struct	cmdk	*dkp;
-	daddr_t		p_lblksrt;
-	long		p_lblkcnt;
+	diskaddr_t	p_lblksrt;
+	diskaddr_t	p_lblkcnt;
 	struct	buf	local;
 	struct	buf	*bp;
 
@@ -579,7 +554,15 @@
 	if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) || (blkno < 0))
 		return (ENXIO);
 
-	DKLB_PARTINFO(dkp->dk_lbobjp, &p_lblksrt, &p_lblkcnt, CMDKPART(dev));
+	if (cmlb_partinfo(
+	    dkp->dk_cmlbhandle,
+	    CMDKPART(dev),
+	    &p_lblkcnt,
+	    &p_lblksrt,
+	    NULL,
+	    NULL)) {
+		return (ENXIO);
+	}
 
 	if ((blkno+nblk) > p_lblkcnt)
 		return (EINVAL);
@@ -684,8 +667,7 @@
  * ioctl routine
  */
 static int
-cmdkioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp,
-	int *rvalp)
+cmdkioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp)
 {
 	int 		instance;
 	struct scsi_device *devp;
@@ -704,6 +686,7 @@
 		struct dk_minfo	media_info;
 		struct  tgdk_geom phyg;
 
+		/* dadk_getphygeom always returns success */
 		(void) dadk_getphygeom(DKTP_DATA, &phyg);
 
 		media_info.dki_lbsize = phyg.g_secsiz;
@@ -722,7 +705,7 @@
 		struct dk_cinfo *info = (struct dk_cinfo *)data;
 
 		/* controller information */
-		info->dki_ctype = DKTP_EXT->tg_ctype;
+		info->dki_ctype = (DKTP_EXT->tg_ctype);
 		info->dki_cnum = ddi_get_instance(ddi_get_parent(dkp->dk_dip));
 		(void) strcpy(info->dki_cname,
 		    ddi_get_name(ddi_get_parent(dkp->dk_dip)));
@@ -747,35 +730,11 @@
 			return (0);
 	}
 
-	case DKIOCPARTINFO: {
-		daddr_t	start;
-		long len;
-		STRUCT_DECL(part_info, p);
-
-		/*
-		 * force re-read of MBR and label and partition info
-		 */
-		if (!cmdk_part_info(dkp, TRUE, &start, &len, CMDKPART(dev)))
-			return (ENXIO);
-
-		if (len > INT_MAX)
-			return (EOVERFLOW);
-
-		STRUCT_INIT(p, flag & FMODELS);
-		STRUCT_FSET(p, p_start, start);
-		STRUCT_FSET(p, p_length, (int)len);
-		if (ddi_copyout(STRUCT_BUF(p), (caddr_t)arg, STRUCT_SIZE(p),
-		    flag))
-			return (EFAULT);
-		return (0);
-	}
-
 	case DKIOCSTATE: {
 		int	state;
 		int	rval;
-		int 	part;
-		daddr_t	p_lblksrt;
-		long	p_lblkcnt;
+		diskaddr_t	p_lblksrt;
+		diskaddr_t	p_lblkcnt;
 
 		if (ddi_copyin((void *)arg, &state, sizeof (int), flag))
 			return (EFAULT);
@@ -784,16 +743,18 @@
 		if (rval = dadk_check_media(DKTP_DATA, &state))
 			return (rval);
 
+		cmlb_invalidate(dkp->dk_cmlbhandle);
 		if (state == DKIO_INSERTED) {
-			part = CMDKPART(dev);
-			/*
-			 * force re-read of MBR and label and partition info
-			 */
-			if (!cmdk_part_info(dkp, TRUE, &p_lblksrt, &p_lblkcnt,
-			    part))
+			if (cmlb_partinfo(
+			    dkp->dk_cmlbhandle,
+			    CMDKPART(dev),
+			    &p_lblkcnt,
+			    &p_lblksrt,
+			    NULL,
+			    NULL))
 				return (ENXIO);
 
-			if (part < 0 || p_lblkcnt <= 0)
+			if (p_lblkcnt <= 0)
 				return (ENXIO);
 		}
 
@@ -817,22 +778,49 @@
 		return (0);
 	}
 
+	case DKIOCADDBAD:
+		/*
+		 * This is not an update mechanism to add bad blocks
+		 * to the bad block structures stored on disk.
+		 *
+		 * addbadsec(1M) will update the bad block data on disk
+		 * and use this ioctl to force the driver to re-initialize
+		 * the list of bad blocks in the driver.
+		 */
+
+		/* start BBH */
+		cmdk_bbh_reopen(dkp);
+		return (0);
+
 	case DKIOCG_PHYGEOM:
 	case DKIOCG_VIRTGEOM:
 	case DKIOCGGEOM:
 	case DKIOCSGEOM:
-	case DKIOCSVTOC:
-	case DKIOCGVTOC:
 	case DKIOCGAPART:
 	case DKIOCSAPART:
-	case DKIOCADDBAD:
+	case DKIOCGVTOC:
+	case DKIOCSVTOC:
+	case DKIOCPARTINFO:
+	case DKIOCGMBOOT:
+	case DKIOCSMBOOT:
+	case DKIOCGETEFI:
+	case DKIOCSETEFI:
+	case DKIOCPARTITION:
+	{
+		int rc;
 
-		/* If we don't have a label obj we can't call its ioctl */
-		if (!dkp->dk_lbobjp)
-			return (EIO);
-
-		return (DKLB_IOCTL(dkp->dk_lbobjp, cmd, arg, flag,
-			credp, rvalp));
+		rc = cmlb_ioctl(
+		    dkp->dk_cmlbhandle,
+		    dev,
+		    cmd,
+		    arg,
+		    flag,
+		    credp,
+		    rvalp);
+		if (cmd == DKIOCSVTOC)
+			cmdk_devid_setup(dkp);
+		return (rc);
+	}
 
 	case DIOCTL_RWCMD: {
 		struct	dadkio_rwcmd *rwcmdp;
@@ -878,55 +866,32 @@
 	ulong_t		partbit;
 	int 		instance;
 	struct cmdk	*dkp;
-	int		i;
 
 	instance = CMDKUNIT(dev);
 	if (!(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
 	    (otyp >= OTYPCNT))
 		return (ENXIO);
 
-	sema_p(&dkp->dk_semoclose);
+	mutex_enter(&dkp->dk_mutex);
 
-	/* check for device has been opened */
+	/* check if device has been opened */
 	if (!(dkp->dk_flag & CMDK_OPEN)) {
-		sema_v(&dkp->dk_semoclose);
+		mutex_exit(&dkp->dk_mutex);
 		return (ENXIO);
 	}
 
 	part = CMDKPART(dev);
-	if (part < 0) {
-		sema_v(&dkp->dk_semoclose);
-		return (ENXIO);
-	}
 	partbit = 1 << part;
 
 	/* account for close */
 	if (otyp == OTYP_LYR) {
-		if (dkp->dk_open.dk_lyr[part])
-			dkp->dk_open.dk_lyr[part]--;
+		if (dkp->dk_open_lyr[part])
+			dkp->dk_open_lyr[part]--;
 	} else
-		dkp->dk_open.dk_reg[otyp] &= ~partbit;
-	dkp->dk_open.dk_exl &= ~partbit;
+		dkp->dk_open_reg[otyp] &= ~partbit;
+	dkp->dk_open_exl &= ~partbit;
 
-	/* check for last close */
-	for (i = 0; i < OTYPCNT; i++) {
-		if (dkp->dk_open.dk_reg[i])
-			break;
-	}
-	if (i >= OTYPCNT) {
-		for (i = 0; i < CMDK_MAXPART; i++) {
-			if (dkp->dk_open.dk_lyr[i])
-				break;
-		}
-		if (i >= CMDK_MAXPART) {
-			/* OK, last close */
-			(void) dadk_close(DKTP_DATA);
-			dkp->dk_flag &=
-			    ~(CMDK_OPEN | CMDK_TGDK_OPEN | CMDK_VALID_LABEL);
-		}
-	}
-
-	sema_v(&dkp->dk_semoclose);
+	mutex_exit(&dkp->dk_mutex);
 	return (DDI_SUCCESS);
 }
 
@@ -939,8 +904,8 @@
 	ulong_t		partbit;
 	int 		instance;
 	struct	cmdk	*dkp;
-	daddr_t		p_lblksrt;
-	long		p_lblkcnt;
+	diskaddr_t	p_lblksrt;
+	diskaddr_t	p_lblkcnt;
 	int		i;
 
 	instance = CMDKUNIT(dev);
@@ -950,88 +915,68 @@
 	if (otyp >= OTYPCNT)
 		return (EINVAL);
 
-	if ((part = CMDKPART(dev)) < 0)
-		return (ENXIO);
+	part = CMDKPART(dev);
+	partbit = 1 << part;
 
-	sema_p(&dkp->dk_semoclose);
+	mutex_enter(&dkp->dk_mutex);
 
 	/* re-do the target open */
-	if (cmdk_part_info(dkp, TRUE, &p_lblksrt, &p_lblkcnt, part)) {
+	cmlb_invalidate(dkp->dk_cmlbhandle);
+	if (!cmlb_partinfo(
+	    dkp->dk_cmlbhandle,
+	    part,
+	    &p_lblkcnt,
+	    &p_lblksrt,
+	    NULL,
+	    NULL)) {
 		if (p_lblkcnt <= 0 &&
 		    ((flag & (FNDELAY|FNONBLOCK)) == 0 || otyp != OTYP_CHR)) {
-			sema_v(&dkp->dk_semoclose);
+			mutex_exit(&dkp->dk_mutex);
 			return (ENXIO);
 		}
 	} else {
 		/* fail if not doing non block open */
 		if ((flag & (FNONBLOCK|FNDELAY)) == 0) {
-			sema_v(&dkp->dk_semoclose);
+			mutex_exit(&dkp->dk_mutex);
 			return (ENXIO);
 		}
 	}
-	if (DKTP_EXT->tg_rdonly && (flag & FWRITE)) {
-		sema_v(&dkp->dk_semoclose);
+	if ((DKTP_EXT->tg_rdonly) && (flag & FWRITE)) {
+		mutex_exit(&dkp->dk_mutex);
 		return (EROFS);
 	}
 
-	partbit = 1 << part;
-
 	/* check for part already opend exclusively */
-	if (dkp->dk_open.dk_exl & partbit)
+	if (dkp->dk_open_exl & partbit)
 		goto excl_open_fail;
 
 	/* check if we can establish exclusive open */
 	if (flag & FEXCL) {
-		if (dkp->dk_open.dk_lyr[part])
+		if (dkp->dk_open_lyr[part])
 			goto excl_open_fail;
 		for (i = 0; i < OTYPCNT; i++) {
-			if (dkp->dk_open.dk_reg[i] & partbit)
+			if (dkp->dk_open_reg[i] & partbit)
 				goto excl_open_fail;
 		}
 	}
 
-	/* open will succeed, acount for open */
+	/* open will succeed, account for open */
 	dkp->dk_flag |= CMDK_OPEN;
 	if (otyp == OTYP_LYR)
-		dkp->dk_open.dk_lyr[part]++;
+		dkp->dk_open_lyr[part]++;
 	else
-		dkp->dk_open.dk_reg[otyp] |= partbit;
+		dkp->dk_open_reg[otyp] |= partbit;
 	if (flag & FEXCL)
-		dkp->dk_open.dk_exl |= partbit;
+		dkp->dk_open_exl |= partbit;
 
-	sema_v(&dkp->dk_semoclose);
+	mutex_exit(&dkp->dk_mutex);
 	return (DDI_SUCCESS);
 
 excl_open_fail:
-	sema_v(&dkp->dk_semoclose);
+	mutex_exit(&dkp->dk_mutex);
 	return (EBUSY);
 }
 
-static int
-cmdk_reopen(struct cmdk *dkp)
-{
-	/* open the target disk	 */
-	if (dadk_open(DKTP_DATA, 0) != DDI_SUCCESS)
-		return (FALSE);
-
-	/* mark as having opened target */
-	dkp->dk_flag |= CMDK_TGDK_OPEN;
-
-	/* check for valid label object */
-	if (!dkp->dk_lbobjp)
-		if (cmdk_create_lbobj(dkp->dk_dip, dkp) != DDI_SUCCESS)
-			return (FALSE);
-
-	/* reset back to pseudo bbh */
-	(void) dadk_set_bbhobj(DKTP_DATA, &cmdk_bbh_obj);
-
-	/* search for proper disk label object */
-	(void) DKLB_OPEN(dkp->dk_lbobjp, dkp->dk_dev, dkp->dk_dip);
-
-	dkp->dk_flag |= CMDK_VALID_LABEL;
-	return (TRUE);
-}
-
 /*
  * read routine
  */
@@ -1100,8 +1045,8 @@
 	int 		instance;
 	struct	cmdk 	*dkp;
 	long		d_cnt;
-	daddr_t		p_lblksrt;
-	long		p_lblkcnt;
+	diskaddr_t	p_lblksrt;
+	diskaddr_t	p_lblkcnt;
 
 	instance = CMDKUNIT(bp->b_edev);
 	if (cmdk_indump || !(dkp = ddi_get_soft_state(cmdk_state, instance)) ||
@@ -1119,8 +1064,13 @@
 	/*
 	 * only re-read the vtoc if necessary (force == FALSE)
 	 */
-	if (!cmdk_part_info(dkp, FALSE, &p_lblksrt, &p_lblkcnt,
-	    CMDKPART(bp->b_edev))) {
+	if (cmlb_partinfo(
+	    dkp->dk_cmlbhandle,
+	    CMDKPART(bp->b_edev),
+	    &p_lblkcnt,
+	    &p_lblksrt,
+	    NULL,
+	    NULL)) {
 		SETBPERR(bp, ENXIO);
 	}
 
@@ -1158,6 +1108,9 @@
 	char		flc_keyvalp[64];
 	int		flc_keylen;
 
+	ASSERT(mutex_owned(&dkp->dk_mutex));
+
+	/* Create linkage to queueing routines based on property */
 	que_keylen = sizeof (que_keyvalp);
 	if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
 	    DDI_PROP_CANSLEEP, "queue", que_keyvalp, &que_keylen) !=
@@ -1175,6 +1128,7 @@
 		return (DDI_FAILURE);
 	}
 
+	/* Create linkage to dequeueing routines based on property */
 	flc_keylen = sizeof (flc_keyvalp);
 	if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
 	    DDI_PROP_CANSLEEP, "flow_control", flc_keyvalp, &flc_keylen) !=
@@ -1194,11 +1148,15 @@
 		return (DDI_FAILURE);
 	}
 
+	/* populate bbh_obj object stored in dkp */
+	dkp->dk_bbh_obj.bbh_data = dkp;
+	dkp->dk_bbh_obj.bbh_ops = &cmdk_bbh_ops;
+
+	/* create linkage to dadk */
 	dkp->dk_tgobjp = (opaque_t)dadk_create();
 
 	devp = ddi_get_driver_private(dip);
-
-	(void) dadk_init(DKTP_DATA, devp, flcobjp, queobjp, &cmdk_bbh_obj,
+	(void) dadk_init(DKTP_DATA, devp, flcobjp, queobjp, &dkp->dk_bbh_obj,
 	    NULL);
 
 	return (DDI_SUCCESS);
@@ -1212,8 +1170,10 @@
 	char		flc_keyvalp[64];
 	int		flc_keylen;
 
-	(void) dadk_free(DKTP_DATA);
-	DKTP_DATA = NULL;
+	ASSERT(mutex_owned(&dkp->dk_mutex));
+
+	(void) dadk_free((dkp->dk_tgobjp));
+	dkp->dk_tgobjp = NULL;
 
 	que_keylen = sizeof (que_keyvalp);
 	if (ddi_prop_op(DDI_DEV_T_NONE, dip, PROP_LEN_AND_VAL_BUF,
@@ -1235,199 +1195,781 @@
 	flc_keyvalp[flc_keylen] = (char)0;
 }
 
-/*ARGSUSED*/
+static int
+cmdk_lb_rdwr(
+    dev_info_t *dip,
+    uchar_t cmd,
+    void *bufaddr,
+    diskaddr_t start,
+    size_t count)
+{
+	struct cmdk	*dkp;
+	opaque_t	handle;
+	int		rc = 0;
+	char		*bufa;
+
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+	if (dkp == NULL)
+		return (ENXIO);
+
+	if (cmd != TG_READ && cmd != TG_WRITE)
+		return (EINVAL);
+
+	/* count must be multiple of 512 */
+	count = (count + NBPSCTR - 1) & -NBPSCTR;
+	handle = dadk_iob_alloc(DKTP_DATA, start, count, KM_SLEEP);
+	if (!handle)
+		return (DDI_FAILURE);
+
+	if (cmd == TG_READ) {
+		bufa = dadk_iob_xfer(DKTP_DATA, handle, B_READ);
+		if (!bufa)
+			rc = EIO;
+		else
+			bcopy(bufa, bufaddr, count);
+	} else {
+		bufa = dadk_iob_htoc(DKTP_DATA, handle);
+		bcopy(bufaddr, bufa, count);
+		bufa = dadk_iob_xfer(DKTP_DATA, handle, B_WRITE);
+		if (!bufa)
+			rc = EIO;
+	}
+	(void) dadk_iob_free(DKTP_DATA, handle);
+
+	return (rc);
+}
+
+static int
+cmdk_lb_getcapacity(
+    dev_info_t *dip,
+    diskaddr_t *capp)
+{
+	struct cmdk		*dkp;
+	struct tgdk_geom	phyg;
+
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+	if (dkp == NULL)
+		return (ENXIO);
+
+	/* dadk_getphygeom always returns success */
+	(void) dadk_getphygeom(DKTP_DATA, &phyg);
+
+	*capp = phyg.g_cap;
+
+	return (0);
+}
+
 static int
-cmdk_create_lbobj(dev_info_t *dip, struct cmdk *dkp)
+cmdk_lb_getvirtgeom(
+    dev_info_t *dip,
+    cmlb_geom_t *virtgeomp)
+{
+	struct cmdk		*dkp;
+	struct tgdk_geom	phyg;
+	diskaddr_t		capacity;
+
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+	if (dkp == NULL)
+		return (ENXIO);
+
+	(void) dadk_getgeom(DKTP_DATA, &phyg);
+	capacity = phyg.g_cap;
+
+	/*
+	 * If the controller returned us something that doesn't
+	 * really fit into an Int 13/function 8 geometry
+	 * result, just fail the ioctl.  See PSARC 1998/313.
+	 */
+	if (capacity < 0 || capacity >= 63 * 254 * 1024)
+		return (EINVAL);
+
+	virtgeomp->g_capacity	= capacity;
+	virtgeomp->g_nsect	= 63;
+	virtgeomp->g_nhead	= 254;
+	virtgeomp->g_ncyl	= capacity / (63 * 254);
+	virtgeomp->g_acyl	= 0;
+	virtgeomp->g_secsize	= 512;
+	virtgeomp->g_intrlv	= 1;
+	virtgeomp->g_rpm	= 3600;
+
+	return (0);
+}
+
+static int
+cmdk_lb_getphygeom(
+    dev_info_t *dip,
+    cmlb_geom_t *phygeomp)
+{
+	struct cmdk		*dkp;
+	struct tgdk_geom	phyg;
+
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+	if (dkp == NULL)
+		return (ENXIO);
+
+	/* dadk_getphygeom always returns success */
+	(void) dadk_getphygeom(DKTP_DATA, &phyg);
+
+	phygeomp->g_capacity	= phyg.g_cap;
+	phygeomp->g_nsect	= phyg.g_sec;
+	phygeomp->g_nhead	= phyg.g_head;
+	phygeomp->g_acyl	= phyg.g_acyl;
+	phygeomp->g_ncyl	= phyg.g_cyl;
+	phygeomp->g_secsize	= phyg.g_secsiz;
+	phygeomp->g_intrlv	= 1;
+	phygeomp->g_rpm		= 3600;
+
+	return (0);
+}
+
+static int
+cmdk_lb_getattribute(
+    dev_info_t *dip,
+    tg_attribute_t *tgattribute)
+{
+	struct cmdk *dkp;
+
+	dkp = ddi_get_soft_state(cmdk_state, ddi_get_instance(dip));
+	if (dkp == NULL)
+		return (ENXIO);
+
+	if ((DKTP_EXT->tg_rdonly))
+		tgattribute->media_is_writable = FALSE;
+	else
+		tgattribute->media_is_writable = TRUE;
+
+	return (0);
+}
+
+/*
+ * Create and register the devid.
+ * There are 4 different ways we can get a device id:
+ *    1. Already have one - nothing to do
+ *    2. Build one from the drive's model and serial numbers
+ *    3. Read one from the disk (first sector of last track)
+ *    4. Fabricate one and write it on the disk.
+ * If any of these succeeds, register the deviceid
+ */
+static void
+cmdk_devid_setup(struct cmdk *dkp)
 {
-	dkp->dk_lbobjp = (opaque_t)snlb_create();
-	if (!(dkp->dk_lbobjp)) {
-		cmn_err(CE_WARN,
-		    "cmdk_create_lbobj: ERROR creating disklabel %s",
-		    "snlb");
+	int	rc;
+
+	/* Try options until one succeeds, or all have failed */
+
+	/* 1. All done if already registered */
+	if (dkp->dk_devid != NULL)
+		return;
+
+	/* 2. Build a devid from the model and serial number */
+	rc = cmdk_devid_modser(dkp);
+	if (rc != DDI_SUCCESS) {
+		/* 3. Read devid from the disk, if present */
+		rc = cmdk_devid_read(dkp);
+
+		/* 4. otherwise make one up and write it on the disk */
+		if (rc != DDI_SUCCESS)
+			rc = cmdk_devid_fabricate(dkp);
+	}
+
+	/* If we managed to get a devid any of the above ways, register it */
+	if (rc == DDI_SUCCESS)
+		(void) ddi_devid_register(dkp->dk_dip, dkp->dk_devid);
+
+}
+
+/*
+ * Build a devid from the model and serial number
+ * Return DDI_SUCCESS or DDI_FAILURE.
+ */
+static int
+cmdk_devid_modser(struct cmdk *dkp)
+{
+	int	rc = DDI_FAILURE;
+	char	*hwid;
+	int	modlen;
+	int	serlen;
+
+	/*
+	 * device ID is a concatenation of model number, '=', serial number.
+	 */
+	hwid = kmem_alloc(CMDK_HWIDLEN, KM_SLEEP);
+	modlen = cmdk_get_modser(dkp, DIOCTL_GETMODEL, hwid, CMDK_HWIDLEN);
+	if (modlen == 0) {
+		rc = DDI_FAILURE;
+		goto err;
+	}
+	hwid[modlen++] = '=';
+	serlen = cmdk_get_modser(dkp, DIOCTL_GETSERIAL,
+	    hwid + modlen, CMDK_HWIDLEN - modlen);
+	if (serlen == 0) {
+		rc = DDI_FAILURE;
+		goto err;
+	}
+	hwid[modlen + serlen] = 0;
+
+	/* Initialize the device ID, trailing NULL not included */
+	rc = ddi_devid_init(dkp->dk_dip, DEVID_ATA_SERIAL, modlen + serlen,
+	    hwid, (ddi_devid_t *)&dkp->dk_devid);
+	if (rc != DDI_SUCCESS) {
+		rc = DDI_FAILURE;
+		goto err;
+	}
+
+	rc = DDI_SUCCESS;
+
+err:
+	kmem_free(hwid, CMDK_HWIDLEN);
+	return (rc);
+}
+
+static int
+cmdk_get_modser(struct cmdk *dkp, int ioccmd, char *buf, int len)
+{
+	dadk_ioc_string_t strarg;
+	int		rval;
+	char		*s;
+	char		ch;
+	boolean_t	ret;
+	int		i;
+	int		tb;
+
+	strarg.is_buf = buf;
+	strarg.is_size = len;
+	if (dadk_ioctl(DKTP_DATA,
+	    dkp->dk_dev,
+	    ioccmd,
+	    (uintptr_t)&strarg,
+	    FNATIVE | FKIOCTL,
+	    NULL,
+	    &rval) != 0)
+		return (0);
+
+	/*
+	 * valid model/serial string must contain a non-zero non-space
+	 * trim trailing spaces/NULL
+	 */
+	ret = B_FALSE;
+	s = buf;
+	for (i = 0; i < strarg.is_size; i++) {
+		ch = *s++;
+		if (ch != ' ' && ch != '\0')
+			tb = i + 1;
+		if (ch != ' ' && ch != '\0' && ch != '0')
+			ret = B_TRUE;
+	}
+
+	if (ret == B_FALSE)
+		return (0);
+
+	return (tb);
+}
+
+/*
+ * Read a devid from on the first block of the last track of
+ * the last cylinder.  Make sure what we read is a valid devid.
+ * Return DDI_SUCCESS or DDI_FAILURE.
+ */
+static int
+cmdk_devid_read(struct cmdk *dkp)
+{
+	diskaddr_t	blk;
+	struct dk_devid *dkdevidp;
+	uint_t		*ip;
+	int		chksum;
+	int		i, sz;
+	tgdk_iob_handle	handle;
+	int		rc = DDI_FAILURE;
+
+	if (cmlb_get_devid_block(dkp->dk_cmlbhandle, &blk))
+		goto err;
+
+	/* read the devid */
+	handle = dadk_iob_alloc(DKTP_DATA, blk, NBPSCTR, KM_SLEEP);
+	if (handle == NULL)
+		goto err;
+
+	dkdevidp = (struct dk_devid *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
+	if (dkdevidp == NULL)
+		goto err;
+
+	/* Validate the revision */
+	if ((dkdevidp->dkd_rev_hi != DK_DEVID_REV_MSB) ||
+	    (dkdevidp->dkd_rev_lo != DK_DEVID_REV_LSB))
+		goto err;
+
+	/* Calculate the checksum */
+	chksum = 0;
+	ip = (uint_t *)dkdevidp;
+	for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
+		chksum ^= ip[i];
+	if (DKD_GETCHKSUM(dkdevidp) != chksum)
+		goto err;
+
+	/* Validate the device id */
+	if (ddi_devid_valid((ddi_devid_t)dkdevidp->dkd_devid) != DDI_SUCCESS)
+		goto err;
+
+	/* keep a copy of the device id */
+	sz = ddi_devid_sizeof((ddi_devid_t)dkdevidp->dkd_devid);
+	dkp->dk_devid = kmem_alloc(sz, KM_SLEEP);
+	bcopy(dkdevidp->dkd_devid, dkp->dk_devid, sz);
+
+	rc = DDI_SUCCESS;
+
+err:
+	if (handle != NULL)
+		(void) dadk_iob_free(DKTP_DATA, handle);
+	return (rc);
+}
+
+/*
+ * Create a devid and write it on the first block of the last track of
+ * the last cylinder.
+ * Return DDI_SUCCESS or DDI_FAILURE.
+ */
+static int
+cmdk_devid_fabricate(struct cmdk *dkp)
+{
+	ddi_devid_t	devid = NULL;	/* devid made by ddi_devid_init  */
+	struct dk_devid	*dkdevidp;	/* devid struct stored on disk */
+	diskaddr_t	blk;
+	tgdk_iob_handle	handle = NULL;
+	uint_t		*ip, chksum;
+	int		i;
+	int		rc;
+
+	rc = ddi_devid_init(dkp->dk_dip, DEVID_FAB, 0, NULL, &devid);
+	if (rc != DDI_SUCCESS)
+		goto err;
+
+	if (cmlb_get_devid_block(dkp->dk_cmlbhandle, &blk)) {
+		/* no device id block address */
 		return (DDI_FAILURE);
 	}
 
-	DKLB_INIT(dkp->dk_lbobjp, dkp->dk_tgobjp, NULL);
+	handle = dadk_iob_alloc(DKTP_DATA, blk, NBPSCTR, KM_SLEEP);
+	if (!handle)
+		goto err;
+
+	/* Locate the buffer */
+	dkdevidp = (struct dk_devid *)dadk_iob_htoc(DKTP_DATA, handle);
+
+	/* Fill in the revision */
+	bzero(dkdevidp, NBPSCTR);
+	dkdevidp->dkd_rev_hi = DK_DEVID_REV_MSB;
+	dkdevidp->dkd_rev_lo = DK_DEVID_REV_LSB;
+
+	/* Copy in the device id */
+	i = ddi_devid_sizeof(devid);
+	if (i > DK_DEVID_SIZE)
+		goto err;
+	bcopy(devid, dkdevidp->dkd_devid, i);
+
+	/* Calculate the chksum */
+	chksum = 0;
+	ip = (uint_t *)dkdevidp;
+	for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
+		chksum ^= ip[i];
+
+	/* Fill in the checksum */
+	DKD_FORMCHKSUM(chksum, dkdevidp);
+
+	/* write the devid */
+	(void) dadk_iob_xfer(DKTP_DATA, handle, B_WRITE);
+
+	dkp->dk_devid = devid;
+
+	rc = DDI_SUCCESS;
+
+err:
+	if (handle != NULL)
+		(void) dadk_iob_free(DKTP_DATA, handle);
+
+	if (rc != DDI_SUCCESS && devid != NULL)
+		ddi_devid_free(devid);
+
+	return (rc);
+}
+
+static void
+cmdk_bbh_free_alts(struct cmdk *dkp)
+{
+	if (dkp->dk_alts_hdl) {
+		(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
+		kmem_free(dkp->dk_slc_cnt,
+		    NDKMAP * (sizeof (uint32_t) + sizeof (struct alts_ent *)));
+		dkp->dk_alts_hdl = NULL;
+	}
+}
+
+static void
+cmdk_bbh_reopen(struct cmdk *dkp)
+{
+	tgdk_iob_handle 	handle = NULL;
+	diskaddr_t		slcb, slcn, slce;
+	struct	alts_parttbl	*ap;
+	struct	alts_ent	*enttblp;
+	uint32_t		altused;
+	uint32_t		altbase;
+	uint32_t		altlast;
+	int			alts;
+	uint16_t		vtoctag;
+	int			i, j;
+
+	/* find slice with V_ALTSCTR tag */
+	for (alts = 0; alts < NDKMAP; alts++) {
+		if (cmlb_partinfo(
+		    dkp->dk_cmlbhandle,
+		    alts,
+		    &slcn,
+		    &slcb,
+		    NULL,
+		    &vtoctag)) {
+			goto empty;	/* no partition table exists */
+		}
+
+		if (vtoctag == V_ALTSCTR && slcn > 1)
+			break;
+	}
+	if (alts >= NDKMAP) {
+		goto empty;	/* no V_ALTSCTR slice defined */
+	}
+
+	/* read in ALTS label block */
+	handle = dadk_iob_alloc(DKTP_DATA, slcb, NBPSCTR, KM_SLEEP);
+	if (!handle) {
+		goto empty;
+	}
+
+	ap = (struct alts_parttbl *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
+	if (!ap || (ap->alts_sanity != ALTS_SANITY)) {
+		goto empty;
+	}
+
+	altused = ap->alts_ent_used;	/* number of BB entries */
+	altbase = ap->alts_ent_base;	/* blk offset from begin slice */
+	altlast = ap->alts_ent_end;	/* blk offset to last block */
+	/* ((altused * sizeof (struct alts_ent) + NBPSCTR - 1) & ~NBPSCTR) */
 
-	return (DDI_SUCCESS);
+	if (altused == 0 ||
+	    altbase < 1 ||
+	    altbase > altlast ||
+	    altlast >= slcn) {
+		goto empty;
+	}
+	(void) dadk_iob_free(DKTP_DATA, handle);
+
+	/* read in ALTS remapping table */
+	handle = dadk_iob_alloc(DKTP_DATA,
+	    slcb + altbase,
+	    (altlast - altbase + 1) << SCTRSHFT, KM_SLEEP);
+	if (!handle) {
+		goto empty;
+	}
+
+	enttblp = (struct alts_ent *)dadk_iob_xfer(DKTP_DATA, handle, B_READ);
+	if (!enttblp) {
+		goto empty;
+	}
+
+	rw_enter(&dkp->dk_bbh_mutex, RW_WRITER);
+
+	/* allocate space for dk_slc_cnt and dk_slc_ent tables */
+	if (dkp->dk_slc_cnt == NULL) {
+		dkp->dk_slc_cnt = kmem_alloc(NDKMAP *
+		    (sizeof (long) + sizeof (struct alts_ent *)), KM_SLEEP);
+	}
+	dkp->dk_slc_ent = (struct alts_ent **)(dkp->dk_slc_cnt + NDKMAP);
+
+	/* free previous BB table (if any) */
+	if (dkp->dk_alts_hdl) {
+		(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
+		dkp->dk_alts_hdl = NULL;
+		dkp->dk_altused = 0;
+	}
+
+	/* save linkage to new BB table */
+	dkp->dk_alts_hdl = handle;
+	dkp->dk_altused = altused;
+
+	/*
+	 * build indexes to BB table by slice
+	 * effectively we have
+	 *	struct alts_ent *enttblp[altused];
+	 *
+	 *	uint32_t	dk_slc_cnt[NDKMAP];
+	 *	struct alts_ent *dk_slc_ent[NDKMAP];
+	 */
+	for (i = 0; i < NDKMAP; i++) {
+		if (cmlb_partinfo(
+		    dkp->dk_cmlbhandle,
+		    i,
+		    &slcn,
+		    &slcb,
+		    NULL,
+		    NULL)) {
+			goto empty1;
+		}
+
+		dkp->dk_slc_cnt[i] = 0;
+		if (slcn == 0)
+			continue;	/* slice is not allocated */
+
+		/* last block in slice */
+		slce = slcb + slcn - 1;
+
+		/* find first remap entry in after beginnning of slice */
+		for (j = 0; j < altused; j++) {
+			if (enttblp[j].bad_start + enttblp[j].bad_end >= slcb)
+				break;
+		}
+		dkp->dk_slc_ent[i] = enttblp + j;
+
+		/* count remap entrys until end of slice */
+		for (; j < altused && enttblp[j].bad_start <= slce; j++) {
+			dkp->dk_slc_cnt[i] += 1;
+		}
+	}
+
+	rw_exit(&dkp->dk_bbh_mutex);
+	return;
+
+empty:
+	rw_enter(&dkp->dk_bbh_mutex, RW_WRITER);
+empty1:
+	if (handle && handle != dkp->dk_alts_hdl)
+		(void) dadk_iob_free(DKTP_DATA, handle);
+
+	if (dkp->dk_alts_hdl) {
+		(void) dadk_iob_free(DKTP_DATA, dkp->dk_alts_hdl);
+		dkp->dk_alts_hdl = NULL;
+	}
+
+	rw_exit(&dkp->dk_bbh_mutex);
+}
+
+/*ARGSUSED*/
+static bbh_cookie_t
+cmdk_bbh_htoc(opaque_t bbh_data, opaque_t handle)
+{
+	struct	bbh_handle *hp;
+	bbh_cookie_t ckp;
+
+	hp = (struct  bbh_handle *)handle;
+	ckp = hp->h_cktab + hp->h_idx;
+	hp->h_idx++;
+	return (ckp);
 }
 
 /*ARGSUSED*/
 static void
-cmdk_destroy_lbobj(dev_info_t *dip, struct cmdk *dkp, int unload)
+cmdk_bbh_freehandle(opaque_t bbh_data, opaque_t handle)
 {
-	if (!dkp->dk_lbobjp)
-		return;
+	struct	bbh_handle *hp;
 
-	DKLB_FREE(dkp->dk_lbobjp);
-	dkp->dk_lbobjp = 0;
+	hp = (struct  bbh_handle *)handle;
+	kmem_free(handle, (sizeof (struct bbh_handle) +
+	    (hp->h_totck * (sizeof (struct bbh_cookie)))));
 }
 
 
 /*
- * cmdk_part_info()
- *
- *	Make the device valid if possible. The dk_pinfo_lock is only
- *	held for very short periods so that there's very little
- *	contention with the cmdk_devstatus() function which can
- *	be called from interrupt context.
- *
- *	This function implements a simple state machine which looks
- *	like this:
- *
+ *	cmdk_bbh_gethandle remaps the bad sectors to alternates.
+ *	There are 7 different cases when the comparison is made
+ *	between the bad sector cluster and the disk section.
  *
- *	   +---------------------------------+
- *         |				     |
- *	   +--> invalid --> busy --> valid --+
- *			     ^|
- *			     |v
- *			    busy2
+ *	bad sector cluster	gggggggggggbbbbbbbggggggggggg
+ *	case 1:			   ddddd
+ *	case 2:				   -d-----
+ *	case 3:					     ddddd
+ *	case 4:			         dddddddddddd
+ *	case 5:			      ddddddd-----
+ *	case 6:			           ---ddddddd
+ *	case 7:			           ddddddd
  *
- *	This function can change the state from invalid to busy, or from
- *	busy2 to busy, or from busy to valid.
- *
- *	The cmdk_devstatus() function can change the state from valid
- *	to invalid or from busy to busy2.
- *
+ *	where:  g = good sector,	b = bad sector
+ *		d = sector in disk section
+ *		- = disk section may be extended to cover those disk area
  */
 
+static opaque_t
+cmdk_bbh_gethandle(opaque_t bbh_data, struct buf *bp)
+{
+	struct cmdk		*dkp = (struct cmdk *)bbh_data;
+	struct bbh_handle	*hp;
+	struct bbh_cookie	*ckp;
+	struct alts_ent		*altp;
+	uint32_t		alts_used;
+	uint32_t		part = CMDKPART(bp->b_edev);
+	daddr32_t		lastsec;
+	long			d_count;
+	int			i;
+	int			idx;
+	int			cnt;
 
-static int
-cmdk_part_info(struct cmdk *dkp, int force, daddr_t *startp, long *countp,
-		int part)
-{
+	if (part >= V_NUMPAR)
+		return (NULL);
+
 	/*
-	 * The dk_pinfo_state variable (and by implication the partition
-	 * info) is always protected by the dk_pinfo_lock mutex.
+	 * This if statement is atomic and it will succeed
+	 * if there are no bad blocks (almost always)
+	 *
+	 * so this if is performed outside of the rw_enter for speed
+	 * and then repeated inside the rw_enter for safety
 	 */
-	mutex_enter(&dkp->dk_pinfo_lock);
+	if (!dkp->dk_alts_hdl) {
+		return (NULL);
+	}
 
-	for (;;) {
-		switch (dkp->dk_pinfo_state) {
+	rw_enter(&dkp->dk_bbh_mutex, RW_READER);
 
-		case CMDK_PARTINFO_VALID:
-			/* it's already valid */
-			if (!force) {
-				goto done;
-			}
-		/*FALLTHROUGH*/
+	if (dkp->dk_alts_hdl == NULL) {
+		rw_exit(&dkp->dk_bbh_mutex);
+		return (NULL);
+	}
+
+	alts_used = dkp->dk_slc_cnt[part];
+	if (alts_used == 0) {
+		rw_exit(&dkp->dk_bbh_mutex);
+		return (NULL);
+	}
+	altp = dkp->dk_slc_ent[part];
 
-		case CMDK_PARTINFO_INVALID:
-			/*
-			 * It's invalid or we're being forced to reread
-			 */
-			goto reopen;
+	/*
+	 * binary search for the largest bad sector index in the alternate
+	 * entry table which overlaps or larger than the starting d_sec
+	 */
+	i = cmdk_bbh_bsearch(altp, alts_used, GET_BP_SEC(bp));
+	/* if starting sector is > the largest bad sector, return */
+	if (i == -1) {
+		rw_exit(&dkp->dk_bbh_mutex);
+		return (NULL);
+	}
+	/* i is the starting index.  Set altp to the starting entry addr */
+	altp += i;
 
-		case CMDK_PARTINFO_BUSY:
-		case CMDK_PARTINFO_BUSY2:
-			/*
-			 * Some other thread has already called
-			 * cmdk_reopen(), wait for it to complete and then
-			 * start over from the top.
-			 */
-			cv_wait(&dkp->dk_pinfo_cv, &dkp->dk_pinfo_lock);
-		}
+	d_count = bp->b_bcount >> SCTRSHFT;
+	lastsec = GET_BP_SEC(bp) + d_count - 1;
+
+	/* calculate the number of bad sectors */
+	for (idx = i, cnt = 0; idx < alts_used; idx++, altp++, cnt++) {
+		if (lastsec < altp->bad_start)
+			break;
+	}
+
+	if (!cnt) {
+		rw_exit(&dkp->dk_bbh_mutex);
+		return (NULL);
 	}
 
-reopen:
-	/*
-	 * ASSERT: only one thread at a time can possibly reach this point
-	 * and invoke cmdk_reopen()
-	 */
-	dkp->dk_pinfo_state = CMDK_PARTINFO_BUSY;
+	/* calculate the maximum number of reserved cookies */
+	cnt <<= 1;
+	cnt++;
 
-	for (;;)  {
-		int	rc;
+	/* allocate the handle */
+	hp = (struct bbh_handle *)kmem_zalloc((sizeof (*hp) +
+	    (cnt * sizeof (*ckp))), KM_SLEEP);
+
+	hp->h_idx = 0;
+	hp->h_totck = cnt;
+	ckp = hp->h_cktab = (struct bbh_cookie *)(hp + 1);
+	ckp[0].ck_sector = GET_BP_SEC(bp);
+	ckp[0].ck_seclen = d_count;
 
-		/*
-		 * drop the mutex while in cmdk_reopen() because
-		 * it may take a long time to return
-		 */
-		mutex_exit(&dkp->dk_pinfo_lock);
-		rc = cmdk_reopen(dkp);
-		mutex_enter(&dkp->dk_pinfo_lock);
+	altp = dkp->dk_slc_ent[part];
+	altp += i;
+	for (idx = 0; i < alts_used; i++, altp++) {
+		/* CASE 1: */
+		if (lastsec < altp->bad_start)
+			break;
 
-		if (rc == FALSE) {
-			/*
-			 * bailout, probably due to no device,
-			 * or invalid label
-			 */
-			goto error;
+		/* CASE 3: */
+		if (ckp[idx].ck_sector > altp->bad_end)
+			continue;
+
+		/* CASE 2 and 7: */
+		if ((ckp[idx].ck_sector >= altp->bad_start) &&
+		    (lastsec <= altp->bad_end)) {
+			ckp[idx].ck_sector = altp->good_start +
+			    ckp[idx].ck_sector - altp->bad_start;
+			break;
 		}
 
-		switch (dkp->dk_pinfo_state) {
-
-		case CMDK_PARTINFO_BUSY:
-			dkp->dk_pinfo_state = CMDK_PARTINFO_VALID;
-			cv_broadcast(&dkp->dk_pinfo_cv);
-			goto done;
-
-		case CMDK_PARTINFO_BUSY2:
-			/*
-			 * device status changed by cmdk_devstatus(),
-			 * redo the reopen
-			 */
-			dkp->dk_pinfo_state = CMDK_PARTINFO_BUSY;
+		/* at least one bad sector in our section.  break it. */
+		/* CASE 5: */
+		if ((lastsec >= altp->bad_start) &&
+			    (lastsec <= altp->bad_end)) {
+			ckp[idx+1].ck_seclen = lastsec - altp->bad_start + 1;
+			ckp[idx].ck_seclen -= ckp[idx+1].ck_seclen;
+			ckp[idx+1].ck_sector = altp->good_start;
 			break;
 		}
+		/* CASE 6: */
+		if ((ckp[idx].ck_sector <= altp->bad_end) &&
+		    (ckp[idx].ck_sector >= altp->bad_start)) {
+			ckp[idx+1].ck_seclen = ckp[idx].ck_seclen;
+			ckp[idx].ck_seclen = altp->bad_end -
+			    ckp[idx].ck_sector + 1;
+			ckp[idx+1].ck_seclen -= ckp[idx].ck_seclen;
+			ckp[idx].ck_sector = altp->good_start +
+			    ckp[idx].ck_sector - altp->bad_start;
+			idx++;
+			ckp[idx].ck_sector = altp->bad_end + 1;
+			continue;	/* check rest of section */
+		}
+
+		/* CASE 4: */
+		ckp[idx].ck_seclen = altp->bad_start - ckp[idx].ck_sector;
+		ckp[idx+1].ck_sector = altp->good_start;
+		ckp[idx+1].ck_seclen = altp->bad_end - altp->bad_start + 1;
+		idx += 2;
+		ckp[idx].ck_sector = altp->bad_end + 1;
+		ckp[idx].ck_seclen = lastsec - altp->bad_end;
 	}
 
-
-done:
-	/*
-	 * finished cmdk_reopen() without any device status change
-	 */
-	DKLB_PARTINFO(dkp->dk_lbobjp, startp, countp, part);
-	mutex_exit(&dkp->dk_pinfo_lock);
-	return (TRUE);
-
-error:
-	dkp->dk_pinfo_state = CMDK_PARTINFO_INVALID;
-	cv_broadcast(&dkp->dk_pinfo_cv);
-	mutex_exit(&dkp->dk_pinfo_lock);
-	return (FALSE);
+	rw_exit(&dkp->dk_bbh_mutex);
+	return ((opaque_t)hp);
 }
 
-#ifdef	NOT_USED
-static void
-cmdk_devstatus(struct cmdk *dkp)
+static int
+cmdk_bbh_bsearch(struct alts_ent *buf, int cnt, daddr32_t key)
 {
-	mutex_enter(&dkp->dk_pinfo_lock);
-	switch (dkp->dk_pinfo_state) {
+	int	i;
+	int	ind;
+	int	interval;
+	int	mystatus = -1;
+
+	if (!cnt)
+		return (mystatus);
+
+	ind = 1; /* compiler complains about possible uninitialized var	*/
+	for (i = 1; i <= cnt; i <<= 1)
+		ind = i;
 
-	case CMDK_PARTINFO_VALID:
-		dkp->dk_pinfo_state = CMDK_PARTINFO_INVALID;
-		break;
-
-	case CMDK_PARTINFO_INVALID:
-		break;
-
-	case CMDK_PARTINFO_BUSY:
-		dkp->dk_pinfo_state = CMDK_PARTINFO_BUSY2;
-		break;
-
-	case CMDK_PARTINFO_BUSY2:
-		break;
+	for (interval = ind; interval; ) {
+		if ((key >= buf[ind-1].bad_start) &&
+		    (key <= buf[ind-1].bad_end)) {
+			return (ind-1);
+		} else {
+			interval >>= 1;
+			if (key < buf[ind-1].bad_start) {
+				/* record the largest bad sector index */
+				mystatus = ind-1;
+				if (!interval)
+					break;
+				ind = ind - interval;
+			} else {
+				/*
+				 * if key is larger than the last element
+				 * then break
+				 */
+				if ((ind == cnt) || !interval)
+					break;
+				if ((ind+interval) <= cnt)
+					ind += interval;
+			}
+		}
 	}
-	mutex_exit(&dkp->dk_pinfo_lock);
+	return (mystatus);
 }
-#endif	/* NOT_USED */
-
-
-/*
- * initialize the state for cmdk_part_info()
- */
-static void
-cmdk_part_info_init(struct cmdk *dkp)
-{
-	mutex_init(&dkp->dk_pinfo_lock, NULL, MUTEX_DRIVER, NULL);
-	cv_init(&dkp->dk_pinfo_cv, NULL, CV_DRIVER, NULL);
-	dkp->dk_pinfo_state = CMDK_PARTINFO_INVALID;
-}
-
-static void
-cmdk_part_info_fini(struct cmdk *dkp)
-{
-	mutex_destroy(&dkp->dk_pinfo_lock);
-	cv_destroy(&dkp->dk_pinfo_cv);
-}
--- a/usr/src/uts/common/sys/Makefile	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/common/sys/Makefile	Fri Oct 28 23:47:40 2005 -0700
@@ -110,6 +110,7 @@
 	cladm.h			\
 	class.h			\
 	clconf.h		\
+	cmlb.h			\
 	cmn_err.h		\
 	compress.h		\
 	condvar.h		\
@@ -875,7 +876,6 @@
 	dadev.h		\
 	dadk.h		\
 	dadkio.h	\
-	dklb.h		\
 	fctypes.h	\
 	fdisk.h		\
 	flowctrl.h	\
@@ -883,7 +883,6 @@
 	hba.h		\
 	quetypes.h	\
 	queue.h		\
-	snlb.h		\
 	tgcom.h		\
 	tgdk.h
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/cmlb.h	Fri Oct 28 23:47:40 2005 -0700
@@ -0,0 +1,461 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CMLB_H
+#define	_SYS_CMLB_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/dktp/fdisk.h>
+
+/*
+ * structure used for getting phygeom and virtgeom from target driver
+ */
+typedef struct cmlb_geom {
+	unsigned int    g_ncyl;
+	unsigned short  g_acyl;
+	unsigned short  g_nhead;
+	unsigned short  g_nsect;
+	unsigned short  g_secsize;
+	diskaddr_t	g_capacity;
+	unsigned short  g_intrlv;
+	unsigned short  g_rpm;
+} cmlb_geom_t;
+
+
+typedef struct tg_attribute {
+	int media_is_writable;
+} tg_attribute_t;
+
+#define	TG_READ		0
+#define	TG_WRITE	1
+
+#define	TG_DK_OPS_VERSION_0	0x0
+
+/* flag definitions for alter_behavior arg on attach */
+
+#define	CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT	0x00000001
+#define	CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8		0x00000002
+
+/*
+ * Ops vector including utility functions into target driver that cmlb uses.
+ */
+typedef struct cmlb_tg_ops {
+	int	version;
+	/*
+	 * tg_rdwr:
+	 *	perform read/write on target device associated with devi.
+	 * Arguments:
+	 *	devi:		pointer to device's dev_info structure.
+	 *	cmd:		operation to perform.
+	 *			Possible values: TG_READ, TG_WRITE
+	 *	bufp:		pointer to allocated buffer for transfer
+	 *	start_block:	starting block number to read/write (based on
+	 *			system blocksize, DEV_BSIZE)
+	 *
+	 *	reqlength:	requested transfer length (in bytes)
+	 *
+	 * Note: It is the responsibility of caller to make sure
+	 *	length of buffer pointed to by bufp is at least equal to
+	 *	requested transfer length
+	 *
+	 * Return values:
+	 *	0		success
+	 *	ENOMEM		can not allocate memory
+	 *	EACCESS  	reservation conflict
+	 *	EIO		I/O error
+	 *	EFAULT		copyin/copyout error
+	 *	ENXIO		internal error/ invalid devi
+	 *	EINVAL		invalid command value.
+	 */
+	int (*tg_rdwr)(dev_info_t *devi, uchar_t cmd, void *bufp,
+	    diskaddr_t start_block, size_t reqlength);
+
+	/*
+	 * tg_getphygeom:
+	 *	Obtain raw physical geometry from target, and store in structure
+	 *	pointed to by phygeomp
+	 *
+	 * Arguments:
+	 *	devi:		pointer to device's dev_info structure.
+	 *	phygeomp	pointer to allocated structure for
+	 *			physical geometry info.
+	 * Return values:
+	 *	0		success
+	 * 	EACCESS		reservation conflict
+	 *	EINVAL		not applicable
+	 *	EIO		other errors occurred.
+	 *	ENXIO		internal error/ invalid devi
+	 */
+	int (*tg_getphygeom)(dev_info_t *devi, cmlb_geom_t *phygeomp);
+
+	/*
+	 * tg_getvirtgeom:
+	 *	obtain HBA geometry for the target and store in struct pointed
+	 *	to by virtgeomp
+	 * Arguments:
+	 *	devi:		pointer to device's dev_info structure.
+	 *	virtgeomp	pointer to allocated structure for
+	 *			virtual geometry info.
+	 * Return values:
+	 *	0		success
+	 * 	EACCESS		reservation conflict
+	 *	EINVAL		not applicable or HBA does not provide info.
+	 *	EIO		other errors occured.
+	 *	ENXIO		internal error/ invalid devi
+	 *
+	 */
+	int (*tg_getvirtgeom)(dev_info_t *devi, cmlb_geom_t *virtgeomp);
+
+	/*
+	 * tg_getcapacity
+	 *	Report the capacity of the target (in system blocksize,
+	 *	DEV_BSIZE) and store the value where capp is pointing to.
+	 *
+	 * Arguments:
+	 *	devi:		pointer to device's dev_info structure.
+	 *	capp		pointer to capacity value.
+	 *
+	 * Return values:
+	 *	0		success
+	 * 	EINVAL		no media in drive
+	 *	EIO		error occured.
+	 *	ENOTSUP		target does not support getting capacity info.
+	 *	EACCESS		reservation conflict
+	 *	ENXIO		internal error/ invalid devi
+	 */
+	int (*tg_getcapacity)(dev_info_t *devi, diskaddr_t *capp);
+
+	/*
+	 * tg_getattribute:
+	 * 	Report the information requested on device/media and
+	 *	store in area pointed to by tgdevmediainfop
+	 *
+	 * Arguments:
+	 *	devi:		pointer to device's dev_info structure.
+	 *	tgattribute	pointer to area for attribute info
+	 *
+	 * Return values:
+	 *	0		success
+	 * 	EINVAL		no media in drive
+	 *	EIO		error occured.
+	 *	ENOTSUP		target does not support getting capacity info.
+	 *	EACCESS		reservation conflict
+	 *
+	 * Return values:
+	 *	ENXIO		internal error/ invalid devi
+	 *	EACCESS		reservation conflict
+	 * 	EINVAL		not applicable
+	 * 	EIO		I/O failed
+	 */
+	int (*tg_getattribute)(dev_info_t *devi, tg_attribute_t
+	    *tgattribute);
+} cmlb_tg_ops_t;
+
+
+typedef struct __cmlb_handle *cmlb_handle_t;
+
+/*
+ *
+ * Functions exported from cmlb
+ *
+ * Note: Most these functions can callback to target driver through the
+ * tg_ops functions. Target driver should consider this for synchronization.
+ * Any functions that may adjust minor nodes should be called when
+ * the target driver ensures it is safe to do so.
+ */
+
+/*
+ * cmlb_alloc_handle:
+ *
+ *	Allocates a handle.
+ *
+ * Arguments:
+ *	cmlbhandlep	pointer to handle
+ *
+ * Notes:
+ *	Allocates a handle and stores the allocated handle in the area
+ *	pointed to by cmlbhandlep
+ *
+ * Context:
+ *	Kernel thread only (can sleep).
+ */
+void
+cmlb_alloc_handle(cmlb_handle_t *cmlbhandlep);
+
+
+/*
+ * cmlb_attach:
+ *
+ *	Attach handle to device, create minor nodes for device.
+ *
+ *
+ * Arguments:
+ * 	devi		pointer to device's dev_info structure.
+ * 	tgopsp		pointer to array of functions cmlb can use to callback
+ *			to target driver.
+ *
+ *	device_type	Peripheral device type as defined in
+ *			scsi/generic/inquiry.h
+ *
+ *	is_removable	whether or not device is removable.
+ *			0 non-removable, 1 removable.
+ *
+ *	node_type	minor node type (as used by ddi_create_minor_node)
+ *
+ *	alter_behavior
+ *			bit flags:
+ *
+ *			CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT: create
+ *			an alternate slice for the default label, if
+ *			device type is DTYPE_DIRECT an architectures default
+ *			label type is VTOC16.
+ *			Otherwise alternate slice will no be created.
+ *
+ *
+ *			CMLB_FAKE_GEOM_LABEL_IOCTLS_VTOC8: report a default
+ *			geometry and label for DKIOCGGEOM and DKIOCGVTOC
+ *			on architecture with VTOC 8 label types.
+ *
+ *
+ *	cmlbhandle	cmlb handle associated with device
+ *
+ * Notes:
+ *	Assumes a default label based on capacity for non-removable devices.
+ *	If capacity > 1TB, EFI is assumed otherwise VTOC (default VTOC
+ *	for the architecture).
+ *	For removable devices, default label type is assumed to be VTOC
+ *	type. Create minor nodes based on a default label type.
+ *	Label on the media is not validated.
+ *	minor number consists of:
+ *		if _SUNOS_VTOC_8 is defined
+ *			lowest 3 bits is taken as partition number
+ *			the rest is instance number
+ *		if _SUNOS_VTOC_16 is defined
+ *			lowest 6 bits is taken as partition number
+ *			the rest is instance number
+ *
+ *
+ * Return values:
+ *	0 	Success
+ * 	ENXIO 	creating minor nodes failed.
+ *
+ */
+int
+cmlb_attach(dev_info_t *devi, cmlb_tg_ops_t *tgopsp, int device_type,
+    int is_removable, char *node_type, int alter_behavior, cmlb_handle_t
+    cmlbhandle);
+
+
+/*
+ * cmlb_validate:
+ *
+ *	Validates label.
+ *
+ * Arguments
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ * Notes:
+ *	If new label type is different from the current, adjust minor nodes
+ *	accordingly.
+ *
+ * Return values:
+ *	0		success
+ *			Note: having fdisk but no solaris partition is assumed
+ *			success.
+ *
+ *	ENOMEM		memory allocation failed
+ *	EIO		i/o errors during read or get capacity
+ * 	EACCESS		reservation conflicts
+ * 	EINVAL		label was corrupt, or no default label was assumed
+ *	ENXIO		invalid handle
+ *
+ */
+int
+cmlb_validate(cmlb_handle_t cmlbhandle);
+
+/*
+ * cmlb_invalidate:
+ *	Invalidate in core label data
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ */
+void
+cmlb_invalidate(cmlb_handle_t cmlbhandle);
+
+
+/*
+ * cmlb_partinfo:
+ *	Get partition info for specified partition number.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *	part		partition number
+ *	nblocksp	pointer to number of blocks
+ *	startblockp	pointer to starting block
+ *	partnamep	pointer to name of partition
+ *	tagp		pointer to tag info
+ *
+ *
+ * Notes:
+ *	If in-core label is not valid, this functions tries to revalidate
+ *	the label. If label is valid, it stores the total number of blocks
+ *	in this partition in the area pointed to by nblocksp, starting
+ *	block number in area pointed to by startblockp,  pointer to partition
+ *	name in area pointed to by partnamep, and tag value in area
+ *	pointed by tagp.
+ *	For EFI labels, tag value will be set to 0.
+ *
+ *	For all nblocksp, startblockp and partnamep, tagp, a value of NULL
+ *	indicates the corresponding info is not requested.
+ *
+ *
+ * Return values:
+ *	0	success
+ *	EINVAL  no valid label or requested partition number is invalid.
+ *
+ */
+int
+cmlb_partinfo(cmlb_handle_t cmlbhandle, int part, diskaddr_t *nblocksp,
+    diskaddr_t *startblockp, char **partnamep, uint16_t *tagp);
+
+
+/*
+ * cmlb_ioctl:
+ * Ioctls for label handling will be handled by this function.
+ * These are:
+ *	DKIOCGGEOM
+ *	DKIOCSGEOM
+ *	DKIOCGAPART
+ *	DKIOCSAPART
+ *	DKIOCGVTOC
+ *	DKIOCGETEFI
+ *	DKIOCPARTITION
+ *	DKIOCSVTOC
+ * 	DKIOCSETEFI
+ *	DKIOCGMBOOT
+ *	DKIOCSMBOOT
+ *	DKIOCG_PHYGEOM
+ *	DKIOCG_VIRTGEOM
+ *	DKIOCPARTINFO
+ *
+ *
+ *   Arguments:
+ *	cmlbhandle 	handle associated with device.
+ *      cmd     	ioctl operation to be performed
+ *      arg     	user argument, contains data to be set or reference
+ *                      parameter for get
+ *	flag    	bit flag, indicating open settings, 32/64 bit type
+ *      cred_p  	user credential pointer (not currently used)
+ *	rval_p  	not currently used
+ *
+ *
+ * Return values:
+ *	0
+ *	EINVAL
+ *	ENOTTY
+ *	ENXIO
+ *	EIO
+ *	EFAULT
+ *	ENOTSUP
+ *	EPERM
+ */
+int
+cmlb_ioctl(cmlb_handle_t cmlbhandle, dev_t dev, int cmd, intptr_t arg,
+    int flag, cred_t *cred_p, int *rval_p);
+
+/*
+ * cmlb_get_devid_block:
+ *	 get the block number where device id is stored.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *	devidblockp	pointer to block number.
+ *
+ * Notes:
+ *	It stores the block number of device id in the area pointed to
+ *	by devidblockp.
+ * 	with the block number of device id.
+ *
+ * Return values:
+ *	0	success
+ *	EINVAL 	device id does not apply to current label type.
+ */
+int
+cmlb_get_devid_block(cmlb_handle_t cmlbhandle, diskaddr_t *devidblockp);
+
+
+/*
+ * cmlb_close:
+ *
+ * Close the device, revert to a default label minor node for the device,
+ * if it is removable.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ * Return values:
+ *	0	Success
+ * 	ENXIO	Re-creating minor node failed.
+ */
+int
+cmlb_close(cmlb_handle_t cmlbhandle);
+
+/*
+ * cmlb_detach:
+ *
+ * Invalidate in-core labeling data and remove all minor nodes for
+ * the device associate with handle.
+ *
+ * Arguments:
+ *	cmlbhandle	cmlb handle associated with device.
+ *
+ */
+void
+cmlb_detach(cmlb_handle_t cmlbhandle);
+
+/*
+ * cmlb_free_handle
+ *
+ *	Frees handle.
+ *
+ * Arguments:
+ *	cmlbhandlep	pointer to handle
+ *
+ */
+void
+cmlb_free_handle(cmlb_handle_t *cmlbhandlep);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _SYS_CMLB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/cmlb_impl.h	Fri Oct 28 23:47:40 2005 -0700
@@ -0,0 +1,225 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_CMLB_IMPL_H
+#define	_SYS_CMLB_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/cmlb.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#if defined(_SUNOS_VTOC_8)
+#define	NSDMAP			NDKMAP
+#elif defined(_SUNOS_VTOC_16)
+#define	NSDMAP			(NDKMAP + FD_NUMPART + 1)
+#else
+#error "No VTOC format defined."
+#endif
+
+#define	MAXPART			(NSDMAP + 1)
+#define	WD_NODE			7
+
+
+#if defined(__i386) || defined(__amd64)
+
+#define	P0_RAW_DISK		(NDKMAP)
+#define	FDISK_P1		(NDKMAP+1)
+#define	FDISK_P2		(NDKMAP+2)
+#define	FDISK_P3		(NDKMAP+3)
+#define	FDISK_P4		(NDKMAP+4)
+
+#endif  /* __i386 || __amd64 */
+
+/* Driver Logging Levels */
+#define	CMLB_LOGMASK_ERROR	0x00000001
+#define	CMLB_LOGMASK_INFO	0x00000002
+#define	CMLB_LOGMASK_TRACE	0x00000004
+
+#define	CMLB_TRACE		0x00000001
+#define	CMLB_INFO		0x00000002
+#define	CMLB_ERROR		0x00000004
+
+
+#define	CMLB_MUTEX(un)		(&((un)->un_mutex))
+#define	CMLB_DEVINFO(un)	((un)->un_devi)
+#define	CMLB_LABEL(un)		(DEVI(((un)->un_devi))->devi_binding_name)
+
+
+#define	ISREMOVABLE(un)		(un->un_is_removable == 1)
+#define	ISCD(un)		(un->un_device_type == DTYPE_RODIRECT)
+
+#if defined(_SUNOS_VTOC_8)
+
+#define	CMLBUNIT_SHIFT		3
+#define	CMLBPART_MASK		7
+
+#elif defined(_SUNOS_VTOC_16)
+
+#define	CMLBUNIT_SHIFT		6
+#define	CMLBPART_MASK		63
+
+#else
+#error "No VTOC format defined."
+#endif
+
+#define	CMLBUNIT(dev)		(getminor((dev)) >> CMLBUNIT_SHIFT)
+#define	CMLBPART(dev)		(getminor((dev)) &  CMLBPART_MASK)
+
+
+#define	TRUE 			1
+#define	FALSE			0
+
+/*
+ * Return codes of cmlb_uselabel().
+ */
+#define	CMLB_LABEL_IS_VALID	0
+#define	CMLB_LABEL_IS_INVALID	1
+
+/*
+ * fdisk partition mapping structure
+ */
+struct fmap {
+	daddr_t fmap_start;	/* starting block number */
+	daddr_t fmap_nblk;	/* number of blocks */
+};
+
+/* for cm_state */
+typedef enum  {
+	CMLB_INITED = 0,
+	CMLB_ATTACHED
+} cmlb_state_t;
+
+typedef enum
+{
+	CMLB_LABEL_UNDEF = 0,
+	CMLB_LABEL_VTOC,
+	CMLB_LABEL_EFI
+} cmlb_label_t;
+
+
+typedef struct cmlb_lun {
+	dev_info_t	*un_devi;		/* pointer to devinfo */
+	struct  	dk_vtoc un_vtoc;	/* disk VTOC */
+	struct  	dk_geom un_g;		/* disk geometry */
+
+	diskaddr_t	un_blockcount;		/* capacity */
+
+	diskaddr_t	un_solaris_size;	/* size of Solaris partition */
+	uint_t		un_solaris_offset;	/* offset to Solaris part. */
+
+	struct  dk_map  un_map[MAXPART];	/* logical partitions */
+	diskaddr_t	un_offset[MAXPART];	/* partition start blocks */
+
+	struct fmap	un_fmap[FD_NUMPART];	/* fdisk partitions */
+
+	uchar_t		un_asciilabel[LEN_DKL_ASCII];	/* Disk ASCII label */
+
+	/*
+	 * This is the HBAs current notion of the geometry of the drive,
+	 * for HBAs that support the "geometry" property.
+	 */
+	struct cmlb_geom	un_lgeom;
+
+	/*
+	 * This is the geometry of the device as reported by the MODE SENSE,
+	 * command, Page 3 (Format Device Page) and Page 4 (Rigid Disk Drive
+	 * Geometry Page), assuming MODE SENSE is supported by the target.
+	 */
+	struct cmlb_geom	un_pgeom;
+
+	ushort_t	un_dkg_skew;		/* skew */
+
+	cmlb_label_t	un_def_labeltype;	/* default label type */
+
+	/* label type based on which minor nodes were created last */
+	cmlb_label_t	un_last_labeltype;
+
+	cmlb_label_t	un_cur_labeltype;	/* current label type */
+
+	/* indicates whether vtoc label is read from media */
+	uchar_t		un_vtoc_label_is_from_media;
+
+	cmlb_state_t	un_state;		/* state of handle */
+
+	int		un_f_geometry_is_valid;
+	int		un_sys_blocksize;
+
+	kmutex_t	un_mutex;
+
+	/* the following are passed in at attach time */
+	int		un_is_removable;	/* 1 is removable */
+
+	int		un_alter_behavior;
+	char 		*un_node_type;		/* DDI_NT_... */
+	int		un_device_type;		/* DTYPE_DIRECT,.. */
+	cmlb_tg_ops_t 	*cmlb_tg_ops;
+
+
+} cmlb_lun_t;
+
+_NOTE(MUTEX_PROTECTS_DATA(cmlb_lun::un_mutex, cmlb_lun))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", cmlb_lun::cmlb_tg_ops))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", cmlb_lun::un_devi))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", cmlb_lun::un_is_removable))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", cmlb_lun::un_node_type))
+_NOTE(SCHEME_PROTECTS_DATA("stable data", cmlb_lun::un_sys_blocksize))
+_NOTE(SCHEME_PROTECTS_DATA("private data", cmlb_geom))
+
+
+#define	DK_TG_READ(ihdlp, bufaddr, start_block, reqlength)\
+	(ihdlp->cmlb_tg_ops->tg_rdwr)(CMLB_DEVINFO(ihdlp), TG_READ, bufaddr,\
+	    start_block, reqlength)
+
+#define	DK_TG_WRITE(ihdlp, bufaddr, start_block, reqlength)\
+	(ihdlp->cmlb_tg_ops->tg_rdwr)(CMLB_DEVINFO(ihdlp), TG_WRITE, bufaddr,\
+	    start_block, reqlength)
+
+#define	DK_TG_GETPHYGEOM(ihdlp, phygeomp) \
+	(ihdlp->cmlb_tg_ops->tg_getphygeom)(CMLB_DEVINFO(ihdlp), \
+	    (cmlb_geom_t *)phygeomp)
+
+#define	DK_TG_GETVIRTGEOM(ihdlp, virtgeomp) \
+	(ihdlp->cmlb_tg_ops->tg_getvirtgeom)(CMLB_DEVINFO(ihdlp),\
+	    (cmlb_geom_t *)virtgeomp)
+
+#define	DK_TG_GETCAP(ihdlp, capp) \
+	(ihdlp->cmlb_tg_ops->tg_getcapacity)(CMLB_DEVINFO(ihdlp), capp)
+
+#define	DK_TG_GETATTRIBUTE(ihdlp, attributep) \
+	(ihdlp->cmlb_tg_ops->tg_getattribute)(CMLB_DEVINFO(ihdlp), \
+	    attributep)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _SYS_CMLB_IMPL_H */
--- a/usr/src/uts/common/sys/dktp/altsctr.h	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/common/sys/dktp/altsctr.h	Fri Oct 28 23:47:40 2005 -0700
@@ -20,7 +20,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 1992 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -44,22 +44,22 @@
 
 /*	alternate sector partition information table			*/
 struct	alts_parttbl {
-	long	alts_sanity;	/* to validate correctness		*/
-	long  	alts_version;	/* version number			*/
-	daddr_t	alts_map_base;	/* disk offset of alts_partmap		*/
-	long	alts_map_len;	/* byte length of alts_partmap		*/
-	daddr_t	alts_ent_base;	/* disk offset of alts_entry		*/
-	long	alts_ent_used;	/* number of alternate entries used	*/
-	long	alts_ent_end;	/* disk offset of top of alts_entry	*/
-	daddr_t	alts_resv_base;	/* disk offset of alts_reserved		*/
-	long 	alts_pad[5];	/* reserved fields			*/
+	uint32_t	alts_sanity;	/* to validate correctness	*/
+	uint32_t  	alts_version;	/* version number		*/
+	daddr32_t	alts_map_base;	/* disk offset of alts_partmap	*/
+	uint32_t	alts_map_len;	/* byte length of alts_partmap	*/
+	daddr32_t	alts_ent_base;	/* disk offset of alts_entry	*/
+	uint32_t	alts_ent_used;	/* number of alternate entries used */
+	daddr32_t	alts_ent_end;	/* disk offset of top of alts_entry */
+	daddr32_t	alts_resv_base;	/* disk offset of alts_reserved	*/
+	uint32_t 	alts_pad[5];	/* reserved fields		*/
 };
 
 /*	alternate sector remap entry table				*/
 struct	alts_ent {
-	daddr_t	bad_start;	/* starting bad sector number		*/
-	daddr_t	bad_end;	/* ending bad sector number		*/
-	daddr_t	good_start;	/* starting alternate sector to use	*/
+	daddr32_t	bad_start;	/* starting bad sector number	*/
+	daddr32_t	bad_end;	/* ending bad sector number	*/
+	daddr32_t	good_start;	/* starting alternate sector to use */
 };
 
 /*	size of alternate partition table structure			*/
--- a/usr/src/uts/common/sys/dktp/cmdk.h	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/common/sys/dktp/cmdk.h	Fri Oct 28 23:47:40 2005 -0700
@@ -33,50 +33,55 @@
 extern "C" {
 #endif
 
+#include <sys/cmlb.h>
 #include <sys/dktp/tgdk.h>
 
 #define	CMDK_UNITSHF	6
 #define	CMDK_MAXPART	(1 << CMDK_UNITSHF)
 
-struct	dk_openinfo {
-	uint64_t	dk_reg[OTYPCNT];	/* bit per partition: 2^6 */
-	ulong_t		dk_lyr[CMDK_MAXPART];	/* OTYP_LYR cnt per partition */
-	uint64_t	dk_exl;			/* bit per partition: 2^6 */
-};
+/*
+ * Model number is 40 ASCII characters
+ * Serial number is 20 ASCII characters
+ */
+#define	CMDK_HWIDLEN	(64)
 
 struct	cmdk {
-	long		dk_flag;
+	/* set during probe */
 	dev_info_t	*dk_dip;
 	dev_t		dk_dev;
+	struct tgdk_obj *dk_tgobjp;		/* target disk object pointer */
 
-	ksema_t		dk_semoclose;	/* lock for opens/closes 	*/
-	struct		dk_openinfo dk_open;
+	/* set during attach */
+	cmlb_handle_t	dk_cmlbhandle;
+	ddi_devid_t	dk_devid;
+
+	kmutex_t	dk_mutex;		/* mutex for cmdk struct */
 
-	struct tgdk_obj	*dk_tgobjp;	/* target disk object pointer	*/
-	opaque_t 	dk_lbobjp;
+	long		dk_flag;
+	uint64_t	dk_open_reg[OTYPCNT];	/* bit per partition: 2^6 */
+	ulong_t		dk_open_lyr[CMDK_MAXPART]; /* OTYP_LYR cnt/partition */
+	uint64_t	dk_open_exl;		/* bit per partition: 2^6 */
+
+	struct bbh_obj	dk_bbh_obj;
 
-	kmutex_t	dk_pinfo_lock;
-	kcondvar_t	dk_pinfo_cv;
-	int		dk_pinfo_state;
+	/*
+	 * BBH variables
+	 * protected by dk_bbh_mutex
+	 */
+	krwlock_t	dk_bbh_mutex;		/* bbh mutex */
+	tgdk_iob_handle	dk_alts_hdl;		/* iob for V_ALTSCTR */
+	uint32_t	dk_altused;		/* num entries in V_ALTSCTR */
+	uint32_t	*dk_slc_cnt;		/* entries per slice */
+	struct alts_ent	**dk_slc_ent;		/* link to remap data */
 };
 
 /*	common disk flags definitions					*/
 #define	CMDK_OPEN		0x1
-#define	CMDK_VALID_LABEL	0x2
 #define	CMDK_TGDK_OPEN		0x4
 
 #define	CMDKUNIT(dev) (getminor((dev)) >> CMDK_UNITSHF)
 #define	CMDKPART(dev) (getminor((dev)) & (CMDK_MAXPART - 1))
 
-#define	CMDK_TGOBJP(dkp)	(dkp)->dk_tgobjp
-
-/*	dk_pinfo_states for cmdk_part_info() */
-#define	CMDK_PARTINFO_INVALID	0
-#define	CMDK_PARTINFO_BUSY	1
-#define	CMDK_PARTINFO_BUSY2	2
-#define	CMDK_PARTINFO_VALID	3
-
-
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/uts/intel/Makefile.files	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/intel/Makefile.files	Fri Oct 28 23:47:40 2005 -0700
@@ -113,6 +113,8 @@
 #
 SD_OBJS += sd.o sd_xbuf.o
 
+CMLB_OBJS += cmlb.o
+
 VGATEXT_OBJS += vgatext.o vgasubr.o
 
 #
--- a/usr/src/uts/intel/Makefile.intel	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/intel/Makefile.intel	Fri Oct 28 23:47:40 2005 -0700
@@ -438,6 +438,7 @@
 MISC_KMODS	+= amsrc1 amsrc2
 MISC_KMODS	+= audiosup
 MISC_KMODS	+= busra
+MISC_KMODS	+= cmlb
 MISC_KMODS	+= consconfig
 MISC_KMODS	+= ctf
 MISC_KMODS	+= dadk
@@ -473,7 +474,6 @@
 MISC_KMODS	+= rpcsec_gss
 MISC_KMODS	+= rsmops
 MISC_KMODS	+= scsi
-MISC_KMODS	+= snlb
 MISC_KMODS	+= strategy
 MISC_KMODS	+= strplumb
 MISC_KMODS	+= sysinit
--- a/usr/src/uts/intel/cmdk/Makefile	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/intel/cmdk/Makefile	Fri Oct 28 23:47:40 2005 -0700
@@ -65,7 +65,7 @@
 #
 DEBUG_FLGS	=
 DEBUG_DEFS	+= $(DEBUG_FLGS)
-LDFLAGS		+= -dy -Nmisc/dadk -Nmisc/strategy -Nmisc/snlb
+LDFLAGS		+= -dy -Nmisc/dadk -Nmisc/strategy -Nmisc/cmlb
 
 #
 #	Default build targets.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/cmlb/Makefile	Fri Oct 28 23:47:40 2005 -0700
@@ -0,0 +1,70 @@
+#
+# uts/intel/io/cmlb/Makefile
+#
+# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the cmlb "misc"
+#	kernel module.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cmlb
+OBJECTS		= $(CMLB_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CMLB_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- a/usr/src/uts/sparc/Makefile.sparc	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/sparc/Makefile.sparc	Fri Oct 28 23:47:40 2005 -0700
@@ -341,6 +341,7 @@
 MISC_KMODS	+= ctf 
 MISC_KMODS	+= zmod
 MISC_KMODS	+= mac dls
+MISC_KMODS	+= cmlb
 
 #
 #	Software Cryptographic Providers (/kernel/crypto):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/cmlb/Makefile	Fri Oct 28 23:47:40 2005 -0700
@@ -0,0 +1,93 @@
+#
+# uts/sparc/cmlb/Makefile
+#
+# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the cmlb "misc"
+#	kernel module.
+#
+#
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= cmlb
+OBJECTS		= $(CMLB_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(CMLB_OBJS:%.o=$(LINTS_DIR)/%.ln)
+WARLOCK_OUT	= $(CMLB_OBJS:%.o=%.ll)
+WARLOCK_OK	= $(MODULE).ok
+ROOTMODULE	= $(ROOT_MISC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+DEBUG_FLGS	=
+DEBUG_DEFS	+= $(DEBUG_FLGS)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS); \
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+clobber:	$(CLOBBER_DEPS) \
+		$(RM) $(WARLOCK_OUT) $(WARLOCK_OK)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
+#
+#	Defines for local commands.
+#
+WLCC		= wlcc
+TOUCH		= touch
+WARLOCK		= warlock
+
+#
+#	Warlock targets
+#
+
+CMLB_FILES = $(CMLB_OBJS:%.o=%.ll)
+
+warlock: $(MODULE).ok 
+
+%.ok:	$(CMLB_FILES)
+	$(TOUCH) $@
+
+%.ll: $(UTSBASE)/common/io/%.c
+	$(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $<
--- a/usr/src/uts/sun/Makefile.files	Fri Oct 28 18:56:44 2005 -0700
+++ b/usr/src/uts/sun/Makefile.files	Fri Oct 28 23:47:40 2005 -0700
@@ -81,6 +81,8 @@
 
 SD_OBJS +=	sd.o sd_xbuf.o
 
+CMLB_OBJS +=	cmlb.o
+
 DAD_OBJS +=	dad.o
 
 DADA_OBJS +=	dcd_hba.o       dcd_transport.o \