1275c9da8Seschrock /* 2275c9da8Seschrock * CDDL HEADER START 3275c9da8Seschrock * 4275c9da8Seschrock * The contents of this file are subject to the terms of the 5275c9da8Seschrock * Common Development and Distribution License (the "License"). 6275c9da8Seschrock * You may not use this file except in compliance with the License. 7275c9da8Seschrock * 8275c9da8Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9275c9da8Seschrock * or http://www.opensolaris.org/os/licensing. 10275c9da8Seschrock * See the License for the specific language governing permissions 11275c9da8Seschrock * and limitations under the License. 12275c9da8Seschrock * 13275c9da8Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14275c9da8Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15275c9da8Seschrock * If applicable, add the following below this CDDL HEADER, with the 16275c9da8Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17275c9da8Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18275c9da8Seschrock * 19275c9da8Seschrock * CDDL HEADER END 20275c9da8Seschrock */ 21275c9da8Seschrock 22275c9da8Seschrock /* 23*ac88567aSHyon Kim * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24275c9da8Seschrock */ 25275c9da8Seschrock 26275c9da8Seschrock #include <sys/types.h> 27275c9da8Seschrock #include <sys/scsi/generic/commands.h> 28275c9da8Seschrock #include <sys/scsi/impl/spc3_types.h> 29275c9da8Seschrock 30275c9da8Seschrock #include <stddef.h> 31275c9da8Seschrock #include <stdlib.h> 32275c9da8Seschrock #include <string.h> 33275c9da8Seschrock #include <strings.h> 34275c9da8Seschrock #include <alloca.h> 35275c9da8Seschrock #include <stdio.h> 36275c9da8Seschrock #include <unistd.h> 37275c9da8Seschrock #include <dlfcn.h> 38275c9da8Seschrock 39275c9da8Seschrock #include <scsi/libscsi.h> 40275c9da8Seschrock #include "libscsi_impl.h" 41275c9da8Seschrock 42275c9da8Seschrock int 43275c9da8Seschrock libscsi_assert(const char *expr, const char *file, int line) 44275c9da8Seschrock { 45275c9da8Seschrock char *msg; 46275c9da8Seschrock size_t len; 47275c9da8Seschrock 48275c9da8Seschrock len = snprintf(NULL, 0, 49275c9da8Seschrock "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr); 50275c9da8Seschrock 51275c9da8Seschrock msg = alloca(len + 1); 52275c9da8Seschrock 53275c9da8Seschrock (void) snprintf(msg, len + 1, 54275c9da8Seschrock "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr); 55275c9da8Seschrock 56275c9da8Seschrock (void) write(STDERR_FILENO, msg, strlen(msg)); 57275c9da8Seschrock 58275c9da8Seschrock abort(); 59275c9da8Seschrock _exit(1); 60275c9da8Seschrock 61275c9da8Seschrock /*NOTREACHED*/ 62275c9da8Seschrock return (0); 63275c9da8Seschrock } 64275c9da8Seschrock 65275c9da8Seschrock int 66275c9da8Seschrock libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err) 67275c9da8Seschrock { 68275c9da8Seschrock hp->lsh_errno = err; 69275c9da8Seschrock hp->lsh_errmsg[0] = '\0'; 70275c9da8Seschrock 71275c9da8Seschrock return (-1); 72275c9da8Seschrock } 73275c9da8Seschrock 74275c9da8Seschrock /* 75275c9da8Seschrock * Internal routine for setting both _ue_errno and _ue_errmsg. We save 76275c9da8Seschrock * and restore the UNIX errno across this routing so the caller can use either 77275c9da8Seschrock * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value 78275c9da8Seschrock * changing. 79275c9da8Seschrock */ 80275c9da8Seschrock int 81275c9da8Seschrock libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, 82275c9da8Seschrock va_list ap) 83275c9da8Seschrock { 84275c9da8Seschrock size_t n; 85275c9da8Seschrock char *errmsg; 86275c9da8Seschrock 87275c9da8Seschrock /* 88275c9da8Seschrock * To allow the existing error message to itself be used in an error 89275c9da8Seschrock * message, we put the new error message into a buffer on the stack, 90275c9da8Seschrock * and then copy it into lsh_errmsg. We also need to set the errno, 91275c9da8Seschrock * but because the call to libscsi_set_errno() is destructive to 92275c9da8Seschrock * lsh_errmsg, we do this after we print into our temporary buffer 93275c9da8Seschrock * (in case _libscsi_errmsg is part of the error message) and before we 94275c9da8Seschrock * copy the temporary buffer on to _libscsi_errmsg (to prevent our new 95275c9da8Seschrock * message from being nuked by the call to libscsi_set_errno()). 96275c9da8Seschrock */ 97275c9da8Seschrock errmsg = alloca(sizeof (hp->lsh_errmsg)); 98275c9da8Seschrock (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap); 99275c9da8Seschrock (void) libscsi_set_errno(hp, err); 100275c9da8Seschrock 101275c9da8Seschrock n = strlen(errmsg); 102275c9da8Seschrock 103275c9da8Seschrock if (n != 0 && errmsg[n - 1] == '\n') 104275c9da8Seschrock errmsg[n - 1] = '\0'; 105275c9da8Seschrock 106275c9da8Seschrock bcopy(errmsg, hp->lsh_errmsg, n + 1); 107275c9da8Seschrock 108275c9da8Seschrock return (-1); 109275c9da8Seschrock } 110275c9da8Seschrock 111275c9da8Seschrock /*PRINTFLIKE3*/ 112275c9da8Seschrock int 113275c9da8Seschrock libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...) 114275c9da8Seschrock { 115275c9da8Seschrock va_list ap; 116275c9da8Seschrock 117275c9da8Seschrock if (fmt == NULL) 118275c9da8Seschrock return (libscsi_set_errno(hp, err)); 119275c9da8Seschrock 120275c9da8Seschrock va_start(ap, fmt); 121275c9da8Seschrock err = libscsi_verror(hp, err, fmt, ap); 122275c9da8Seschrock va_end(ap); 123275c9da8Seschrock 124275c9da8Seschrock return (err); 125275c9da8Seschrock } 126275c9da8Seschrock 127275c9da8Seschrock libscsi_errno_t 128275c9da8Seschrock libscsi_errno(libscsi_hdl_t *hp) 129275c9da8Seschrock { 130275c9da8Seschrock return (hp->lsh_errno); 131275c9da8Seschrock } 132275c9da8Seschrock 133275c9da8Seschrock const char * 134275c9da8Seschrock libscsi_errmsg(libscsi_hdl_t *hp) 135275c9da8Seschrock { 136275c9da8Seschrock if (hp->lsh_errmsg[0] == '\0') 137275c9da8Seschrock (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno), 138275c9da8Seschrock sizeof (hp->lsh_errmsg)); 139275c9da8Seschrock 140275c9da8Seschrock return (hp->lsh_errmsg); 141275c9da8Seschrock } 142275c9da8Seschrock 143275c9da8Seschrock void * 144275c9da8Seschrock libscsi_alloc(libscsi_hdl_t *hp, size_t size) 145275c9da8Seschrock { 146275c9da8Seschrock void *mem; 147275c9da8Seschrock 148275c9da8Seschrock if (size == 0) { 149275c9da8Seschrock (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH); 150275c9da8Seschrock return (NULL); 151275c9da8Seschrock } 152275c9da8Seschrock 153275c9da8Seschrock if ((mem = malloc(size)) == NULL) 154275c9da8Seschrock (void) libscsi_set_errno(hp, ESCSI_NOMEM); 155275c9da8Seschrock 156275c9da8Seschrock return (mem); 157275c9da8Seschrock } 158275c9da8Seschrock 159275c9da8Seschrock void * 160275c9da8Seschrock libscsi_zalloc(libscsi_hdl_t *hp, size_t size) 161275c9da8Seschrock { 162275c9da8Seschrock void *mem; 163275c9da8Seschrock 164275c9da8Seschrock if ((mem = libscsi_alloc(hp, size)) == NULL) 165275c9da8Seschrock return (NULL); 166275c9da8Seschrock 167275c9da8Seschrock bzero(mem, size); 168275c9da8Seschrock 169275c9da8Seschrock return (mem); 170275c9da8Seschrock } 171275c9da8Seschrock 172275c9da8Seschrock char * 173275c9da8Seschrock libscsi_strdup(libscsi_hdl_t *hp, const char *str) 174275c9da8Seschrock { 175275c9da8Seschrock size_t len = strlen(str); 176275c9da8Seschrock char *dup = libscsi_alloc(hp, len + 1); 177275c9da8Seschrock 178275c9da8Seschrock if (dup == NULL) 179275c9da8Seschrock return (NULL); 180275c9da8Seschrock 181275c9da8Seschrock return (strcpy(dup, str)); 182275c9da8Seschrock } 183275c9da8Seschrock 184275c9da8Seschrock /*ARGSUSED*/ 185275c9da8Seschrock void 186275c9da8Seschrock libscsi_free(libscsi_hdl_t *hp, void *ptr) 187275c9da8Seschrock { 188275c9da8Seschrock free(ptr); 189275c9da8Seschrock } 190275c9da8Seschrock 191275c9da8Seschrock libscsi_hdl_t * 192275c9da8Seschrock libscsi_init(uint_t version, libscsi_errno_t *errp) 193275c9da8Seschrock { 194275c9da8Seschrock libscsi_hdl_t *hp; 195275c9da8Seschrock 196275c9da8Seschrock if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) { 197275c9da8Seschrock if (errp != NULL) 198275c9da8Seschrock *errp = ESCSI_NOMEM; 199275c9da8Seschrock return (NULL); 200275c9da8Seschrock } 201275c9da8Seschrock 202275c9da8Seschrock bzero(hp, sizeof (libscsi_hdl_t)); 203275c9da8Seschrock hp->lsh_version = version; 204275c9da8Seschrock 205275c9da8Seschrock return (hp); 206275c9da8Seschrock } 207275c9da8Seschrock 208275c9da8Seschrock void 209275c9da8Seschrock libscsi_fini(libscsi_hdl_t *hp) 210275c9da8Seschrock { 211275c9da8Seschrock libscsi_engine_impl_t *eip, *neip; 212275c9da8Seschrock 213275c9da8Seschrock if (hp == NULL) 214275c9da8Seschrock return; 215275c9da8Seschrock 216275c9da8Seschrock ASSERT(hp->lsh_targets == 0); 217275c9da8Seschrock 218275c9da8Seschrock for (eip = hp->lsh_engines; eip != NULL; eip = neip) { 219275c9da8Seschrock neip = eip->lsei_next; 220275c9da8Seschrock (void) dlclose(eip->lsei_dl_hdl); 221275c9da8Seschrock libscsi_free(hp, eip); 222275c9da8Seschrock } 223275c9da8Seschrock 224275c9da8Seschrock free(hp); 225275c9da8Seschrock } 226275c9da8Seschrock 227275c9da8Seschrock size_t 228275c9da8Seschrock libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd) 229275c9da8Seschrock { 230275c9da8Seschrock size_t sz; 231275c9da8Seschrock 232275c9da8Seschrock switch (CDB_GROUPID(cmd)) { 233275c9da8Seschrock case CDB_GROUPID_0: 234275c9da8Seschrock sz = CDB_GROUP0; 235275c9da8Seschrock break; 236275c9da8Seschrock case CDB_GROUPID_1: 237275c9da8Seschrock sz = CDB_GROUP1; 238275c9da8Seschrock break; 239275c9da8Seschrock case CDB_GROUPID_2: 240275c9da8Seschrock sz = CDB_GROUP2; 241275c9da8Seschrock break; 242275c9da8Seschrock case CDB_GROUPID_3: 243275c9da8Seschrock sz = CDB_GROUP3; 244275c9da8Seschrock break; 245275c9da8Seschrock case CDB_GROUPID_4: 246275c9da8Seschrock sz = CDB_GROUP4; 247275c9da8Seschrock break; 248275c9da8Seschrock case CDB_GROUPID_5: 249275c9da8Seschrock sz = CDB_GROUP5; 250275c9da8Seschrock break; 251275c9da8Seschrock case CDB_GROUPID_6: 252275c9da8Seschrock sz = CDB_GROUP6; 253275c9da8Seschrock break; 254275c9da8Seschrock case CDB_GROUPID_7: 255275c9da8Seschrock sz = CDB_GROUP7; 256275c9da8Seschrock break; 257275c9da8Seschrock default: 258275c9da8Seschrock sz = 0; 259275c9da8Seschrock } 260275c9da8Seschrock 261275c9da8Seschrock if (sz == 0) 262275c9da8Seschrock (void) libscsi_error(hp, ESCSI_BADCMD, 263275c9da8Seschrock "unknown or unsupported command %u", cmd); 264275c9da8Seschrock 265275c9da8Seschrock return (sz); 266275c9da8Seschrock } 267275c9da8Seschrock 268275c9da8Seschrock static char * 269275c9da8Seschrock libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len) 270275c9da8Seschrock { 271275c9da8Seschrock char *buf; 272275c9da8Seschrock 273275c9da8Seschrock buf = alloca(len + 1); 274275c9da8Seschrock bcopy(raw, buf, len); 275275c9da8Seschrock 276275c9da8Seschrock for (; len > 0; len--) { 277275c9da8Seschrock if (buf[len - 1] != ' ') 278275c9da8Seschrock break; 279275c9da8Seschrock } 280275c9da8Seschrock 281275c9da8Seschrock buf[len] = '\0'; 282275c9da8Seschrock 283275c9da8Seschrock return (libscsi_strdup(hp, buf)); 284275c9da8Seschrock } 285275c9da8Seschrock 286275c9da8Seschrock /* 287275c9da8Seschrock * As part of basic initialization, we always retrieve the INQUIRY information 288275c9da8Seschrock * to have the vendor/product/revision information available for all consumers. 289275c9da8Seschrock */ 290275c9da8Seschrock int 291275c9da8Seschrock libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp) 292275c9da8Seschrock { 293275c9da8Seschrock libscsi_action_t *ap; 294275c9da8Seschrock spc3_inquiry_cdb_t *cp; 295275c9da8Seschrock spc3_inquiry_data_t data; 296275c9da8Seschrock size_t len; 297275c9da8Seschrock 298275c9da8Seschrock if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY, 299275c9da8Seschrock LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data, 300*ac88567aSHyon Kim offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL) 301275c9da8Seschrock return (-1); 302275c9da8Seschrock 303275c9da8Seschrock cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap); 304275c9da8Seschrock 305*ac88567aSHyon Kim SCSI_WRITE16(&cp->ic_allocation_length, 306*ac88567aSHyon Kim offsetof(spc3_inquiry_data_t, id_vs_36[0])); 307275c9da8Seschrock 308275c9da8Seschrock if (libscsi_exec(ap, tp) != 0 || 309275c9da8Seschrock libscsi_action_get_status(ap) != 0) { 310275c9da8Seschrock libscsi_action_free(ap); 311275c9da8Seschrock return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED)); 312275c9da8Seschrock } 313275c9da8Seschrock 314275c9da8Seschrock (void) libscsi_action_get_buffer(ap, NULL, NULL, &len); 315275c9da8Seschrock libscsi_action_free(ap); 316275c9da8Seschrock 317275c9da8Seschrock if (len < offsetof(spc3_inquiry_data_t, id_vs_36)) 318275c9da8Seschrock return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED)); 319275c9da8Seschrock 320275c9da8Seschrock if ((tp->lst_vendor = libscsi_process_inquiry_string(hp, 321275c9da8Seschrock data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL || 322275c9da8Seschrock (tp->lst_product = libscsi_process_inquiry_string(hp, 323275c9da8Seschrock data.id_product_id, sizeof (data.id_product_id))) == NULL || 324275c9da8Seschrock (tp->lst_revision = libscsi_process_inquiry_string(hp, 325275c9da8Seschrock data.id_product_revision, 326275c9da8Seschrock sizeof (data.id_product_revision))) == NULL) { 327275c9da8Seschrock return (-1); 328275c9da8Seschrock } 329275c9da8Seschrock 330275c9da8Seschrock return (0); 331275c9da8Seschrock } 332275c9da8Seschrock 333275c9da8Seschrock const char * 334275c9da8Seschrock libscsi_vendor(libscsi_target_t *tp) 335275c9da8Seschrock { 336275c9da8Seschrock return (tp->lst_vendor); 337275c9da8Seschrock } 338275c9da8Seschrock 339275c9da8Seschrock const char * 340275c9da8Seschrock libscsi_product(libscsi_target_t *tp) 341275c9da8Seschrock { 342275c9da8Seschrock return (tp->lst_product); 343275c9da8Seschrock } 344275c9da8Seschrock 345275c9da8Seschrock const char * 346275c9da8Seschrock libscsi_revision(libscsi_target_t *tp) 347275c9da8Seschrock { 348275c9da8Seschrock return (tp->lst_revision); 349275c9da8Seschrock } 350