summaryrefslogtreecommitdiff
path: root/usr/src/lib/scsi/libscsi/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/scsi/libscsi/common')
-rw-r--r--usr/src/lib/scsi/libscsi/common/libscsi.h182
-rw-r--r--usr/src/lib/scsi/libscsi/common/libscsi_impl.h100
-rw-r--r--usr/src/lib/scsi/libscsi/common/mkerrno.sh88
-rw-r--r--usr/src/lib/scsi/libscsi/common/scsi_engine.c621
-rw-r--r--usr/src/lib/scsi/libscsi/common/scsi_status.c723
-rw-r--r--usr/src/lib/scsi/libscsi/common/scsi_subr.c351
6 files changed, 2065 insertions, 0 deletions
diff --git a/usr/src/lib/scsi/libscsi/common/libscsi.h b/usr/src/lib/scsi/libscsi/common/libscsi.h
new file mode 100644
index 0000000000..748913ee1e
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/libscsi.h
@@ -0,0 +1,182 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSCSI_H
+#define _LIBSCSI_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/scsi/impl/spc3_types.h>
+#include <stdarg.h>
+
+#define LIBSCSI_VERSION 1
+#define LIBSCSI_STATUS_INVALID ((sam4_status_t)-1)
+#define LIBSCSI_DEFAULT_ENGINE_PATH "/usr/lib/scsi/plugins/scsi/engines"
+#define LIBSCSI_DEFAULT_ENGINE "uscsi"
+
+/*
+ * Flags for action creation. Selected to avoid overlap with the uscsi
+ * flags with similar or identical meaning.
+ */
+#define LIBSCSI_AF_READ 0x80000000
+#define LIBSCSI_AF_WRITE 0x40000000
+#define LIBSCSI_AF_SILENT 0x20000000
+#define LIBSCSI_AF_DIAGNOSE 0x10000000
+#define LIBSCSI_AF_ISOLATE 0x08000000
+#define LIBSCSI_AF_RQSENSE 0x04000000
+
+typedef enum libscsi_errno {
+ ESCSI_NONE, /* no error */
+ ESCSI_NOMEM, /* no memory */
+ ESCSI_ZERO_LENGTH, /* zero-length allocation requested */
+ ESCSI_VERSION, /* library version mismatch */
+ ESCSI_BADTARGET, /* invalid target specification */
+ ESCSI_BADCMD, /* invalid SCSI command */
+ ESCSI_BADENGINE, /* engine library corrupt */
+ ESCSI_NOENGINE, /* engine library not found */
+ ESCSI_ENGINE_INIT, /* engine initialization failed */
+ ESCSI_ENGINE_VER, /* engine version mismatch */
+ ESCSI_ENGINE_BADPATH, /* engine path contains no usable components */
+ ESCSI_BADFLAGS, /* incorrect action flags */
+ ESCSI_BOGUSFLAGS, /* unknown flag value */
+ ESCSI_BADLENGTH, /* buffer length overflow */
+ ESCSI_NEEDBUF, /* missing required buffer */
+ ESCSI_IO, /* I/O operation failed */
+ ESCSI_SYS, /* system call failed */
+ ESCSI_PERM, /* insufficient permissions */
+ ESCSI_RANGE, /* parameter outside valid range */
+ ESCSI_NOTSUP, /* operation not supported */
+ ESCSI_UNKNOWN, /* error of unknown type */
+ ESCSI_INQUIRY_FAILED, /* initial inquiry command failed */
+ ESCSI_MAX /* maximum libscsi errno value */
+} libscsi_errno_t;
+
+struct libscsi_hdl;
+typedef struct libscsi_hdl libscsi_hdl_t;
+
+struct libscsi_target;
+typedef struct libscsi_target libscsi_target_t;
+
+typedef struct libscsi_status {
+ uint64_t lss_status; /* SCSI status of this command */
+ size_t lss_sense_len; /* Length in bytes of sense data */
+ uint8_t *lss_sense_data; /* Pointer to sense data */
+} libscsi_status_t;
+
+struct libscsi_action;
+typedef struct libscsi_action libscsi_action_t;
+
+struct libscsi_result;
+typedef struct libscsi_result libscsi_result_t;
+
+typedef struct libscsi_engine_ops {
+ void *(*lseo_open)(libscsi_hdl_t *, const void *);
+ void (*lseo_close)(libscsi_hdl_t *, void *);
+ int (*lseo_exec)(libscsi_hdl_t *, void *, libscsi_action_t *);
+ void (*lseo_target_name)(libscsi_hdl_t *, void *, char *, size_t);
+} libscsi_engine_ops_t;
+
+typedef struct libscsi_engine {
+ const char *lse_name;
+ uint_t lse_libversion;
+ const libscsi_engine_ops_t *lse_ops;
+} libscsi_engine_t;
+
+extern libscsi_hdl_t *libscsi_init(uint_t, libscsi_errno_t *);
+extern void libscsi_fini(libscsi_hdl_t *);
+
+extern libscsi_target_t *libscsi_open(libscsi_hdl_t *, const char *,
+ const void *);
+extern void libscsi_close(libscsi_hdl_t *, libscsi_target_t *);
+extern libscsi_hdl_t *libscsi_get_handle(libscsi_target_t *);
+
+extern const char *libscsi_vendor(libscsi_target_t *);
+extern const char *libscsi_product(libscsi_target_t *);
+extern const char *libscsi_revision(libscsi_target_t *);
+
+extern libscsi_errno_t libscsi_errno(libscsi_hdl_t *);
+extern const char *libscsi_errmsg(libscsi_hdl_t *);
+extern const char *libscsi_strerror(libscsi_errno_t);
+extern const char *libscsi_errname(libscsi_errno_t);
+extern libscsi_errno_t libscsi_errcode(const char *);
+
+extern libscsi_action_t *libscsi_action_alloc(libscsi_hdl_t *, spc3_cmd_t,
+ uint_t, void *, size_t);
+extern sam4_status_t libscsi_action_get_status(const libscsi_action_t *);
+extern void libscsi_action_set_timeout(libscsi_action_t *, uint32_t);
+extern uint32_t libscsi_action_get_timeout(const libscsi_action_t *);
+extern uint_t libscsi_action_get_flags(const libscsi_action_t *);
+extern uint8_t *libscsi_action_get_cdb(const libscsi_action_t *);
+extern int libscsi_action_get_buffer(const libscsi_action_t *,
+ uint8_t **, size_t *, size_t *);
+extern int libscsi_action_get_sense(const libscsi_action_t *,
+ uint8_t **, size_t *, size_t *);
+extern int libscsi_action_parse_sense(const libscsi_action_t *, uint64_t *,
+ uint64_t *, uint64_t *, diskaddr_t *);
+extern void libscsi_action_set_status(libscsi_action_t *, sam4_status_t);
+extern int libscsi_action_set_datalen(libscsi_action_t *, size_t);
+extern int libscsi_action_set_senselen(libscsi_action_t *, size_t);
+extern int libscsi_exec(libscsi_action_t *, libscsi_target_t *);
+extern void libscsi_action_free(libscsi_action_t *);
+
+extern const char *libscsi_sense_key_name(uint64_t);
+extern const char *libscsi_sense_code_name(uint64_t, uint64_t);
+
+/*
+ * Interfaces for engine providers
+ */
+extern void *libscsi_alloc(libscsi_hdl_t *, size_t);
+extern void *libscsi_zalloc(libscsi_hdl_t *, size_t);
+extern char *libscsi_strdup(libscsi_hdl_t *, const char *);
+extern void libscsi_free(libscsi_hdl_t *, void *);
+extern libscsi_status_t *libscsi_status_alloc(libscsi_hdl_t *, size_t);
+extern int libscsi_status_fill(libscsi_hdl_t *, libscsi_status_t *,
+ uint16_t, size_t);
+extern void libscsi_status_free(libscsi_hdl_t *, libscsi_status_t *);
+
+extern int libscsi_set_errno(libscsi_hdl_t *, libscsi_errno_t);
+extern int libscsi_verror(libscsi_hdl_t *, libscsi_errno_t, const char *,
+ va_list);
+extern int libscsi_error(libscsi_hdl_t *, libscsi_errno_t, const char *, ...);
+
+typedef const libscsi_engine_t *(*libscsi_engine_init_f)(libscsi_hdl_t *);
+
+/*
+ * Generic SCSI utility functions.
+ */
+extern size_t libscsi_cmd_cdblen(libscsi_hdl_t *, uint8_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSCSI_H */
diff --git a/usr/src/lib/scsi/libscsi/common/libscsi_impl.h b/usr/src/lib/scsi/libscsi/common/libscsi_impl.h
new file mode 100644
index 0000000000..c34879dab4
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/libscsi_impl.h
@@ -0,0 +1,100 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSCSI_IMPL_H
+#define _LIBSCSI_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libscsi.h>
+
+#define LIBSCSI_ENGINE_EXT ".so"
+
+#define LIBSCSI_ERRMSGLEN 512
+
+typedef struct libscsi_engine_impl {
+ const libscsi_engine_t *lsei_engine;
+ void *lsei_dl_hdl;
+ struct libscsi_engine_impl *lsei_next;
+} libscsi_engine_impl_t;
+
+typedef struct libscsi_action_impl {
+ libscsi_hdl_t *lsai_hdl;
+ uint_t lsai_flags;
+ uint32_t lsai_timeout;
+ uint8_t *lsai_cdb;
+ size_t lsai_cdb_len;
+ size_t lsai_data_len;
+ size_t lsai_data_alloc;
+ uint8_t *lsai_data;
+ sam4_status_t lsai_status;
+ size_t lsai_sense_len;
+ uint8_t *lsai_sense_data;
+ uint8_t lsai_buf[1];
+} libscsi_action_impl_t;
+
+struct libscsi_hdl {
+ uint_t lsh_version;
+ libscsi_errno_t lsh_errno;
+ char lsh_errmsg[LIBSCSI_ERRMSGLEN];
+ libscsi_engine_impl_t *lsh_engines;
+ uint_t lsh_targets;
+};
+
+struct libscsi_target {
+ const libscsi_engine_t *lst_engine;
+ char *lst_vendor;
+ char *lst_product;
+ char *lst_revision;
+ void *lst_priv;
+ uint_t lst_mtbf_cdb;
+ uint_t lst_mtbf_read;
+ uint_t lst_mtbf_write;
+ struct libscsi_hdl *lst_hdl;
+};
+
+#define VERIFY(x) ((void)((x) || libscsi_assert(#x, __FILE__, __LINE__)))
+
+#ifdef DEBUG
+#define ASSERT(x) VERIFY(x)
+#else
+#define ASSERT(x)
+#endif
+
+#define LXOR(l, r) (((l) != 0) ^ ((r) != 0))
+
+extern int libscsi_assert(const char *, const char *, int);
+extern int libscsi_get_inquiry(struct libscsi_hdl *, struct libscsi_target *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSCSI_IMPL_H */
diff --git a/usr/src/lib/scsi/libscsi/common/mkerrno.sh b/usr/src/lib/scsi/libscsi/common/mkerrno.sh
new file mode 100644
index 0000000000..604c63e643
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/mkerrno.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+echo "\
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident\t\"%Z%%M%\t%I%\t%E%\tSMI\"
+
+#include <strings.h>
+#include <scsi/libscsi.h>
+
+static const struct {
+\tchar *name;\t\t/* error name */
+\tchar *msg;\t\t/* error message */
+} _libscsi_errstr[] = {"
+
+pattern='^ \(ESCSI_[A-Z0-9_]*\),*'
+replace=' { "\1",'
+open=' \/\* '
+openrepl='"'
+close=' \*\/$'
+closerepl='" },'
+
+( sed -n "s/$pattern/$replace/p" | sed -n "s/$open/$openrepl/p" |
+ sed -n "s/$close/$closerepl/p" ) || exit 1
+
+echo "\
+};\n\
+\n\
+static int _libscsi_nerrno = sizeof (_libscsi_errstr) /\n\
+ sizeof (_libscsi_errstr[0]);\n\
+\n\
+const char *
+libscsi_strerror(libscsi_errno_t err)
+{
+ return (err < 0 || err >= _libscsi_nerrno ? \"unknown error\" :
+ _libscsi_errstr[err].msg);
+}
+
+const char *
+libscsi_errname(libscsi_errno_t err)
+{
+ return (err < 0 || err >= _libscsi_nerrno ? NULL :
+ _libscsi_errstr[err].name);
+}
+
+libscsi_errno_t
+libscsi_errcode(const char *name)
+{
+ libscsi_errno_t err;
+
+ for (err = 0; err < _libscsi_nerrno; err++) {
+ if (strcmp(name, _libscsi_errstr[err].name) == 0)
+ return (err);
+ }
+
+ return (ESCSI_UNKNOWN);
+}"
+
+exit 0
diff --git a/usr/src/lib/scsi/libscsi/common/scsi_engine.c b/usr/src/lib/scsi/libscsi/common/scsi_engine.c
new file mode 100644
index 0000000000..df221eea55
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/scsi_engine.c
@@ -0,0 +1,621 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/isa_defs.h>
+#include <sys/systeminfo.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/commands.h>
+#include <sys/scsi/impl/uscsi.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <limits.h>
+
+#include <scsi/libscsi.h>
+#include "libscsi_impl.h"
+
+static const libscsi_engine_t *
+get_engine(libscsi_hdl_t *hp, const char *name)
+{
+ libscsi_engine_impl_t *eip;
+ const libscsi_engine_t *ep;
+ const char *engine_path, *p, *q;
+ char engine_dir[MAXPATHLEN];
+ char engine_lib[MAXPATHLEN];
+ char init_name[MAXPATHLEN];
+ void *dl_hdl;
+ libscsi_engine_init_f init;
+ boolean_t found_lib = B_FALSE, found_init = B_FALSE;
+ int dirs_tried = 0;
+ char isa[257];
+
+ for (eip = hp->lsh_engines; eip != NULL; eip = eip->lsei_next) {
+ if (strcmp(eip->lsei_engine->lse_name, name) == 0)
+ return (eip->lsei_engine);
+ }
+
+ if ((engine_path = getenv("LIBSCSI_ENGINE_PATH")) == NULL)
+ engine_path = LIBSCSI_DEFAULT_ENGINE_PATH;
+
+#if defined(_LP64)
+ if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
+ isa[0] = '\0';
+#else
+ isa[0] = '\0';
+#endif
+
+ for (p = engine_path, q = strchr(p, ':'); p != NULL; p = q) {
+ if (q != NULL) {
+ ptrdiff_t len = q - p;
+ (void) strncpy(engine_dir, p, len);
+ engine_dir[len] = '\0';
+ while (*q == ':')
+ ++q;
+ if (*q == '\0')
+ q = NULL;
+ if (len == 0)
+ continue;
+ } else {
+ (void) strcpy(engine_dir, p);
+ }
+ if (engine_dir[0] != '/')
+ continue;
+
+ ++dirs_tried;
+
+ (void) snprintf(engine_lib, MAXPATHLEN, "%s/%s/%s%s",
+ engine_dir, isa, name, LIBSCSI_ENGINE_EXT);
+
+ dl_hdl = dlopen(engine_lib,
+ RTLD_LOCAL | RTLD_LAZY | RTLD_PARENT);
+ if (dl_hdl == NULL) {
+ if (!found_lib)
+ (void) libscsi_error(hp, ESCSI_NOENGINE,
+ "unable to dlopen %s: %s", engine_lib,
+ dlerror());
+ continue;
+ }
+ found_lib = B_TRUE;
+ (void) snprintf(init_name, MAXPATHLEN, "libscsi_%s_init", name);
+ init = (libscsi_engine_init_f)dlsym(dl_hdl, init_name);
+ if (init == NULL) {
+ if (!found_init)
+ (void) libscsi_error(hp, ESCSI_NOENGINE,
+ "failed to find %s in %s: %s", init_name,
+ engine_lib, dlerror());
+ (void) dlclose(dl_hdl);
+ continue;
+ }
+ if ((ep = init(hp)) == NULL) {
+ (void) dlclose(dl_hdl);
+ /*
+ * libscsi errno set by init.
+ */
+ return (NULL);
+ }
+ if (ep->lse_libversion != hp->lsh_version) {
+ (void) dlclose(dl_hdl);
+ (void) libscsi_error(hp, ESCSI_ENGINE_VER, "engine "
+ "%s version %u does not match library version %u",
+ engine_lib, ep->lse_libversion, hp->lsh_version);
+ return (NULL);
+ }
+
+ eip = libscsi_zalloc(hp, sizeof (libscsi_engine_impl_t));
+ if (eip == NULL) {
+ (void) dlclose(dl_hdl);
+ return (NULL);
+ }
+ eip->lsei_engine = ep;
+ eip->lsei_dl_hdl = dl_hdl;
+ eip->lsei_next = hp->lsh_engines;
+ hp->lsh_engines = eip;
+
+ return (ep);
+ }
+
+ if (dirs_tried == 0)
+ (void) libscsi_error(hp, ESCSI_ENGINE_BADPATH, "no valid "
+ "directories found in engine path %s", engine_path);
+
+ return (NULL);
+}
+
+static void
+scsi_parse_mtbf(const char *envvar, uint_t *intp)
+{
+ const char *strval;
+ int intval;
+
+ if ((strval = getenv(envvar)) != NULL &&
+ (intval = atoi(strval)) > 0) {
+ srand48(gethrtime());
+ *intp = intval;
+ }
+}
+
+libscsi_target_t *
+libscsi_open(libscsi_hdl_t *hp, const char *engine, const void *target)
+{
+ const libscsi_engine_t *ep;
+ libscsi_target_t *tp;
+ void *private;
+
+ if (engine == NULL) {
+ if ((engine = getenv("LIBSCSI_DEFAULT_ENGINE")) == NULL)
+ engine = LIBSCSI_DEFAULT_ENGINE;
+ }
+
+ if ((ep = get_engine(hp, engine)) == NULL)
+ return (NULL);
+
+ if ((tp = libscsi_zalloc(hp, sizeof (libscsi_target_t))) == NULL)
+ return (NULL);
+
+ if ((private = ep->lse_ops->lseo_open(hp, target)) == NULL) {
+ libscsi_free(hp, tp);
+ return (NULL);
+ }
+
+ scsi_parse_mtbf("LIBSCSI_MTBF_CDB", &tp->lst_mtbf_cdb);
+ scsi_parse_mtbf("LIBSCSI_MTBF_READ", &tp->lst_mtbf_read);
+ scsi_parse_mtbf("LIBSCSI_MTBF_WRITE", &tp->lst_mtbf_write);
+
+ tp->lst_hdl = hp;
+ tp->lst_engine = ep;
+ tp->lst_priv = private;
+
+ ++hp->lsh_targets;
+
+ if (libscsi_get_inquiry(hp, tp) != 0) {
+ libscsi_close(hp, tp);
+ return (NULL);
+ }
+
+ return (tp);
+}
+
+libscsi_hdl_t *
+libscsi_get_handle(libscsi_target_t *tp)
+{
+ return (tp->lst_hdl);
+}
+
+void
+libscsi_close(libscsi_hdl_t *hp, libscsi_target_t *tp)
+{
+ tp->lst_engine->lse_ops->lseo_close(hp, tp->lst_priv);
+ libscsi_free(hp, tp->lst_vendor);
+ libscsi_free(hp, tp->lst_product);
+ libscsi_free(hp, tp->lst_revision);
+ libscsi_free(hp, tp);
+ --hp->lsh_targets;
+}
+
+sam4_status_t
+libscsi_action_get_status(const libscsi_action_t *ap)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ return (aip->lsai_status);
+}
+
+/*
+ * Set the timeout in seconds for this action. If no timeout is specified
+ * or if the timeout is set to 0, an implementation-specific timeout will be
+ * used (which may vary based on the target, command or other variables).
+ * Not all engines support all timeout values. Setting the timeout to a value
+ * not supported by the engine will cause engine-defined behavior when the
+ * action is executed.
+ */
+void
+libscsi_action_set_timeout(libscsi_action_t *ap, uint32_t timeout)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+
+ aip->lsai_timeout = timeout;
+}
+
+/*
+ * Obtain the timeout setting for this action.
+ */
+uint32_t
+libscsi_action_get_timeout(const libscsi_action_t *ap)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ return (aip->lsai_timeout);
+}
+
+/*
+ * Returns the flags associated with this action. Never fails.
+ */
+uint_t
+libscsi_action_get_flags(const libscsi_action_t *ap)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ return (aip->lsai_flags);
+}
+
+/*
+ * Returns the address of the action's CDB. The CDB buffer is guaranteed to
+ * be large enough to hold the complete CDB for the command specified when the
+ * action was allocated. Therefore, changing the command/opcode portion of
+ * the CDB has undefined effects. The remainder of the CDB may be modified.
+ */
+uint8_t *
+libscsi_action_get_cdb(const libscsi_action_t *ap)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ return (aip->lsai_cdb);
+}
+
+/*
+ * Places the address of the action buffer in the location pointed to by bp,
+ * if bp is not NULL. If ap is not NULL, it will contain the allocated size
+ * of the buffer itself. If vp is not NULL, it will contain the number of
+ * bytes of valid data currently stored in the buffer.
+ *
+ * If the action has LIBSCSI_AF_WRITE set and it has not yet been executed
+ * successfully, the entire buffer is assumed to contain valid data.
+ *
+ * If the action has LIBSCSI_AF_READ set and it has not yet been executed
+ * successfully, the amount of valid data is 0.
+ *
+ * If both LIBSCSI_AF_READ and LIBSCSI_AF_WRITE are clear, this function
+ * fails with ESCSI_BADFLAGS to indicate that the action flags are
+ * incompatible with the action data buffer.
+ */
+int
+libscsi_action_get_buffer(const libscsi_action_t *ap, uint8_t **bp,
+ size_t *sp, size_t *vp)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ if ((aip->lsai_flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE)) == 0)
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
+ "data buffer not supported for actions with both "
+ "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE clear"));
+
+ if ((aip->lsai_flags & LIBSCSI_AF_WRITE) &&
+ aip->lsai_status == LIBSCSI_STATUS_INVALID) {
+ if (bp != NULL)
+ *bp = aip->lsai_data;
+ if (sp != NULL)
+ *sp = aip->lsai_data_alloc;
+ if (vp != NULL)
+ *vp = aip->lsai_data_alloc;
+
+ return (0);
+ }
+
+ if ((aip->lsai_flags & LIBSCSI_AF_READ) &&
+ aip->lsai_status != LIBSCSI_STATUS_INVALID) {
+ if (bp != NULL)
+ *bp = aip->lsai_data;
+ if (sp != NULL)
+ *sp = aip->lsai_data_alloc;
+ if (vp != NULL)
+ *vp = aip->lsai_data_len;
+
+ return (0);
+ }
+
+ if (aip->lsai_flags & LIBSCSI_AF_WRITE) {
+ if (bp != NULL)
+ *bp = NULL;
+ if (sp != NULL)
+ *sp = NULL;
+ if (vp != NULL)
+ *vp = 0;
+ } else {
+ if (bp != NULL)
+ *bp = aip->lsai_data;
+ if (sp != NULL)
+ *sp = aip->lsai_data_alloc;
+ if (vp != NULL)
+ *vp = 0;
+ }
+
+ return (0);
+}
+
+/*
+ * Obtain a pointer to the sense buffer for this action, if any, along with
+ * the size of the sense buffer and the amount of valid data it contains.
+ */
+int
+libscsi_action_get_sense(const libscsi_action_t *ap, uint8_t **bp,
+ size_t *sp, size_t *vp)
+{
+ const libscsi_action_impl_t *aip = (const libscsi_action_impl_t *)ap;
+
+ if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
+ "sense data unavailable: LIBSCSI_AF_RQSENSE is clear"));
+
+ if (vp != NULL) {
+ if (aip->lsai_status == LIBSCSI_STATUS_INVALID)
+ *vp = 0;
+ else
+ *vp = aip->lsai_sense_len;
+ }
+
+ if (bp != NULL) {
+ ASSERT(aip->lsai_sense_data != NULL);
+ *bp = aip->lsai_sense_data;
+ }
+
+ if (sp != NULL)
+ *sp = UINT8_MAX;
+
+ return (0);
+}
+
+/*
+ * Set the SCSI status of the action.
+ *
+ * Engines only.
+ */
+void
+libscsi_action_set_status(libscsi_action_t *ap, sam4_status_t status)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+
+ ASSERT(aip->lsai_status == LIBSCSI_STATUS_INVALID);
+
+ aip->lsai_status = status;
+}
+
+/*
+ * Set the length of valid data returned by a READ action. If the action is
+ * not a READ action, or the length exceeds the size of the buffer, an error
+ * results.
+ *
+ * Engines only.
+ */
+int
+libscsi_action_set_datalen(libscsi_action_t *ap, size_t len)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+
+ if ((aip->lsai_flags & LIBSCSI_AF_READ) == 0)
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
+ "data cannot be returned for actions with LIBSCSI_AF_READ "
+ "clear"));
+ if (len > aip->lsai_data_alloc)
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
+ "data length %lu exceeds allocated buffer capacity %lu",
+ (ulong_t)len, (ulong_t)aip->lsai_data_alloc));
+
+ ASSERT(aip->lsai_data_len == 0);
+ aip->lsai_data_len = len;
+
+ return (0);
+}
+
+/*
+ * Set the length of the valid sense data returned following the command, if
+ * LIBSCSI_AF_RQSENSE is set for this action. Otherwise, fail.
+ *
+ * Engines only.
+ */
+int
+libscsi_action_set_senselen(libscsi_action_t *ap, size_t len)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+
+ if (!(aip->lsai_flags & LIBSCSI_AF_RQSENSE))
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADFLAGS,
+ "sense data not supported: LIBSCSI_AF_RQSENSE is clear"));
+
+ if (len > UINT8_MAX)
+ return (libscsi_error(aip->lsai_hdl, ESCSI_BADLENGTH,
+ "sense length %lu exceeds allocated buffer capacity %lu",
+ (ulong_t)len, (ulong_t)UINT8_MAX));
+
+ ASSERT(aip->lsai_sense_len == 0);
+ aip->lsai_sense_len = len;
+
+ return (0);
+}
+
+/*
+ * Allocate an action object. The object will contain a CDB area sufficiently
+ * large to hold a CDB for the given command, and the CDB's opcode will be
+ * filled in. A pointer to this CDB, the contents of which may be modified by
+ * the caller, may be obtained by a subsequent call to libscsi_action_cdb().
+ *
+ * If flags includes LIBSCSI_AF_READ or LIBSCSI_AF_WRITE, buflen must be
+ * greater than zero. Otherwise, buflen must be 0 and buf must be NULL.
+ * If buflen is nonzero but buf is NULL, a suitably-sized buffer will be
+ * allocated; otherwise, the specified buffer will be used. In either case,
+ * a pointer to the buffer may be obtained via a subsequent call to
+ * libscsi_action_buffer().
+ *
+ * If flags includes LIBSCSI_AF_RQSENSE, a REQUEST SENSE command will be
+ * issued immediately following the termination of the specified command.
+ * A buffer will be allocated to receive this sense data. Following successful
+ * execution of the action, a pointer to this buffer and the length of
+ * valid sense data may be obtained by a call to libscsi_action_sense().
+ * If cmd is SPC3_CMD_REQUEST_SENSE, this flag must be clear.
+ */
+libscsi_action_t *
+libscsi_action_alloc(libscsi_hdl_t *hp, spc3_cmd_t cmd, uint_t flags,
+ void *buf, size_t buflen)
+{
+ libscsi_action_impl_t *aip;
+ size_t cdbsz, sz;
+ ptrdiff_t off;
+
+ /*
+ * If there's no buffer, it makes no sense to try to read or write
+ * data. Likewise, if we're neither reading nor writing data, we
+ * should not have a buffer. Both of these are programmer error.
+ */
+ if (buflen == 0 && (flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
+ (void) libscsi_error(hp, ESCSI_NEEDBUF, "a buffer is "
+ "required when reading or writing");
+ return (NULL);
+ }
+ if (buflen > 0 && !(flags & (LIBSCSI_AF_READ | LIBSCSI_AF_WRITE))) {
+ (void) libscsi_error(hp, ESCSI_BADFLAGS, "one of "
+ "LIBSCSI_AF_READ and LIBSCSI_AF_WRITE must be specified "
+ "in order to use a buffer");
+ return (NULL);
+ }
+ if (cmd == SPC3_CMD_REQUEST_SENSE && (flags & LIBSCSI_AF_RQSENSE)) {
+ (void) libscsi_error(hp, ESCSI_BADFLAGS, "request sense "
+ "flag not allowed for request sense command");
+ return (NULL);
+ }
+
+ if ((sz = cdbsz = libscsi_cmd_cdblen(hp, cmd)) == 0)
+ return (NULL);
+
+ /*
+ * If the caller has asked for a buffer but has not provided one, we
+ * will allocate it in our internal buffer along with the CDB and
+ * request sense space (if requested).
+ */
+ if (buf == NULL)
+ sz += buflen;
+
+ if (flags & LIBSCSI_AF_RQSENSE)
+ sz += UINT8_MAX;
+
+ sz += offsetof(libscsi_action_impl_t, lsai_buf[0]);
+
+ if ((aip = libscsi_zalloc(hp, sz)) == NULL)
+ return (NULL);
+
+ aip->lsai_hdl = hp;
+ aip->lsai_flags = flags;
+
+ off = 0;
+
+ aip->lsai_cdb = aip->lsai_buf + off;
+ aip->lsai_cdb_len = cdbsz;
+ off += cdbsz;
+ aip->lsai_cdb[0] = (uint8_t)cmd;
+
+ if (buflen > 0) {
+ if (buf != NULL) {
+ aip->lsai_data = buf;
+ } else {
+ aip->lsai_data = aip->lsai_buf + off;
+ off += buflen;
+ }
+ aip->lsai_data_alloc = buflen;
+ if (flags & LIBSCSI_AF_WRITE)
+ aip->lsai_data_len = buflen;
+ }
+
+ if (flags & LIBSCSI_AF_RQSENSE) {
+ aip->lsai_sense_data = aip->lsai_buf + off;
+ off += UINT8_MAX;
+ }
+
+ aip->lsai_status = LIBSCSI_STATUS_INVALID;
+
+ return ((libscsi_action_t *)aip);
+}
+
+void
+libscsi_action_free(libscsi_action_t *ap)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+
+ libscsi_free(aip->lsai_hdl, aip);
+}
+
+/*
+ * For testing purposes, we allow data to be corrupted via an environment
+ * variable setting. This helps ensure that higher level software can cope with
+ * arbitrarily broken targets. The mtbf value represents the number of bytes we
+ * will see, on average, in between each failure. Therefore, for each N bytes,
+ * we would expect to see (N / mtbf) bytes of corruption.
+ */
+static void
+scsi_inject_errors(void *data, size_t len, uint_t mtbf)
+{
+ char *buf = data;
+ double prob;
+ size_t index;
+
+ if (len == 0)
+ return;
+
+ prob = (double)len / mtbf;
+
+ while (prob > 1) {
+ index = lrand48() % len;
+ buf[index] = (lrand48() % 256);
+ prob -= 1;
+ }
+
+ if (drand48() <= prob) {
+ index = lrand48() % len;
+ buf[index] = (lrand48() % 256);
+ }
+}
+
+int
+libscsi_exec(libscsi_action_t *ap, libscsi_target_t *tp)
+{
+ libscsi_action_impl_t *aip = (libscsi_action_impl_t *)ap;
+ libscsi_hdl_t *hp = aip->lsai_hdl;
+ int ret;
+
+ if (tp->lst_mtbf_write != 0 &&
+ (aip->lsai_flags & LIBSCSI_AF_WRITE)) {
+ scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
+ tp->lst_mtbf_write);
+ }
+
+ if (tp->lst_mtbf_cdb != 0) {
+ scsi_inject_errors(aip->lsai_cdb, aip->lsai_cdb_len,
+ tp->lst_mtbf_cdb);
+ }
+
+ ret = tp->lst_engine->lse_ops->lseo_exec(hp, tp->lst_priv, ap);
+
+ if (ret == 0 && tp->lst_mtbf_read != 0 &&
+ (aip->lsai_flags & LIBSCSI_AF_READ)) {
+ scsi_inject_errors(aip->lsai_data, aip->lsai_data_len,
+ tp->lst_mtbf_read);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/lib/scsi/libscsi/common/scsi_status.c b/usr/src/lib/scsi/libscsi/common/scsi_status.c
new file mode 100644
index 0000000000..ab6b23b72b
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/scsi_status.c
@@ -0,0 +1,723 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/status.h>
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <scsi/libscsi.h>
+#include "libscsi_impl.h"
+
+typedef struct slist {
+ char *str;
+ int value;
+} slist_t;
+
+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 struct 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 }
+};
+
+static const char *
+find_string(slist_t *slist, int match_value)
+{
+ for (; slist->str != NULL; slist++) {
+ if (slist->value == match_value) {
+ return (slist->str);
+ }
+ }
+
+ return (NULL);
+}
+
+const char *
+libscsi_sense_key_name(uint64_t key)
+{
+ return (find_string(sensekey_strings, (int)key));
+}
+
+/*
+ * Given an asc (Additional Sense Code) and ascq (Additional Sense Code
+ * Qualifier), return a string describing the error information.
+ */
+const char *
+libscsi_sense_code_name(uint64_t asc, uint64_t ascq)
+{
+ 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++;
+ }
+
+ return (NULL);
+}
+
+/*
+ * 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, size_t len)
+{
+ diskaddr_t result;
+ uint8_t *descr_offset;
+ size_t 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), len);
+
+ /*
+ * 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);
+}
+
+int
+libscsi_action_parse_sense(const libscsi_action_t *ap, uint64_t *keyp,
+ uint64_t *ascp, uint64_t *ascqp, diskaddr_t *blkp)
+{
+ struct scsi_extended_sense *xsp;
+ struct scsi_descr_sense_hdr *sdsp;
+ size_t len;
+
+ if (libscsi_action_get_sense(ap, (uint8_t **)&xsp, NULL, &len) != 0)
+ return (-1);
+
+ sdsp = (struct scsi_descr_sense_hdr *)xsp;
+
+ if (keyp != NULL)
+ *keyp = (uint64_t)xsp->es_key;
+
+ switch (xsp->es_code) {
+ case CODE_FMT_DESCR_CURRENT:
+ case CODE_FMT_DESCR_DEFERRED:
+ if (blkp != NULL)
+ *blkp = (diskaddr_t)
+ scsi_extract_sense_info_descr(sdsp, len);
+ if (ascp != NULL)
+ *ascp = (uint64_t)sdsp->ds_add_code;
+ if (ascqp != NULL)
+ *ascqp = (uint64_t)sdsp->ds_qual_code;
+ break;
+ case CODE_FMT_FIXED_CURRENT:
+ case CODE_FMT_FIXED_DEFERRED:
+ default:
+ if (xsp->es_valid && blkp != NULL)
+ *blkp = (diskaddr_t)
+ ((xsp->es_info_1 << 24) | (xsp->es_info_2 << 16) |
+ (xsp->es_info_3 << 8) | xsp->es_info_4);
+ if (xsp->es_add_len >= 6) {
+ if (ascp != NULL)
+ *ascp = (uint64_t)xsp->es_add_code;
+ if (ascqp != NULL)
+ *ascqp = (uint64_t)xsp->es_qual_code;
+ }
+ break;
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/scsi/libscsi/common/scsi_subr.c b/usr/src/lib/scsi/libscsi/common/scsi_subr.c
new file mode 100644
index 0000000000..fb9a4eb650
--- /dev/null
+++ b/usr/src/lib/scsi/libscsi/common/scsi_subr.c
@@ -0,0 +1,351 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/spc3_types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <alloca.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include <scsi/libscsi.h>
+#include "libscsi_impl.h"
+
+int
+libscsi_assert(const char *expr, const char *file, int line)
+{
+ char *msg;
+ size_t len;
+
+ len = snprintf(NULL, 0,
+ "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
+
+ msg = alloca(len + 1);
+
+ (void) snprintf(msg, len + 1,
+ "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
+
+ (void) write(STDERR_FILENO, msg, strlen(msg));
+
+ abort();
+ _exit(1);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+int
+libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err)
+{
+ hp->lsh_errno = err;
+ hp->lsh_errmsg[0] = '\0';
+
+ return (-1);
+}
+
+/*
+ * Internal routine for setting both _ue_errno and _ue_errmsg. We save
+ * and restore the UNIX errno across this routing so the caller can use either
+ * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value
+ * changing.
+ */
+int
+libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt,
+ va_list ap)
+{
+ size_t n;
+ char *errmsg;
+
+ /*
+ * To allow the existing error message to itself be used in an error
+ * message, we put the new error message into a buffer on the stack,
+ * and then copy it into lsh_errmsg. We also need to set the errno,
+ * but because the call to libscsi_set_errno() is destructive to
+ * lsh_errmsg, we do this after we print into our temporary buffer
+ * (in case _libscsi_errmsg is part of the error message) and before we
+ * copy the temporary buffer on to _libscsi_errmsg (to prevent our new
+ * message from being nuked by the call to libscsi_set_errno()).
+ */
+ errmsg = alloca(sizeof (hp->lsh_errmsg));
+ (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap);
+ (void) libscsi_set_errno(hp, err);
+
+ n = strlen(errmsg);
+
+ if (n != 0 && errmsg[n - 1] == '\n')
+ errmsg[n - 1] = '\0';
+
+ bcopy(errmsg, hp->lsh_errmsg, n + 1);
+
+ return (-1);
+}
+
+/*PRINTFLIKE3*/
+int
+libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt == NULL)
+ return (libscsi_set_errno(hp, err));
+
+ va_start(ap, fmt);
+ err = libscsi_verror(hp, err, fmt, ap);
+ va_end(ap);
+
+ return (err);
+}
+
+libscsi_errno_t
+libscsi_errno(libscsi_hdl_t *hp)
+{
+ return (hp->lsh_errno);
+}
+
+const char *
+libscsi_errmsg(libscsi_hdl_t *hp)
+{
+ if (hp->lsh_errmsg[0] == '\0')
+ (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno),
+ sizeof (hp->lsh_errmsg));
+
+ return (hp->lsh_errmsg);
+}
+
+void *
+libscsi_alloc(libscsi_hdl_t *hp, size_t size)
+{
+ void *mem;
+
+ if (size == 0) {
+ (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH);
+ return (NULL);
+ }
+
+ if ((mem = malloc(size)) == NULL)
+ (void) libscsi_set_errno(hp, ESCSI_NOMEM);
+
+ return (mem);
+}
+
+void *
+libscsi_zalloc(libscsi_hdl_t *hp, size_t size)
+{
+ void *mem;
+
+ if ((mem = libscsi_alloc(hp, size)) == NULL)
+ return (NULL);
+
+ bzero(mem, size);
+
+ return (mem);
+}
+
+char *
+libscsi_strdup(libscsi_hdl_t *hp, const char *str)
+{
+ size_t len = strlen(str);
+ char *dup = libscsi_alloc(hp, len + 1);
+
+ if (dup == NULL)
+ return (NULL);
+
+ return (strcpy(dup, str));
+}
+
+/*ARGSUSED*/
+void
+libscsi_free(libscsi_hdl_t *hp, void *ptr)
+{
+ free(ptr);
+}
+
+libscsi_hdl_t *
+libscsi_init(uint_t version, libscsi_errno_t *errp)
+{
+ libscsi_hdl_t *hp;
+
+ if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) {
+ if (errp != NULL)
+ *errp = ESCSI_NOMEM;
+ return (NULL);
+ }
+
+ bzero(hp, sizeof (libscsi_hdl_t));
+ hp->lsh_version = version;
+
+ return (hp);
+}
+
+void
+libscsi_fini(libscsi_hdl_t *hp)
+{
+ libscsi_engine_impl_t *eip, *neip;
+
+ if (hp == NULL)
+ return;
+
+ ASSERT(hp->lsh_targets == 0);
+
+ for (eip = hp->lsh_engines; eip != NULL; eip = neip) {
+ neip = eip->lsei_next;
+ (void) dlclose(eip->lsei_dl_hdl);
+ libscsi_free(hp, eip);
+ }
+
+ free(hp);
+}
+
+size_t
+libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd)
+{
+ size_t sz;
+
+ switch (CDB_GROUPID(cmd)) {
+ case CDB_GROUPID_0:
+ sz = CDB_GROUP0;
+ break;
+ case CDB_GROUPID_1:
+ sz = CDB_GROUP1;
+ break;
+ case CDB_GROUPID_2:
+ sz = CDB_GROUP2;
+ break;
+ case CDB_GROUPID_3:
+ sz = CDB_GROUP3;
+ break;
+ case CDB_GROUPID_4:
+ sz = CDB_GROUP4;
+ break;
+ case CDB_GROUPID_5:
+ sz = CDB_GROUP5;
+ break;
+ case CDB_GROUPID_6:
+ sz = CDB_GROUP6;
+ break;
+ case CDB_GROUPID_7:
+ sz = CDB_GROUP7;
+ break;
+ default:
+ sz = 0;
+ }
+
+ if (sz == 0)
+ (void) libscsi_error(hp, ESCSI_BADCMD,
+ "unknown or unsupported command %u", cmd);
+
+ return (sz);
+}
+
+static char *
+libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len)
+{
+ char *buf;
+
+ buf = alloca(len + 1);
+ bcopy(raw, buf, len);
+
+ for (; len > 0; len--) {
+ if (buf[len - 1] != ' ')
+ break;
+ }
+
+ buf[len] = '\0';
+
+ return (libscsi_strdup(hp, buf));
+}
+
+/*
+ * As part of basic initialization, we always retrieve the INQUIRY information
+ * to have the vendor/product/revision information available for all consumers.
+ */
+int
+libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp)
+{
+ libscsi_action_t *ap;
+ spc3_inquiry_cdb_t *cp;
+ spc3_inquiry_data_t data;
+ size_t len;
+
+ if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
+ LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
+ sizeof (data))) == NULL)
+ return (-1);
+
+ cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
+
+ SCSI_WRITE16(&cp->ic_allocation_length, sizeof (data));
+
+ if (libscsi_exec(ap, tp) != 0 ||
+ libscsi_action_get_status(ap) != 0) {
+ libscsi_action_free(ap);
+ return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
+ }
+
+ (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
+ libscsi_action_free(ap);
+
+ if (len < offsetof(spc3_inquiry_data_t, id_vs_36))
+ return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
+
+ if ((tp->lst_vendor = libscsi_process_inquiry_string(hp,
+ data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL ||
+ (tp->lst_product = libscsi_process_inquiry_string(hp,
+ data.id_product_id, sizeof (data.id_product_id))) == NULL ||
+ (tp->lst_revision = libscsi_process_inquiry_string(hp,
+ data.id_product_revision,
+ sizeof (data.id_product_revision))) == NULL) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+const char *
+libscsi_vendor(libscsi_target_t *tp)
+{
+ return (tp->lst_vendor);
+}
+
+const char *
+libscsi_product(libscsi_target_t *tp)
+{
+ return (tp->lst_product);
+}
+
+const char *
+libscsi_revision(libscsi_target_t *tp)
+{
+ return (tp->lst_revision);
+}