--- /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);
-}