1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/scsi/generic/commands.h>
  29 #include <sys/scsi/impl/spc3_types.h>
  30 
  31 #include <stddef.h>
  32 #include <stdlib.h>
  33 #include <string.h>
  34 #include <strings.h>
  35 #include <alloca.h>
  36 #include <stdio.h>
  37 #include <unistd.h>
  38 #include <dlfcn.h>
  39 
  40 #include <scsi/libscsi.h>
  41 #include <sys/byteorder.h>
  42 #include "libscsi_impl.h"
  43 
  44 int
  45 libscsi_assert(const char *expr, const char *file, int line)
  46 {
  47         char *msg;
  48         size_t len;
  49 
  50         len = snprintf(NULL, 0,
  51             "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
  52 
  53         msg = alloca(len + 1);
  54 
  55         (void) snprintf(msg, len + 1,
  56             "ABORT: \"%s\", line %d: assertion failed: %s\n", file, line, expr);
  57 
  58         (void) write(STDERR_FILENO, msg, strlen(msg));
  59 
  60         abort();
  61         _exit(1);
  62 
  63         /*NOTREACHED*/
  64         return (0);
  65 }
  66 
  67 int
  68 libscsi_set_errno(libscsi_hdl_t *hp, libscsi_errno_t err)
  69 {
  70         hp->lsh_errno = err;
  71         hp->lsh_errmsg[0] = '\0';
  72 
  73         return (-1);
  74 }
  75 
  76 /*
  77  * Internal routine for setting both _ue_errno and _ue_errmsg.  We save
  78  * and restore the UNIX errno across this routing so the caller can use either
  79  * libscsi_set_errno(), libscsi_error(), or libscsi_verror() without this value
  80  * changing.
  81  */
  82 int
  83 libscsi_verror(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt,
  84     va_list ap)
  85 {
  86         size_t n;
  87         char *errmsg;
  88 
  89         /*
  90          * To allow the existing error message to itself be used in an error
  91          * message, we put the new error message into a buffer on the stack,
  92          * and then copy it into lsh_errmsg.  We also need to set the errno,
  93          * but because the call to libscsi_set_errno() is destructive to
  94          * lsh_errmsg, we do this after we print into our temporary buffer
  95          * (in case _libscsi_errmsg is part of the error message) and before we
  96          * copy the temporary buffer on to _libscsi_errmsg (to prevent our new
  97          * message from being nuked by the call to libscsi_set_errno()).
  98          */
  99         errmsg = alloca(sizeof (hp->lsh_errmsg));
 100         (void) vsnprintf(errmsg, sizeof (hp->lsh_errmsg), fmt, ap);
 101         (void) libscsi_set_errno(hp, err);
 102 
 103         n = strlen(errmsg);
 104 
 105         if (n != 0 && errmsg[n - 1] == '\n')
 106                 errmsg[n - 1] = '\0';
 107 
 108         bcopy(errmsg, hp->lsh_errmsg, n + 1);
 109 
 110         return (-1);
 111 }
 112 
 113 /*PRINTFLIKE3*/
 114 int
 115 libscsi_error(libscsi_hdl_t *hp, libscsi_errno_t err, const char *fmt, ...)
 116 {
 117         va_list ap;
 118 
 119         if (fmt == NULL)
 120                 return (libscsi_set_errno(hp, err));
 121 
 122         va_start(ap, fmt);
 123         err = libscsi_verror(hp, err, fmt, ap);
 124         va_end(ap);
 125 
 126         return (err);
 127 }
 128 
 129 libscsi_errno_t
 130 libscsi_errno(libscsi_hdl_t *hp)
 131 {
 132         return (hp->lsh_errno);
 133 }
 134 
 135 const char *
 136 libscsi_errmsg(libscsi_hdl_t *hp)
 137 {
 138         if (hp->lsh_errmsg[0] == '\0')
 139                 (void) strlcpy(hp->lsh_errmsg, libscsi_strerror(hp->lsh_errno),
 140                     sizeof (hp->lsh_errmsg));
 141 
 142         return (hp->lsh_errmsg);
 143 }
 144 
 145 void *
 146 libscsi_alloc(libscsi_hdl_t *hp, size_t size)
 147 {
 148         void *mem;
 149 
 150         if (size == 0) {
 151                 (void) libscsi_set_errno(hp, ESCSI_ZERO_LENGTH);
 152                 return (NULL);
 153         }
 154 
 155         if ((mem = malloc(size)) == NULL)
 156                 (void) libscsi_set_errno(hp, ESCSI_NOMEM);
 157 
 158         return (mem);
 159 }
 160 
 161 void *
 162 libscsi_zalloc(libscsi_hdl_t *hp, size_t size)
 163 {
 164         void *mem;
 165 
 166         if ((mem = libscsi_alloc(hp, size)) == NULL)
 167                 return (NULL);
 168 
 169         bzero(mem, size);
 170 
 171         return (mem);
 172 }
 173 
 174 char *
 175 libscsi_strdup(libscsi_hdl_t *hp, const char *str)
 176 {
 177         size_t len = strlen(str);
 178         char *dup = libscsi_alloc(hp, len + 1);
 179 
 180         if (dup == NULL)
 181                 return (NULL);
 182 
 183         return (strcpy(dup, str));
 184 }
 185 
 186 /*ARGSUSED*/
 187 void
 188 libscsi_free(libscsi_hdl_t *hp, void *ptr)
 189 {
 190         free(ptr);
 191 }
 192 
 193 libscsi_hdl_t *
 194 libscsi_init(uint_t version, libscsi_errno_t *errp)
 195 {
 196         libscsi_hdl_t *hp;
 197 
 198         if ((hp = malloc(sizeof (libscsi_hdl_t))) == NULL) {
 199                 if (errp != NULL)
 200                         *errp = ESCSI_NOMEM;
 201                 return (NULL);
 202         }
 203 
 204         bzero(hp, sizeof (libscsi_hdl_t));
 205         hp->lsh_version = version;
 206 
 207         return (hp);
 208 }
 209 
 210 void
 211 libscsi_fini(libscsi_hdl_t *hp)
 212 {
 213         libscsi_engine_impl_t *eip, *neip;
 214 
 215         if (hp == NULL)
 216                 return;
 217 
 218         ASSERT(hp->lsh_targets == 0);
 219 
 220         for (eip = hp->lsh_engines; eip != NULL; eip = neip) {
 221                 neip = eip->lsei_next;
 222                 (void) dlclose(eip->lsei_dl_hdl);
 223                 libscsi_free(hp, eip);
 224         }
 225 
 226         free(hp);
 227 }
 228 
 229 size_t
 230 libscsi_cmd_cdblen(libscsi_hdl_t *hp, uint8_t cmd)
 231 {
 232         size_t sz;
 233 
 234         switch (CDB_GROUPID(cmd)) {
 235         case CDB_GROUPID_0:
 236                 sz = CDB_GROUP0;
 237                 break;
 238         case CDB_GROUPID_1:
 239                 sz = CDB_GROUP1;
 240                 break;
 241         case CDB_GROUPID_2:
 242                 sz = CDB_GROUP2;
 243                 break;
 244         case CDB_GROUPID_3:
 245                 sz = CDB_GROUP3;
 246                 break;
 247         case CDB_GROUPID_4:
 248                 sz = CDB_GROUP4;
 249                 break;
 250         case CDB_GROUPID_5:
 251                 sz = CDB_GROUP5;
 252                 break;
 253         case CDB_GROUPID_6:
 254                 sz = CDB_GROUP6;
 255                 break;
 256         case CDB_GROUPID_7:
 257                 sz = CDB_GROUP7;
 258                 break;
 259         default:
 260                 sz = 0;
 261         }
 262 
 263         if (sz == 0)
 264                 (void) libscsi_error(hp, ESCSI_BADCMD,
 265                     "unknown or unsupported command %u", cmd);
 266 
 267         return (sz);
 268 }
 269 
 270 static char *
 271 libscsi_process_inquiry_string(libscsi_hdl_t *hp, const char *raw, size_t len)
 272 {
 273         char *buf;
 274 
 275         buf = alloca(len + 1);
 276         bcopy(raw, buf, len);
 277 
 278         for (; len > 0; len--) {
 279                 if (buf[len - 1] != ' ')
 280                         break;
 281         }
 282 
 283         buf[len] = '\0';
 284 
 285         return (libscsi_strdup(hp, buf));
 286 }
 287 
 288 /*
 289  * As part of basic initialization, we always retrieve the INQUIRY information
 290  * to have the vendor/product/revision information available for all consumers.
 291  */
 292 int
 293 libscsi_get_inquiry(libscsi_hdl_t *hp, libscsi_target_t *tp)
 294 {
 295         libscsi_action_t *ap;
 296         spc3_inquiry_cdb_t *cp;
 297         spc3_inquiry_data_t data;
 298         size_t len;
 299 
 300         if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
 301             LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
 302             offsetof(spc3_inquiry_data_t, id_vs_36[0]))) == NULL)
 303                 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
 304 
 305         cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
 306 
 307         SCSI_WRITE16(&cp->ic_allocation_length,
 308             offsetof(spc3_inquiry_data_t, id_vs_36[0]));
 309 
 310         if (libscsi_exec(ap, tp) != 0 ||
 311             libscsi_action_get_status(ap) != 0) {
 312                 libscsi_action_free(ap);
 313                 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
 314         }
 315 
 316         (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
 317         libscsi_action_free(ap);
 318 
 319         if (len < offsetof(spc3_inquiry_data_t, id_vs_36))
 320                 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
 321 
 322         if ((tp->lst_vendor = libscsi_process_inquiry_string(hp,
 323             data.id_vendor_id, sizeof (data.id_vendor_id))) == NULL ||
 324             (tp->lst_product = libscsi_process_inquiry_string(hp,
 325             data.id_product_id, sizeof (data.id_product_id))) == NULL ||
 326             (tp->lst_revision = libscsi_process_inquiry_string(hp,
 327             data.id_product_revision,
 328             sizeof (data.id_product_revision))) == NULL) {
 329                 return (libscsi_set_errno(hp, ESCSI_INQUIRY_FAILED));
 330         }
 331 
 332         return (0);
 333 }
 334 
 335 /*
 336  * A designation descriptor consists of the header followed by data.
 337  * When given a pointer to the header to get to next descriptor we need to add
 338  * to hdr pointer the number of data bytes plus size of the header itself.
 339  */
 340 #define NEXT_DESC(hdr, data_len, hdr_type) ((hdr_type *)((((uint8_t *)hdr) + \
 341     data_len + sizeof (hdr_type))))
 342 
 343 int
 344 libscsi_get_inquiry_dev_id(libscsi_hdl_t *hp, libscsi_target_t *tp)
 345 {
 346         libscsi_action_t *ap;
 347         spc3_inquiry_cdb_t *cp;
 348         spc3_dev_id_vpd_page_impl_t data;
 349         size_t len;
 350         int des_bytes_left;
 351         struct vpd_desc *cur_desc;
 352         char lid[17];
 353 
 354         if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
 355             LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
 356             sizeof (spc3_dev_id_vpd_page_impl_t))) == NULL)
 357                 return (libscsi_set_errno(hp, ESCSI_NOMEM));
 358 
 359         cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
 360         cp->ic_evpd = 1; /* return vital product data for bellow page code */
 361         cp->ic_page_code = DEV_ID_VPD_PAGE_CODE;
 362         SCSI_WRITE16(&cp->ic_allocation_length,
 363             sizeof (spc3_dev_id_vpd_page_impl_t));
 364 
 365         if (libscsi_exec(ap, tp) != 0 ||
 366             libscsi_action_get_status(ap) != 0) {
 367                 libscsi_action_free(ap);
 368                 return (libscsi_set_errno(hp, ESCSI_IO));
 369         }
 370 
 371         (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
 372         libscsi_action_free(ap);
 373 
 374         /* make sure we at least got the header */
 375         if (len < offsetof(spc3_dev_id_vpd_page_impl_t, divpi_descrs[0]))
 376                 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
 377 
 378         /* make sure we got the page we asked for */
 379         if (data.divpi_hdr.page_code != DEV_ID_VPD_PAGE_CODE)
 380                 return (libscsi_set_errno(hp, ESCSI_IO));
 381 
 382         /* check for page truncation */
 383         len = ((data.divpi_hdr.page_len)[0] << 8 |
 384             (data.divpi_hdr.page_len)[1]);
 385         if (len > sizeof (data.divpi_descrs))
 386                 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
 387 
 388         /* get the first descriptor */
 389         cur_desc = (struct vpd_desc *)(data.divpi_descrs);
 390         /* iterate over descriptors looking for the one we need */
 391         des_bytes_left = len;
 392         for (; des_bytes_left > sizeof (struct vpd_desc);
 393             des_bytes_left -= (sizeof (struct vpd_desc) + cur_desc->len),
 394             cur_desc = NEXT_DESC(cur_desc, cur_desc->len, struct vpd_desc)) {
 395 
 396                 /*
 397                  * Len for the NAA IEEE designators is 12 (aka 0x08).
 398                  * Designator type (id_type) 3 means a NAA formatted
 399                  * designator.
 400                  * Code set for the NAA IEEE designators is 1 (binary format).
 401                  * Association 0 means this designator is for a Logical Unit.
 402                  * Association 2 means this designator is for a SCSI device
 403                  * that contains the Logical Unit.
 404                  * With the ASSOCIATION field set to 0 or 2, device shall
 405                  * return the same descriptor when it is accessed through any
 406                  * other I_T nexus. See SPC4 7.8.6.1
 407                  */
 408                 if (cur_desc->len == 0x08 && cur_desc->id_type == 0x3 &&
 409                     cur_desc->code_set == 0x1 &&
 410                     (cur_desc->association == 0x0 ||
 411                      cur_desc->association == 0x2)) {
 412                         /* get to the data - skip the descriptor header */
 413                         cur_desc = (struct vpd_desc *)(((uint8_t *)cur_desc) +
 414                             sizeof (struct vpd_desc));
 415 
 416                         /*
 417                          * Bits 7-4 of the NAA formatted designator hold
 418                          * the designator type. We're only interested
 419                          * in designator type 0x5 - a 64bit value
 420                          * (including this type filed) that represents a
 421                          * NAA IEEE Registered designator that we use as
 422                          * the LID.
 423                          * See SPC4 "NAA designator format" section.
 424                          */
 425                         if (((*((uint8_t *)cur_desc)) & 0x50) != 0x50) {
 426                                 /*
 427                                  * This is not an IEEE Registered NAA
 428                                  * designator, point cur_desc back to the
 429                                  * header and skip this designator.
 430                                  */
 431                                 cur_desc = (struct vpd_desc * )
 432                                     (((uint8_t *)cur_desc) -
 433                                     sizeof (struct vpd_desc));
 434                                 continue;
 435                         }
 436 
 437                         /* byte swap to have LID match what libses displays */
 438                         if (snprintf(lid, sizeof (lid), "%llx",
 439                                     BE_IN64(cur_desc)) < 0)
 440                                 return (libscsi_set_errno(hp, ESCSI_UNKNOWN));
 441 
 442                         if ((tp->lst_lid = libscsi_process_inquiry_string(hp,
 443                             lid, sizeof (lid))) == NULL)
 444                                 return (libscsi_set_errno(hp, ESCSI_NOMEM));
 445 
 446                         return (0);
 447                 }
 448         }
 449 
 450         return (libscsi_set_errno(hp, ESCSI_NOTSUP));
 451 }
 452 
 453 /*
 454  * Execute inquiry for VPD page 0x80 (unit serial #) and extract the USN
 455  */
 456 int
 457 libscsi_get_inquiry_usn(libscsi_hdl_t *hp, libscsi_target_t *tp)
 458 {
 459         libscsi_action_t *ap;
 460         spc3_inquiry_cdb_t *cp;
 461         spc3_usn_vpd_page_impl_t data;
 462         size_t len;
 463 
 464         if ((ap = libscsi_action_alloc(hp, SPC3_CMD_INQUIRY,
 465             LIBSCSI_AF_READ | LIBSCSI_AF_SILENT | LIBSCSI_AF_DIAGNOSE, &data,
 466             sizeof (spc3_usn_vpd_page_impl_t))) == NULL)
 467                 return (libscsi_set_errno(hp, ESCSI_NOMEM));
 468 
 469         cp = (spc3_inquiry_cdb_t *)libscsi_action_get_cdb(ap);
 470         cp->ic_evpd = 1; /* return vital product data for bellow page code */
 471         cp->ic_page_code = USN_VPD_PAGE_CODE;
 472         SCSI_WRITE16(&cp->ic_allocation_length,
 473             sizeof (spc3_usn_vpd_page_impl_t));
 474 
 475         if (libscsi_exec(ap, tp) != 0 ||
 476             libscsi_action_get_status(ap) != 0) {
 477                 libscsi_action_free(ap);
 478                 return (libscsi_set_errno(hp, ESCSI_IO));
 479         }
 480 
 481         (void) libscsi_action_get_buffer(ap, NULL, NULL, &len);
 482         libscsi_action_free(ap);
 483 
 484         /* make sure we at least got the header */
 485         if (len < offsetof(spc3_usn_vpd_page_impl_t, uvpi_usn[0]))
 486                 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
 487 
 488         /* make sure we got the page we asked for */
 489         if (data.uvpi_hdr.page_code != USN_VPD_PAGE_CODE)
 490                 return (libscsi_set_errno(hp, ESCSI_IO));
 491 
 492         /* check for USN truncation */
 493         len = ((data.uvpi_hdr.page_len)[0] << 8 | (data.uvpi_hdr.page_len)[1]);
 494         if (len == 0 || len > sizeof (data.uvpi_usn))
 495                 return (libscsi_set_errno(hp, ESCSI_BADLENGTH));
 496 
 497         /* USN is ASCII encoded */
 498         if ((tp->lst_usn = libscsi_process_inquiry_string(hp,
 499             (char *)data.uvpi_usn, len)) == NULL)
 500             return (libscsi_set_errno(hp, ESCSI_NOMEM));
 501 
 502         return (0);
 503 }
 504 
 505 const char *
 506 libscsi_vendor(libscsi_target_t *tp)
 507 {
 508         return (tp->lst_vendor);
 509 }
 510 
 511 const char *
 512 libscsi_product(libscsi_target_t *tp)
 513 {
 514         return (tp->lst_product);
 515 }
 516 
 517 const char *
 518 libscsi_revision(libscsi_target_t *tp)
 519 {
 520         return (tp->lst_revision);
 521 }
 522 
 523 const char *
 524 libscsi_lid(libscsi_target_t *tp)
 525 {
 526         return (tp->lst_lid);
 527 }
 528 
 529 const char *
 530 libscsi_usn(libscsi_target_t *tp)
 531 {
 532         return (tp->lst_usn);
 533 }