diff options
Diffstat (limited to 'usr/src/lib/scsi/libscsi/common')
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/libscsi.h | 182 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/libscsi_impl.h | 100 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/mkerrno.sh | 88 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/scsi_engine.c | 621 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/scsi_status.c | 723 | ||||
-rw-r--r-- | usr/src/lib/scsi/libscsi/common/scsi_subr.c | 351 |
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); +} |