summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/fm/Makefile1
-rw-r--r--usr/src/lib/fm/libdiskstatus/Makefile56
-rw-r--r--usr/src/lib/fm/libdiskstatus/Makefile.com56
-rw-r--r--usr/src/lib/fm/libdiskstatus/amd64/Makefile30
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_impl.h74
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi.c1352
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi.h327
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c179
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h50
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c1658
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h65
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/ds_util.c172
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c238
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h82
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus31
-rw-r--r--usr/src/lib/fm/libdiskstatus/common/mapfile-vers38
-rw-r--r--usr/src/lib/fm/libdiskstatus/i386/Makefile29
-rw-r--r--usr/src/lib/fm/libdiskstatus/sparc/Makefile29
-rw-r--r--usr/src/lib/fm/libdiskstatus/sparcv9/Makefile30
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/mapfile-vers3
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/scheme.c13
-rw-r--r--usr/src/lib/fm/topo/libtopo/Makefile.com3
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/dev.c176
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/hc.c5
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_fmri.c2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.c9
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_snap.c33
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_subr.c39
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_subr.h2
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/sata/Makefile4
-rw-r--r--usr/src/lib/fm/topo/modules/i86pc/sata/sata.c437
32 files changed, 4956 insertions, 271 deletions
diff --git a/usr/src/lib/fm/Makefile b/usr/src/lib/fm/Makefile
index 925e471c74..046d9346cb 100644
--- a/usr/src/lib/fm/Makefile
+++ b/usr/src/lib/fm/Makefile
@@ -36,6 +36,7 @@ i386_SUBDIRS =
SUBDIRS = \
libdiagcode \
+ libdiskstatus \
libfmd_adm \
libfmd_log \
libfmd_msg \
diff --git a/usr/src/lib/fm/libdiskstatus/Makefile b/usr/src/lib/fm/libdiskstatus/Makefile
new file mode 100644
index 0000000000..b051273f20
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/Makefile
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../../Makefile.lib
+include ../Makefile.lib
+
+HDRDIR= common
+FMHDRS= libdiskstatus.h
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTFMHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
+include ../Makefile.targ
diff --git a/usr/src/lib/fm/libdiskstatus/Makefile.com b/usr/src/lib/fm/libdiskstatus/Makefile.com
new file mode 100644
index 0000000000..ee2fa3725a
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/Makefile.com
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libdiskstatus.a
+VERS= .1
+
+OBJECTS= libdiskstatus.o \
+ ds_scsi.o \
+ ds_scsi_sim.o \
+ ds_scsi_uscsi.o \
+ ds_util.o
+
+include ../../../Makefile.lib
+include ../../Makefile.lib
+
+LIBS= $(DYNLIB) $(LINTLIB)
+
+SRCDIR= ../common
+
+INCS += -I$(SRCDIR)
+LDLIBS += -lc -lnvpair
+CPPFLAGS += $(INCS)
+
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../../Makefile.targ
+include ../../Makefile.targ
diff --git a/usr/src/lib/fm/libdiskstatus/amd64/Makefile b/usr/src/lib/fm/libdiskstatus/amd64/Makefile
new file mode 100644
index 0000000000..5375e35120
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/amd64/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_impl.h b/usr/src/lib/fm/libdiskstatus/common/ds_impl.h
new file mode 100644
index 0000000000..34f8b15d75
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_impl.h
@@ -0,0 +1,74 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DS_IMPL_H
+#define _DS_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <dlfcn.h>
+#include <libnvpair.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct disk_status;
+
+typedef struct ds_transport {
+ void *(*dt_open)(struct disk_status *);
+ void (*dt_close)(void *);
+ int (*dt_scan)(void *);
+} ds_transport_t;
+
+struct disk_status {
+ char *ds_path; /* path to device */
+ int ds_fd; /* device file descriptor */
+ ds_transport_t *ds_transport; /* associated transport */
+ void *ds_data; /* transport-specific data */
+ int ds_faults; /* mask of current faults */
+ nvlist_t *ds_overtemp; /* overtemp */
+ nvlist_t *ds_predfail; /* predict fail */
+ nvlist_t *ds_testfail; /* self test fail */
+ int ds_error; /* last error */
+ nvlist_t *ds_state; /* protocol state */
+};
+
+#define DS_FAULT_OVERTEMP 0x1
+#define DS_FAULT_PREDFAIL 0x2
+#define DS_FAULT_TESTFAIL 0x4
+
+extern void dprintf(const char *, ...);
+extern void ddump(const char *, const void *, size_t);
+extern boolean_t ds_debug;
+
+extern int ds_set_errno(struct disk_status *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DS_IMPL_H */
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c
new file mode 100644
index 0000000000..0b80f4d4c2
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c
@@ -0,0 +1,1352 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <errno.h>
+#include <libdiskstatus.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/fm/io/scsi.h>
+
+#include "ds_scsi.h"
+#include "ds_scsi_sim.h"
+#include "ds_scsi_uscsi.h"
+
+typedef struct ds_scsi_info {
+ disk_status_t *si_dsp;
+ void *si_sim;
+ int si_cdblen;
+ int si_supp_mode;
+ int si_supp_log;
+ int si_extensions;
+ int si_reftemp;
+ scsi_ms_hdrs_t si_hdrs;
+ scsi_ie_page_t si_iec_current;
+ scsi_ie_page_t si_iec_changeable;
+ nvlist_t *si_state_modepage;
+ nvlist_t *si_state_logpage;
+ nvlist_t *si_state_iec;
+} ds_scsi_info_t;
+
+#define scsi_set_errno(sip, errno) (ds_set_errno((sip)->si_dsp, (errno)))
+
+/*
+ * Table to validate log pages
+ */
+typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int, nvlist_t *);
+typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int);
+
+typedef struct logpage_validation_entry {
+ uchar_t ve_code;
+ int ve_supported;
+ const char *ve_desc;
+ logpage_validation_fn_t ve_validate;
+ logpage_analyze_fn_t ve_analyze;
+} logpage_validation_entry_t;
+
+static int logpage_ie_verify(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int, nvlist_t *);
+static int logpage_temp_verify(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int, nvlist_t *);
+static int logpage_selftest_verify(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int, nvlist_t *);
+
+static int logpage_ie_analyze(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int);
+static int logpage_temp_analyze(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int);
+static int logpage_selftest_analyze(ds_scsi_info_t *,
+ scsi_log_parameter_header_t *, int);
+
+static struct logpage_validation_entry log_validation[] = {
+ { LOGPAGE_IE, LOGPAGE_SUPP_IE,
+ "informational-exceptions",
+ logpage_ie_verify, logpage_ie_analyze },
+ { LOGPAGE_TEMP, LOGPAGE_SUPP_TEMP,
+ "temperature",
+ logpage_temp_verify, logpage_temp_analyze },
+ { LOGPAGE_SELFTEST, LOGPAGE_SUPP_SELFTEST,
+ "self-test",
+ logpage_selftest_verify, logpage_selftest_analyze }
+};
+
+#define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0]))
+
+/*
+ * Given an extended sense page, retrieves the sense key, as well as the
+ * additional sense code information.
+ */
+static void
+scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp,
+ uint_t *ascp, uint_t *ascqp)
+{
+ struct scsi_descr_sense_hdr *sdsp =
+ (struct scsi_descr_sense_hdr *)rq;
+
+ *skeyp = rq->es_key;
+
+ /*
+ * Get asc, ascq and info field from sense data. There are two
+ * possible formats (fixed sense data and descriptor sense data)
+ * depending on the value of es_code.
+ */
+ switch (rq->es_code) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+
+ *ascp = sdsp->ds_add_code;
+ *ascqp = sdsp->ds_qual_code;
+ break;
+
+ case CODE_FMT_FIXED_CURRENT:
+ case CODE_FMT_FIXED_DEFERRED:
+ default:
+
+ if (rq->es_add_len >= 6) {
+ *ascp = rq->es_add_code;
+ *ascqp = rq->es_qual_code;
+ } else {
+ *ascp = 0xff;
+ *ascqp = 0xff;
+ }
+ break;
+ }
+}
+
+/*
+ * Routines built atop the bare uscsi commands, which take into account the
+ * command length, automatically translate any scsi errors, and transparently
+ * call into the simulator if active.
+ */
+static int
+scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options,
+ void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
+ uint_t *ascp, uint_t *ascqp)
+{
+ int result;
+ struct scsi_extended_sense sense;
+ int senselen = sizeof (struct scsi_extended_sense);
+ struct mode_page *mp = (struct mode_page *)buf;
+
+ assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
+ sip->si_cdblen == MODE_CMD_LEN_10);
+ assert(headers->ms_length == sip->si_cdblen);
+
+ bzero(&sense, sizeof (struct scsi_extended_sense));
+
+ if (mp->ps) {
+ options |= MODE_SELECT_SP;
+ mp->ps = 0;
+ } else {
+ options &= ~MODE_SELECT_SP;
+ }
+
+ if (sip->si_cdblen == MODE_CMD_LEN_6) {
+ /* The following fields are reserved during mode select: */
+ headers->ms_hdr.g0.ms_header.length = 0;
+ headers->ms_hdr.g0.ms_header.device_specific = 0;
+
+ if (sip->si_sim)
+ result = simscsi_mode_select(sip->si_sim,
+ page_code, options, buf, buflen,
+ &headers->ms_hdr.g0, &sense, &senselen);
+ else
+ result = uscsi_mode_select(sip->si_dsp->ds_fd,
+ page_code, options, buf, buflen,
+ &headers->ms_hdr.g0, &sense, &senselen);
+ } else {
+ /* The following fields are reserved during mode select: */
+ headers->ms_hdr.g1.ms_header.length = 0;
+ headers->ms_hdr.g1.ms_header.device_specific = 0;
+
+ if (sip->si_sim)
+ result = simscsi_mode_select_10(sip->si_sim,
+ page_code, options, buf, buflen,
+ &headers->ms_hdr.g1, &sense, &senselen);
+ else
+ result = uscsi_mode_select_10(sip->si_dsp->ds_fd,
+ page_code, options, buf, buflen,
+ &headers->ms_hdr.g1, &sense, &senselen);
+ }
+
+ if (result != 0)
+ scsi_translate_error(&sense, skp, ascp, ascqp);
+
+ return (result);
+}
+
+static int
+scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc,
+ void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
+ uint_t *ascp, uint_t *ascqp)
+{
+ int result;
+ struct scsi_extended_sense sense;
+ int senselen = sizeof (struct scsi_extended_sense);
+
+ assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
+ sip->si_cdblen == MODE_CMD_LEN_10);
+
+ bzero(&sense, sizeof (struct scsi_extended_sense));
+
+ bzero(headers, sizeof (scsi_ms_hdrs_t));
+ headers->ms_length = sip->si_cdblen;
+
+ if (sip->si_cdblen == MODE_CMD_LEN_6) {
+ if (sip->si_sim)
+ result = simscsi_mode_sense(sip->si_sim,
+ page_code, pc, buf, buflen, &headers->ms_hdr.g0,
+ &sense, &senselen);
+ else
+ result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code,
+ pc, buf, buflen, &headers->ms_hdr.g0, &sense,
+ &senselen);
+ } else {
+ if (sip->si_sim)
+ result = simscsi_mode_sense_10(sip->si_sim,
+ page_code, pc, buf, buflen, &headers->ms_hdr.g1,
+ &sense, &senselen);
+ else
+ result = uscsi_mode_sense_10(sip->si_dsp->ds_fd,
+ page_code, pc, buf, buflen, &headers->ms_hdr.g1,
+ &sense, &senselen);
+ }
+
+ if (result != 0)
+ scsi_translate_error(&sense, skp, ascp, ascqp);
+
+ return (result);
+}
+
+static int
+scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp,
+ uint_t *ascqp)
+{
+ struct scsi_extended_sense sense, sensebuf;
+ int senselen = sizeof (struct scsi_extended_sense);
+ int sensebuflen = sizeof (struct scsi_extended_sense);
+ int result;
+
+ bzero(&sense, sizeof (struct scsi_extended_sense));
+ bzero(&sensebuf, sizeof (struct scsi_extended_sense));
+
+ if (sip->si_sim)
+ result = simscsi_request_sense(sip->si_sim,
+ (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
+ else
+ result = uscsi_request_sense(sip->si_dsp->ds_fd,
+ (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
+
+ if (result == 0)
+ scsi_translate_error(&sensebuf, skp, ascp, ascqp);
+ else
+ scsi_translate_error(&sense, skp, ascp, ascqp);
+
+ return (result);
+}
+
+static int
+scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control,
+ caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp)
+{
+ int result;
+ struct scsi_extended_sense sense;
+ int senselen = sizeof (struct scsi_extended_sense);
+
+ if (sip->si_sim)
+ result = simscsi_log_sense(sip->si_sim,
+ page_code, page_control, page_data, page_size, &sense,
+ &senselen);
+ else
+ result = uscsi_log_sense(sip->si_dsp->ds_fd,
+ page_code, page_control, page_data, page_size, &sense,
+ &senselen);
+
+ if (result != 0)
+ scsi_translate_error(&sense, skp, ascp, ascqp);
+
+ return (result);
+}
+
+/*
+ * Given a list of supported mode pages, determine if the given page is present.
+ */
+static boolean_t
+mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode)
+{
+ uint_t i = 0;
+ struct mode_page *pg;
+ boolean_t found = B_FALSE;
+
+ /*
+ * The mode page list contains all mode pages supported by the device,
+ * one after the other.
+ */
+ while (i < pgdatalen) {
+ pg = (struct mode_page *)&pgdata[i];
+
+ if (pg->code == pagecode) {
+ found = B_TRUE;
+ break;
+ }
+
+ i += MODESENSE_PAGE_LEN(pg);
+ }
+
+ return (found);
+}
+
+/*
+ * Load mode pages and check that the appropriate pages are supported.
+ *
+ * As part of this process, we determine which form of the MODE SENSE / MODE
+ * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE
+ * SENSE command for a page that should be implemented by the device.
+ */
+static int
+load_modepages(ds_scsi_info_t *sip)
+{
+ int allpages_buflen;
+ uchar_t *allpages;
+ scsi_ms_hdrs_t headers;
+ int result;
+ uint_t skey, asc, ascq;
+ int datalength = 0;
+ scsi_ms_header_t *smh = &headers.ms_hdr.g0;
+ scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1;
+ nvlist_t *nvl;
+
+ allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t);
+ if ((allpages = calloc(allpages_buflen, 1)) == NULL)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ bzero(&headers, sizeof (headers));
+
+ /*
+ * Attempt a mode sense(6). If that fails, try a mode sense(10)
+ *
+ * allpages is allocated to be of the maximum size for either a mode
+ * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
+ *
+ * Note that the length passed into uscsi_mode_sense should be set to
+ * the maximum size of the parameter response, which in this case is
+ * UCHAR_MAX - the size of the headers/block descriptors.
+ */
+ sip->si_cdblen = MODE_CMD_LEN_6;
+ if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT,
+ (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t),
+ &headers, &skey, &asc, &ascq)) == 0) {
+ /*
+ * Compute the data length of the page that contains all mode
+ * sense pages. This is a bit tricky because the format of the
+ * response from the lun is:
+ *
+ * header: <length> <medium type byte> <dev specific byte>
+ * <block descriptor length>
+ * [<optional block descriptor>]
+ * data: [<mode page data> <mode page data> ...]
+ *
+ * Since the length field in the header describes the length of
+ * the entire response. This includes the header, but NOT
+ * the length field itself, which is 1 or 2 bytes depending on
+ * which mode sense type (6- or 10- byte) is being executed.
+ *
+ * So, the data length equals the length value in the header
+ * plus 1 (because the length byte was not included in the
+ * length count), minus [[the sum of the length of the header
+ * and the length of the block descriptor]].
+ */
+ datalength = (smh->ms_header.length +
+ sizeof (smh->ms_header.length)) -
+ (sizeof (struct mode_header) +
+ smh->ms_header.bdesc_length);
+ } else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) {
+ /*
+ * Fallback and try the 10-byte version of the command.
+ */
+ sip->si_cdblen = MODE_CMD_LEN_10;
+ result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES,
+ PC_CURRENT, (caddr_t)allpages, allpages_buflen,
+ &headers, &skey, &asc, &ascq);
+
+ if (result == 0) {
+ datalength = (BE_16(smh_g1->ms_header.length) +
+ sizeof (smh_g1->ms_header.length)) -
+ (sizeof (struct mode_header_g1) +
+ BE_16(smh_g1->ms_header.bdesc_length));
+
+ }
+ }
+
+ if (result == 0 && datalength >= 0) {
+ if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length",
+ sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) {
+ free(allpages);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ nvl = NULL;
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages",
+ nvl) != 0) {
+ free(allpages);
+ nvlist_free(nvl);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ nvlist_free(nvl);
+ result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
+ "modepages", &sip->si_state_modepage);
+ assert(result == 0);
+
+ /*
+ * One of the sets of the commands (above) succeeded, so now
+ * look for the mode pages we need and record them appropriately
+ */
+ if (mode_page_present(allpages, datalength,
+ MODEPAGE_INFO_EXCPT)) {
+
+ nvl = NULL;
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_nvlist(sip->si_state_modepage,
+ "informational-exceptions", nvl) != 0) {
+ free(allpages);
+ nvlist_free(nvl);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+ nvlist_free(nvl);
+ sip->si_supp_mode |= MODEPAGE_SUPP_IEC;
+ result = nvlist_lookup_nvlist(sip->si_state_modepage,
+ "informational-exceptions", &sip->si_state_iec);
+ assert(result == 0);
+ }
+
+ } else {
+ /*
+ * If the device failed to respond to one of the basic commands,
+ * then assume it's not a SCSI device or otherwise doesn't
+ * support the necessary transport.
+ */
+ if (datalength < 0)
+ dprintf("command returned invalid data length (%d)\n",
+ datalength);
+ else
+ dprintf("failed to load modepages (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
+
+ result = scsi_set_errno(sip, EDS_NO_TRANSPORT);
+ }
+
+ free(allpages);
+ return (result);
+}
+
+/*
+ * Verify a single logpage. This will do some generic validation and then call
+ * the logpage-specific function for further verification.
+ */
+static int
+verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp)
+{
+ scsi_log_header_t *lhp;
+ struct scsi_extended_sense sense;
+ int buflen;
+ int log_length;
+ int result = 0;
+ uint_t kp, asc, ascq;
+ nvlist_t *nvl;
+
+ buflen = MAX_BUFLEN(scsi_log_header_t);
+ if ((lhp = calloc(buflen, 1)) == NULL)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ bzero(&sense, sizeof (struct scsi_extended_sense));
+
+ nvl = NULL;
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) {
+ nvlist_free(nvl);
+ free(lhp);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+ nvlist_free(nvl);
+ result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl);
+ assert(result == 0);
+
+ result = scsi_log_sense(sip, lp->ve_code,
+ PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq);
+
+ if (result == 0) {
+ log_length = BE_16(lhp->lh_length);
+ if (nvlist_add_uint16(nvl, "length", log_length) != 0) {
+ free(lhp);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ if (lp->ve_validate(sip, (scsi_log_parameter_header_t *)
+ (((char *)lhp) + sizeof (scsi_log_header_t)),
+ log_length, nvl) != 0) {
+ free(lhp);
+ return (-1);
+ }
+ } else {
+ dprintf("failed to load %s log page (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq);
+ }
+
+ free(lhp);
+ return (0);
+}
+
+/*
+ * Load log pages and determine which pages are supported.
+ */
+static int
+load_logpages(ds_scsi_info_t *sip)
+{
+ int buflen;
+ scsi_supported_log_pages_t *sp;
+ struct scsi_extended_sense sense;
+ int result;
+ uint_t sk, asc, ascq;
+ int i, j;
+ nvlist_t *nvl;
+
+ buflen = MAX_BUFLEN(scsi_log_header_t);
+ if ((sp = calloc(buflen, 1)) == NULL)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ bzero(&sense, sizeof (struct scsi_extended_sense));
+
+ if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST,
+ PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) {
+ int pagecount = BE_16(sp->slp_hdr.lh_length);
+
+ for (i = 0; i < pagecount; i++) {
+ for (j = 0; j < NLOG_VALIDATION; j++) {
+ if (log_validation[j].ve_code ==
+ sp->slp_pages[i])
+ sip->si_supp_log |=
+ log_validation[j].ve_supported;
+ }
+ }
+ }
+
+ free(sp);
+ if (result == 0) {
+ nvl = NULL;
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages",
+ nvl) != 0) {
+ nvlist_free(nvl);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ nvlist_free(nvl);
+ result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
+ "logpages", &sip->si_state_logpage);
+ assert(result == 0);
+
+ /*
+ * Validate the logpage contents.
+ */
+ for (i = 0; i < NLOG_VALIDATION; i++) {
+ if ((sip->si_supp_log &
+ log_validation[i].ve_supported) == 0)
+ continue;
+
+ /*
+ * verify_logpage will clear the supported bit if
+ * verification fails.
+ */
+ if (verify_logpage(sip, &log_validation[i]) != 0)
+ return (-1);
+ }
+
+ } else {
+ dprintf("failed to get log pages "
+ "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq);
+ }
+
+ /*
+ * We always return 0 here, even if the required log pages aren't
+ * supported.
+ */
+ return (0);
+}
+
+/*
+ * Verify that the IE log page is sane. This log page is potentially chock-full
+ * of vendor specific information that we do not know how to access. All we can
+ * do is check for the generic predictive failure bit. If this log page is not
+ * well-formed, then bail out.
+ */
+static int
+logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
+ int log_length, nvlist_t *nvl)
+{
+ int i, plen = 0;
+ boolean_t seen = B_FALSE;
+ scsi_ie_log_param_t *iep =
+ (scsi_ie_log_param_t *)lphp;
+
+ for (i = 0; i < log_length; i += plen) {
+ iep = (scsi_ie_log_param_t *)((char *)iep + plen);
+
+ if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) {
+ if (nvlist_add_boolean_value(nvl, "general",
+ B_TRUE) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) {
+ if (nvlist_add_uint8(nvl,
+ "invalid-length", lphp->lph_length) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ } else {
+ seen = B_TRUE;
+ }
+ break;
+ }
+
+ plen = iep->ie_hdr.lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ if (!seen) {
+ sip->si_supp_log &= ~LOGPAGE_SUPP_IE;
+ dprintf("IE logpage validation failed\n");
+ }
+
+ return (0);
+}
+
+/*
+ * Verify the contents of the temperature log page. The temperature log page
+ * contains two log parameters: the current temperature, and (optionally) the
+ * reference temperature. For the verification phase, we check that the two
+ * parameters we care about are well-formed. If there is no reference
+ * temperature, then we cannot use the page for monitoring purposes.
+ */
+static int
+logpage_temp_verify(ds_scsi_info_t *sip,
+ scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
+{
+ int i, plen = 0;
+ boolean_t has_reftemp = B_FALSE;
+ boolean_t bad_length = B_FALSE;
+ ushort_t param_code;
+
+ for (i = 0; i < log_length; i += plen) {
+ lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
+ param_code = BE_16(lphp->lph_param);
+
+ switch (param_code) {
+ case LOGPARAM_TEMP_CURTEMP:
+ if (nvlist_add_boolean_value(nvl, "current-temperature",
+ B_TRUE) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
+ if (nvlist_add_uint8(nvl,
+ "invalid-length", lphp->lph_length) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ bad_length = B_TRUE;
+ }
+ break;
+
+ case LOGPARAM_TEMP_REFTEMP:
+ if (nvlist_add_boolean_value(nvl,
+ "reference-temperature", B_TRUE) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
+ if (nvlist_add_uint8(nvl,
+ "invalid-length", lphp->lph_length) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ bad_length = B_TRUE;
+ }
+ has_reftemp = B_TRUE;
+ break;
+ }
+
+ plen = lphp->lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ if (bad_length || !has_reftemp) {
+ sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP;
+ dprintf("temperature logpage validation failed\n");
+ }
+
+ return (0);
+}
+
+/*
+ * Verify the contents of the self test log page. The log supports a maximum of
+ * 20 entries, where each entry's parameter code is its index in the log. We
+ * check that the parameter codes fall within this range, and that the size of
+ * each page is what we expect. It's perfectly acceptable for there to be no
+ * entries in this log, so we must also be sure to validate the contents as part
+ * of the analysis phase.
+ */
+static int
+logpage_selftest_verify(ds_scsi_info_t *sip,
+ scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
+{
+ int i, plen = 0;
+ boolean_t bad = B_FALSE;
+ int entries = 0;
+ ushort_t param_code;
+
+ for (i = 0; i < log_length; i += plen, entries++) {
+ lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
+ param_code = BE_16(lphp->lph_param);
+
+ if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE ||
+ param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) {
+ if (nvlist_add_uint16(nvl, "invalid-param-code",
+ param_code) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ bad = B_TRUE;
+ break;
+ }
+
+ if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) {
+ if (nvlist_add_uint8(nvl, "invalid-length",
+ lphp->lph_length) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ bad = B_TRUE;
+ break;
+
+ }
+
+ plen = lphp->lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ if (bad) {
+ sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST;
+ dprintf("selftest logpage validation failed\n");
+ }
+
+ return (0);
+}
+
+/*
+ * Load the current IE mode pages
+ */
+static int
+load_ie_modepage(ds_scsi_info_t *sip)
+{
+ struct scsi_ms_hdrs junk_hdrs;
+ int result;
+ uint_t skey, asc, ascq;
+
+ if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
+ return (0);
+
+ bzero(&sip->si_iec_current, sizeof (sip->si_iec_current));
+ bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable));
+
+ if ((result = scsi_mode_sense(sip,
+ MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current,
+ MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc,
+ &ascq)) == 0) {
+ result = scsi_mode_sense(sip,
+ MODEPAGE_INFO_EXCPT, PC_CHANGEABLE,
+ &sip->si_iec_changeable,
+ MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq);
+ }
+
+ if (result != 0) {
+ dprintf("failed to get IEC modepage (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq);
+ sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC;
+ } else {
+ if (nvlist_add_boolean_value(sip->si_state_iec,
+ "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 ||
+ nvlist_add_boolean_value(sip->si_state_iec,
+ "logerr", sip->si_iec_current.ie_logerr) != 0 ||
+ nvlist_add_uint8(sip->si_state_iec,
+ "mrie", sip->si_iec_current.ie_mrie) != 0 ||
+ nvlist_add_boolean_value(sip->si_state_iec,
+ "test", sip->si_iec_current.ie_test) != 0 ||
+ nvlist_add_boolean_value(sip->si_state_iec,
+ "ewasc", sip->si_iec_current.ie_ewasc) != 0 ||
+ nvlist_add_boolean_value(sip->si_state_iec,
+ "perf", sip->si_iec_current.ie_perf) != 0 ||
+ nvlist_add_boolean_value(sip->si_state_iec,
+ "ebf", sip->si_iec_current.ie_ebf) != 0 ||
+ nvlist_add_uint32(sip->si_state_iec,
+ "interval-timer",
+ BE_32(sip->si_iec_current.ie_interval_timer)) != 0 ||
+ nvlist_add_uint32(sip->si_state_iec,
+ "report-count",
+ BE_32(sip->si_iec_current.ie_report_count)) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ return (0);
+}
+
+/*
+ * Enable IE reporting. We prefer the following settings:
+ *
+ * (1) DEXCPT = 0
+ * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
+ * (4) EWASC = 1
+ * (6) REPORT COUNT = 0x00000001
+ * (7) LOGERR = 1
+ *
+ * However, not all drives support changing these values, and the current state
+ * may be useful enough as-is. For example, some drives support IE logging, but
+ * don't support changing the MRIE. In this case, we can still use the
+ * information provided by the log page.
+ */
+static int
+scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed)
+{
+ scsi_ie_page_t new_iec_page;
+ scsi_ms_hdrs_t hdrs;
+ uint_t skey, asc, ascq;
+
+ if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
+ return (0);
+
+ bzero(&new_iec_page, sizeof (new_iec_page));
+ bzero(&hdrs, sizeof (hdrs));
+
+ (void) memcpy(&new_iec_page, &sip->si_iec_current,
+ sizeof (new_iec_page));
+
+ if (IEC_IE_CHANGEABLE(sip->si_iec_changeable))
+ new_iec_page.ie_dexcpt = 0;
+
+ if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable))
+ new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST;
+
+ /*
+ * We only want to enable warning reporting if we are able to change the
+ * mrie to report on request. Otherwise, we risk unnecessarily
+ * interrupting normal SCSI commands with a CHECK CONDITION code.
+ */
+ if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) {
+ if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST)
+ new_iec_page.ie_ewasc = 1;
+ else
+ new_iec_page.ie_ewasc = 0;
+ }
+
+ if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable))
+ new_iec_page.ie_report_count = BE_32(1);
+
+ if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable))
+ new_iec_page.ie_logerr = 1;
+
+ /*
+ * Now compare the new mode page with the existing one.
+ * if there's no difference, there's no need for a mode select
+ */
+ if (memcmp(&new_iec_page, &sip->si_iec_current,
+ MODEPAGE_INFO_EXCPT_LEN) == 0) {
+ *changed = B_FALSE;
+ } else {
+ (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs));
+
+ if (scsi_mode_select(sip,
+ MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page,
+ MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) {
+ *changed = B_TRUE;
+ } else {
+ dprintf("failed to enable IE (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
+ *changed = B_FALSE;
+ }
+ }
+
+ if (nvlist_add_boolean_value(sip->si_state_iec, "changed",
+ *changed) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ return (0);
+}
+
+/*
+ * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
+ * storage.
+ */
+static int
+clear_gltsd(ds_scsi_info_t *sip)
+{
+ scsi_ms_hdrs_t hdrs, junk_hdrs;
+ struct mode_control_scsi3 control_pg_cur, control_pg_chg;
+ int result;
+ uint_t skey, asc, ascq;
+
+ bzero(&hdrs, sizeof (hdrs));
+ bzero(&control_pg_cur, sizeof (control_pg_cur));
+ bzero(&control_pg_chg, sizeof (control_pg_chg));
+
+ result = scsi_mode_sense(sip,
+ MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur,
+ MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
+
+ if (result != 0) {
+ dprintf("failed to read Control mode page (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
+ } else if (control_pg_cur.mode_page.length !=
+ PAGELENGTH_MODE_CONTROL_SCSI3) {
+ dprintf("SCSI-3 control mode page not supported\n");
+ } else if ((result = scsi_mode_sense(sip,
+ MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg,
+ MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq))
+ != 0) {
+ dprintf("failed to read changeable Control mode page (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
+ } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) {
+ dprintf("gltsd is set and not changeable\n");
+ if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
+ "gltsd", control_pg_cur.gltsd) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ } else if (control_pg_cur.gltsd) {
+ control_pg_cur.gltsd = 0;
+ result = scsi_mode_select(sip,
+ MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur,
+ MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
+ if (result != 0)
+ dprintf("failed to enable GLTSD (KEY=0x%x "
+ "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq);
+ if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
+ "gltsd", control_pg_cur.gltsd) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ return (0);
+}
+
+/*
+ * Fetch the contents of the logpage, and then call the logpage-specific
+ * analysis function. The analysis function is responsible for detecting any
+ * faults and filling in the details.
+ */
+static int
+analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry)
+{
+ scsi_log_header_t *lhp;
+ scsi_log_parameter_header_t *lphp;
+ int buflen;
+ int log_length;
+ uint_t skey, asc, ascq;
+ int result;
+
+ buflen = MAX_BUFLEN(scsi_log_header_t);
+ if ((lhp = calloc(buflen, 1)) == NULL)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ result = scsi_log_sense(sip, entry->ve_code,
+ PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq);
+
+ if (result == 0) {
+ log_length = BE_16(lhp->lh_length);
+ lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) +
+ sizeof (scsi_log_header_t));
+
+ result = entry->ve_analyze(sip, lphp, log_length);
+ } else {
+ result = scsi_set_errno(sip, EDS_IO);
+ }
+
+ free(lhp);
+ return (result);
+}
+
+/*
+ * Analyze the IE logpage. If we find an IE log record with a non-zero 'asc',
+ * then we have a fault.
+ */
+static int
+logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
+ int log_length)
+{
+ int i, plen = 0;
+ scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp;
+ nvlist_t *nvl;
+
+ assert(sip->si_dsp->ds_predfail == NULL);
+ if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ nvl = sip->si_dsp->ds_predfail;
+
+ for (i = 0; i < log_length; i += plen) {
+ iep = (scsi_ie_log_param_t *)((char *)iep + plen);
+
+ /*
+ * Even though we validated the length during the initial phase,
+ * never trust the device.
+ */
+ if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE &&
+ iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) {
+ if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC,
+ iep->ie_asc) != 0 ||
+ nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ,
+ iep->ie_ascq) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+
+ if (iep->ie_asc != 0)
+ sip->si_dsp->ds_faults |=
+ DS_FAULT_PREDFAIL;
+ break;
+ }
+ plen = iep->ie_hdr.lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ return (0);
+}
+
+static int
+logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
+ int log_length)
+{
+ int i, plen = 0;
+ uint8_t reftemp, curtemp;
+ ushort_t param_code;
+ scsi_temp_log_param_t *temp;
+ nvlist_t *nvl;
+
+ assert(sip->si_dsp->ds_overtemp == NULL);
+ if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ nvl = sip->si_dsp->ds_overtemp;
+
+ reftemp = curtemp = INVALID_TEMPERATURE;
+ for (i = 0; i < log_length; i += plen) {
+ lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
+ param_code = BE_16(lphp->lph_param);
+ temp = (scsi_temp_log_param_t *)lphp;
+
+ switch (param_code) {
+ case LOGPARAM_TEMP_CURTEMP:
+ if (lphp->lph_length != LOGPARAM_TEMP_LEN)
+ break;
+
+ if (nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_CURTEMP,
+ temp->t_temp) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ curtemp = temp->t_temp;
+ break;
+
+ case LOGPARAM_TEMP_REFTEMP:
+ if (lphp->lph_length != LOGPARAM_TEMP_LEN)
+ break;
+
+ if (nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP,
+ temp->t_temp) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ reftemp = temp->t_temp;
+ break;
+ }
+
+ plen = lphp->lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE &&
+ curtemp > reftemp)
+ sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP;
+
+ return (0);
+}
+
+static int
+logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
+ int log_length)
+{
+ int i, plen = 0;
+ int entries = 0;
+ ushort_t param_code;
+ scsi_selftest_log_param_t *stp;
+ nvlist_t *nvl;
+
+ assert(sip->si_dsp->ds_testfail == NULL);
+ if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ nvl = sip->si_dsp->ds_testfail;
+
+ for (i = 0; i < log_length; i += plen, entries++) {
+ lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
+ param_code = BE_16(lphp->lph_param);
+ stp = (scsi_selftest_log_param_t *)lphp;
+
+ if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE &&
+ param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE &&
+ lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) {
+ /*
+ * We always log the last result, or the result of the
+ * last completed test.
+ */
+ if ((param_code == 1 ||
+ SELFTEST_COMPLETE(stp->st_results))) {
+ if (nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_RESULTCODE,
+ stp->st_results) != 0 ||
+ nvlist_add_uint16(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP,
+ BE_16(stp->st_timestamp)) != 0 ||
+ nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_SEGMENT,
+ stp->st_number) != 0 ||
+ nvlist_add_uint64(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_ADDRESS,
+ BE_64(stp->st_lba)) != 0)
+ return (scsi_set_errno(sip,
+ EDS_NOMEM));
+
+ if (SELFTEST_COMPLETE(stp->st_results)) {
+ if (stp->st_results != SELFTEST_OK)
+ sip->si_dsp->ds_faults |=
+ DS_FAULT_TESTFAIL;
+ return (0);
+ }
+ }
+ }
+
+ plen = lphp->lph_length +
+ sizeof (scsi_log_parameter_header_t);
+ }
+
+ return (0);
+}
+
+/*
+ * Analyze the IE mode sense page explicitly. This is only needed if the IE log
+ * page is not supported.
+ */
+static int
+analyze_ie_sense(ds_scsi_info_t *sip)
+{
+ uint_t skey, asc, ascq;
+ nvlist_t *nvl;
+
+ /*
+ * Don't bother checking if we weren't able to set our MRIE correctly.
+ */
+ if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST)
+ return (0);
+
+ if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) {
+ dprintf("failed to request IE page (KEY=0x%x ASC=0x%x "
+ "ASCQ=0x%x)\n", skey, asc, ascq);
+ return (scsi_set_errno(sip, EDS_IO));
+ } else if (skey == KEY_NO_SENSE) {
+ assert(sip->si_dsp->ds_predfail == NULL);
+ if (nvlist_alloc(&sip->si_dsp->ds_predfail,
+ NV_UNIQUE_NAME, 0) != 0)
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ nvl = sip->si_dsp->ds_predfail;
+
+ if (nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 ||
+ nvlist_add_uint8(nvl,
+ FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) {
+ nvlist_free(nvl);
+ return (scsi_set_errno(sip, EDS_NOMEM));
+ }
+
+ if (asc != 0)
+ sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL;
+ }
+
+ return (0);
+}
+
+/*
+ * Clean up the scsi-specific information structure.
+ */
+static void
+ds_scsi_close(void *arg)
+{
+ ds_scsi_info_t *sip = arg;
+ if (sip->si_sim)
+ (void) dlclose(sip->si_sim);
+
+ free(sip);
+}
+
+/*
+ * Initialize a single disk. Initialization consists of:
+ *
+ * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
+ * Control page (page 0x1C).
+ *
+ * 2. If the IE page is available, try to set the following parameters:
+ *
+ * DEXCPT 0 Enable exceptions
+ * MRIE 6 Only report IE information on request
+ * EWASC 1 Enable warning reporting
+ * REPORT COUNT 1 Only report an IE exception once
+ * LOGERR 1 Enable logging of errors
+ *
+ * The remaining fields are left as-is, preserving the current values. If we
+ * cannot set some of these fields, then we do our best. Some drives may
+ * have a static configuration which still allows for some monitoring.
+ *
+ * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
+ * LOG SENSE command.
+ *
+ * 4. Check to see if the self-test log page (page 0x10) is supported.
+ *
+ * 5. Check to see if the temperature log page (page 0x0D) is supported, and
+ * contains a reference temperature.
+ *
+ * 6. Clear the GLTSD bit in control mode page 0xA. This will allow the drive
+ * to save each of the log pages described above to nonvolatile storage.
+ * This is essential if the drive is to remember its failures across
+ * loss of power.
+ */
+static void *
+ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip)
+{
+ boolean_t changed;
+
+ sip->si_dsp = dsp;
+
+ /* Load and validate mode pages */
+ if (load_modepages(sip) != 0) {
+ ds_scsi_close(sip);
+ return (NULL);
+ }
+
+ /* Load and validate log pages */
+ if (load_logpages(sip) != 0) {
+ ds_scsi_close(sip);
+ return (NULL);
+ }
+
+ /* Load IE state */
+ if (load_ie_modepage(sip) != 0 ||
+ scsi_enable_ie(sip, &changed) != 0 ||
+ (changed && load_ie_modepage(sip) != 0)) {
+ ds_scsi_close(sip);
+ return (NULL);
+ }
+
+ /* Clear the GLTSD bit in the control page */
+ if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) {
+ ds_scsi_close(sip);
+ return (NULL);
+ }
+
+ return (sip);
+}
+
+static void *
+ds_scsi_open_uscsi(disk_status_t *dsp)
+{
+ ds_scsi_info_t *sip;
+
+ if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
+ (void) ds_set_errno(dsp, EDS_NOMEM);
+ return (NULL);
+ }
+
+ return (ds_scsi_open_common(dsp, sip));
+}
+
+static void *
+ds_scsi_open_sim(disk_status_t *dsp)
+{
+ ds_scsi_info_t *sip;
+
+ if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
+ (void) ds_set_errno(dsp, EDS_NOMEM);
+ return (NULL);
+ }
+
+ if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) {
+ (void) ds_set_errno(dsp, EDS_NO_TRANSPORT);
+ free(sip);
+ return (NULL);
+ }
+
+ return (ds_scsi_open_common(dsp, sip));
+}
+
+
+/*
+ * Scan for any faults. The following steps are performed:
+ *
+ * 1. If the temperature log page is supported, check the current temperature
+ * and threshold. If the current temperature exceeds the threshold, report
+ * and overtemp fault.
+ *
+ * 2. If the selftest log page is supported, check to the last completed self
+ * test. If the last completed test resulted in failure, report a selftest
+ * fault.
+ *
+ * 3. If the IE log page is supported, check to see if failure is predicted. If
+ * so, indicate a predictive failure fault.
+ *
+ * 4. If the IE log page is not supported, but the mode page supports report on
+ * request mode, then issue a REQUEST SENSE for the mode page. Indicate a
+ * predictive failure fault if necessary.
+ */
+static int
+ds_scsi_scan(void *arg)
+{
+ ds_scsi_info_t *sip = arg;
+ int i;
+
+ for (i = 0; i < NLOG_VALIDATION; i++) {
+ if ((sip->si_supp_log & log_validation[i].ve_supported) == 0)
+ continue;
+
+ if (analyze_one_logpage(sip, &log_validation[i]) != 0)
+ return (-1);
+ }
+
+ if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) &&
+ (sip->si_supp_mode & MODEPAGE_SUPP_IEC) &&
+ analyze_ie_sense(sip) != 0)
+ return (-1);
+
+ return (0);
+}
+
+ds_transport_t ds_scsi_uscsi_transport = {
+ ds_scsi_open_uscsi,
+ ds_scsi_close,
+ ds_scsi_scan
+};
+
+ds_transport_t ds_scsi_sim_transport = {
+ ds_scsi_open_sim,
+ ds_scsi_close,
+ ds_scsi_scan
+};
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h
new file mode 100644
index 0000000000..6d2648f06b
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi.h
@@ -0,0 +1,327 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DS_SCSI_H
+#define _DS_SCSI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/byteorder.h>
+#include <sys/scsi/scsi.h>
+
+#include "ds_impl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(_BIT_FIELDS_LTOH) && !defined(_BIT_FIELDS_HTOL)
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+
+/*
+ * Log page structures
+ */
+#pragma pack(1)
+
+typedef struct scsi_log_header {
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t lh_code : 6,
+ __reserved : 2;
+#else
+ uint8_t __reserved : 2,
+ lh_code : 6;
+#endif
+ uint8_t __reserved2;
+ uint16_t lh_length;
+} scsi_log_header_t;
+
+typedef struct scsi_log_parameter_header {
+ uint16_t lph_param;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t lph_lp : 1,
+ lph_lbin : 1,
+ lph_tmc : 2,
+ lph_etc : 1,
+ lph_tsd : 1,
+ lph_ds : 1,
+ lph_du : 1;
+#else
+ uint8_t lph_du : 1,
+ lph_ds : 1,
+ lph_tsd : 1,
+ lph_etc : 1,
+ lph_tmc : 2,
+ lph_lbin : 1,
+ lph_lp : 1;
+#endif
+ uint8_t lph_length;
+} scsi_log_parameter_header_t;
+
+typedef struct scsi_supported_log_pages {
+ scsi_log_header_t slp_hdr;
+ uchar_t slp_pages[1];
+} scsi_supported_log_pages_t;
+
+typedef struct scsi_ie_log_param {
+ scsi_log_parameter_header_t ie_hdr;
+ uchar_t ie_asc;
+ uchar_t ie_ascq;
+} scsi_ie_log_param_t;
+
+/*
+ * The SCSI-3 SPC document states that IE log page (0x2F) parameter 0
+ * must have a length of at least 4 (including the length byte).
+ */
+#define LOGPARAM_IE_MIN_LEN 2 /* the asc and ascq fields */
+
+#define INVALID_TEMPERATURE 0xff
+
+#define LOGPARAM_IE 0x0000
+
+typedef struct scsi_temp_log_param {
+ scsi_log_parameter_header_t t_hdr;
+ uchar_t __reserved;
+ uchar_t t_temp;
+} scsi_temp_log_param_t;
+
+typedef struct scsi_selftest_log_param {
+ scsi_log_parameter_header_t st_hdr;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t st_results : 4,
+ __reserved1 : 1,
+ st_testcode : 3;
+#else
+ uint8_t st_testcode : 3,
+ __reserved1 : 1,
+ st_results : 4;
+#endif
+ uint8_t st_number;
+ uint16_t st_timestamp;
+ uint64_t st_lba;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t st_sensekey : 4,
+ __reserved2 : 4;
+#else
+ uint8_t __reserved2 : 4,
+ st_sensekey : 4;
+#endif
+ uint8_t st_asc;
+ uint8_t st_ascq;
+ uint8_t st_vendor;
+} scsi_selftest_log_param_t;
+
+/* The results field of the self-test log parameter */
+#define SELFTEST_OK 0x0
+#define SELFTEST_ABORT_REQUEST 0x1
+#define SELFTEST_ABORT_OTHER 0x2
+#define SELFTEST_FAILURE_INCOMPLETE 0x3
+#define SELFTEST_FAILURE_SEG_UNKNOWN 0x4
+#define SELFTEST_FAILURE_SEG_FIRST 0x5
+#define SELFTEST_FAILURE_SEG_SECOND 0x6
+#define SELFTEST_FAILURE_SEG_OTHER 0x7
+#define SELFTEST_INPROGRESS 0xf
+
+#define SELFTEST_COMPLETE(code) \
+ ((code) == SELFTEST_OK || \
+ ((code) >= SELFTEST_FAILURE_INCOMPLETE && \
+ ((code) <= SELFTEST_FAILURE_SEG_OTHER)))
+
+#define LOGPARAM_TEMP_CURTEMP 0x0000
+#define LOGPARAM_TEMP_REFTEMP 0x0001
+
+#define LOGPARAM_TEMP_LEN \
+ (sizeof (scsi_temp_log_param_t) - \
+ sizeof (scsi_log_parameter_header_t))
+
+/*
+ * Mode sense/select page header information
+ */
+typedef struct scsi_ms_header {
+ struct mode_header ms_header;
+ struct block_descriptor ms_descriptor;
+} scsi_ms_header_t;
+
+typedef struct scsi_ms_header_g1 {
+ struct mode_header_g1 ms_header;
+ struct block_descriptor ms_descriptor;
+} scsi_ms_header_g1_t;
+
+typedef struct scsi_ms_hdrs {
+ int ms_length;
+ union {
+ scsi_ms_header_t g0;
+ scsi_ms_header_g1_t g1;
+ } ms_hdr;
+} scsi_ms_hdrs_t;
+
+typedef struct scsi_ie_page {
+ struct mode_page ie_mp;
+#if defined(_BIT_FIELDS_LTOH)
+ uint8_t ie_logerr : 1, /* Errors should be logged */
+ __reserved1 : 1,
+ ie_test : 1, /* Enable test gen of IEs */
+ ie_dexcpt : 1, /* Disable exceptions */
+ ie_ewasc : 1, /* Enable warning generation */
+ ie_ebf : 1, /* enable backgrnd functions */
+ __reserved2 : 1,
+ ie_perf : 1; /* No delays during excptns */
+ uint8_t ie_mrie : 4, /* Method/reporting excptons */
+ __reserved3 : 4;
+#else
+ uint8_t ie_perf : 1, /* No delays during excptons */
+ __reserved2 : 1,
+ ie_ebf : 1, /* enable background funcs */
+ ie_ewasc : 1, /* Enable warning generation */
+ ie_dexcpt : 1, /* Disable exceptions */
+ ie_test : 1, /* Enable test gen of IEs */
+ __reserved1 : 1,
+ ie_logerr : 1; /* Errors should be logged */
+ uint8_t __reserved3 : 4,
+ ie_mrie : 4; /* Method of report excptns */
+#endif
+ uint32_t ie_interval_timer; /* reporting interval for IEs */
+ uint32_t ie_report_count; /* # of times to report an IE */
+} scsi_ie_page_t;
+
+#pragma pack()
+
+#define MODEPAGE_INFO_EXCPT_LEN (sizeof (scsi_ie_page_t))
+
+#define IEC_IE_ENABLED(ies) ((ies).ie_dexcpt == 0)
+#define IEC_IE_CHANGEABLE(ies) ((ies).ie_dexcpt == 1)
+#define IEC_MRIE_CHANGEABLE(ies) ((ies).ie_mrie == 0xf)
+#define IEC_PERF_CHANGEABLE(ies) ((ies).ie_perf == 1)
+#define IEC_EWASC_CHANGEABLE(ies) ((ies).ie_ewasc == 1)
+#define IEC_TEST_CHANGEABLE(ies) ((ies).ie_test == 1)
+#define IEC_RPTCNT_CHANGEABLE(ies) ((ies).ie_report_count == BE_32(0xffffffff))
+#define IEC_LOGERR_CHANGEABLE(ies) ((ies).ie_logerr == 1)
+
+/*
+ * Values for the MRIE field of the informational exceptions control mode page
+ */
+#define IE_REPORT_NONE 0
+#define IE_REPORT_ASYNCH 1
+#define IE_REPORT_UNIT_ATTN 2
+#define IE_REPORT_RECOV_ERR_COND 3
+#define IE_REPORT_RECOV_ERR_ALWAYS 4
+#define IE_REPORT_NO_SENSE 5
+#define IE_REPORT_ON_REQUEST 6
+
+/*
+ * Constants in support of the CONTROL MODE mode page (page 0xA)
+ */
+#define MODEPAGE_CTRL_MODE_LEN (sizeof (struct mode_control_scsi3))
+#define GLTSD_CHANGEABLE(chg) ((chg).gltsd == 1)
+
+#define LOGPAGE_SELFTEST_MIN_PARAM_CODE 0x0001
+#define LOGPAGE_SELFTEST_MAX_PARAM_CODE 0x0014
+
+#define LOGPAGE_SELFTEST_PARAM_LEN \
+ ((sizeof (scsi_selftest_log_param_t)) - \
+ (sizeof (scsi_log_parameter_header_t)))
+
+/*
+ * Macro to extract the length of a mode sense page
+ * as returned by a target.
+ */
+#define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \
+ sizeof (struct mode_page))
+
+/*
+ * Mode Select options
+ */
+#define MODE_SELECT_SP 0x01
+#define MODE_SELECT_PF 0x10
+
+
+/*
+ * Mode Sense Page Control
+ */
+#define PC_CURRENT (0 << 6)
+#define PC_CHANGEABLE (1 << 6)
+#define PC_DEFAULT (2 << 6)
+#define PC_SAVED (3 << 6)
+
+/*
+ * Log Sense Page Control
+ */
+#define PC_CUMULATIVE (1 << 6)
+
+/*
+ * LOG page codes
+ */
+#define LOGPAGE_SUPP_LIST 0x00
+#define LOGPAGE_TEMP 0x0d
+#define LOGPAGE_SELFTEST 0x10
+#define LOGPAGE_IE 0x2f
+
+/* ASC constants */
+#define ASC_INVALID_OPCODE 0x20
+#define ASC_INVALID_CDB_FIELD 0x24
+#define ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5d
+
+/* ASCQ constants */
+#define ASCQ_INVALID_OPCODE 0
+
+/* Error tests */
+#define SCSI_INVALID_OPCODE(s, a, aq) \
+ (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_OPCODE) && \
+ ((aq) == ASCQ_INVALID_OPCODE))
+
+#define MODE_PAGE_UNSUPPORTED(s, a, aq) \
+ (((s) == KEY_ILLEGAL_REQUEST) && ((a) == ASC_INVALID_CDB_FIELD))
+
+/* command length to use */
+#define MODE_CMD_LEN_UNKNOWN 0
+#define MODE_CMD_LEN_6 1
+#define MODE_CMD_LEN_10 2
+
+/* supported modepages bitmask */
+#define MODEPAGE_SUPP_IEC 0x1
+
+/* supported logpages bitmask */
+#define LOGPAGE_SUPP_IE 0x1
+#define LOGPAGE_SUPP_TEMP 0x2
+#define LOGPAGE_SUPP_SELFTEST 0x4
+
+#define MSG_BUFLEN 256
+
+/*
+ * For SCSI commands which want to accept arbitrary length responses, we need to
+ * allocate an appropriate sized buffer. The maximum length is USHRT_MAX,
+ * because some devices return nothing if the buffer length is too big.
+ */
+#define MAX_BUFLEN(type) (USHRT_MAX - sizeof (type))
+
+extern ds_transport_t ds_scsi_uscsi_transport;
+extern ds_transport_t ds_scsi_sim_transport;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DS_SCSI_H */
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c
new file mode 100644
index 0000000000..1750f67c27
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.c
@@ -0,0 +1,179 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * SCSI simulator.
+ *
+ * For testing purposes, we need a way to simulate arbitrary SCSI responses. A
+ * completely flexible SCSI simulation language would be a large undertaking,
+ * given the number of possible outcomes. Instead, we opt for the simpler route
+ * of using a shared object which implements versions of these functions.
+ *
+ * If a shared object doesn't implement a given function, or if the function
+ * returns non-zero, then the simulator will provide a suitable response
+ * indicating the functionality isn't supported.
+ */
+
+#include <libdiskstatus.h>
+
+#include "ds_scsi.h"
+#include "ds_scsi_sim.h"
+
+static int
+check_invalid_code(int ret, void *rqbuf)
+{
+ if (ret != 0) {
+ struct scsi_extended_sense *sensep = rqbuf;
+
+ sensep->es_key = KEY_ILLEGAL_REQUEST;
+ sensep->es_add_len = 6;
+ sensep->es_code = CODE_FMT_FIXED_CURRENT;
+ sensep->es_add_code = ASC_INVALID_OPCODE;
+ sensep->es_qual_code = ASCQ_INVALID_OPCODE;
+ ret = -1;
+ }
+
+ return (ret);
+}
+
+typedef int (*scsi_mode_sense_f)(int, int, caddr_t, int, scsi_ms_header_t *,
+ void *, int *);
+
+int
+simscsi_mode_sense(void *hdl, int page_code, int page_control,
+ caddr_t page_data, int page_size, scsi_ms_header_t *header,
+ void *rqbuf, int *rqblen)
+{
+ scsi_mode_sense_f dscsi_mode_sense;
+ int ret = -1;
+
+ dscsi_mode_sense = (scsi_mode_sense_f)dlsym(hdl, "scsi_mode_sense");
+
+ if (dscsi_mode_sense != NULL)
+ ret = (*dscsi_mode_sense)(page_code, page_control, page_data,
+ page_size, header, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
+
+typedef int (*scsi_mode_sense_10_f)(int, int, caddr_t, int,
+ scsi_ms_header_g1_t *, void *, int *);
+
+int
+simscsi_mode_sense_10(void *hdl, int page_code, int page_control,
+ caddr_t page_data, int page_size, scsi_ms_header_g1_t *header,
+ void *rqbuf, int *rqblen)
+{
+ scsi_mode_sense_10_f dscsi_mode_sense_10;
+ int ret = -1;
+
+ dscsi_mode_sense_10 = (scsi_mode_sense_10_f)dlsym(hdl,
+ "scsi_mode_sense_10");
+
+ if (dscsi_mode_sense_10 != NULL)
+ ret = (*dscsi_mode_sense_10)(page_code, page_control, page_data,
+ page_size, header, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
+
+typedef int (*scsi_mode_select_f)(int, int, caddr_t, int, scsi_ms_header_t *,
+ void *, int *);
+
+int
+simscsi_mode_select(void *hdl, int page_code, int options, caddr_t page_data,
+ int page_size, scsi_ms_header_t *header, void *rqbuf, int *rqblen)
+{
+ scsi_mode_select_f dscsi_mode_select;
+ int ret = -1;
+
+ dscsi_mode_select = (scsi_mode_select_f)(dlsym(hdl,
+ "scsi_mode_select"));
+
+ if (dscsi_mode_select != NULL)
+ ret = (*dscsi_mode_select)(page_code, options, page_data,
+ page_size, header, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
+
+typedef int (*scsi_mode_select_10_f)(int, int, caddr_t, int,
+ scsi_ms_header_g1_t *, void *, int *);
+
+int
+simscsi_mode_select_10(void *hdl, int page_code, int options,
+ caddr_t page_data, int page_size, scsi_ms_header_g1_t *header,
+ void *rqbuf, int *rqblen)
+{
+ scsi_mode_select_10_f dscsi_mode_select_10;
+ int ret = -1;
+
+ dscsi_mode_select_10 = (scsi_mode_select_10_f)dlsym(hdl,
+ "scsi_mode_select_10");
+
+ if (dscsi_mode_select_10 != NULL)
+ ret = (*dscsi_mode_select_10)(page_code, options, page_data,
+ page_size, header, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
+
+typedef int (*scsi_log_sense_f)(int, int, caddr_t, int, void *, int *);
+
+int
+simscsi_log_sense(void *hdl, int page_code, int page_control,
+ caddr_t page_data, int page_size, void *rqbuf, int *rqblen)
+{
+ scsi_log_sense_f dscsi_log_sense;
+ int ret = -1;
+
+ dscsi_log_sense = (scsi_log_sense_f)dlsym(hdl, "scsi_log_sense");
+
+ if (dscsi_log_sense != NULL)
+ ret = (*dscsi_log_sense)(page_code, page_control, page_data,
+ page_size, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
+
+typedef int (*scsi_request_sense_f)(caddr_t, int, void *, int *);
+
+int
+simscsi_request_sense(void *hdl, caddr_t buf, int buflen,
+ void *rqbuf, int *rqblen)
+{
+ scsi_request_sense_f dscsi_request_sense;
+ int ret = -1;
+
+ dscsi_request_sense = (scsi_request_sense_f)dlsym(hdl,
+ "scsi_request_sense");
+
+ if (dscsi_request_sense != NULL)
+ ret = (*dscsi_request_sense)(buf, buflen, rqbuf, rqblen);
+
+ return (check_invalid_code(ret, rqbuf));
+}
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h
new file mode 100644
index 0000000000..7190dd97ff
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_sim.h
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DS_SCSI_SIM_H
+#define _DS_SCSI_SIM_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int simscsi_mode_sense(void *, int, int, caddr_t, int, scsi_ms_header_t *,
+ void *, int *);
+int simscsi_mode_sense_10(void *, int, int, caddr_t, int,
+ scsi_ms_header_g1_t *, void *, int *);
+int simscsi_mode_select(void *, int, int, caddr_t, int,
+ scsi_ms_header_t *, void *, int *);
+int simscsi_mode_select_10(void *, int, int, caddr_t, int,
+ scsi_ms_header_g1_t *, void *, int *);
+int simscsi_log_sense(void *, int, int, caddr_t, int, void *, int *);
+int simscsi_request_sense(void *, caddr_t, int, void *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DS_SCSI_SIM_H */
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c
new file mode 100644
index 0000000000..81f62ad0cf
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.c
@@ -0,0 +1,1658 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains routines for sending and receiving SCSI commands. The
+ * higher level logic is contained in ds_scsi.c.
+ */
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <utility.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <alloca.h>
+
+#include "ds_scsi.h"
+#include "ds_scsi_uscsi.h"
+
+#define MSGBUFLEN 64
+#define USCSI_DEFAULT_TIMEOUT 45
+#define USCSI_TIMEOUT_MAX INT_MAX
+
+static diskaddr_t scsi_extract_sense_info_descr(
+ struct scsi_descr_sense_hdr *sdsp, int rqlen);
+static void scsi_print_extended_sense(struct scsi_extended_sense *rq,
+ int rqlen);
+static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen);
+
+typedef struct slist {
+ char *str;
+ int value;
+} slist_t;
+
+static char *
+find_string(slist_t *slist, int match_value)
+{
+ for (; slist->str != NULL; slist++) {
+ if (slist->value == match_value) {
+ return (slist->str);
+ }
+ }
+
+ return ((char *)NULL);
+}
+
+/*
+ * Strings for printing mode sense page control values
+ */
+static slist_t page_control_strings[] = {
+ { "current", PC_CURRENT },
+ { "changeable", PC_CHANGEABLE },
+ { "default", PC_DEFAULT },
+ { "saved", PC_SAVED },
+ { NULL, 0 }
+};
+
+/*
+ * Strings for printing the mode select options
+ */
+static slist_t mode_select_strings[] = {
+ { "", 0 },
+ { "(pf)", MODE_SELECT_PF },
+ { "(sp)", MODE_SELECT_SP },
+ { "(pf,sp)", MODE_SELECT_PF|MODE_SELECT_SP },
+ { NULL, 0 }
+};
+
+static slist_t sensekey_strings[] = {
+ { "No sense error", KEY_NO_SENSE },
+ { "Recoverable error", KEY_RECOVERABLE_ERROR },
+ { "Not ready error", KEY_NOT_READY },
+ { "Medium error", KEY_MEDIUM_ERROR },
+ { "Hardware error", KEY_HARDWARE_ERROR },
+ { "Illegal request", KEY_ILLEGAL_REQUEST },
+ { "Unit attention error", KEY_UNIT_ATTENTION },
+ { "Write protect error", KEY_WRITE_PROTECT },
+ { "Blank check error", KEY_BLANK_CHECK },
+ { "Vendor unique error", KEY_VENDOR_UNIQUE },
+ { "Copy aborted error", KEY_COPY_ABORTED },
+ { "Aborted command", KEY_ABORTED_COMMAND },
+ { "Equal error", KEY_EQUAL },
+ { "Volume overflow", KEY_VOLUME_OVERFLOW },
+ { "Miscompare error", KEY_MISCOMPARE },
+ { "Reserved error", KEY_RESERVED },
+ { NULL, 0 }
+};
+
+static slist_t scsi_cmdname_strings[] = {
+ { "mode select", SCMD_MODE_SELECT },
+ { "mode sense", SCMD_MODE_SENSE },
+ { "mode select(10)", SCMD_MODE_SELECT_G1 },
+ { "mode sense(10)", SCMD_MODE_SENSE_G1 },
+ { "log sense", SCMD_LOG_SENSE_G1 },
+ { "request sense", SCMD_REQUEST_SENSE },
+ { NULL, 0 }
+};
+
+static struct _scsi_asq_key_strings {
+ uint_t asc;
+ uint_t ascq;
+ const char *message;
+} extended_sense_list[] = {
+ { 0x00, 0x00, "no additional sense info" },
+ { 0x00, 0x01, "filemark detected" },
+ { 0x00, 0x02, "end of partition/medium detected" },
+ { 0x00, 0x03, "setmark detected" },
+ { 0x00, 0x04, "begining of partition/medium detected" },
+ { 0x00, 0x05, "end of data detected" },
+ { 0x00, 0x06, "i/o process terminated" },
+ { 0x00, 0x11, "audio play operation in progress" },
+ { 0x00, 0x12, "audio play operation paused" },
+ { 0x00, 0x13, "audio play operation successfully completed" },
+ { 0x00, 0x14, "audio play operation stopped due to error" },
+ { 0x00, 0x15, "no current audio status to return" },
+ { 0x00, 0x16, "operation in progress" },
+ { 0x00, 0x17, "cleaning requested" },
+ { 0x00, 0x18, "erase operation in progress" },
+ { 0x00, 0x19, "locate operation in progress" },
+ { 0x00, 0x1A, "rewind operation in progress" },
+ { 0x00, 0x1B, "set capacity operation in progress" },
+ { 0x00, 0x1C, "verify operation in progress" },
+ { 0x01, 0x00, "no index/sector signal" },
+ { 0x02, 0x00, "no seek complete" },
+ { 0x03, 0x00, "peripheral device write fault" },
+ { 0x03, 0x01, "no write current" },
+ { 0x03, 0x02, "excessive write errors" },
+ { 0x04, 0x00, "LUN not ready" },
+ { 0x04, 0x01, "LUN is becoming ready" },
+ { 0x04, 0x02, "LUN initializing command required" },
+ { 0x04, 0x03, "LUN not ready intervention required" },
+ { 0x04, 0x04, "LUN not ready format in progress" },
+ { 0x04, 0x05, "LUN not ready, rebuild in progress" },
+ { 0x04, 0x06, "LUN not ready, recalculation in progress" },
+ { 0x04, 0x07, "LUN not ready, operation in progress" },
+ { 0x04, 0x08, "LUN not ready, long write in progress" },
+ { 0x04, 0x09, "LUN not ready, self-test in progress" },
+ { 0x04, 0x0A, "LUN not accessible, asymmetric access state "
+ "transition" },
+ { 0x04, 0x0B, "LUN not accessible, target port in standby state" },
+ { 0x04, 0x0C, "LUN not accessible, target port in unavailable state" },
+ { 0x04, 0x10, "LUN not ready, auxiliary memory not accessible" },
+ { 0x05, 0x00, "LUN does not respond to selection" },
+ { 0x06, 0x00, "reference position found" },
+ { 0x07, 0x00, "multiple peripheral devices selected" },
+ { 0x08, 0x00, "LUN communication failure" },
+ { 0x08, 0x01, "LUN communication time-out" },
+ { 0x08, 0x02, "LUN communication parity error" },
+ { 0x08, 0x03, "LUN communication crc error (ultra-DMA/32)" },
+ { 0x08, 0x04, "unreachable copy target" },
+ { 0x09, 0x00, "track following error" },
+ { 0x09, 0x01, "tracking servo failure" },
+ { 0x09, 0x02, "focus servo failure" },
+ { 0x09, 0x03, "spindle servo failure" },
+ { 0x09, 0x04, "head select fault" },
+ { 0x0a, 0x00, "error log overflow" },
+ { 0x0b, 0x00, "warning" },
+ { 0x0b, 0x01, "warning - specified temperature exceeded" },
+ { 0x0b, 0x02, "warning - enclosure degraded" },
+ { 0x0c, 0x00, "write error" },
+ { 0x0c, 0x01, "write error - recovered with auto reallocation" },
+ { 0x0c, 0x02, "write error - auto reallocation failed" },
+ { 0x0c, 0x03, "write error - recommend reassignment" },
+ { 0x0c, 0x04, "compression check miscompare error" },
+ { 0x0c, 0x05, "data expansion occurred during compression" },
+ { 0x0c, 0x06, "block not compressible" },
+ { 0x0c, 0x07, "write error - recovery needed" },
+ { 0x0c, 0x08, "write error - recovery failed" },
+ { 0x0c, 0x09, "write error - loss of streaming" },
+ { 0x0c, 0x0a, "write error - padding blocks added" },
+ { 0x0c, 0x0b, "auxiliary memory write error" },
+ { 0x0c, 0x0c, "write error - unexpected unsolicited data" },
+ { 0x0c, 0x0d, "write error - not enough unsolicited data" },
+ { 0x0d, 0x00, "error detected by third party temporary initiator" },
+ { 0x0d, 0x01, "third party device failure" },
+ { 0x0d, 0x02, "copy target device not reachable" },
+ { 0x0d, 0x03, "incorrect copy target device type" },
+ { 0x0d, 0x04, "copy target device data underrun" },
+ { 0x0d, 0x05, "copy target device data overrun" },
+ { 0x0e, 0x00, "invalid information unit" },
+ { 0x0e, 0x01, "information unit too short" },
+ { 0x0e, 0x02, "information unit too long" },
+ { 0x10, 0x00, "ID CRC or ECC error" },
+ { 0x11, 0x00, "unrecovered read error" },
+ { 0x11, 0x01, "read retries exhausted" },
+ { 0x11, 0x02, "error too long to correct" },
+ { 0x11, 0x03, "multiple read errors" },
+ { 0x11, 0x04, "unrecovered read error - auto reallocate failed" },
+ { 0x11, 0x05, "L-EC uncorrectable error" },
+ { 0x11, 0x06, "CIRC unrecovered error" },
+ { 0x11, 0x07, "data re-synchronization error" },
+ { 0x11, 0x08, "incomplete block read" },
+ { 0x11, 0x09, "no gap found" },
+ { 0x11, 0x0a, "miscorrected error" },
+ { 0x11, 0x0b, "unrecovered read error - recommend reassignment" },
+ { 0x11, 0x0c, "unrecovered read error - recommend rewrite the data" },
+ { 0x11, 0x0d, "de-compression crc error" },
+ { 0x11, 0x0e, "cannot decompress using declared algorithm" },
+ { 0x11, 0x0f, "error reading UPC/EAN number" },
+ { 0x11, 0x10, "error reading ISRC number" },
+ { 0x11, 0x11, "read error - loss of streaming" },
+ { 0x11, 0x12, "auxiliary memory read error" },
+ { 0x11, 0x13, "read error - failed retransmission request" },
+ { 0x12, 0x00, "address mark not found for ID field" },
+ { 0x13, 0x00, "address mark not found for data field" },
+ { 0x14, 0x00, "recorded entity not found" },
+ { 0x14, 0x01, "record not found" },
+ { 0x14, 0x02, "filemark or setmark not found" },
+ { 0x14, 0x03, "end-of-data not found" },
+ { 0x14, 0x04, "block sequence error" },
+ { 0x14, 0x05, "record not found - recommend reassignment" },
+ { 0x14, 0x06, "record not found - data auto-reallocated" },
+ { 0x14, 0x07, "locate operation failure" },
+ { 0x15, 0x00, "random positioning error" },
+ { 0x15, 0x01, "mechanical positioning error" },
+ { 0x15, 0x02, "positioning error detected by read of medium" },
+ { 0x16, 0x00, "data sync mark error" },
+ { 0x16, 0x01, "data sync error - data rewritten" },
+ { 0x16, 0x02, "data sync error - recommend rewrite" },
+ { 0x16, 0x03, "data sync error - data auto-reallocated" },
+ { 0x16, 0x04, "data sync error - recommend reassignment" },
+ { 0x17, 0x00, "recovered data with no error correction" },
+ { 0x17, 0x01, "recovered data with retries" },
+ { 0x17, 0x02, "recovered data with positive head offset" },
+ { 0x17, 0x03, "recovered data with negative head offset" },
+ { 0x17, 0x04, "recovered data with retries and/or CIRC applied" },
+ { 0x17, 0x05, "recovered data using previous sector id" },
+ { 0x17, 0x06, "recovered data without ECC - data auto-reallocated" },
+ { 0x17, 0x07, "recovered data without ECC - recommend reassignment" },
+ { 0x17, 0x08, "recovered data without ECC - recommend rewrite" },
+ { 0x17, 0x09, "recovered data without ECC - data rewritten" },
+ { 0x18, 0x00, "recovered data with error correction" },
+ { 0x18, 0x01, "recovered data with error corr. & retries applied" },
+ { 0x18, 0x02, "recovered data - data auto-reallocated" },
+ { 0x18, 0x03, "recovered data with CIRC" },
+ { 0x18, 0x04, "recovered data with L-EC" },
+ { 0x18, 0x05, "recovered data - recommend reassignment" },
+ { 0x18, 0x06, "recovered data - recommend rewrite" },
+ { 0x18, 0x07, "recovered data with ECC - data rewritten" },
+ { 0x18, 0x08, "recovered data with linking" },
+ { 0x19, 0x00, "defect list error" },
+ { 0x1a, 0x00, "parameter list length error" },
+ { 0x1b, 0x00, "synchronous data xfer error" },
+ { 0x1c, 0x00, "defect list not found" },
+ { 0x1c, 0x01, "primary defect list not found" },
+ { 0x1c, 0x02, "grown defect list not found" },
+ { 0x1d, 0x00, "miscompare during verify" },
+ { 0x1e, 0x00, "recovered ID with ECC" },
+ { 0x1f, 0x00, "partial defect list transfer" },
+ { 0x20, 0x00, "invalid command operation code" },
+ { 0x20, 0x01, "access denied - initiator pending-enrolled" },
+ { 0x20, 0x02, "access denied - no access rights" },
+ { 0x20, 0x03, "access denied - invalid mgmt id key" },
+ { 0x20, 0x04, "illegal command while in write capable state" },
+ { 0x20, 0x06, "illegal command while in explicit address mode" },
+ { 0x20, 0x07, "illegal command while in implicit address mode" },
+ { 0x20, 0x08, "access denied - enrollment conflict" },
+ { 0x20, 0x09, "access denied - invalid lu identifier" },
+ { 0x20, 0x0a, "access denied - invalid proxy token" },
+ { 0x20, 0x0b, "access denied - ACL LUN conflict" },
+ { 0x21, 0x00, "logical block address out of range" },
+ { 0x21, 0x01, "invalid element address" },
+ { 0x21, 0x02, "invalid address for write" },
+ { 0x22, 0x00, "illegal function" },
+ { 0x24, 0x00, "invalid field in cdb" },
+ { 0x24, 0x01, "cdb decryption error" },
+ { 0x25, 0x00, "LUN not supported" },
+ { 0x26, 0x00, "invalid field in param list" },
+ { 0x26, 0x01, "parameter not supported" },
+ { 0x26, 0x02, "parameter value invalid" },
+ { 0x26, 0x03, "threshold parameters not supported" },
+ { 0x26, 0x04, "invalid release of persistent reservation" },
+ { 0x26, 0x05, "data decryption error" },
+ { 0x26, 0x06, "too many target descriptors" },
+ { 0x26, 0x07, "unsupported target descriptor type code" },
+ { 0x26, 0x08, "too many segment descriptors" },
+ { 0x26, 0x09, "unsupported segment descriptor type code" },
+ { 0x26, 0x0a, "unexpected inexact segment" },
+ { 0x26, 0x0b, "inline data length exceeded" },
+ { 0x26, 0x0c, "invalid operation for copy source or destination" },
+ { 0x26, 0x0d, "copy segment granularity violation" },
+ { 0x27, 0x00, "write protected" },
+ { 0x27, 0x01, "hardware write protected" },
+ { 0x27, 0x02, "LUN software write protected" },
+ { 0x27, 0x03, "associated write protect" },
+ { 0x27, 0x04, "persistent write protect" },
+ { 0x27, 0x05, "permanent write protect" },
+ { 0x27, 0x06, "conditional write protect" },
+ { 0x28, 0x00, "medium may have changed" },
+ { 0x28, 0x01, "import or export element accessed" },
+ { 0x29, 0x00, "power on, reset, or bus reset occurred" },
+ { 0x29, 0x01, "power on occurred" },
+ { 0x29, 0x02, "scsi bus reset occurred" },
+ { 0x29, 0x03, "bus device reset message occurred" },
+ { 0x29, 0x04, "device internal reset" },
+ { 0x29, 0x05, "transceiver mode changed to single-ended" },
+ { 0x29, 0x06, "transceiver mode changed to LVD" },
+ { 0x29, 0x07, "i_t nexus loss occurred" },
+ { 0x2a, 0x00, "parameters changed" },
+ { 0x2a, 0x01, "mode parameters changed" },
+ { 0x2a, 0x02, "log parameters changed" },
+ { 0x2a, 0x03, "reservations preempted" },
+ { 0x2a, 0x04, "reservations released" },
+ { 0x2a, 0x05, "registrations preempted" },
+ { 0x2a, 0x06, "asymmetric access state changed" },
+ { 0x2a, 0x07, "implicit asymmetric access state transition failed" },
+ { 0x2b, 0x00, "copy cannot execute since host cannot disconnect" },
+ { 0x2c, 0x00, "command sequence error" },
+ { 0x2c, 0x03, "current program area is not empty" },
+ { 0x2c, 0x04, "current program area is empty" },
+ { 0x2c, 0x06, "persistent prevent conflict" },
+ { 0x2c, 0x07, "previous busy status" },
+ { 0x2c, 0x08, "previous task set full status" },
+ { 0x2c, 0x09, "previous reservation conflict status" },
+ { 0x2d, 0x00, "overwrite error on update in place" },
+ { 0x2e, 0x00, "insufficient time for operation" },
+ { 0x2f, 0x00, "commands cleared by another initiator" },
+ { 0x30, 0x00, "incompatible medium installed" },
+ { 0x30, 0x01, "cannot read medium - unknown format" },
+ { 0x30, 0x02, "cannot read medium - incompatible format" },
+ { 0x30, 0x03, "cleaning cartridge installed" },
+ { 0x30, 0x04, "cannot write medium - unknown format" },
+ { 0x30, 0x05, "cannot write medium - incompatible format" },
+ { 0x30, 0x06, "cannot format medium - incompatible medium" },
+ { 0x30, 0x07, "cleaning failure" },
+ { 0x30, 0x08, "cannot write - application code mismatch" },
+ { 0x30, 0x09, "current session not fixated for append" },
+ { 0x30, 0x10, "medium not formatted" },
+ { 0x31, 0x00, "medium format corrupted" },
+ { 0x31, 0x01, "format command failed" },
+ { 0x31, 0x02, "zoned formatting failed due to spare linking" },
+ { 0x32, 0x00, "no defect spare location available" },
+ { 0x32, 0x01, "defect list update failure" },
+ { 0x33, 0x00, "tape length error" },
+ { 0x34, 0x00, "enclosure failure" },
+ { 0x35, 0x00, "enclosure services failure" },
+ { 0x35, 0x01, "unsupported enclosure function" },
+ { 0x35, 0x02, "enclosure services unavailable" },
+ { 0x35, 0x03, "enclosure services transfer failure" },
+ { 0x35, 0x04, "enclosure services transfer refused" },
+ { 0x36, 0x00, "ribbon, ink, or toner failure" },
+ { 0x37, 0x00, "rounded parameter" },
+ { 0x39, 0x00, "saving parameters not supported" },
+ { 0x3a, 0x00, "medium not present" },
+ { 0x3a, 0x01, "medium not present - tray closed" },
+ { 0x3a, 0x02, "medium not present - tray open" },
+ { 0x3a, 0x03, "medium not present - loadable" },
+ { 0x3a, 0x04, "medium not present - medium auxiliary memory "
+ "accessible" },
+ { 0x3b, 0x00, "sequential positioning error" },
+ { 0x3b, 0x01, "tape position error at beginning-of-medium" },
+ { 0x3b, 0x02, "tape position error at end-of-medium" },
+ { 0x3b, 0x08, "reposition error" },
+ { 0x3b, 0x0c, "position past beginning of medium" },
+ { 0x3b, 0x0d, "medium destination element full" },
+ { 0x3b, 0x0e, "medium source element empty" },
+ { 0x3b, 0x0f, "end of medium reached" },
+ { 0x3b, 0x11, "medium magazine not accessible" },
+ { 0x3b, 0x12, "medium magazine removed" },
+ { 0x3b, 0x13, "medium magazine inserted" },
+ { 0x3b, 0x14, "medium magazine locked" },
+ { 0x3b, 0x15, "medium magazine unlocked" },
+ { 0x3b, 0x16, "mechanical positioning or changer error" },
+ { 0x3d, 0x00, "invalid bits in indentify message" },
+ { 0x3e, 0x00, "LUN has not self-configured yet" },
+ { 0x3e, 0x01, "LUN failure" },
+ { 0x3e, 0x02, "timeout on LUN" },
+ { 0x3e, 0x03, "LUN failed self-test" },
+ { 0x3e, 0x04, "LUN unable to update self-test log" },
+ { 0x3f, 0x00, "target operating conditions have changed" },
+ { 0x3f, 0x01, "microcode has been changed" },
+ { 0x3f, 0x02, "changed operating definition" },
+ { 0x3f, 0x03, "inquiry data has changed" },
+ { 0x3f, 0x04, "component device attached" },
+ { 0x3f, 0x05, "device identifier changed" },
+ { 0x3f, 0x06, "redundancy group created or modified" },
+ { 0x3f, 0x07, "redundancy group deleted" },
+ { 0x3f, 0x08, "spare created or modified" },
+ { 0x3f, 0x09, "spare deleted" },
+ { 0x3f, 0x0a, "volume set created or modified" },
+ { 0x3f, 0x0b, "volume set deleted" },
+ { 0x3f, 0x0c, "volume set deassigned" },
+ { 0x3f, 0x0d, "volume set reassigned" },
+ { 0x3f, 0x0e, "reported LUNs data has changed" },
+ { 0x3f, 0x0f, "echo buffer overwritten" },
+ { 0x3f, 0x10, "medium loadable" },
+ { 0x3f, 0x11, "medium auxiliary memory accessible" },
+ { 0x40, 0x00, "ram failure" },
+ { 0x41, 0x00, "data path failure" },
+ { 0x42, 0x00, "power-on or self-test failure" },
+ { 0x43, 0x00, "message error" },
+ { 0x44, 0x00, "internal target failure" },
+ { 0x45, 0x00, "select or reselect failure" },
+ { 0x46, 0x00, "unsuccessful soft reset" },
+ { 0x47, 0x00, "scsi parity error" },
+ { 0x47, 0x01, "data phase crc error detected" },
+ { 0x47, 0x02, "scsi parity error detected during st data phase" },
+ { 0x47, 0x03, "information unit iucrc error detected" },
+ { 0x47, 0x04, "asynchronous information protection error detected" },
+ { 0x47, 0x05, "protocol service crc error" },
+ { 0x47, 0x7f, "some commands cleared by iscsi protocol event" },
+ { 0x48, 0x00, "initiator detected error message received" },
+ { 0x49, 0x00, "invalid message error" },
+ { 0x4a, 0x00, "command phase error" },
+ { 0x4b, 0x00, "data phase error" },
+ { 0x4b, 0x01, "invalid target port transfer tag received" },
+ { 0x4b, 0x02, "too much write data" },
+ { 0x4b, 0x03, "ack/nak timeout" },
+ { 0x4b, 0x04, "nak received" },
+ { 0x4b, 0x05, "data offset error" },
+ { 0x4c, 0x00, "logical unit failed self-configuration" },
+ { 0x4d, 0x00, "tagged overlapped commands (ASCQ = queue tag)" },
+ { 0x4e, 0x00, "overlapped commands attempted" },
+ { 0x50, 0x00, "write append error" },
+ { 0x51, 0x00, "erase failure" },
+ { 0x52, 0x00, "cartridge fault" },
+ { 0x53, 0x00, "media load or eject failed" },
+ { 0x53, 0x01, "unload tape failure" },
+ { 0x53, 0x02, "medium removal prevented" },
+ { 0x54, 0x00, "scsi to host system interface failure" },
+ { 0x55, 0x00, "system resource failure" },
+ { 0x55, 0x01, "system buffer full" },
+ { 0x55, 0x02, "insufficient reservation resources" },
+ { 0x55, 0x03, "insufficient resources" },
+ { 0x55, 0x04, "insufficient registration resources" },
+ { 0x55, 0x05, "insufficient access control resources" },
+ { 0x55, 0x06, "auxiliary memory out of space" },
+ { 0x57, 0x00, "unable to recover TOC" },
+ { 0x58, 0x00, "generation does not exist" },
+ { 0x59, 0x00, "updated block read" },
+ { 0x5a, 0x00, "operator request or state change input" },
+ { 0x5a, 0x01, "operator medium removal request" },
+ { 0x5a, 0x02, "operator selected write protect" },
+ { 0x5a, 0x03, "operator selected write permit" },
+ { 0x5b, 0x00, "log exception" },
+ { 0x5b, 0x01, "threshold condition met" },
+ { 0x5b, 0x02, "log counter at maximum" },
+ { 0x5b, 0x03, "log list codes exhausted" },
+ { 0x5c, 0x00, "RPL status change" },
+ { 0x5c, 0x01, "spindles synchronized" },
+ { 0x5c, 0x02, "spindles not synchronized" },
+ { 0x5d, 0x00, "drive operation marginal, service immediately"
+ " (failure prediction threshold exceeded)" },
+ { 0x5d, 0x01, "media failure prediction threshold exceeded" },
+ { 0x5d, 0x02, "LUN failure prediction threshold exceeded" },
+ { 0x5d, 0x03, "spare area exhaustion prediction threshold exceeded" },
+ { 0x5d, 0x10, "hardware impending failure general hard drive failure" },
+ { 0x5d, 0x11, "hardware impending failure drive error rate too high" },
+ { 0x5d, 0x12, "hardware impending failure data error rate too high" },
+ { 0x5d, 0x13, "hardware impending failure seek error rate too high" },
+ { 0x5d, 0x14, "hardware impending failure too many block reassigns" },
+ { 0x5d, 0x15, "hardware impending failure access times too high" },
+ { 0x5d, 0x16, "hardware impending failure start unit times too high" },
+ { 0x5d, 0x17, "hardware impending failure channel parametrics" },
+ { 0x5d, 0x18, "hardware impending failure controller detected" },
+ { 0x5d, 0x19, "hardware impending failure throughput performance" },
+ { 0x5d, 0x1a, "hardware impending failure seek time performance" },
+ { 0x5d, 0x1b, "hardware impending failure spin-up retry count" },
+ { 0x5d, 0x1c, "hardware impending failure drive calibration retry "
+ "count" },
+ { 0x5d, 0x20, "controller impending failure general hard drive "
+ "failure" },
+ { 0x5d, 0x21, "controller impending failure drive error rate too "
+ "high" },
+ { 0x5d, 0x22, "controller impending failure data error rate too high" },
+ { 0x5d, 0x23, "controller impending failure seek error rate too high" },
+ { 0x5d, 0x24, "controller impending failure too many block reassigns" },
+ { 0x5d, 0x25, "controller impending failure access times too high" },
+ { 0x5d, 0x26, "controller impending failure start unit times too "
+ "high" },
+ { 0x5d, 0x27, "controller impending failure channel parametrics" },
+ { 0x5d, 0x28, "controller impending failure controller detected" },
+ { 0x5d, 0x29, "controller impending failure throughput performance" },
+ { 0x5d, 0x2a, "controller impending failure seek time performance" },
+ { 0x5d, 0x2b, "controller impending failure spin-up retry count" },
+ { 0x5d, 0x2c, "controller impending failure drive calibration retry "
+ "cnt" },
+ { 0x5d, 0x30, "data channel impending failure general hard drive "
+ "failure" },
+ { 0x5d, 0x31, "data channel impending failure drive error rate too "
+ "high" },
+ { 0x5d, 0x32, "data channel impending failure data error rate too "
+ "high" },
+ { 0x5d, 0x33, "data channel impending failure seek error rate too "
+ "high" },
+ { 0x5d, 0x34, "data channel impending failure too many block "
+ "reassigns" },
+ { 0x5d, 0x35, "data channel impending failure access times too high" },
+ { 0x5d, 0x36, "data channel impending failure start unit times too "
+ "high" },
+ { 0x5d, 0x37, "data channel impending failure channel parametrics" },
+ { 0x5d, 0x38, "data channel impending failure controller detected" },
+ { 0x5d, 0x39, "data channel impending failure throughput performance" },
+ { 0x5d, 0x3a, "data channel impending failure seek time performance" },
+ { 0x5d, 0x3b, "data channel impending failure spin-up retry count" },
+ { 0x5d, 0x3c, "data channel impending failure drive calibrate retry "
+ "cnt" },
+ { 0x5d, 0x40, "servo impending failure general hard drive failure" },
+ { 0x5d, 0x41, "servo impending failure drive error rate too high" },
+ { 0x5d, 0x42, "servo impending failure data error rate too high" },
+ { 0x5d, 0x43, "servo impending failure seek error rate too high" },
+ { 0x5d, 0x44, "servo impending failure too many block reassigns" },
+ { 0x5d, 0x45, "servo impending failure access times too high" },
+ { 0x5d, 0x46, "servo impending failure start unit times too high" },
+ { 0x5d, 0x47, "servo impending failure channel parametrics" },
+ { 0x5d, 0x48, "servo impending failure controller detected" },
+ { 0x5d, 0x49, "servo impending failure throughput performance" },
+ { 0x5d, 0x4a, "servo impending failure seek time performance" },
+ { 0x5d, 0x4b, "servo impending failure spin-up retry count" },
+ { 0x5d, 0x4c, "servo impending failure drive calibration retry count" },
+ { 0x5d, 0x50, "spindle impending failure general hard drive failure" },
+ { 0x5d, 0x51, "spindle impending failure drive error rate too high" },
+ { 0x5d, 0x52, "spindle impending failure data error rate too high" },
+ { 0x5d, 0x53, "spindle impending failure seek error rate too high" },
+ { 0x5d, 0x54, "spindle impending failure too many block reassigns" },
+ { 0x5d, 0x55, "spindle impending failure access times too high" },
+ { 0x5d, 0x56, "spindle impending failure start unit times too high" },
+ { 0x5d, 0x57, "spindle impending failure channel parametrics" },
+ { 0x5d, 0x58, "spindle impending failure controller detected" },
+ { 0x5d, 0x59, "spindle impending failure throughput performance" },
+ { 0x5d, 0x5a, "spindle impending failure seek time performance" },
+ { 0x5d, 0x5b, "spindle impending failure spin-up retry count" },
+ { 0x5d, 0x5c, "spindle impending failure drive calibration retry "
+ "count" },
+ { 0x5d, 0x60, "firmware impending failure general hard drive failure" },
+ { 0x5d, 0x61, "firmware impending failure drive error rate too high" },
+ { 0x5d, 0x62, "firmware impending failure data error rate too high" },
+ { 0x5d, 0x63, "firmware impending failure seek error rate too high" },
+ { 0x5d, 0x64, "firmware impending failure too many block reassigns" },
+ { 0x5d, 0x65, "firmware impending failure access times too high" },
+ { 0x5d, 0x66, "firmware impending failure start unit times too high" },
+ { 0x5d, 0x67, "firmware impending failure channel parametrics" },
+ { 0x5d, 0x68, "firmware impending failure controller detected" },
+ { 0x5d, 0x69, "firmware impending failure throughput performance" },
+ { 0x5d, 0x6a, "firmware impending failure seek time performance" },
+ { 0x5d, 0x6b, "firmware impending failure spin-up retry count" },
+ { 0x5d, 0x6c, "firmware impending failure drive calibration retry "
+ "count" },
+ { 0x5d, 0xff, "failure prediction threshold exceeded (false)" },
+ { 0x5e, 0x00, "low power condition active" },
+ { 0x5e, 0x01, "idle condition activated by timer" },
+ { 0x5e, 0x02, "standby condition activated by timer" },
+ { 0x5e, 0x03, "idle condition activated by command" },
+ { 0x5e, 0x04, "standby condition activated by command" },
+ { 0x60, 0x00, "lamp failure" },
+ { 0x61, 0x00, "video aquisition error" },
+ { 0x62, 0x00, "scan head positioning error" },
+ { 0x63, 0x00, "end of user area encountered on this track" },
+ { 0x63, 0x01, "packet does not fit in available space" },
+ { 0x64, 0x00, "illegal mode for this track" },
+ { 0x64, 0x01, "invalid packet size" },
+ { 0x65, 0x00, "voltage fault" },
+ { 0x66, 0x00, "automatic document feeder cover up" },
+ { 0x67, 0x00, "configuration failure" },
+ { 0x67, 0x01, "configuration of incapable LUNs failed" },
+ { 0x67, 0x02, "add LUN failed" },
+ { 0x67, 0x03, "modification of LUN failed" },
+ { 0x67, 0x04, "exchange of LUN failed" },
+ { 0x67, 0x05, "remove of LUN failed" },
+ { 0x67, 0x06, "attachment of LUN failed" },
+ { 0x67, 0x07, "creation of LUN failed" },
+ { 0x67, 0x08, "assign failure occurred" },
+ { 0x67, 0x09, "multiply assigned LUN" },
+ { 0x67, 0x0a, "set target port groups command failed" },
+ { 0x68, 0x00, "logical unit not configured" },
+ { 0x69, 0x00, "data loss on logical unit" },
+ { 0x69, 0x01, "multiple LUN failures" },
+ { 0x69, 0x02, "parity/data mismatch" },
+ { 0x6a, 0x00, "informational, refer to log" },
+ { 0x6b, 0x00, "state change has occured" },
+ { 0x6b, 0x01, "redundancy level got better" },
+ { 0x6b, 0x02, "redundancy level got worse" },
+ { 0x6c, 0x00, "rebuild failure occured" },
+ { 0x6d, 0x00, "recalculate failure occured" },
+ { 0x6e, 0x00, "command to logical unit failed" },
+ { 0x6f, 0x00, "copy protect key exchange failure authentication "
+ "failure" },
+ { 0x6f, 0x01, "copy protect key exchange failure key not present" },
+ { 0x6f, 0x02, "copy protect key exchange failure key not established" },
+ { 0x6f, 0x03, "read of scrambled sector without authentication" },
+ { 0x6f, 0x04, "media region code is mismatched to LUN region" },
+ { 0x6f, 0x05, "drive region must be permanent/region reset count "
+ "error" },
+ { 0x70, 0xffff, "decompression exception short algorithm id of ASCQ" },
+ { 0x71, 0x00, "decompression exception long algorithm id" },
+ { 0x72, 0x00, "session fixation error" },
+ { 0x72, 0x01, "session fixation error writing lead-in" },
+ { 0x72, 0x02, "session fixation error writing lead-out" },
+ { 0x72, 0x03, "session fixation error - incomplete track in session" },
+ { 0x72, 0x04, "empty or partially written reserved track" },
+ { 0x72, 0x05, "no more track reservations allowed" },
+ { 0x73, 0x00, "cd control error" },
+ { 0x73, 0x01, "power calibration area almost full" },
+ { 0x73, 0x02, "power calibration area is full" },
+ { 0x73, 0x03, "power calibration area error" },
+ { 0x73, 0x04, "program memory area update failure" },
+ { 0x73, 0x05, "program memory area is full" },
+ { 0x73, 0x06, "rma/pma is almost full" },
+ { 0xffff, 0xffff, NULL }
+};
+
+/*
+ * Given an asc (Additional Sense Code) and ascq (Additional Sense Code
+ * Qualifier), return a string describing the error information.
+ */
+static char *
+scsi_util_asc_ascq_name(uint_t asc, uint_t ascq, char *buf, int buflen)
+{
+ int i = 0;
+
+ while (extended_sense_list[i].asc != 0xffff) {
+ if ((asc == extended_sense_list[i].asc) &&
+ ((ascq == extended_sense_list[i].ascq) ||
+ (extended_sense_list[i].ascq == 0xffff))) {
+ return ((char *)extended_sense_list[i].message);
+ }
+ i++;
+ }
+ (void) snprintf(buf, buflen, "<vendor unique code 0x%x>", asc);
+ return (buf);
+}
+
+/*
+ * Dumps detailed information about a particular SCSI error condition.
+ */
+static void
+scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen)
+{
+ diskaddr_t blkno;
+ struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq;
+ char msgbuf[MSGBUFLEN];
+
+ if (find_string(sensekey_strings, rq->es_key) == NULL)
+ dprintf("unknown error");
+
+ dprintf("during %s:",
+ find_string(scsi_cmdname_strings, ucmd->uscsi_cdb[0]));
+
+ /*
+ * Get asc, ascq and info field from sense data. There are two
+ * possible formats (fixed sense data and descriptor sense data)
+ * depending on the value of es_code.
+ */
+ switch (rq->es_code) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+ blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen);
+ if (blkno != (diskaddr_t)-1)
+ dprintf(": block %lld (0x%llx)", blkno, blkno);
+ dprintf("\n");
+ dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n",
+ sdsp->ds_add_code, sdsp->ds_qual_code,
+ scsi_util_asc_ascq_name(sdsp->ds_add_code,
+ sdsp->ds_qual_code, msgbuf, MSGBUFLEN));
+
+ break;
+
+ case CODE_FMT_FIXED_CURRENT:
+ case CODE_FMT_FIXED_DEFERRED:
+ default:
+ if (rq->es_valid) {
+ blkno = (rq->es_info_1 << 24) |
+ (rq->es_info_2 << 16) |
+ (rq->es_info_3 << 8) | rq->es_info_4;
+ dprintf(": block %lld (0x%llx)", blkno, blkno);
+ }
+ dprintf("\n");
+ if (rq->es_add_len >= 6) {
+ dprintf("ASC: 0x%x ASCQ: 0x%x (%s)\n",
+ rq->es_add_code,
+ rq->es_qual_code,
+ scsi_util_asc_ascq_name(rq->es_add_code,
+ rq->es_qual_code, msgbuf, MSGBUFLEN));
+ }
+ break;
+ }
+
+ if (rq->es_key == KEY_ILLEGAL_REQUEST) {
+ ddump("cmd:", (caddr_t)ucmd,
+ sizeof (struct uscsi_cmd));
+ ddump("cdb:", (caddr_t)ucmd->uscsi_cdb,
+ ucmd->uscsi_cdblen);
+ }
+ ddump("sense:", (caddr_t)rq, rqlen);
+
+ switch (rq->es_code) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+ scsi_print_descr_sense(sdsp, rqlen);
+ break;
+ case CODE_FMT_FIXED_CURRENT:
+ case CODE_FMT_FIXED_DEFERRED:
+ default:
+ scsi_print_extended_sense(rq, rqlen);
+ break;
+ }
+}
+
+/*
+ * Retrieve "information" field from descriptor format sense data. Iterates
+ * through each sense descriptor looking for the information descriptor and
+ * returns the information field from that descriptor.
+ */
+static diskaddr_t
+scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen)
+{
+ diskaddr_t result;
+ uint8_t *descr_offset;
+ int valid_sense_length;
+ struct scsi_information_sense_descr *isd;
+
+ /*
+ * Initialize result to -1 indicating there is no information
+ * descriptor
+ */
+ result = (diskaddr_t)-1;
+
+ /*
+ * The first descriptor will immediately follow the header
+ */
+ descr_offset = (uint8_t *)(sdsp+1);
+
+ /*
+ * Calculate the amount of valid sense data
+ */
+ valid_sense_length =
+ MIN((sizeof (struct scsi_descr_sense_hdr) +
+ sdsp->ds_addl_sense_length), rqlen);
+
+ /*
+ * Iterate through the list of descriptors, stopping when we run out of
+ * sense data
+ */
+ while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <=
+ (uint8_t *)sdsp + valid_sense_length) {
+ /*
+ * Check if this is an information descriptor. We can use the
+ * scsi_information_sense_descr structure as a template since
+ * the first two fields are always the same
+ */
+ isd = (struct scsi_information_sense_descr *)descr_offset;
+ if (isd->isd_descr_type == DESCR_INFORMATION) {
+ /*
+ * Found an information descriptor. Copy the
+ * information field. There will only be one
+ * information descriptor so we can stop looking.
+ */
+ result =
+ (((diskaddr_t)isd->isd_information[0] << 56) |
+ ((diskaddr_t)isd->isd_information[1] << 48) |
+ ((diskaddr_t)isd->isd_information[2] << 40) |
+ ((diskaddr_t)isd->isd_information[3] << 32) |
+ ((diskaddr_t)isd->isd_information[4] << 24) |
+ ((diskaddr_t)isd->isd_information[5] << 16) |
+ ((diskaddr_t)isd->isd_information[6] << 8) |
+ ((diskaddr_t)isd->isd_information[7]));
+ break;
+ }
+
+ /*
+ * Get pointer to the next descriptor. The "additional length"
+ * field holds the length of the descriptor except for the
+ * "type" and "additional length" fields, so we need to add 2 to
+ * get the total length.
+ */
+ descr_offset += (isd->isd_addl_length + 2);
+ }
+
+ return (result);
+}
+
+/*
+ * Display the full scsi_extended_sense as returned by the device
+ */
+static void
+scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen)
+{
+ static char *scsi_extended_sense_labels[] = {
+ "Request sense valid: ",
+ "Error class and code: ",
+ "Segment number: ",
+ "Filemark: ",
+ "End-of-medium: ",
+ "Incorrect length indicator: ",
+ "Sense key: ",
+ "Information field: ",
+ "Additional sense length: ",
+ "Command-specific information: ",
+ "Additional sense code: ",
+ "Additional sense code qualifier: ",
+ "Field replaceable unit code: ",
+ "Sense-key specific: ",
+ "Additional sense bytes: "
+ };
+
+ char **p = scsi_extended_sense_labels;
+
+ if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) {
+ /*
+ * target should be capable of returning at least 18
+ * bytes of data, i.e upto rq->es_skey_specific field.
+ * The additional sense bytes (2 or more ...) are optional.
+ */
+ return;
+ }
+
+ dprintf("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no");
+ dprintf("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code);
+ dprintf("%s%d\n", *p++, rq->es_segnum);
+ dprintf("%s%s\n", *p++, rq->es_filmk ? "yes" : "no");
+ dprintf("%s%s\n", *p++, rq->es_eom ? "yes" : "no");
+ dprintf("%s%s\n", *p++, rq->es_ili ? "yes" : "no");
+ dprintf("%s%d\n", *p++, rq->es_key);
+
+ dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1,
+ rq->es_info_2, rq->es_info_3, rq->es_info_4);
+ dprintf("%s%d\n", *p++, rq->es_add_len);
+ dprintf("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++,
+ rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2],
+ rq->es_cmd_info[3]);
+ dprintf("%s0x%02x = %d\n", *p++, rq->es_add_code,
+ rq->es_add_code);
+ dprintf("%s0x%02x = %d\n", *p++, rq->es_qual_code,
+ rq->es_qual_code);
+ dprintf("%s%d\n", *p++, rq->es_fru_code);
+ dprintf("%s0x%02x 0x%02x 0x%02x\n", *p++,
+ rq->es_skey_specific[0], rq->es_skey_specific[1],
+ rq->es_skey_specific[2]);
+ if (rqlen >= sizeof (*rq)) {
+ dprintf("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0],
+ rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : "");
+ }
+
+ dprintf("\n");
+}
+
+/*
+ * Display the full descriptor sense data as returned by the device
+ */
+static void
+scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen)
+{
+ /*
+ * Labels for the various fields of the scsi_descr_sense_hdr structure
+ */
+ static char *scsi_descr_sense_labels[] = {
+ "Error class and code: ",
+ "Sense key: ",
+ "Additional sense length: ",
+ "Additional sense code: ",
+ "Additional sense code qualifier: ",
+ "Additional sense bytes: "
+ };
+
+ struct scsi_information_sense_descr *isd;
+ uint8_t *descr_offset;
+ int valid_sense_length;
+ char **p = scsi_descr_sense_labels;
+
+ /* Target must return at least 8 bytes of data */
+ if (rqlen < sizeof (struct scsi_descr_sense_hdr))
+ return;
+
+ /* Print descriptor sense header */
+ dprintf("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code);
+ dprintf("%s%d\n", *p++, rq->ds_key);
+
+ dprintf("%s%d\n", *p++, rq->ds_addl_sense_length);
+ dprintf("%s0x%02x = %d\n", *p++, rq->ds_add_code,
+ rq->ds_add_code);
+ dprintf("%s0x%02x = %d\n", *p++, rq->ds_qual_code,
+ rq->ds_qual_code);
+ dprintf("\n");
+
+ /*
+ * Now print any sense descriptors. The first descriptor will
+ * immediately follow the header
+ */
+ descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */
+
+ /*
+ * Calculate the amount of valid sense data
+ */
+ valid_sense_length =
+ MIN((sizeof (struct scsi_descr_sense_hdr) +
+ rq->ds_addl_sense_length), rqlen);
+
+ /*
+ * Iterate through the list of descriptors, stopping when we
+ * run out of sense data. Descriptor format is:
+ *
+ * <Descriptor type> <Descriptor length> <Descriptor data> ...
+ */
+ while ((descr_offset + *(descr_offset + 1)) <=
+ (uint8_t *)rq + valid_sense_length) {
+ /*
+ * Determine descriptor type. We can use the
+ * scsi_information_sense_descr structure as a
+ * template since the first two fields are always the
+ * same.
+ */
+ isd = (struct scsi_information_sense_descr *)descr_offset;
+ switch (isd->isd_descr_type) {
+ case DESCR_INFORMATION: {
+ uint64_t information;
+
+ information =
+ (((uint64_t)isd->isd_information[0] << 56) |
+ ((uint64_t)isd->isd_information[1] << 48) |
+ ((uint64_t)isd->isd_information[2] << 40) |
+ ((uint64_t)isd->isd_information[3] << 32) |
+ ((uint64_t)isd->isd_information[4] << 24) |
+ ((uint64_t)isd->isd_information[5] << 16) |
+ ((uint64_t)isd->isd_information[6] << 8) |
+ ((uint64_t)isd->isd_information[7]));
+ dprintf("Information field: "
+ "%0" PRIx64 "\n", information);
+ break;
+ }
+ case DESCR_COMMAND_SPECIFIC: {
+ struct scsi_cmd_specific_sense_descr *c =
+ (struct scsi_cmd_specific_sense_descr *)isd;
+ uint64_t cmd_specific;
+
+ cmd_specific =
+ (((uint64_t)c->css_cmd_specific_info[0] << 56) |
+ ((uint64_t)c->css_cmd_specific_info[1] << 48) |
+ ((uint64_t)c->css_cmd_specific_info[2] << 40) |
+ ((uint64_t)c->css_cmd_specific_info[3] << 32) |
+ ((uint64_t)c->css_cmd_specific_info[4] << 24) |
+ ((uint64_t)c->css_cmd_specific_info[5] << 16) |
+ ((uint64_t)c->css_cmd_specific_info[6] << 8) |
+ ((uint64_t)c->css_cmd_specific_info[7]));
+ dprintf("Command-specific information: "
+ "%0" PRIx64 "\n", cmd_specific);
+ break;
+ }
+ case DESCR_SENSE_KEY_SPECIFIC: {
+ struct scsi_sk_specific_sense_descr *ssd =
+ (struct scsi_sk_specific_sense_descr *)isd;
+ uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data;
+ dprintf("Sense-key specific: "
+ "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0],
+ sk_spec_ptr[1], sk_spec_ptr[2]);
+ break;
+ }
+ case DESCR_FRU: {
+ struct scsi_fru_sense_descr *fsd =
+ (struct scsi_fru_sense_descr *)isd;
+ dprintf("Field replaceable unit code: "
+ "%d\n", fsd->fs_fru_code);
+ break;
+ }
+ case DESCR_BLOCK_COMMANDS: {
+ struct scsi_block_cmd_sense_descr *bsd =
+ (struct scsi_block_cmd_sense_descr *)isd;
+ dprintf("Incorrect length indicator: "
+ "%s\n", bsd->bcs_ili ? "yes" : "no");
+ break;
+ }
+ default:
+ /* Ignore */
+ break;
+ }
+
+ /*
+ * Get pointer to the next descriptor. The "additional
+ * length" field holds the length of the descriptor except
+ * for the "type" and "additional length" fields, so
+ * we need to add 2 to get the total length.
+ */
+ descr_offset += (isd->isd_addl_length + 2);
+ }
+
+ dprintf("\n");
+}
+
+static int
+uscsi_timeout(void)
+{
+ const char *env = getenv("USCSI_TIMEOUT");
+ static int timeo = -1;
+ int i;
+
+ if (timeo > 0)
+ return (timeo);
+
+ if (env != NULL) {
+ i = atoi(env);
+ if (i > USCSI_TIMEOUT_MAX)
+ i = USCSI_TIMEOUT_MAX;
+ else if (i < 0)
+ i = USCSI_DEFAULT_TIMEOUT;
+ } else
+ i = USCSI_DEFAULT_TIMEOUT;
+
+ timeo = i;
+ return (i);
+}
+
+/*
+ * Execute a command and determine the result. Uses the "uscsi" ioctl
+ * interface, which is fully supported.
+ *
+ * If the user wants request sense data to be returned in case of error then ,
+ * the "uscsi_cmd" structure should have the request sense buffer allocated in
+ * uscsi_rqbuf.
+ */
+static int
+uscsi_cmd(int fd, struct uscsi_cmd *ucmd, void *rqbuf, int *rqlen)
+{
+ struct scsi_extended_sense *rq;
+ int status;
+
+ /*
+ * Set function flags for driver.
+ */
+ ucmd->uscsi_flags = USCSI_ISOLATE;
+ if (!ds_debug)
+ ucmd->uscsi_flags |= USCSI_SILENT;
+
+ /*
+ * If this command will perform a read, set the USCSI_READ flag
+ */
+ if (ucmd->uscsi_buflen > 0) {
+ /*
+ * uscsi_cdb is declared as a caddr_t, so any CDB
+ * command byte with the MSB set will result in a
+ * compiler error unless we cast to an unsigned value.
+ */
+ switch ((uint8_t)ucmd->uscsi_cdb[0]) {
+ case SCMD_MODE_SENSE:
+ case SCMD_MODE_SENSE_G1:
+ case SCMD_LOG_SENSE_G1:
+ case SCMD_REQUEST_SENSE:
+ ucmd->uscsi_flags |= USCSI_READ;
+ break;
+
+ case SCMD_MODE_SELECT:
+ case SCMD_MODE_SELECT_G1:
+ /* LINTED */
+ ucmd->uscsi_flags |= USCSI_WRITE;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ /* Set timeout */
+ ucmd->uscsi_timeout = uscsi_timeout();
+
+ /*
+ * Set up Request Sense buffer
+ */
+
+ if (ucmd->uscsi_rqbuf == NULL) {
+ ucmd->uscsi_rqbuf = rqbuf;
+ ucmd->uscsi_rqlen = *rqlen;
+ ucmd->uscsi_rqresid = *rqlen;
+ }
+ if (ucmd->uscsi_rqbuf)
+ ucmd->uscsi_flags |= USCSI_RQENABLE;
+ ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
+
+ if (ucmd->uscsi_rqbuf != NULL && ucmd->uscsi_rqlen > 0)
+ (void) memset(ucmd->uscsi_rqbuf, 0, ucmd->uscsi_rqlen);
+
+ /*
+ * Execute the ioctl
+ */
+ status = ioctl(fd, USCSICMD, ucmd);
+ if (status == 0 && ucmd->uscsi_status == 0)
+ return (status);
+
+ /*
+ * If an automatic Request Sense gave us valid info about the error, we
+ * may be able to use that to print a reasonable error msg.
+ */
+ if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) {
+ dprintf("No request sense for command %s\n",
+ find_string(scsi_cmdname_strings,
+ ucmd->uscsi_cdb[0]));
+ return (-1);
+ }
+ if (ucmd->uscsi_rqstatus != STATUS_GOOD) {
+ dprintf("Request sense status for command %s: 0x%x\n",
+ find_string(scsi_cmdname_strings,
+ ucmd->uscsi_cdb[0]),
+ ucmd->uscsi_rqstatus);
+ return (-1);
+ }
+
+ rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf;
+ *rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid;
+
+ if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN ||
+ rq->es_class != CLASS_EXTENDED_SENSE ||
+ *rqlen < MIN_REQUEST_SENSE_LEN) {
+ dprintf("Request sense for command %s failed\n",
+ find_string(scsi_cmdname_strings,
+ ucmd->uscsi_cdb[0]));
+
+ dprintf("Sense data:\n");
+ ddump(NULL, (caddr_t)rqbuf, *rqlen);
+
+ return (-1);
+ }
+
+ /*
+ * If the failed command is a Mode Select, and the
+ * target is indicating that it has rounded one of
+ * the mode select parameters, as defined in the SCSI-2
+ * specification, then we should accept the command
+ * as successful.
+ */
+ if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT ||
+ ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT_G1) {
+ if (rq->es_key == KEY_RECOVERABLE_ERROR &&
+ rq->es_add_code == ROUNDED_PARAMETER &&
+ rq->es_qual_code == 0) {
+ return (0);
+ }
+ }
+
+ if (ds_debug)
+ scsi_printerr(ucmd, rq, *rqlen);
+ if (rq->es_key != KEY_RECOVERABLE_ERROR)
+ return (-1);
+ return (0);
+}
+
+int
+uscsi_request_sense(int fd, caddr_t buf, int buflen, void *rqbuf, int *rqblen)
+{
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+
+ (void) memset(buf, 0, buflen);
+ (void) memset(&ucmd, 0, sizeof (ucmd));
+ (void) memset(&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_REQUEST_SENSE;
+ FORMG0COUNT(&cdb, (uchar_t)buflen);
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = buf;
+ ucmd.uscsi_buflen = buflen;
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+ if (status)
+ dprintf("Request sense failed\n");
+ if (status == 0)
+ ddump("Request Sense data:", buf, buflen);
+
+ return (status);
+}
+
+/*
+ * Execute a uscsi mode sense command. This can only be used to return one page
+ * at a time. Return the mode header/block descriptor and the actual page data
+ * separately - this allows us to support devices which return either 0 or 1
+ * block descriptors. Whatever a device gives us in the mode header/block
+ * descriptor will be returned to it upon subsequent mode selects.
+ */
+int
+uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
+ int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
+{
+ caddr_t mode_sense_buf;
+ struct mode_header *hdr;
+ struct mode_page *pg;
+ int nbytes;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ int maximum;
+ char *pc;
+
+ assert(page_size >= 0 && page_size < 256);
+ assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
+ page_control == PC_DEFAULT || page_control == PC_SAVED);
+
+ nbytes = sizeof (struct scsi_ms_header) + page_size;
+ mode_sense_buf = alloca((uint_t)nbytes);
+
+ /*
+ * Build and execute the uscsi ioctl
+ */
+ (void) memset(mode_sense_buf, 0, nbytes);
+ (void) memset(&ucmd, 0, sizeof (ucmd));
+ (void) memset(&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_MODE_SENSE;
+ FORMG0COUNT(&cdb, (uchar_t)nbytes);
+ cdb.cdb_opaque[2] = page_control | page_code;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = mode_sense_buf;
+ ucmd.uscsi_buflen = nbytes;
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+ if (status) {
+ dprintf("Mode sense page 0x%x failed\n", page_code);
+ return (-1);
+ }
+
+ ddump("RAW MODE SENSE BUFFER", mode_sense_buf, nbytes);
+
+ /*
+ * Verify that the returned data looks reasonable, find the actual page
+ * data, and copy it into the user's buffer. Copy the mode_header and
+ * block_descriptor into the header structure, which can then be used to
+ * return the same data to the drive when issuing a mode select.
+ */
+ hdr = (struct mode_header *)mode_sense_buf;
+ (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
+ if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
+ hdr->bdesc_length != 0) {
+ dprintf("\nMode sense page 0x%x: block descriptor "
+ "length %d incorrect\n", page_code, hdr->bdesc_length);
+ ddump("Mode sense:", mode_sense_buf, nbytes);
+ return (-1);
+ }
+ (void) memcpy((caddr_t)header, mode_sense_buf,
+ (int)(MODE_HEADER_LENGTH + hdr->bdesc_length));
+ pg = (struct mode_page *)((ulong_t)mode_sense_buf +
+ MODE_HEADER_LENGTH + hdr->bdesc_length);
+
+ if (page_code == MODEPAGE_ALLPAGES) {
+ /* special case */
+
+ (void) memcpy(page_data, (caddr_t)pg,
+ (hdr->length + sizeof (header->ms_header.length)) -
+ (MODE_HEADER_LENGTH + hdr->bdesc_length));
+
+ pc = find_string(page_control_strings, page_control);
+ dprintf("\nMode sense page 0x%x (%s):\n", page_code,
+ pc != NULL ? pc : "");
+ ddump("header:", (caddr_t)header,
+ sizeof (struct scsi_ms_header));
+ ddump("data:", page_data,
+ (hdr->length +
+ sizeof (header->ms_header.length)) -
+ (MODE_HEADER_LENGTH + hdr->bdesc_length));
+
+ return (0);
+ }
+
+ if (pg->code != page_code) {
+ dprintf("\nMode sense page 0x%x: incorrect page code 0x%x\n",
+ page_code, pg->code);
+ ddump("Mode sense:", mode_sense_buf, nbytes);
+ return (-1);
+ }
+
+ /*
+ * Accept up to "page_size" bytes of mode sense data. This allows us to
+ * accept both CCS and SCSI-2 structures, as long as we request the
+ * greater of the two.
+ */
+ maximum = page_size - sizeof (struct mode_page);
+ if (((int)pg->length) > maximum) {
+ dprintf("Mode sense page 0x%x: incorrect page "
+ "length %d - expected max %d\n",
+ page_code, pg->length, maximum);
+ ddump("Mode sense:", mode_sense_buf, nbytes);
+ return (-1);
+ }
+
+ (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
+
+ pc = find_string(page_control_strings, page_control);
+ dprintf("\nMode sense page 0x%x (%s):\n", page_code,
+ pc != NULL ? pc : "");
+ ddump("header:", (caddr_t)header, sizeof (struct scsi_ms_header));
+ ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
+
+ return (0);
+}
+
+/*
+ * Execute a uscsi MODE SENSE(10) command. This can only be used to return one
+ * page at a time. Return the mode header/block descriptor and the actual page
+ * data separately - this allows us to support devices which return either 0 or
+ * 1 block descriptors. Whatever a device gives us in the mode header/block
+ * descriptor will be returned to it upon subsequent mode selects.
+ */
+int
+uscsi_mode_sense_10(int fd, int page_code, int page_control,
+ caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
+ void *rqbuf, int *rqblen)
+{
+ caddr_t mode_sense_buf;
+ struct mode_header_g1 *hdr;
+ struct mode_page *pg;
+ int nbytes;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ int maximum;
+ ushort_t length, bdesc_length;
+ char *pc;
+
+ assert(page_size >= 0 && page_size < UINT16_MAX);
+ assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
+ page_control == PC_DEFAULT || page_control == PC_SAVED);
+
+ nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
+ mode_sense_buf = alloca((uint_t)nbytes);
+
+ (void) memset(mode_sense_buf, 0, nbytes);
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_MODE_SENSE_G1;
+ FORMG1COUNT(&cdb, (uint16_t)nbytes);
+ cdb.cdb_opaque[2] = page_control | page_code;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP1;
+ ucmd.uscsi_bufaddr = mode_sense_buf;
+ ucmd.uscsi_buflen = nbytes;
+
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+ if (status) {
+ dprintf("Mode sense(10) page 0x%x failed\n",
+ page_code);
+ return (-1);
+ }
+
+ ddump("RAW MODE SENSE(10) BUFFER", mode_sense_buf, nbytes);
+
+ /*
+ * Verify that the returned data looks reasonable, find the actual page
+ * data, and copy it into the user's buffer. Copy the mode_header and
+ * block_descriptor into the header structure, which can then be used to
+ * return the same data to the drive when issuing a mode select.
+ */
+ /* LINTED */
+ hdr = (struct mode_header_g1 *)mode_sense_buf;
+
+ length = BE_16(hdr->length);
+ bdesc_length = BE_16(hdr->bdesc_length);
+
+ (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header_g1));
+ if (bdesc_length != sizeof (struct block_descriptor) &&
+ bdesc_length != 0) {
+ dprintf("\nMode sense(10) page 0x%x: block descriptor "
+ "length %d incorrect\n", page_code, bdesc_length);
+ ddump("Mode sense(10):", mode_sense_buf, nbytes);
+ return (-1);
+ }
+ (void) memcpy((caddr_t)header, mode_sense_buf,
+ (int)(MODE_HEADER_LENGTH_G1 + bdesc_length));
+ pg = (struct mode_page *)((ulong_t)mode_sense_buf +
+ MODE_HEADER_LENGTH_G1 + bdesc_length);
+
+ if (page_code == MODEPAGE_ALLPAGES) {
+ /* special case */
+
+ (void) memcpy(page_data, (caddr_t)pg,
+ (length + sizeof (header->ms_header.length)) -
+ (MODE_HEADER_LENGTH_G1 + bdesc_length));
+
+ pc = find_string(page_control_strings, page_control);
+ dprintf("\nMode sense(10) page 0x%x (%s):\n",
+ page_code, pc != NULL ? pc : "");
+ ddump("header:", (caddr_t)header,
+ MODE_HEADER_LENGTH_G1 + bdesc_length);
+
+ ddump("data:", page_data,
+ (length + sizeof (header->ms_header.length)) -
+ (MODE_HEADER_LENGTH_G1 + bdesc_length));
+
+ return (0);
+ }
+
+ if (pg->code != page_code) {
+ dprintf("\nMode sense(10) page 0x%x: incorrect page "
+ "code 0x%x\n", page_code, pg->code);
+ ddump("Mode sense(10):", mode_sense_buf, nbytes);
+ return (-1);
+ }
+
+ /*
+ * Accept up to "page_size" bytes of mode sense data. This allows us to
+ * accept both CCS and SCSI-2 structures, as long as we request the
+ * greater of the two.
+ */
+ maximum = page_size - sizeof (struct mode_page);
+ if (((int)pg->length) > maximum) {
+ dprintf("Mode sense(10) page 0x%x: incorrect page "
+ "length %d - expected max %d\n",
+ page_code, pg->length, maximum);
+ ddump("Mode sense(10):", mode_sense_buf,
+ nbytes);
+ return (-1);
+ }
+
+ (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
+
+ pc = find_string(page_control_strings, page_control);
+ dprintf("\nMode sense(10) page 0x%x (%s):\n", page_code,
+ pc != NULL ? pc : "");
+ ddump("header:", (caddr_t)header,
+ sizeof (struct scsi_ms_header_g1));
+ ddump("data:", page_data, MODESENSE_PAGE_LEN(pg));
+
+ return (0);
+}
+
+/*
+ * Execute a uscsi mode select command.
+ */
+int
+uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data,
+ int page_size, struct scsi_ms_header *header, void *rqbuf, int *rqblen)
+{
+ caddr_t mode_select_buf;
+ int nbytes;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ char *s;
+
+ assert(((struct mode_page *)page_data)->ps == 0);
+ assert(header->ms_header.length == 0);
+ assert(header->ms_header.device_specific == 0);
+ assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
+
+ nbytes = sizeof (struct scsi_ms_header) + page_size;
+ mode_select_buf = alloca((uint_t)nbytes);
+
+ /*
+ * Build the mode select data out of the header and page data This
+ * allows us to support devices which return either 0 or 1 block
+ * descriptors.
+ */
+ (void) memset(mode_select_buf, 0, nbytes);
+ nbytes = MODE_HEADER_LENGTH;
+ if (header->ms_header.bdesc_length ==
+ sizeof (struct block_descriptor)) {
+ nbytes += sizeof (struct block_descriptor);
+ }
+
+ s = find_string(mode_select_strings,
+ options & (MODE_SELECT_SP|MODE_SELECT_PF));
+ dprintf("\nMode select page 0x%x%s:\n", page_code,
+ s != NULL ? s : "");
+ ddump("header:", (caddr_t)header, nbytes);
+ ddump("data:", (caddr_t)page_data, page_size);
+
+ /*
+ * Put the header and data together
+ */
+ (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
+ (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
+ nbytes += page_size;
+
+ /*
+ * Build and execute the uscsi ioctl
+ */
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_MODE_SELECT;
+ FORMG0COUNT(&cdb, (uchar_t)nbytes);
+ cdb.cdb_opaque[1] = (uchar_t)options;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = mode_select_buf;
+ ucmd.uscsi_buflen = nbytes;
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+
+ if (status)
+ dprintf("Mode select page 0x%x failed\n", page_code);
+
+ return (status);
+}
+
+/*
+ * Execute a uscsi mode select(10) command.
+ */
+int
+uscsi_mode_select_10(int fd, int page_code, int options,
+ caddr_t page_data, int page_size, struct scsi_ms_header_g1 *header,
+ void *rqbuf, int *rqblen)
+{
+ caddr_t mode_select_buf;
+ int nbytes;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ char *s;
+
+ assert(((struct mode_page *)page_data)->ps == 0);
+ assert(header->ms_header.length == 0);
+ assert(header->ms_header.device_specific == 0);
+ assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0);
+
+ nbytes = sizeof (struct scsi_ms_header_g1) + page_size;
+ mode_select_buf = alloca((uint_t)nbytes);
+
+ /*
+ * Build the mode select data out of the header and page data
+ * This allows us to support devices which return either
+ * 0 or 1 block descriptors.
+ */
+ (void) memset(mode_select_buf, 0, nbytes);
+ nbytes = sizeof (struct mode_header_g1);
+ if (BE_16(header->ms_header.bdesc_length) ==
+ sizeof (struct block_descriptor)) {
+ nbytes += sizeof (struct block_descriptor);
+ }
+
+ /*
+ * Dump the structures
+ */
+ s = find_string(mode_select_strings,
+ options & (MODE_SELECT_SP|MODE_SELECT_PF));
+ dprintf("\nMode select(10) page 0x%x%s:\n", page_code,
+ s != NULL ? s : "");
+ ddump("header:", (caddr_t)header, nbytes);
+ ddump("data:", (caddr_t)page_data, page_size);
+
+ /*
+ * Put the header and data together
+ */
+ (void) memcpy(mode_select_buf, (caddr_t)header, nbytes);
+ (void) memcpy(mode_select_buf + nbytes, page_data, page_size);
+ nbytes += page_size;
+
+ /*
+ * Build and execute the uscsi ioctl
+ */
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_MODE_SELECT_G1;
+ FORMG1COUNT(&cdb, (uint16_t)nbytes);
+ cdb.cdb_opaque[1] = (uchar_t)options;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP1;
+ ucmd.uscsi_bufaddr = mode_select_buf;
+ ucmd.uscsi_buflen = nbytes;
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+
+ if (status)
+ dprintf("Mode select(10) page 0x%x failed\n", page_code);
+
+ return (status);
+}
+
+int
+uscsi_log_sense(int fd, int page_code, int page_control, caddr_t page_data,
+ int page_size, void *rqbuf, int *rqblen)
+{
+ caddr_t log_sense_buf;
+ scsi_log_header_t *hdr;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ ushort_t len;
+ char *pc;
+
+ assert(page_size >= 0 && page_size < UINT16_MAX);
+ assert(page_control == PC_CURRENT || page_control == PC_CHANGEABLE ||
+ page_control == PC_DEFAULT || page_control == PC_SAVED);
+
+ if (page_size < sizeof (scsi_log_header_t))
+ return (-1);
+
+ log_sense_buf = alloca((uint_t)page_size);
+
+ /*
+ * Build and execute the uscsi ioctl
+ */
+ (void) memset(log_sense_buf, 0, page_size);
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
+ cdb.scc_cmd = SCMD_LOG_SENSE_G1;
+ FORMG1COUNT(&cdb, (uint16_t)page_size);
+ cdb.cdb_opaque[2] = page_control | page_code;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP1;
+ ucmd.uscsi_bufaddr = log_sense_buf;
+ ucmd.uscsi_buflen = page_size;
+ status = uscsi_cmd(fd, &ucmd, rqbuf, rqblen);
+ if (status) {
+ dprintf("Log sense page 0x%x failed\n", page_code);
+ return (-1);
+ }
+
+ /*
+ * Verify that the returned data looks reasonable, then copy it into the
+ * user's buffer.
+ */
+ hdr = (scsi_log_header_t *)log_sense_buf;
+
+ /*
+ * Ensure we have a host-understandable length field
+ */
+ len = BE_16(hdr->lh_length);
+
+ if (hdr->lh_code != page_code) {
+ dprintf("\nLog sense page 0x%x: incorrect page code 0x%x\n",
+ page_code, hdr->lh_code);
+ ddump("Log sense:", log_sense_buf, page_size);
+ return (-1);
+ }
+
+ ddump("LOG SENSE RAW OUTPUT", log_sense_buf,
+ sizeof (scsi_log_header_t) + len);
+
+ /*
+ * Accept up to "page_size" bytes of mode sense data. This allows us to
+ * accept both CCS and SCSI-2 structures, as long as we request the
+ * greater of the two.
+ */
+ (void) memcpy(page_data, (caddr_t)hdr, len +
+ sizeof (scsi_log_header_t));
+
+ pc = find_string(page_control_strings, page_control);
+ dprintf("\nLog sense page 0x%x (%s):\n", page_code,
+ pc != NULL ? pc : "");
+ ddump("header:", (caddr_t)hdr,
+ sizeof (scsi_log_header_t));
+ ddump("data:", (caddr_t)hdr +
+ sizeof (scsi_log_header_t), len);
+
+ return (0);
+}
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h
new file mode 100644
index 0000000000..e46caa4766
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_scsi_uscsi.h
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DS_SCSI_USCSI_H
+#define _DS_SCSI_USCSI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * "impossible" status value
+ */
+#define IMPOSSIBLE_SCSI_STATUS 0xff
+
+/*
+ * Minimum length of Request Sense data that we can accept
+ */
+#define MIN_REQUEST_SENSE_LEN 18
+
+/*
+ * Rounded parameter, as returned in Extended Sense information
+ */
+#define ROUNDED_PARAMETER 0x37
+
+int uscsi_mode_sense(int, int, int, caddr_t, int, scsi_ms_header_t *,
+ void *, int *);
+int uscsi_mode_sense_10(int, int, int, caddr_t, int, scsi_ms_header_g1_t *,
+ void *, int *);
+int uscsi_mode_select(int, int, int, caddr_t, int, scsi_ms_header_t *,
+ void *, int *);
+int uscsi_mode_select_10(int, int, int, caddr_t, int, scsi_ms_header_g1_t *,
+ void *, int *);
+int uscsi_log_sense(int, int, int, caddr_t, int, void *, int *);
+int uscsi_request_sense(int, caddr_t, int, void *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DS_SCSI_USCSI_H */
diff --git a/usr/src/lib/fm/libdiskstatus/common/ds_util.c b/usr/src/lib/fm/libdiskstatus/common/ds_util.c
new file mode 100644
index 0000000000..fe59b12801
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/ds_util.c
@@ -0,0 +1,172 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctype.h>
+#include <libdiskstatus.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ds_impl.h"
+
+boolean_t ds_debug;
+
+/*PRINTFLIKE1*/
+void
+dprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!ds_debug)
+ return;
+
+ va_start(ap, fmt);
+ (void) vprintf(fmt, ap);
+ va_end(ap);
+}
+
+void
+ddump(const char *label, const void *data, size_t length)
+{
+ int byte_count;
+ int i;
+#define LINEBUFLEN 128
+ char linebuf[LINEBUFLEN];
+ char *linep;
+ int bufleft, len;
+ const char *start = data;
+
+ if (!ds_debug)
+ return;
+
+ if (label != NULL)
+ dprintf("%s\n", label);
+
+ linep = linebuf;
+ bufleft = LINEBUFLEN;
+
+ for (byte_count = 0; byte_count < length; byte_count += i) {
+
+ (void) snprintf(linep, bufleft, "0x%08x ", byte_count);
+ len = strlen(linep);
+ bufleft -= len;
+ linep += len;
+
+ /*
+ * Inner loop processes 16 bytes at a time, or less
+ * if we have less than 16 bytes to go
+ */
+ for (i = 0; (i < 16) && ((byte_count + i) < length); i++) {
+ (void) snprintf(linep, bufleft, "%02X", (unsigned int)
+ (unsigned char) start[byte_count + i]);
+
+ len = strlen(linep);
+ bufleft -= len;
+ linep += len;
+
+ if (bufleft >= 2) {
+ if (i == 7)
+ *linep = '-';
+ else
+ *linep = ' ';
+
+ --bufleft;
+ ++linep;
+ }
+ }
+
+ /*
+ * If i is less than 16, then we had less than 16 bytes
+ * written to the output. We need to fixup the alignment
+ * to allow the "text" output to be aligned
+ */
+ if (i < 16) {
+ int numspaces = (16 - i) * 3;
+ while (numspaces-- > 0) {
+ if (bufleft >= 2) {
+ *linep = ' ';
+ --bufleft;
+ linep++;
+ }
+ }
+ }
+
+ if (bufleft >= 2) {
+ *linep = ' ';
+ --bufleft;
+ ++linep;
+ }
+
+ for (i = 0; (i < 16) && ((byte_count + i) < length); i++) {
+ int subscript = byte_count + i;
+ char ch = (isprint(start[subscript]) ?
+ start[subscript] : '.');
+
+ if (bufleft >= 2) {
+ *linep = ch;
+ --bufleft;
+ ++linep;
+ }
+ }
+
+ linebuf[LINEBUFLEN - bufleft] = 0;
+
+ dprintf("%s\n", linebuf);
+
+ linep = linebuf;
+ bufleft = LINEBUFLEN;
+ }
+
+}
+
+const char *
+disk_status_errmsg(int error)
+{
+ switch (error) {
+ case EDS_NOMEM:
+ return ("memory allocation failure");
+ case EDS_CANT_OPEN:
+ return ("failed to open device");
+ case EDS_NO_TRANSPORT:
+ return ("no supported communication protocol");
+ case EDS_NOT_SUPPORTED:
+ return ("disk status information not supported");
+ case EDS_NOT_SIMULATOR:
+ return ("not a valid simulator file");
+ case EDS_IO:
+ return ("I/O error from device");
+ default:
+ return ("unknown error");
+ }
+}
+
+int
+ds_set_errno(disk_status_t *dsp, int error)
+{
+ dsp->ds_error = error;
+ return (-1);
+}
diff --git a/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c
new file mode 100644
index 0000000000..e2cf5ad7a2
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.c
@@ -0,0 +1,238 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Disk status library
+ *
+ * This library is responsible for querying health and other status information
+ * from disk drives. It is intended to be a generic interface, however only
+ * SCSI (and therefore SATA) disks are currently supported. The library is
+ * capable of detecting the following status conditions:
+ *
+ * - Predictive failure
+ * - Overtemp
+ * - Self-test failure
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libdevinfo.h>
+#include <libdiskstatus.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fm/io/scsi.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "ds_impl.h"
+#include "ds_scsi.h"
+
+static ds_transport_t *ds_transports[] = {
+ &ds_scsi_sim_transport,
+ &ds_scsi_uscsi_transport
+};
+
+#define NTRANSPORTS (sizeof (ds_transports) / sizeof (ds_transports[0]))
+
+/*
+ * Open a handle to a disk. This will fail if the device cannot be opened, or
+ * if no suitable transport exists for communicating with the device.
+ */
+disk_status_t *
+disk_status_open(const char *path, int *error)
+{
+ disk_status_t *dsp;
+ ds_transport_t *t;
+ int i;
+
+ if ((dsp = calloc(sizeof (disk_status_t), 1)) == NULL) {
+ *error = EDS_NOMEM;
+ return (NULL);
+ }
+
+ if ((dsp->ds_fd = open(path, O_RDWR)) < 0) {
+ *error = EDS_CANT_OPEN;
+ free(dsp);
+ return (NULL);
+ }
+
+ if ((dsp->ds_path = strdup(path)) == NULL) {
+ *error = EDS_NOMEM;
+ disk_status_close(dsp);
+ return (NULL);
+ }
+
+ for (i = 0; i < NTRANSPORTS; i++) {
+ t = ds_transports[i];
+
+ dsp->ds_transport = t;
+
+ nvlist_free(dsp->ds_state);
+ dsp->ds_state = NULL;
+ if (nvlist_alloc(&dsp->ds_state, NV_UNIQUE_NAME, 0) != 0) {
+ *error = EDS_NOMEM;
+ disk_status_close(dsp);
+ return (NULL);
+ }
+
+ if ((dsp->ds_data = t->dt_open(dsp)) == NULL) {
+ if (dsp->ds_error != EDS_NO_TRANSPORT) {
+ *error = dsp->ds_error;
+ disk_status_close(dsp);
+ return (NULL);
+ }
+ } else {
+ dsp->ds_error = 0;
+ break;
+ }
+ }
+
+ if (dsp->ds_error == EDS_NO_TRANSPORT) {
+ *error = dsp->ds_error;
+ disk_status_close(dsp);
+ return (NULL);
+ }
+
+ return (dsp);
+}
+
+/*
+ * Close a handle to a disk.
+ */
+void
+disk_status_close(disk_status_t *dsp)
+{
+ nvlist_free(dsp->ds_state);
+ nvlist_free(dsp->ds_predfail);
+ nvlist_free(dsp->ds_overtemp);
+ nvlist_free(dsp->ds_testfail);
+ if (dsp->ds_data)
+ dsp->ds_transport->dt_close(dsp->ds_data);
+ (void) close(dsp->ds_fd);
+ free(dsp->ds_path);
+ free(dsp);
+}
+
+void
+disk_status_set_debug(boolean_t value)
+{
+ ds_debug = value;
+}
+
+/*
+ * Query basic information
+ */
+const char *
+disk_status_path(disk_status_t *dsp)
+{
+ return (dsp->ds_path);
+}
+
+int
+disk_status_errno(disk_status_t *dsp)
+{
+ return (dsp->ds_error);
+}
+
+nvlist_t *
+disk_status_get(disk_status_t *dsp)
+{
+ nvlist_t *nvl = NULL;
+ nvlist_t *faults = NULL;
+ int err;
+
+ /*
+ * Scan (or rescan) the current device.
+ */
+ nvlist_free(dsp->ds_testfail);
+ nvlist_free(dsp->ds_predfail);
+ nvlist_free(dsp->ds_overtemp);
+ dsp->ds_testfail = dsp->ds_overtemp = dsp->ds_predfail = NULL;
+ dsp->ds_faults = 0;
+
+ /*
+ * Even if there is an I/O failure when trying to scan the device, we
+ * can still return the current state.
+ */
+ if (dsp->ds_transport->dt_scan(dsp->ds_data) != 0 &&
+ dsp->ds_error != EDS_IO)
+ return (NULL);
+
+ if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0)
+ goto nverror;
+
+ if ((err = nvlist_add_string(nvl, "protocol", "scsi")) != 0 ||
+ (err = nvlist_add_nvlist(nvl, "status", dsp->ds_state)) != 0)
+ goto nverror;
+
+ /*
+ * Construct the list of faults.
+ */
+ if ((err = nvlist_alloc(&faults, NV_UNIQUE_NAME, 0)) != 0)
+ goto nverror;
+
+ if (dsp->ds_predfail != NULL) {
+ if ((err = nvlist_add_boolean_value(faults,
+ FM_EREPORT_SCSI_PREDFAIL,
+ (dsp->ds_faults & DS_FAULT_PREDFAIL) != 0)) != 0 ||
+ (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_PREDFAIL,
+ dsp->ds_predfail)) != 0)
+ goto nverror;
+ }
+
+ if (dsp->ds_testfail != NULL) {
+ if ((err = nvlist_add_boolean_value(faults,
+ FM_EREPORT_SCSI_TESTFAIL,
+ (dsp->ds_faults & DS_FAULT_TESTFAIL) != 0)) != 0 ||
+ (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_TESTFAIL,
+ dsp->ds_testfail)) != 0)
+ goto nverror;
+ }
+
+ if (dsp->ds_overtemp != NULL) {
+ if ((err = nvlist_add_boolean_value(faults,
+ FM_EREPORT_SCSI_OVERTEMP,
+ (dsp->ds_faults & DS_FAULT_OVERTEMP) != 0)) != 0 ||
+ (err = nvlist_add_nvlist(nvl, FM_EREPORT_SCSI_OVERTEMP,
+ dsp->ds_overtemp)) != 0)
+ goto nverror;
+ }
+
+ if ((err = nvlist_add_nvlist(nvl, "faults", faults)) != 0)
+ goto nverror;
+
+ nvlist_free(faults);
+ return (nvl);
+
+nverror:
+ assert(err == ENOMEM);
+ nvlist_free(nvl);
+ nvlist_free(faults);
+ (void) ds_set_errno(dsp, EDS_NOMEM);
+ return (NULL);
+}
diff --git a/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h
new file mode 100644
index 0000000000..b659de316e
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/libdiskstatus.h
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBDISKSTATUS_H
+#define _LIBDISKSTATUS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libnvpair.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct disk_status disk_status_t;
+
+/*
+ * Error definitions
+ */
+#define EDS_BASE 2000
+
+enum {
+ EDS_NOMEM = EDS_BASE, /* memory allocation failure */
+ EDS_CANT_OPEN, /* failed to open device */
+ EDS_NO_TRANSPORT, /* no supported transport */
+ EDS_NOT_SUPPORTED, /* status information not supported */
+ EDS_NOT_SIMULATOR, /* not a valid simulator file */
+ EDS_IO /* I/O error */
+};
+
+/*
+ * Basic library functions
+ */
+extern disk_status_t *disk_status_open(const char *, int *);
+extern void disk_status_close(disk_status_t *);
+extern const char *disk_status_errmsg(int);
+extern void disk_status_set_debug(boolean_t);
+extern int disk_status_errno(disk_status_t *);
+
+/*
+ * Miscellaneous functions.
+ */
+extern const char *disk_status_path(disk_status_t *);
+
+/*
+ * Main entry point.
+ */
+extern nvlist_t *disk_status_get(disk_status_t *);
+
+/*
+ * Utility function to simulate predictive failure (device-specific).
+ */
+extern int disk_status_test_predfail(disk_status_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDISKSTATUS_H */
diff --git a/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus b/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus
new file mode 100644
index 0000000000..a20222355f
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/llib-ldiskstatus
@@ -0,0 +1,31 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libdiskstatus.h>
diff --git a/usr/src/lib/fm/libdiskstatus/common/mapfile-vers b/usr/src/lib/fm/libdiskstatus/common/mapfile-vers
new file mode 100644
index 0000000000..912475031e
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/common/mapfile-vers
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+SUNWprivate_1.1 {
+ global:
+ disk_status_close;
+ disk_status_errmsg;
+ disk_status_errno;
+ disk_status_get;
+ disk_status_open;
+ disk_status_path;
+ disk_status_set_debug;
+ local:
+ *;
+};
diff --git a/usr/src/lib/fm/libdiskstatus/i386/Makefile b/usr/src/lib/fm/libdiskstatus/i386/Makefile
new file mode 100644
index 0000000000..741cbcaecd
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/i386/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/fm/libdiskstatus/sparc/Makefile b/usr/src/lib/fm/libdiskstatus/sparc/Makefile
new file mode 100644
index 0000000000..741cbcaecd
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/sparc/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile b/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile
new file mode 100644
index 0000000000..5375e35120
--- /dev/null
+++ b/usr/src/lib/fm/libdiskstatus/sparcv9/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers b/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers
index 4af3e953f4..c2d380e684 100644
--- a/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers
+++ b/usr/src/lib/fm/libfmd_snmp/common/mapfile-vers
@@ -38,7 +38,8 @@ SUNWprivate {
fmd_fmri_strdup;
fmd_fmri_strescape;
fmd_fmri_strfree;
- fmd_fmri_topology;
+ fmd_fmri_topo_hold;
+ fmd_fmri_topo_rele;
fmd_fmri_warn;
fmd_fmri_zalloc;
init_sunFM;
diff --git a/usr/src/lib/fm/libfmd_snmp/common/scheme.c b/usr/src/lib/fm/libfmd_snmp/common/scheme.c
index a9b776b6bf..55c45a7ae3 100644
--- a/usr/src/lib/fm/libfmd_snmp/common/scheme.c
+++ b/usr/src/lib/fm/libfmd_snmp/common/scheme.c
@@ -299,12 +299,14 @@ fmd_fmri_warn(const char *format, ...)
{
}
-/*ARGSUSED*/
struct topo_hdl *
-fmd_fmri_topology(int version)
+fmd_fmri_topo_hold(int version)
{
int err;
+ if (version != TOPO_VERSION)
+ return (NULL);
+
if (g_thp == NULL) {
if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) {
DEBUGMSGTL((MODNAME_STR, "topo_open failed: %s\n",
@@ -315,3 +317,10 @@ fmd_fmri_topology(int version)
return (g_thp);
}
+
+/*ARGSUSED*/
+void
+fmd_fmri_topo_rele(struct topo_hdl *thp)
+{
+ /* nothing to do */
+}
diff --git a/usr/src/lib/fm/topo/libtopo/Makefile.com b/usr/src/lib/fm/topo/libtopo/Makefile.com
index 9725caf851..d9d09adf4b 100644
--- a/usr/src/lib/fm/topo/libtopo/Makefile.com
+++ b/usr/src/lib/fm/topo/libtopo/Makefile.com
@@ -80,7 +80,8 @@ LINTFLAGS = -msux
LINTFLAGS64 = -msux -Xarch=$(MACH64:sparcv9=v9)
$(DYNLIB) := LDLIBS += \
- -lnvpair -lelf -lumem -lxml2 -lkstat -luuid -ldevinfo -lsmbios -lc
+ -lnvpair -lelf -lumem -lxml2 -lkstat -luuid -ldevinfo \
+ -lsmbios -lc -ldevid
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
$(LINTLIB) := LINTFLAGS = -nsvx
diff --git a/usr/src/lib/fm/topo/libtopo/common/dev.c b/usr/src/lib/fm/topo/libtopo/common/dev.c
index 18defd40d3..abbf934245 100644
--- a/usr/src/lib/fm/topo/libtopo/common/dev.c
+++ b/usr/src/lib/fm/topo/libtopo/common/dev.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <stdio.h>
#include <alloca.h>
+#include <devid.h>
#include <libnvpair.h>
#include <fm/topo_mod.h>
#include <sys/fm/protocol.h>
@@ -49,6 +50,10 @@ static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t,
nvlist_t *, nvlist_t **);
static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t,
nvlist_t *, nvlist_t **);
+static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
+static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t,
+ nvlist_t *, nvlist_t **);
static const topo_method_t dev_methods[] = {
{ TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION,
@@ -57,6 +62,11 @@ static const topo_method_t dev_methods[] = {
TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl },
{ TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION,
TOPO_STABILITY_INTERNAL, dev_fmri_create_meth },
+ { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
+ TOPO_STABILITY_INTERNAL, dev_fmri_present },
+ { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
+ TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
+ dev_fmri_unusable },
{ NULL }
};
@@ -290,6 +300,170 @@ dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version,
return (0);
}
+/*
+ * callback routine for di_walk_minor()
+ */
+struct walkinfo {
+ int matched;
+ const char *path;
+ const char *devid;
+ int len;
+};
+
+static int
+dev_match(di_node_t node, void *arg)
+{
+ struct walkinfo *wip = (struct walkinfo *)arg;
+ char *path = di_devfs_path(node);
+ ddi_devid_t devid;
+ char *devidstr = NULL;
+
+ if (path != NULL && strncmp(path, wip->path, wip->len) == 0) {
+ /*
+ * If we found the match we were looking for, check to see if a
+ * devid was specified. If so, then this must also match.
+ */
+ if (wip->devid) {
+ if ((devid = di_devid(node)) == NULL ||
+ (devidstr = devid_str_encode(devid, NULL)) == NULL)
+ goto out;
+
+ if (strcmp(devidstr, wip->devid) != 0)
+ goto out;
+ }
+
+ /*
+ * Either the devid wasn't specified, or it correctly matched.
+ * In this case, indicate a successful match and terminate the
+ * walk.
+ */
+ wip->matched = 1;
+ di_devfs_path_free(path);
+ devid_str_free(devidstr);
+ return (DI_WALK_TERMINATE);
+ }
+
+out:
+ if (path != NULL)
+ di_devfs_path_free(path);
+ devid_str_free(devidstr);
+ return (DI_WALK_CONTINUE);
+}
+
+/*ARGSUSED*/
+static int
+dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ di_node_t parent;
+ uint8_t fmversion;
+ char *devpath = NULL;
+ char *parentpath;
+ char *cp;
+ struct walkinfo walkinfo;
+ uint32_t present;
+ char *devid = NULL;
+
+ if (version > TOPO_METH_PRESENT_VERSION)
+ return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+ if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
+ fmversion > FM_DEV_SCHEME_VERSION ||
+ nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+
+ (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid);
+
+ if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0)
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+
+ /* strip off last component of path */
+ parentpath = alloca(walkinfo.len + 1);
+ (void) strcpy(parentpath, devpath);
+ if ((cp = strrchr(parentpath, '/')) == NULL)
+ parentpath = "/";
+ else
+ *cp = '\0';
+
+ /* if the result is an empty path, start walk at "/" */
+ if (*parentpath == '\0')
+ parentpath = "/";
+
+ /*
+ * DINFOFORCE is required for the devid to be present.
+ */
+ if ((parent = di_init(parentpath,
+ DINFOSUBTREE | DINFOFORCE)) == DI_NODE_NIL) {
+ if (errno != ENXIO)
+ return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
+ present = 0;
+ } else {
+ walkinfo.matched = 0;
+ walkinfo.path = devpath;
+ walkinfo.devid = devid;
+ (void) di_walk_node(parent,
+ DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match);
+ di_fini(parent);
+
+ present = walkinfo.matched;
+ }
+
+ if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
+ return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+ if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
+ nvlist_free(*out);
+ return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version,
+ nvlist_t *in, nvlist_t **out)
+{
+ di_node_t dnode;
+ uint8_t fmversion;
+ char *devpath = NULL;
+ uint32_t unusable;
+ uint_t state;
+
+ if (version > TOPO_METH_PRESENT_VERSION)
+ return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+ if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 ||
+ fmversion > FM_DEV_SCHEME_VERSION ||
+ nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0)
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+
+ if (devpath == NULL)
+ return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM));
+
+ if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) {
+ if (errno != ENXIO)
+ return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
+ unusable = 1;
+ } else {
+ state = di_state(dnode);
+ if (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN |
+ DI_BUS_QUIESCED | DI_BUS_DOWN))
+ unusable = 1;
+ else
+ unusable = 0;
+ di_fini(dnode);
+ }
+
+ if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
+ return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+ if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, unusable) != 0) {
+ nvlist_free(*out);
+ return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+ }
+
+ return (0);
+}
+
static nvlist_t *
dev_fmri_create(topo_mod_t *mp, const char *id, const char *path)
{
diff --git a/usr/src/lib/fm/topo/libtopo/common/hc.c b/usr/src/lib/fm/topo/libtopo/common/hc.c
index 1df1fa04f0..4567fd80b7 100644
--- a/usr/src/lib/fm/topo/libtopo/common/hc.c
+++ b/usr/src/lib/fm/topo/libtopo/common/hc.c
@@ -751,7 +751,8 @@ char **rev, nvlist_t **auth)
if (na == NULL) {
if (topo_mod_nvalloc(mod, &na,
NV_UNIQUE_NAME) == 0) {
- (void) nvlist_add_string(na, aname, aid);
+ (void) nvlist_add_string(na, aname,
+ aid);
}
} else {
(void) nvlist_add_string(na, aname, aid);
@@ -1375,7 +1376,7 @@ hc_is_present(topo_mod_t *mod, tnode_t *node, void *pdata)
return (ETOPO_PROP_NVL);
}
- return (err);
+ return (0);
}
static int
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
index 7fc51be9c8..1847757626 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c
@@ -359,7 +359,7 @@ topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
nvlist_t *fp, *prop = NULL;
if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
- nvl, fru, err) < 0)
+ nvl, &prop, err) < 0)
return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0)
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
index 5318bd1c12..04b6043648 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.c
@@ -41,14 +41,14 @@
*
* Module Plugin API
*
- * Enumerators must provide entry points for intialization and clean-up
+ * Enumerators must provide entry points for initialization and clean-up
* (_topo_init() and _topo_fini()). In their _topo_init() function, an
* enumerator should register (topo_mod_register()) its enumeration callback
* and allocate resources required for a subsequent call to the callback.
* Optionally, methods may also be registered with topo_method_register().
*
* In its enumeration callback routine, the module should search for resources
- * within its realm of resposibility and create any node ranges,
+ * within its realm of responsibility and create any node ranges,
* topo_node_range_create() and nodes, topo_node_bind(). The Enumerator
* module is handed a node to which it may begin attaching additional
* topology nodes. The enumerator may only access those nodes within its
@@ -306,7 +306,7 @@ topo_mod_hcfmri(topo_mod_t *mod, tnode_t *pnode, int version, const char *name,
if (pnode != NULL || auth != NULL || part != NULL || rev != NULL ||
serial != NULL || hc_specific != NULL) {
if (topo_mod_nvalloc(mod, &args, NV_UNIQUE_NAME) != 0)
- return (set_fmri_err(mod, EMOD_FMRI_NVL));
+ return (set_fmri_err(mod, EMOD_FMRI_NVL));
}
if (pnode != NULL) {
@@ -686,7 +686,8 @@ topo_mod_csn(topo_mod_t *mod)
return (NULL);
}
- return (topo_mod_strdup(mod, csn));
+
+ return (topo_cleanup_auth_str(mod->tm_hdl, csn));
}
nvlist_t *
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
index 43d217330a..d3f46d1b51 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
@@ -153,6 +153,10 @@ extern nvlist_t *topo_mod_auth(topo_mod_t *, tnode_t *);
#define TOPO_METH_FRU_COMPUTE_VERSION 0
#define TOPO_METH_FRU_COMPUTE_DESC "Dynamic FRU constructor"
+#define TOPO_METH_DISK_STATUS "topo_disk_status"
+#define TOPO_METH_DISK_STATUS_VERSION 0
+#define TOPO_METH_DISK_STATUS_DESC "Disk status"
+
extern void *topo_mod_alloc(topo_mod_t *, size_t);
extern void *topo_mod_zalloc(topo_mod_t *, size_t);
extern void topo_mod_free(topo_mod_t *, void *, size_t);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
index ec333ee7c3..1645fb5a50 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c
@@ -102,37 +102,6 @@ set_open_errno(topo_hdl_t *thp, int *errp, int err)
return (NULL);
}
-static char *
-smbios_fix(topo_hdl_t *thp, char *begin)
-{
- char buf[MAXNAMELEN];
- size_t count;
- char *str, *end, *pp;
-
- end = begin + strlen(begin);
-
- while (begin < end && isspace(*begin))
- begin++;
- while (begin < end && isspace(*(end - 1)))
- end--;
-
- if (begin >= end)
- return (NULL);
-
- count = end - begin;
- count += 1;
-
- if (count > sizeof (buf))
- return (NULL);
-
- (void) snprintf(buf, count, "%s", begin);
- while ((str = strchr(buf, ' ')) != NULL)
- *str = '-';
-
- pp = topo_hdl_strdup(thp, buf);
- return (pp);
-}
-
topo_hdl_t *
topo_open(int version, const char *rootdir, int *errp)
{
@@ -218,7 +187,7 @@ topo_open(int version, const char *rootdir, int *errp)
if (strcmp(s2.smbi_product, SMB_DEFAULT1) != 0 &&
strcmp(s2.smbi_product, SMB_DEFAULT2) != 0) {
- thp->th_product = smbios_fix(thp,
+ thp->th_product = topo_cleanup_auth_str(thp,
(char *)s2.smbi_product);
}
}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
index 50f69156c4..d89ecbb1fa 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -313,3 +313,40 @@ topo_search_path(topo_mod_t *mod, const char *rootdir, const char *file)
return (pp);
}
+
+/*
+ * SMBIOS serial numbers can contain characters (particularly ':' and ' ')
+ * that are invalid for the authority and can break FMRI parsing. We translate
+ * any invalid characters to a safe '-', as well as trimming any leading or
+ * trailing whitespace.
+ */
+char *
+topo_cleanup_auth_str(topo_hdl_t *thp, char *begin)
+{
+ char buf[MAXNAMELEN];
+ size_t count;
+ char *str, *end, *pp;
+
+ end = begin + strlen(begin);
+
+ while (begin < end && isspace(*begin))
+ begin++;
+ while (begin < end && isspace(*(end - 1)))
+ end--;
+
+ if (begin >= end)
+ return (NULL);
+
+ count = end - begin;
+ count += 1;
+
+ if (count > sizeof (buf))
+ return (NULL);
+
+ (void) snprintf(buf, count, "%s", begin);
+ while ((str = strpbrk(buf, " :=")) != NULL)
+ *str = '-';
+
+ pp = topo_hdl_strdup(thp, buf);
+ return (pp);
+}
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_subr.h b/usr/src/lib/fm/topo/libtopo/common/topo_subr.h
index e3df346dc2..d3e56e5921 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_subr.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_subr.h
@@ -90,6 +90,8 @@ extern void topo_fmristr_build(ssize_t *, char *, size_t, char *, char *,
extern int topo_walk_byid(topo_walk_t *wp, const char *name, topo_instance_t);
+extern char *topo_cleanup_auth_str(topo_hdl_t *, char *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile b/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile
index bd57a80a5f..cce64c561c 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile
+++ b/usr/src/lib/fm/topo/modules/i86pc/sata/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -31,4 +31,4 @@ MODULESRCS = sata.c sfx4500_props.c
include ../../Makefile.plugin
-LDLIBS += -lcfgadm -lsmbios -ldevinfo
+LDLIBS += -lcfgadm -lsmbios -ldevinfo -ldiskstatus -ldevid
diff --git a/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c b/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c
index 5a3ea47846..a1329eac7b 100644
--- a/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c
+++ b/usr/src/lib/fm/topo/modules/i86pc/sata/sata.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -54,6 +54,9 @@
#include <assert.h>
#include <sys/dkio.h>
#include <pthread.h>
+#include <fm/libdiskstatus.h>
+#include <sys/fm/io/scsi.h>
+#include <devid.h>
#include "sata.h"
#include "sfx4500_props.h"
@@ -63,6 +66,18 @@
#define MAX_MACHNAMELEN 256
+/*
+ * Given a /devices path for a whole disk, appending this extension gives the
+ * path to a raw device that can be opened.
+ */
+#if defined(__i386) || defined(__amd64)
+#define PHYS_EXTN ":q,raw"
+#elif defined(__sparc) || defined(__sparcv9)
+#define PHYS_EXTN ":c,raw"
+#else
+#error Unknown architecture
+#endif
+
struct sata_machine_specific_properties *machprops[] = {
&SFX4500_machprops, NULL
};
@@ -86,13 +101,12 @@ static tnode_t *node_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
char *label, cfga_list_data_t *, int *err);
static char *trimdup(const char *s, int *slen, topo_mod_t *mod);
static boolean_t get_machine_name(char **name, int *namelen, topo_mod_t *mod);
-static int make_legacyhc_fmri(topo_mod_t *mod, const char *str,
- nvlist_t **fmri);
static int sata_minorname_to_ap(char *minorname, char **ap, int *apbuflen,
cfga_list_data_t **list_array, topo_mod_t *mod);
static sata_dev_prop_t *lookup_sdp_by_minor(char *minorpath);
static boolean_t find_physical_disk_node(char *physpath, int pathbuflen,
int portnum, topo_mod_t *mod);
+static char *devpath_to_devid(char *devpath);
static int sata_maximum_port(char *dpath, topo_mod_t *mod);
static void sata_add_port_props(tnode_t *cnode, sata_dev_prop_t *sdp, int *err);
static void sata_port_add_private_props(tnode_t *cnode, char *minorpath,
@@ -100,34 +114,32 @@ static void sata_port_add_private_props(tnode_t *cnode, char *minorpath,
static void sata_info_to_fru(char *info, char **model, int *modlen,
char **manuf, int *manulen, char **serial, int *serlen, char **firm,
int *firmlen, topo_mod_t *mod);
-static void sata_add_disk_props(tnode_t *cnode, int portnum,
+static void sata_add_disk_props(tnode_t *cnode, const char *physpath,
cfga_list_data_t *cfgap, int *err, topo_mod_t *mod);
static boolean_t is_sata_controller(char *dpath);
-static int sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru,
- const char *name, int portnum, cfga_list_data_t *cfglist, int ndisks,
- int *err);
+static int sata_disks_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ int portnum, cfga_list_data_t *cfglist, int ndisks, int *err);
static int sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
topo_instance_t min, topo_instance_t max);
static int sata_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
topo_instance_t min, topo_instance_t max, void *notused1, void *notused2);
-static int sata_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
+static int sata_disk_status(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
nvlist_t **);
static void sata_release(topo_mod_t *mod, tnode_t *nodep);
/*
- * The topo method is bound to the sata-port nodes because we want
- * the hc scheme's lookup to find the method and execute it. If it
- * were bound to the disk nodes, and if a disk were not inserted,
- * the present method would never execute, and the hc scheme plugin
- * would return PRESENT (because other hc-schemed FMRIs do not implement
- * a presence method, and in those cases, the correct default is to
- * assert presence).
+ * Methods for SATA disks. This is used by the disk-transport module to
+ * generate ereports based off SCSI disk status. Unlike the present method,
+ * this can only function when we have a /devices path (i.e. the disk is
+ * configured) and so must be a function of the disk itself.
*/
-static const topo_method_t SATA_METHODS[] = {
- { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION,
- TOPO_STABILITY_INTERNAL, sata_present },
+static const topo_method_t sata_disk_methods[] = {
+ { TOPO_METH_DISK_STATUS, TOPO_METH_DISK_STATUS_DESC,
+ TOPO_METH_DISK_STATUS_VERSION, TOPO_STABILITY_INTERNAL,
+ sata_disk_status },
{ NULL }
};
+
/*
* Since the global data is only initialized ONCE in _topo_init
* and multiple threads that use libtopo can cause _topo_init to
@@ -156,10 +168,7 @@ static const topo_modinfo_t sata_info =
static void
sata_release(topo_mod_t *mod, tnode_t *nodep)
{
- /* Only need to unregister methods for the sata port nodes */
- if (strcmp(topo_node_name(nodep), SATA_PORT) == 0)
- topo_method_unregister_all(mod, nodep);
-
+ topo_method_unregister_all(mod, nodep);
topo_node_unbind(nodep);
}
@@ -373,17 +382,6 @@ _topo_fini(topo_mod_t *mod)
}
static int
-make_legacyhc_fmri(topo_mod_t *mod, const char *str, nvlist_t **fmri)
-{
- char buf[PATH_MAX];
-
- (void) snprintf(buf, PATH_MAX, "hc:///component=%s", str);
-
- return (topo_mod_str2nvl(mod, buf, fmri));
-}
-
-
-static int
config_list_ext_poll(int num, char * const *path,
cfga_list_data_t **list_array, int *nlist)
{
@@ -428,113 +426,6 @@ config_list_ext_poll(int num, char * const *path,
return (e);
}
-static boolean_t
-sata_serial_match(topo_mod_t *mod, char *sn, char *ap_lname, char *info)
-{
- char *p, *apsn;
- boolean_t rv = B_FALSE;
- int buflen;
-
- /* Some cfgadm plugins provide serial number information (e.g. sata) */
- if (strncmp(ap_lname, "sata", 4) == 0 && isdigit(ap_lname[4])) {
- /* The sata plugin provides the serial number after "SN: " */
- if ((p = strstr(info, "SN: ")) != NULL) {
-
- p += 4 /* strlen("SN: ") */;
-
- /*
- * Terminate the string at any spaces (the serial
- * number is a string with no spaces)
- */
- buflen = strlen(p) + 1;
- apsn = topo_mod_alloc(mod, buflen);
- (void) strcpy(apsn, p);
- if ((p = strpbrk(apsn, " \t")) != NULL)
- *p = 0;
-
- rv = (strcasecmp(apsn, sn) == 0);
-
- topo_mod_free(mod, apsn, buflen);
- }
- } else /* Unknown cfgadm plugin ap type -- assume sn matches */
- rv = B_TRUE;
-
- return (rv);
-}
-
-/*ARGSUSED*/
-static int
-sata_present(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
- nvlist_t *nvl, nvlist_t **out_nvl)
-{
- nvlist_t **hcprs = NULL;
- uint8_t version;
- char *nm = NULL;
- char *id = NULL;
- uint_t hcnprs;
- int err;
- cfga_list_data_t *list_array = NULL;
- int nlist;
- char *ap_path[1];
- int present = 1;
- char *sn;
-
- /*
- * If this FMRI is a legacy hc FMRI with a component
- * looking like an attachment point, use libcfgadm to
- * detect its state:
- */
-
- if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 ||
- version > FM_HC_SCHEME_VERSION) {
-
- return (topo_mod_seterrno(mod, EINVAL));
- }
-
- err = nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcprs, &hcnprs);
- if (err != 0 || hcprs == NULL) {
-
- return (topo_mod_seterrno(mod, EINVAL));
- }
-
- (void) nvlist_lookup_string(hcprs[0], FM_FMRI_HC_NAME, &nm);
- (void) nvlist_lookup_string(hcprs[0], FM_FMRI_HC_ID, &id);
- /* The hc-list must have 1 entry and that entry must be a component */
- if (hcnprs == 1 && nm != NULL && id != NULL &&
- strcmp(nm, FM_FMRI_LEGACY_HC) == 0 && strchr(id, '/') != NULL) {
- ap_path[0] = id;
-
- if (config_list_ext_poll(1, ap_path, &list_array, &nlist)
- == CFGA_OK) {
-
- if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sn)
- != 0)
- sn = NULL;
-
- /*
- * If a serial number is included in the FMRI, use it
- * to verify presence
- */
- present = (list_array[0].ap_r_state
- == CFGA_STAT_CONNECTED && (sn == NULL ||
- (sn != NULL &&
- sata_serial_match(mod, sn, list_array[0].ap_log_id,
- list_array[0].ap_info))));
-
- free(list_array);
- }
- }
-
- if (topo_mod_nvalloc(mod, out_nvl, NV_UNIQUE_NAME) != 0)
- return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
- if (nvlist_add_uint32(*out_nvl, TOPO_METH_PRESENT_RET, present) != 0) {
- nvlist_free(*out_nvl);
- return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
- }
-
- return (0);
-}
-
/*
* Translates a device minor name into an attachment point (`sataX/Y`)
* Allocates memory and returns the attachment point name in *ap.
@@ -635,6 +526,27 @@ find_physical_disk_node(char *physpath, int pathbuflen, int portnum,
return (found);
}
+static char *
+devpath_to_devid(char *devpath)
+{
+ di_node_t node;
+ ddi_devid_t devid;
+ char *devidstr = NULL;
+
+ if ((node = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL)
+ return (NULL);
+
+ if ((devid = di_devid(node)) == NULL) {
+ di_fini(node);
+ return (NULL);
+ }
+
+ devidstr = devid_str_encode(devid, NULL);
+ di_fini(node);
+
+ return (devidstr);
+}
+
static int
sata_maximum_port(char *dpath, topo_mod_t *mod)
{
@@ -837,39 +749,25 @@ sata_info_to_fru(char *info, char **model, int *modlen, char **manuf,
}
static void
-sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap,
- int *err, topo_mod_t *mod)
+sata_add_disk_props(tnode_t *cnode, const char *physpath,
+ cfga_list_data_t *cfgap, int *err, topo_mod_t *mod)
{
- char physpath[PATH_MAX];
char *ldev, *p;
char *model = NULL, *manuf = NULL, *serial = NULL, *firm = NULL;
- boolean_t physpath_found = B_FALSE;
int manuf_len, model_len, serial_len, firm_len;
- /*
- * The physical path to the drive can be derived by
- * taking the attachment point physical path, chopping off the
- * minor portion, and looking for the target node of the form
- * <nodename>@<X>,0 (where <X> is the port number)
- */
- strncpy(physpath, cfgap->ap_phys_id, PATH_MAX);
-
- /* The AP phys path MUST have a colon: */
- *strchr(physpath, ':') = 0;
-
- if (find_physical_disk_node(physpath, PATH_MAX, portnum, mod)) {
+ (void) topo_pgroup_create(cnode, &storage_pgroup, err);
- (void) topo_pgroup_create(cnode, &io_pgroup, err);
+ if (physpath) {
+ (void) topo_pgroup_create(cnode, &io_pgroup,
+ err);
(void) topo_prop_set_string(cnode, TOPO_PGROUP_IO,
TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE,
physpath + 8 /* strlen("/devices") */,
err);
- physpath_found = B_TRUE;
}
- (void) topo_pgroup_create(cnode, &storage_pgroup, err);
-
if ((ldev = strstr(cfgap->ap_log_id, "::")) != NULL) {
ldev += 2 /* strlen("::") */;
if ((p = strchr(ldev, '/')) != NULL) {
@@ -903,22 +801,21 @@ sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap,
topo_mod_free(mod, firm, firm_len);
}
-#if defined(__i386) || defined(__amd64)
/*
* Try to get the disk capacity and store it in a property if the
* device is accessible
*/
- if (physpath_found) {
+ if (physpath) {
int fd;
struct dk_minfo dkmi;
uint64_t capacity = 0;
char capstr[32];
- /*
- * On x86, for disk target drivers, `:q,raw' means "access the
- * character device that corresponds to the whole disk".
- */
- strncat(physpath, ":q,raw", PATH_MAX);
- if ((fd = open(physpath, O_RDONLY)) >= 0) {
+ char buf[PATH_MAX];
+
+ (void) snprintf(buf, sizeof (buf), "%s%s", physpath,
+ PHYS_EXTN);
+
+ if ((fd = open(buf, O_RDONLY)) >= 0) {
if (ioctl(fd, DKIOCGMEDIAINFO, &dkmi) == 0) {
capacity = dkmi.dki_capacity *
(uint64_t)dkmi.dki_lbsize;
@@ -933,7 +830,6 @@ sata_add_disk_props(tnode_t *cnode, int portnum, cfga_list_data_t *cfgap,
err);
}
}
-#endif
}
static boolean_t
@@ -957,14 +853,17 @@ is_sata_controller(char *dpath)
}
static int
-sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru,
- const char *name, int portnum, cfga_list_data_t *cfglist,
- int ndisks, int *err)
+sata_disks_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
+ int portnum, cfga_list_data_t *cfglist, int ndisks, int *err)
{
tnode_t *cnode;
sata_dev_prop_t *sdp;
nvlist_t *fru = NULL;
int i, nerrs = 0;
+ char physpath_buf[PATH_MAX];
+ char *physpath, *devpath;
+ char *devid;
+ nvlist_t *asru;
if (topo_node_range_create(mod, pnode, name, 0, ndisks - 1) < 0) {
topo_mod_dprintf(mod, "Unable to create "
@@ -981,33 +880,57 @@ sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru,
(cfglist[i].ap_o_state == CFGA_STAT_CONFIGURED ||
cfglist[i].ap_o_state == CFGA_STAT_UNCONFIGURED)) {
- if ((sdp = lookup_sdp_by_minor(cfglist[i].ap_phys_id))
- != NULL) {
- if (make_legacyhc_fmri(mod, sdp->label, &fru)
- != 0) {
- topo_mod_dprintf(mod, "Error creating "
- "FRU while creating " SATA_DISK
- " nodes: %s\n",
- topo_mod_errmsg(mod));
- }
- }
+ sdp = lookup_sdp_by_minor(cfglist[i].ap_phys_id);
/*
- * If this disk is one which we're decorating with
- * machine-specific properties, set the FRU to
- * a legacy-hc FMRI with the label as the component,
- * the ASRU as the parent's ASRU (the ASRU of the
- * sata port, e.g. the attachment point); otherwise,
- * use the node's FMRI as its FRU (fru will be NULL
- * in that case, causing node_create to use the FMRI).
+ * The physical path to the drive can be derived by
+ * taking the attachment point physical path, chopping
+ * off the minor portion, and looking for the target
+ * node of the form <nodename>@<X>,0 (where <X> is the
+ * port number)
*/
+ strncpy(physpath_buf, cfglist[i].ap_phys_id, PATH_MAX);
+
+ /* The AP phys path MUST have a colon: */
+ *strchr(physpath_buf, ':') = 0;
+
+ /*
+ * Create the ASRU. If the device is attached to the
+ * system but unconfigured, then it will have no
+ * associated ASRU.
+ */
+ if (find_physical_disk_node(physpath_buf, PATH_MAX,
+ portnum, mod)) {
+ physpath = physpath_buf;
+ devpath = physpath_buf +
+ sizeof ("/devices") - 1;
+ /*
+ * Given the /devices path, attempt to find an
+ * associated devid.
+ */
+ devid = devpath_to_devid(devpath);
+ asru = topo_mod_devfmri(mod,
+ FM_DEV_SCHEME_VERSION, devpath, devid);
+ devid_str_free(devid);
+ } else {
+ physpath = NULL;
+ asru = NULL;
+ }
+
if ((cnode = node_create(mod, pnode, name, i,
- B_TRUE, fru, pasru, sdp ? (char *)sdp->label : NULL,
+ B_TRUE, fru, asru, sdp ? (char *)sdp->label : NULL,
&cfglist[i], err)) != NULL) {
- sata_add_disk_props(cnode, portnum, &cfglist[i],
- err, mod);
+ sata_add_disk_props(cnode, physpath,
+ &cfglist[i], err, mod);
+ if (topo_method_register(mod, cnode,
+ sata_disk_methods) < 0) {
+ topo_mod_dprintf(mod,
+ "topo_method_register failed: %s\n",
+ topo_strerror(topo_mod_errno(mod)));
+ ++nerrs;
+ }
} else {
topo_mod_dprintf(mod, "Error creating disk "
"node for port %d: %s\n", portnum,
@@ -1015,8 +938,8 @@ sata_disks_create(topo_mod_t *mod, tnode_t *pnode, nvlist_t *pasru,
++nerrs;
}
- if (fru)
- nvlist_free(fru);
+ nvlist_free(fru);
+ nvlist_free(asru);
}
}
@@ -1032,7 +955,7 @@ static int
sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
topo_instance_t min, topo_instance_t max)
{
- nvlist_t *asru, *fru;
+ nvlist_t *asru;
tnode_t *cnode;
int err = 0, nerr = 0;
int i;
@@ -1066,15 +989,6 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
return (-1);
}
- /* Create the FRU - a legacy component FMRI */
- if (make_legacyhc_fmri(mod, "motherboard", &fru) != 0) {
- topo_mod_dprintf(mod, "Unable to create legacy FRU FMRI for "
- "node " SATA_PORT ": %s\n",
- topo_strerror(err));
- topo_mod_free(mod, dpath, dpathlen);
- return (-1);
- }
-
for (i = min; i <= max; i++) {
/* The minor node name = "/devices" + dpath + ":" + i */
@@ -1088,8 +1002,9 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
continue;
}
- /* Create the ASRU - a legacy component FMRI */
- if (make_legacyhc_fmri(mod, ap, &asru) != 0) {
+ /* Create the ASRU - a dev:// FMRI */
+ if ((asru = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION,
+ dpath, NULL)) == NULL) {
free(cfglist);
topo_mod_free(mod, ap, apbuflen);
topo_mod_dprintf(mod, "failed to make ASRU FMRI: "
@@ -1099,12 +1014,10 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
}
/*
- * The ASRU is a legacy-hc component FMRI with the
- * component being the attachment point
- * The FRU is a legacy-hc component FMRI with the
- * component being "MB".
+ * The ASRU is the devices path
+ * The FRU is a the component FRMI
*/
- if ((cnode = node_create(mod, pnode, name, i, B_TRUE, fru,
+ if ((cnode = node_create(mod, pnode, name, i, B_TRUE, NULL,
asru, ap, NULL, &err)) == NULL) {
nvlist_free(asru);
free(cfglist);
@@ -1115,24 +1028,13 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
continue;
}
- if (topo_method_register(mod, cnode, SATA_METHODS) < 0) {
- topo_mod_dprintf(mod, "topo_method_register failed: "
- "%s\n", topo_strerror(topo_mod_errno(mod)));
- topo_node_unbind(cnode);
- nvlist_free(asru);
- free(cfglist);
- topo_mod_free(mod, ap, apbuflen);
- ++nerr;
- continue;
- }
-
topo_mod_free(mod, ap, apbuflen);
/* For now, ignore errors from private property creation */
sata_port_add_private_props(cnode, minorname, &err);
/* Create the disk node(s) under this sata-port: */
- if (sata_disks_create(mod, cnode, asru, SATA_DISK, i, cfglist,
+ if (sata_disks_create(mod, cnode, SATA_DISK, i, cfglist,
1 /* one disk for now */, &err) != 0) {
topo_mod_dprintf(mod, "Error while creating " SATA_DISK
" node(s): %s\n", topo_strerror(err));
@@ -1144,7 +1046,6 @@ sata_port_create(topo_mod_t *mod, tnode_t *pnode, const char *name,
nvlist_free(asru);
}
- nvlist_free(fru);
topo_mod_free(mod, dpath, dpathlen);
if (nerr != 0) {
@@ -1164,3 +1065,91 @@ sata_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
return (0);
}
+
+/*
+ * Query the current disk status. If successful, the disk status is returned as
+ * an nvlist consisting of at least the following members:
+ *
+ * protocol string Supported protocol (currently "scsi")
+ *
+ * status nvlist Arbitrary protocol-specific information
+ * about the current state of the disk.
+ *
+ * faults nvlist A list of supported faults. Each
+ * element of this list is a boolean value.
+ * An element's existence indicates that
+ * the drive supports detecting this fault,
+ * and the value indicates the current
+ * state of the fault.
+ *
+ * <fault-name> nvlist For each fault named in 'faults', a
+ * nvlist describing protocol-specific
+ * attributes of the fault.
+ *
+ * This method relies on the libdiskstatus library to query this information.
+ */
+static int
+sata_disk_status(topo_mod_t *mod, tnode_t *nodep, topo_version_t vers,
+ nvlist_t *in_nvl, nvlist_t **out_nvl)
+{
+ disk_status_t *dsp;
+ char *devpath, *fullpath;
+ size_t pathlen;
+ int err;
+ nvlist_t *status;
+ *out_nvl = NULL;
+
+ if (vers != TOPO_METH_DISK_STATUS_VERSION)
+ return (topo_mod_seterrno(mod, EMOD_VER_NEW));
+
+ /*
+ * If the caller specifies the "path" parameter, then this indicates
+ * that we should use this instead of deriving it from the topo node
+ * itself.
+ */
+ if (nvlist_lookup_string(in_nvl, "path", &fullpath) == 0) {
+ devpath = NULL;
+ } else {
+ /*
+ * Get the /devices path and attempt to open the disk status
+ * handle.
+ */
+ if (topo_prop_get_string(nodep, TOPO_PGROUP_IO,
+ TOPO_IO_DEV_PATH, &devpath, &err) != 0)
+ return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
+
+ /*
+ * Note that sizeof(string) includes the terminating NULL byte
+ */
+ pathlen = strlen(devpath) + sizeof ("/devices") +
+ sizeof (PHYS_EXTN) - 1;
+
+ if ((fullpath = topo_mod_alloc(mod, pathlen)) == NULL)
+ return (topo_mod_seterrno(mod, EMOD_NOMEM));
+
+ (void) snprintf(fullpath, pathlen, "/devices%s%s", devpath,
+ PHYS_EXTN);
+
+ topo_mod_strfree(mod, devpath);
+ }
+
+ if ((dsp = disk_status_open(fullpath, &err)) == NULL) {
+ topo_mod_free(mod, fullpath, pathlen);
+ return (topo_mod_seterrno(mod, err == EDS_NOMEM ?
+ EMOD_NOMEM : EMOD_METHOD_NOTSUP));
+ }
+
+ if (devpath)
+ topo_mod_free(mod, fullpath, pathlen);
+
+ if ((status = disk_status_get(dsp)) == NULL) {
+ err = (disk_status_errno(dsp) == EDS_NOMEM ?
+ EMOD_NOMEM : EMOD_METHOD_NOTSUP);
+ disk_status_close(dsp);
+ return (topo_mod_seterrno(mod, err));
+ }
+
+ *out_nvl = status;
+ disk_status_close(dsp);
+ return (0);
+}