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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2017 RackTop Systems.
  26  */
  27 
  28 #include <assert.h>
  29 #include <errno.h>
  30 #include <libdiskstatus.h>
  31 #include <limits.h>
  32 #include <stdlib.h>
  33 #include <strings.h>
  34 #include <sys/fm/io/scsi.h>
  35 
  36 #include "ds_scsi.h"
  37 #include "ds_scsi_sim.h"
  38 #include "ds_scsi_uscsi.h"
  39 
  40 typedef struct ds_scsi_info {
  41         disk_status_t           *si_dsp;
  42         void                    *si_sim;
  43         int                     si_cdblen;
  44         int                     si_supp_mode;
  45         int                     si_supp_log;
  46         int                     si_extensions;
  47         int                     si_reftemp;
  48         scsi_ms_hdrs_t          si_hdrs;
  49         scsi_ie_page_t          si_iec_current;
  50         scsi_ie_page_t          si_iec_changeable;
  51         nvlist_t                *si_state_modepage;
  52         nvlist_t                *si_state_logpage;
  53         nvlist_t                *si_state_iec;
  54 } ds_scsi_info_t;
  55 
  56 #define scsi_set_errno(sip, errno)      (ds_set_errno((sip)->si_dsp, (errno)))
  57 
  58 /*
  59  * Table to validate log pages
  60  */
  61 typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *,
  62     scsi_log_parameter_header_t *, int, nvlist_t *);
  63 typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *,
  64     scsi_log_parameter_header_t *, int);
  65 
  66 typedef struct logpage_validation_entry {
  67         uchar_t                 ve_code;
  68         int                     ve_supported;
  69         const char              *ve_desc;
  70         logpage_validation_fn_t ve_validate;
  71         logpage_analyze_fn_t    ve_analyze;
  72 } logpage_validation_entry_t;
  73 
  74 static int logpage_ie_verify(ds_scsi_info_t *,
  75     scsi_log_parameter_header_t *, int, nvlist_t *);
  76 static int logpage_temp_verify(ds_scsi_info_t *,
  77     scsi_log_parameter_header_t *, int, nvlist_t *);
  78 static int logpage_selftest_verify(ds_scsi_info_t *,
  79     scsi_log_parameter_header_t *, int, nvlist_t *);
  80 
  81 static int logpage_ie_analyze(ds_scsi_info_t *,
  82     scsi_log_parameter_header_t *, int);
  83 static int logpage_temp_analyze(ds_scsi_info_t *,
  84     scsi_log_parameter_header_t *, int);
  85 static int logpage_selftest_analyze(ds_scsi_info_t *,
  86     scsi_log_parameter_header_t *, int);
  87 
  88 static struct logpage_validation_entry log_validation[] = {
  89         { LOGPAGE_IE,           LOGPAGE_SUPP_IE,
  90             "informational-exceptions",
  91             logpage_ie_verify,  logpage_ie_analyze },
  92         { LOGPAGE_TEMP,         LOGPAGE_SUPP_TEMP,
  93             "temperature",
  94             logpage_temp_verify, logpage_temp_analyze },
  95         { LOGPAGE_SELFTEST,     LOGPAGE_SUPP_SELFTEST,
  96             "self-test",
  97             logpage_selftest_verify, logpage_selftest_analyze }
  98 };
  99 
 100 #define NLOG_VALIDATION (sizeof (log_validation) / sizeof (log_validation[0]))
 101 
 102 /*
 103  * Given an extended sense page, retrieves the sense key, as well as the
 104  * additional sense code information.
 105  */
 106 static void
 107 scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp,
 108     uint_t *ascp, uint_t *ascqp)
 109 {
 110         struct scsi_descr_sense_hdr *sdsp =
 111             (struct scsi_descr_sense_hdr *)rq;
 112 
 113         *skeyp = rq->es_key;
 114 
 115         /*
 116          * Get asc, ascq and info field from sense data.  There are two
 117          * possible formats (fixed sense data and descriptor sense data)
 118          * depending on the value of es_code.
 119          */
 120         switch (rq->es_code) {
 121         case CODE_FMT_DESCR_CURRENT:
 122         case CODE_FMT_DESCR_DEFERRED:
 123 
 124                 *ascp = sdsp->ds_add_code;
 125                 *ascqp = sdsp->ds_qual_code;
 126                 break;
 127 
 128         case CODE_FMT_FIXED_CURRENT:
 129         case CODE_FMT_FIXED_DEFERRED:
 130         default:
 131 
 132                 if (rq->es_add_len >= 6) {
 133                         *ascp = rq->es_add_code;
 134                         *ascqp = rq->es_qual_code;
 135                 } else {
 136                         *ascp = 0xff;
 137                         *ascqp = 0xff;
 138                 }
 139                 break;
 140         }
 141 }
 142 
 143 /*
 144  * Routines built atop the bare uscsi commands, which take into account the
 145  * command length, automatically translate any scsi errors, and transparently
 146  * call into the simulator if active.
 147  */
 148 static int
 149 scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options,
 150     void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
 151     uint_t *ascp, uint_t *ascqp)
 152 {
 153         int result;
 154         struct scsi_extended_sense sense;
 155         int senselen = sizeof (struct scsi_extended_sense);
 156         struct mode_page *mp = (struct mode_page *)buf;
 157 
 158         assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
 159             sip->si_cdblen == MODE_CMD_LEN_10);
 160         assert(headers->ms_length == sip->si_cdblen);
 161 
 162         bzero(&sense, sizeof (struct scsi_extended_sense));
 163 
 164         if (mp->ps) {
 165                 options |= MODE_SELECT_SP;
 166                 mp->ps = 0;
 167         } else {
 168                 options &= ~MODE_SELECT_SP;
 169         }
 170 
 171         if (sip->si_cdblen == MODE_CMD_LEN_6) {
 172                 /* The following fields are reserved during mode select: */
 173                 headers->ms_hdr.g0.ms_header.length = 0;
 174                 headers->ms_hdr.g0.ms_header.device_specific = 0;
 175 
 176                 if (sip->si_sim)
 177                         result = simscsi_mode_select(sip->si_sim,
 178                             page_code, options, buf, buflen,
 179                             &headers->ms_hdr.g0, &sense, &senselen);
 180                 else
 181                         result = uscsi_mode_select(sip->si_dsp->ds_fd,
 182                             page_code, options, buf, buflen,
 183                             &headers->ms_hdr.g0, &sense, &senselen);
 184         } else {
 185                 /* The following fields are reserved during mode select: */
 186                 headers->ms_hdr.g1.ms_header.length = 0;
 187                 headers->ms_hdr.g1.ms_header.device_specific = 0;
 188 
 189                 if (sip->si_sim)
 190                         result = simscsi_mode_select_10(sip->si_sim,
 191                             page_code, options, buf, buflen,
 192                             &headers->ms_hdr.g1, &sense, &senselen);
 193                 else
 194                         result = uscsi_mode_select_10(sip->si_dsp->ds_fd,
 195                             page_code, options, buf, buflen,
 196                             &headers->ms_hdr.g1, &sense, &senselen);
 197         }
 198 
 199         if (result != 0)
 200                 scsi_translate_error(&sense, skp, ascp, ascqp);
 201 
 202         return (result);
 203 }
 204 
 205 static int
 206 scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc,
 207     void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
 208     uint_t *ascp, uint_t *ascqp)
 209 {
 210         int result;
 211         struct scsi_extended_sense sense;
 212         int senselen = sizeof (struct scsi_extended_sense);
 213 
 214         assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
 215             sip->si_cdblen == MODE_CMD_LEN_10);
 216 
 217         bzero(&sense, sizeof (struct scsi_extended_sense));
 218 
 219         bzero(headers, sizeof (scsi_ms_hdrs_t));
 220         headers->ms_length = sip->si_cdblen;
 221 
 222         if (sip->si_cdblen == MODE_CMD_LEN_6) {
 223                 if (sip->si_sim)
 224                         result = simscsi_mode_sense(sip->si_sim,
 225                             page_code, pc, buf, buflen, &headers->ms_hdr.g0,
 226                             &sense, &senselen);
 227                 else
 228                         result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code,
 229                             pc, buf, buflen, &headers->ms_hdr.g0, &sense,
 230                             &senselen);
 231         } else {
 232                 if (sip->si_sim)
 233                         result = simscsi_mode_sense_10(sip->si_sim,
 234                             page_code, pc, buf, buflen, &headers->ms_hdr.g1,
 235                             &sense, &senselen);
 236                 else
 237                         result = uscsi_mode_sense_10(sip->si_dsp->ds_fd,
 238                             page_code, pc, buf, buflen, &headers->ms_hdr.g1,
 239                             &sense, &senselen);
 240         }
 241 
 242         if (result != 0)
 243                 scsi_translate_error(&sense, skp, ascp, ascqp);
 244 
 245         return (result);
 246 }
 247 
 248 static int
 249 scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp,
 250     uint_t *ascqp)
 251 {
 252         struct scsi_extended_sense sense, sensebuf;
 253         int senselen = sizeof (struct scsi_extended_sense);
 254         int sensebuflen = sizeof (struct scsi_extended_sense);
 255         int result;
 256 
 257         bzero(&sense, sizeof (struct scsi_extended_sense));
 258         bzero(&sensebuf, sizeof (struct scsi_extended_sense));
 259 
 260         if (sip->si_sim)
 261                 result = simscsi_request_sense(sip->si_sim,
 262                     (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
 263         else
 264                 result = uscsi_request_sense(sip->si_dsp->ds_fd,
 265                     (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
 266 
 267         if (result == 0)
 268                 scsi_translate_error(&sensebuf, skp, ascp, ascqp);
 269         else
 270                 scsi_translate_error(&sense, skp, ascp, ascqp);
 271 
 272         return (result);
 273 }
 274 
 275 static int
 276 scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control,
 277     caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp)
 278 {
 279         int result;
 280         struct scsi_extended_sense sense;
 281         int senselen = sizeof (struct scsi_extended_sense);
 282 
 283         if (sip->si_sim)
 284                 result = simscsi_log_sense(sip->si_sim,
 285                     page_code, page_control, page_data, page_size, &sense,
 286                     &senselen);
 287         else
 288                 result = uscsi_log_sense(sip->si_dsp->ds_fd,
 289                     page_code, page_control, page_data, page_size, &sense,
 290                     &senselen);
 291 
 292         if (result != 0)
 293                 scsi_translate_error(&sense, skp, ascp, ascqp);
 294 
 295         return (result);
 296 }
 297 
 298 /*
 299  * Given a list of supported mode pages, determine if the given page is present.
 300  */
 301 static boolean_t
 302 mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode)
 303 {
 304         uint_t i = 0;
 305         struct mode_page *pg;
 306         boolean_t found = B_FALSE;
 307 
 308         /*
 309          * The mode page list contains all mode pages supported by the device,
 310          * one after the other.
 311          */
 312         while (i < pgdatalen) {
 313                 pg = (struct mode_page *)&pgdata[i];
 314 
 315                 if (pg->code == pagecode) {
 316                         found = B_TRUE;
 317                         break;
 318                 }
 319 
 320                 i += MODESENSE_PAGE_LEN(pg);
 321         }
 322 
 323         return (found);
 324 }
 325 
 326 /*
 327  * Load mode pages and check that the appropriate pages are supported.
 328  *
 329  * As part of this process, we determine which form of the MODE SENSE / MODE
 330  * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE
 331  * SENSE command for a page that should be implemented by the device.
 332  */
 333 static int
 334 load_modepages(ds_scsi_info_t *sip)
 335 {
 336         int allpages_buflen;
 337         uchar_t *allpages;
 338         scsi_ms_hdrs_t headers;
 339         int result;
 340         uint_t skey, asc, ascq;
 341         int datalength = 0;
 342         scsi_ms_header_t *smh = &headers.ms_hdr.g0;
 343         scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1;
 344         nvlist_t *nvl;
 345 
 346         allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t);
 347         if ((allpages = calloc(allpages_buflen, 1)) == NULL)
 348                 return (scsi_set_errno(sip, EDS_NOMEM));
 349 
 350         bzero(&headers, sizeof (headers));
 351 
 352         /*
 353          * Attempt a mode sense(6).  If that fails, try a mode sense(10)
 354          *
 355          * allpages is allocated to be of the maximum size for either a mode
 356          * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
 357          *
 358          * Note that the length passed into uscsi_mode_sense should be set to
 359          * the maximum size of the parameter response, which in this case is
 360          * UCHAR_MAX - the size of the headers/block descriptors.
 361          */
 362         sip->si_cdblen = MODE_CMD_LEN_6;
 363         if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT,
 364             (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t),
 365             &headers, &skey, &asc, &ascq)) == 0) {
 366                 /*
 367                  * Compute the data length of the page that contains all mode
 368                  * sense pages.  This is a bit tricky because the format of the
 369                  * response from the lun is:
 370                  *
 371                  * header: <length> <medium type byte> <dev specific byte>
 372                  *         <block descriptor length>
 373                  *         [<optional block descriptor>]
 374                  * data:   [<mode page data> <mode page data> ...]
 375                  *
 376                  * Since the length field in the header describes the length of
 377                  * the entire response.  This includes the header, but NOT
 378                  * the length field itself, which is 1 or 2 bytes depending on
 379                  * which mode sense type (6- or 10- byte) is being executed.
 380                  *
 381                  * So, the data length equals the length value in the header
 382                  * plus 1 (because the length byte was not included in the
 383                  * length count), minus [[the sum of the length of the header
 384                  * and the length of the block descriptor]].
 385                  */
 386                 datalength = (smh->ms_header.length +
 387                     sizeof (smh->ms_header.length)) -
 388                     (sizeof (struct mode_header) +
 389                     smh->ms_header.bdesc_length);
 390         } else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) {
 391                 /*
 392                  * Fallback and try the 10-byte version of the command.
 393                  */
 394                 sip->si_cdblen = MODE_CMD_LEN_10;
 395                 result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES,
 396                     PC_CURRENT, (caddr_t)allpages, allpages_buflen,
 397                     &headers, &skey, &asc, &ascq);
 398 
 399                 if (result == 0) {
 400                         datalength = (BE_16(smh_g1->ms_header.length) +
 401                             sizeof (smh_g1->ms_header.length)) -
 402                             (sizeof (struct mode_header_g1) +
 403                             BE_16(smh_g1->ms_header.bdesc_length));
 404 
 405                 }
 406         }
 407 
 408         if (result == 0 && datalength >= 0) {
 409                 if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length",
 410                     sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) {
 411                         free(allpages);
 412                         return (scsi_set_errno(sip, EDS_NOMEM));
 413                 }
 414 
 415                 nvl = NULL;
 416                 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
 417                     nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages",
 418                     nvl) != 0) {
 419                         free(allpages);
 420                         nvlist_free(nvl);
 421                         return (scsi_set_errno(sip, EDS_NOMEM));
 422                 }
 423 
 424                 nvlist_free(nvl);
 425                 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
 426                     "modepages", &sip->si_state_modepage);
 427                 assert(result == 0);
 428 
 429                 /*
 430                  * One of the sets of the commands (above) succeeded, so now
 431                  * look for the mode pages we need and record them appropriately
 432                  */
 433                 if (mode_page_present(allpages, datalength,
 434                     MODEPAGE_INFO_EXCPT)) {
 435 
 436                         nvl = NULL;
 437                         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
 438                             nvlist_add_nvlist(sip->si_state_modepage,
 439                             "informational-exceptions", nvl) != 0) {
 440                                 free(allpages);
 441                                 nvlist_free(nvl);
 442                                 return (scsi_set_errno(sip, EDS_NOMEM));
 443                         }
 444                         nvlist_free(nvl);
 445                         sip->si_supp_mode |= MODEPAGE_SUPP_IEC;
 446                         result = nvlist_lookup_nvlist(sip->si_state_modepage,
 447                             "informational-exceptions", &sip->si_state_iec);
 448                         assert(result == 0);
 449                 }
 450 
 451         } else {
 452                 /*
 453                  * If the device failed to respond to one of the basic commands,
 454                  * then assume it's not a SCSI device or otherwise doesn't
 455                  * support the necessary transport.
 456                  */
 457                 if (datalength < 0)
 458                         dprintf("command returned invalid data length (%d)\n",
 459                             datalength);
 460                 else
 461                         dprintf("failed to load modepages (KEY=0x%x "
 462                             "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
 463 
 464                 result = scsi_set_errno(sip, EDS_NO_TRANSPORT);
 465         }
 466 
 467         free(allpages);
 468         return (result);
 469 }
 470 
 471 /*
 472  * Verify a single logpage.  This will do some generic validation and then call
 473  * the logpage-specific function for further verification.
 474  */
 475 static int
 476 verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp)
 477 {
 478         scsi_log_header_t *lhp;
 479         struct scsi_extended_sense sense;
 480         int buflen;
 481         int log_length;
 482         int result = 0;
 483         uint_t kp, asc, ascq;
 484         nvlist_t *nvl;
 485 
 486         buflen = MAX_BUFLEN(scsi_log_header_t);
 487         if ((lhp = calloc(buflen, 1)) == NULL)
 488                 return (scsi_set_errno(sip, EDS_NOMEM));
 489         bzero(&sense, sizeof (struct scsi_extended_sense));
 490 
 491         nvl = NULL;
 492         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
 493             nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) {
 494                 nvlist_free(nvl);
 495                 free(lhp);
 496                 return (scsi_set_errno(sip, EDS_NOMEM));
 497         }
 498         nvlist_free(nvl);
 499         result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl);
 500         assert(result == 0);
 501 
 502         result = scsi_log_sense(sip, lp->ve_code,
 503             PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq);
 504 
 505         if (result == 0) {
 506                 log_length = BE_16(lhp->lh_length);
 507                 if (nvlist_add_uint16(nvl, "length", log_length) != 0) {
 508                         free(lhp);
 509                         return (scsi_set_errno(sip, EDS_NOMEM));
 510                 }
 511 
 512                 if (lp->ve_validate(sip, (scsi_log_parameter_header_t *)
 513                     (((char *)lhp) + sizeof (scsi_log_header_t)),
 514                     log_length, nvl) != 0) {
 515                         free(lhp);
 516                         return (-1);
 517                 }
 518         } else {
 519                 dprintf("failed to load %s log page (KEY=0x%x "
 520                     "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq);
 521         }
 522 
 523         free(lhp);
 524         return (0);
 525 }
 526 
 527 /*
 528  * Load log pages and determine which pages are supported.
 529  */
 530 static int
 531 load_logpages(ds_scsi_info_t *sip)
 532 {
 533         int buflen;
 534         scsi_supported_log_pages_t *sp;
 535         struct scsi_extended_sense sense;
 536         int result;
 537         uint_t sk, asc, ascq;
 538         int i, j;
 539         nvlist_t *nvl;
 540 
 541         buflen = MAX_BUFLEN(scsi_log_header_t);
 542         if ((sp = calloc(buflen, 1)) == NULL)
 543                 return (scsi_set_errno(sip, EDS_NOMEM));
 544 
 545         bzero(&sense, sizeof (struct scsi_extended_sense));
 546 
 547         if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST,
 548             PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) {
 549                 int pagecount = BE_16(sp->slp_hdr.lh_length);
 550 
 551                 for (i = 0; i < pagecount; i++) {
 552                         for (j = 0; j < NLOG_VALIDATION; j++) {
 553                                 if (log_validation[j].ve_code ==
 554                                     sp->slp_pages[i])
 555                                         sip->si_supp_log |=
 556                                             log_validation[j].ve_supported;
 557                         }
 558                 }
 559         }
 560 
 561         free(sp);
 562         if (result == 0) {
 563                 nvl = NULL;
 564                 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
 565                     nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages",
 566                     nvl) != 0) {
 567                         nvlist_free(nvl);
 568                         return (scsi_set_errno(sip, EDS_NOMEM));
 569                 }
 570 
 571                 nvlist_free(nvl);
 572                 result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
 573                     "logpages", &sip->si_state_logpage);
 574                 assert(result == 0);
 575 
 576                 /*
 577                  * Validate the logpage contents.
 578                  */
 579                 for (i = 0; i < NLOG_VALIDATION; i++) {
 580                         if ((sip->si_supp_log &
 581                             log_validation[i].ve_supported) == 0)
 582                                 continue;
 583 
 584                         /*
 585                          * verify_logpage will clear the supported bit if
 586                          * verification fails.
 587                          */
 588                         if (verify_logpage(sip, &log_validation[i]) != 0)
 589                                 return (-1);
 590                 }
 591 
 592         } else {
 593                 dprintf("failed to get log pages "
 594                     "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq);
 595         }
 596 
 597         /*
 598          * We always return 0 here, even if the required log pages aren't
 599          * supported.
 600          */
 601         return (0);
 602 }
 603 
 604 /*
 605  * Verify that the IE log page is sane.  This log page is potentially chock-full
 606  * of vendor specific information that we do not know how to access.  All we can
 607  * do is check for the generic predictive failure bit.  If this log page is not
 608  * well-formed, then bail out.
 609  */
 610 static int
 611 logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
 612     int log_length, nvlist_t *nvl)
 613 {
 614         int i, plen = 0;
 615         boolean_t seen = B_FALSE;
 616         scsi_ie_log_param_t *iep =
 617             (scsi_ie_log_param_t *)lphp;
 618 
 619         for (i = 0; i < log_length; i += plen) {
 620                 iep = (scsi_ie_log_param_t *)((char *)iep + plen);
 621 
 622                 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) {
 623                         if (nvlist_add_boolean_value(nvl, "general",
 624                             B_TRUE) != 0)
 625                                 return (scsi_set_errno(sip, EDS_NOMEM));
 626 
 627                         if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) {
 628                                 if (nvlist_add_uint8(nvl,
 629                                     "invalid-length", lphp->lph_length) != 0)
 630                                         return (scsi_set_errno(sip, EDS_NOMEM));
 631                         } else {
 632                                 seen = B_TRUE;
 633                         }
 634                         break;
 635                 }
 636 
 637                 plen = iep->ie_hdr.lph_length +
 638                     sizeof (scsi_log_parameter_header_t);
 639         }
 640 
 641         if (!seen) {
 642                 sip->si_supp_log &= ~LOGPAGE_SUPP_IE;
 643                 dprintf("IE logpage validation failed\n");
 644         }
 645 
 646         return (0);
 647 }
 648 
 649 /*
 650  * Verify the contents of the temperature log page.  The temperature log page
 651  * contains two log parameters: the current temperature, and (optionally) the
 652  * reference temperature.  For the verification phase, we check that the two
 653  * parameters we care about are well-formed.  If there is no reference
 654  * temperature, then we cannot use the page for monitoring purposes.
 655  */
 656 static int
 657 logpage_temp_verify(ds_scsi_info_t *sip,
 658     scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
 659 {
 660         int i, plen = 0;
 661         boolean_t has_reftemp = B_FALSE;
 662         boolean_t bad_length = B_FALSE;
 663         ushort_t param_code;
 664 
 665         for (i = 0; i < log_length; i += plen) {
 666                 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
 667                 param_code = BE_16(lphp->lph_param);
 668 
 669                 switch (param_code) {
 670                 case LOGPARAM_TEMP_CURTEMP:
 671                         if (nvlist_add_boolean_value(nvl, "current-temperature",
 672                             B_TRUE) != 0)
 673                                 return (scsi_set_errno(sip, EDS_NOMEM));
 674                         if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
 675                                 if (nvlist_add_uint8(nvl,
 676                                     "invalid-length", lphp->lph_length) != 0)
 677                                         return (scsi_set_errno(sip, EDS_NOMEM));
 678                                 bad_length = B_TRUE;
 679                         }
 680                         break;
 681 
 682                 case LOGPARAM_TEMP_REFTEMP:
 683                         if (nvlist_add_boolean_value(nvl,
 684                             "reference-temperature", B_TRUE) != 0)
 685                                 return (scsi_set_errno(sip, EDS_NOMEM));
 686                         if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
 687                                 if (nvlist_add_uint8(nvl,
 688                                     "invalid-length", lphp->lph_length) != 0)
 689                                         return (scsi_set_errno(sip, EDS_NOMEM));
 690                                 bad_length = B_TRUE;
 691                         }
 692                         has_reftemp = B_TRUE;
 693                         break;
 694                 }
 695 
 696                 plen = lphp->lph_length +
 697                     sizeof (scsi_log_parameter_header_t);
 698         }
 699 
 700         if (bad_length || !has_reftemp) {
 701                 sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP;
 702                 dprintf("temperature logpage validation failed\n");
 703         }
 704 
 705         return (0);
 706 }
 707 
 708 /*
 709  * Verify the contents of the self test log page.  The log supports a maximum of
 710  * 20 entries, where each entry's parameter code is its index in the log.  We
 711  * check that the parameter codes fall within this range, and that the size of
 712  * each page is what we expect.  It's perfectly acceptable for there to be no
 713  * entries in this log, so we must also be sure to validate the contents as part
 714  * of the analysis phase.
 715  */
 716 static int
 717 logpage_selftest_verify(ds_scsi_info_t *sip,
 718     scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
 719 {
 720         int i, plen = 0;
 721         boolean_t bad = B_FALSE;
 722         int entries = 0;
 723         ushort_t param_code;
 724 
 725         for (i = 0; i < log_length; i += plen, entries++) {
 726                 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
 727                 param_code = BE_16(lphp->lph_param);
 728 
 729                 if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE ||
 730                     param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) {
 731                         if (nvlist_add_uint16(nvl, "invalid-param-code",
 732                             param_code) != 0)
 733                                 return (scsi_set_errno(sip, EDS_NOMEM));
 734                         bad = B_TRUE;
 735                         break;
 736                 }
 737 
 738                 if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) {
 739                         if (nvlist_add_uint8(nvl, "invalid-length",
 740                             lphp->lph_length) != 0)
 741                                 return (scsi_set_errno(sip, EDS_NOMEM));
 742                         bad = B_TRUE;
 743                         break;
 744 
 745                 }
 746 
 747                 plen = lphp->lph_length +
 748                     sizeof (scsi_log_parameter_header_t);
 749         }
 750 
 751         if (bad) {
 752                 sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST;
 753                 dprintf("selftest logpage validation failed\n");
 754         }
 755 
 756         return (0);
 757 }
 758 
 759 /*
 760  * Load the current IE mode pages
 761  */
 762 static int
 763 load_ie_modepage(ds_scsi_info_t *sip)
 764 {
 765         struct scsi_ms_hdrs junk_hdrs;
 766         int result;
 767         uint_t skey, asc, ascq;
 768 
 769         if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
 770                 return (0);
 771 
 772         bzero(&sip->si_iec_current, sizeof (sip->si_iec_current));
 773         bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable));
 774 
 775         if ((result = scsi_mode_sense(sip,
 776             MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current,
 777             MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc,
 778             &ascq)) == 0) {
 779                 result = scsi_mode_sense(sip,
 780                     MODEPAGE_INFO_EXCPT, PC_CHANGEABLE,
 781                     &sip->si_iec_changeable,
 782                     MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq);
 783         }
 784 
 785         if (result != 0) {
 786                 dprintf("failed to get IEC modepage (KEY=0x%x "
 787                     "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq);
 788                 sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC;
 789         } else  {
 790                 if (nvlist_add_boolean_value(sip->si_state_iec,
 791                     "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 ||
 792                     nvlist_add_boolean_value(sip->si_state_iec,
 793                     "logerr", sip->si_iec_current.ie_logerr) != 0 ||
 794                     nvlist_add_uint8(sip->si_state_iec,
 795                     "mrie", sip->si_iec_current.ie_mrie) != 0 ||
 796                     nvlist_add_boolean_value(sip->si_state_iec,
 797                     "test", sip->si_iec_current.ie_test) != 0 ||
 798                     nvlist_add_boolean_value(sip->si_state_iec,
 799                     "ewasc", sip->si_iec_current.ie_ewasc) != 0 ||
 800                     nvlist_add_boolean_value(sip->si_state_iec,
 801                     "perf", sip->si_iec_current.ie_perf) != 0 ||
 802                     nvlist_add_boolean_value(sip->si_state_iec,
 803                     "ebf", sip->si_iec_current.ie_ebf) != 0 ||
 804                     nvlist_add_uint32(sip->si_state_iec,
 805                     "interval-timer",
 806                     BE_32(sip->si_iec_current.ie_interval_timer)) != 0 ||
 807                     nvlist_add_uint32(sip->si_state_iec,
 808                     "report-count",
 809                     BE_32(sip->si_iec_current.ie_report_count)) != 0)
 810                         return (scsi_set_errno(sip, EDS_NOMEM));
 811         }
 812 
 813         return (0);
 814 }
 815 
 816 /*
 817  * Enable IE reporting.  We prefer the following settings:
 818  *
 819  * (1) DEXCPT = 0
 820  * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
 821  * (4) EWASC = 1
 822  * (6) REPORT COUNT = 0x00000001
 823  * (7) LOGERR = 1
 824  *
 825  * However, not all drives support changing these values, and the current state
 826  * may be useful enough as-is.  For example, some drives support IE logging, but
 827  * don't support changing the MRIE.  In this case, we can still use the
 828  * information provided by the log page.
 829  */
 830 static int
 831 scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed)
 832 {
 833         scsi_ie_page_t new_iec_page;
 834         scsi_ms_hdrs_t hdrs;
 835         uint_t skey, asc, ascq;
 836 
 837         if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
 838                 return (0);
 839 
 840         bzero(&new_iec_page, sizeof (new_iec_page));
 841         bzero(&hdrs, sizeof (hdrs));
 842 
 843         (void) memcpy(&new_iec_page, &sip->si_iec_current,
 844             sizeof (new_iec_page));
 845 
 846         if (IEC_IE_CHANGEABLE(sip->si_iec_changeable))
 847                 new_iec_page.ie_dexcpt = 0;
 848 
 849         if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable))
 850                 new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST;
 851 
 852         /*
 853          * We only want to enable warning reporting if we are able to change the
 854          * mrie to report on request.  Otherwise, we risk unnecessarily
 855          * interrupting normal SCSI commands with a CHECK CONDITION code.
 856          */
 857         if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) {
 858                 if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST)
 859                         new_iec_page.ie_ewasc = 1;
 860                 else
 861                         new_iec_page.ie_ewasc = 0;
 862         }
 863 
 864         if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable))
 865                 new_iec_page.ie_report_count = BE_32(1);
 866 
 867         if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable))
 868                 new_iec_page.ie_logerr = 1;
 869 
 870         /*
 871          * Now compare the new mode page with the existing one.
 872          * if there's no difference, there's no need for a mode select
 873          */
 874         if (memcmp(&new_iec_page, &sip->si_iec_current,
 875             MODEPAGE_INFO_EXCPT_LEN) == 0) {
 876                 *changed = B_FALSE;
 877         } else {
 878                 (void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs));
 879 
 880                 if (scsi_mode_select(sip,
 881                     MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page,
 882                     MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) {
 883                         *changed = B_TRUE;
 884                 } else {
 885                         dprintf("failed to enable IE (KEY=0x%x "
 886                             "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
 887                         *changed = B_FALSE;
 888                 }
 889         }
 890 
 891         if (nvlist_add_boolean_value(sip->si_state_iec, "changed",
 892             *changed) != 0)
 893                 return (scsi_set_errno(sip, EDS_NOMEM));
 894 
 895         return (0);
 896 }
 897 
 898 /*
 899  * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
 900  * storage.
 901  */
 902 static int
 903 clear_gltsd(ds_scsi_info_t *sip)
 904 {
 905         scsi_ms_hdrs_t hdrs, junk_hdrs;
 906         struct mode_control_scsi3 control_pg_cur, control_pg_chg;
 907         int result;
 908         uint_t skey, asc, ascq;
 909 
 910         bzero(&hdrs, sizeof (hdrs));
 911         bzero(&control_pg_cur, sizeof (control_pg_cur));
 912         bzero(&control_pg_chg, sizeof (control_pg_chg));
 913 
 914         result = scsi_mode_sense(sip,
 915             MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur,
 916             MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
 917 
 918         if (result != 0) {
 919                 dprintf("failed to read Control mode page (KEY=0x%x "
 920                     "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
 921         } else if (control_pg_cur.mode_page.length !=
 922             PAGELENGTH_MODE_CONTROL_SCSI3) {
 923                 dprintf("SCSI-3 control mode page not supported\n");
 924         } else if ((result = scsi_mode_sense(sip,
 925             MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg,
 926             MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq))
 927             != 0) {
 928                 dprintf("failed to read changeable Control mode page (KEY=0x%x "
 929                     "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
 930         } else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) {
 931                 dprintf("gltsd is set and not changeable\n");
 932                 if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
 933                     "gltsd", control_pg_cur.gltsd) != 0)
 934                         return (scsi_set_errno(sip, EDS_NOMEM));
 935         } else if (control_pg_cur.gltsd) {
 936                 control_pg_cur.gltsd = 0;
 937                 result = scsi_mode_select(sip,
 938                     MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur,
 939                     MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
 940                 if (result != 0)
 941                         dprintf("failed to enable GLTSD (KEY=0x%x "
 942                             "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq);
 943                 if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
 944                     "gltsd", control_pg_cur.gltsd) != 0)
 945                         return (scsi_set_errno(sip, EDS_NOMEM));
 946         }
 947 
 948         return (0);
 949 }
 950 
 951 /*
 952  * Fetch the contents of the logpage, and then call the logpage-specific
 953  * analysis function.  The analysis function is responsible for detecting any
 954  * faults and filling in the details.
 955  */
 956 static int
 957 analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry)
 958 {
 959         scsi_log_header_t *lhp;
 960         scsi_log_parameter_header_t *lphp;
 961         int buflen;
 962         int log_length;
 963         uint_t skey, asc, ascq;
 964         int result;
 965 
 966         buflen = MAX_BUFLEN(scsi_log_header_t);
 967         if ((lhp = calloc(buflen, 1)) == NULL)
 968                 return (scsi_set_errno(sip, EDS_NOMEM));
 969 
 970         result = scsi_log_sense(sip, entry->ve_code,
 971             PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq);
 972 
 973         if (result == 0) {
 974                 log_length = BE_16(lhp->lh_length);
 975                 lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) +
 976                     sizeof (scsi_log_header_t));
 977 
 978                 result = entry->ve_analyze(sip, lphp, log_length);
 979         } else {
 980                 result = scsi_set_errno(sip, EDS_IO);
 981         }
 982 
 983         free(lhp);
 984         return (result);
 985 }
 986 
 987 /*
 988  * Analyze the IE logpage.  If we find an IE log record with a non-zero 'asc',
 989  * then we have a fault.
 990  */
 991 static int
 992 logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
 993     int log_length)
 994 {
 995         int i, plen = 0;
 996         scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp;
 997         nvlist_t *nvl;
 998 
 999         assert(sip->si_dsp->ds_predfail == NULL);
1000         if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0)
1001                 return (scsi_set_errno(sip, EDS_NOMEM));
1002         nvl = sip->si_dsp->ds_predfail;
1003 
1004         for (i = 0; i < log_length; i += plen) {
1005                 iep = (scsi_ie_log_param_t *)((char *)iep + plen);
1006 
1007                 /*
1008                  * Even though we validated the length during the initial phase,
1009                  * never trust the device.
1010                  */
1011                 if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE &&
1012                     iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) {
1013                         if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC,
1014                             iep->ie_asc) != 0 ||
1015                             nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ,
1016                             iep->ie_ascq) != 0)
1017                                 return (scsi_set_errno(sip, EDS_NOMEM));
1018 
1019                         if (iep->ie_asc != 0)
1020                                 sip->si_dsp->ds_faults |=
1021                                     DS_FAULT_PREDFAIL;
1022                         break;
1023                 }
1024                 plen = iep->ie_hdr.lph_length +
1025                     sizeof (scsi_log_parameter_header_t);
1026         }
1027 
1028         return (0);
1029 }
1030 
1031 static int
1032 logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1033     int log_length)
1034 {
1035         int i, plen = 0;
1036         uint8_t reftemp, curtemp;
1037         ushort_t param_code;
1038         scsi_temp_log_param_t *temp;
1039         nvlist_t *nvl;
1040 
1041         assert(sip->si_dsp->ds_overtemp == NULL);
1042         if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0)
1043                 return (scsi_set_errno(sip, EDS_NOMEM));
1044         nvl = sip->si_dsp->ds_overtemp;
1045 
1046         reftemp = curtemp = INVALID_TEMPERATURE;
1047         for (i = 0; i < log_length; i += plen) {
1048                 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
1049                 param_code = BE_16(lphp->lph_param);
1050                 temp = (scsi_temp_log_param_t *)lphp;
1051 
1052                 switch (param_code) {
1053                 case LOGPARAM_TEMP_CURTEMP:
1054                         if (lphp->lph_length != LOGPARAM_TEMP_LEN)
1055                                 break;
1056 
1057                         if (nvlist_add_uint8(nvl,
1058                             FM_EREPORT_PAYLOAD_SCSI_CURTEMP,
1059                             temp->t_temp) != 0)
1060                                 return (scsi_set_errno(sip, EDS_NOMEM));
1061                         curtemp = temp->t_temp;
1062                         break;
1063 
1064                 case LOGPARAM_TEMP_REFTEMP:
1065                         if (lphp->lph_length != LOGPARAM_TEMP_LEN)
1066                                 break;
1067 
1068                         if (nvlist_add_uint8(nvl,
1069                             FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP,
1070                             temp->t_temp) != 0)
1071                                 return (scsi_set_errno(sip, EDS_NOMEM));
1072                         reftemp = temp->t_temp;
1073                         break;
1074                 }
1075 
1076                 plen = lphp->lph_length +
1077                     sizeof (scsi_log_parameter_header_t);
1078         }
1079 
1080         /*
1081          * Take the reference temperature under advisory since it is often
1082          * reported incorrectly.  If it's 40C or less we ignore it and only
1083          * register a fault once the current temperature reaches above 60C.
1084          */
1085         if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE &&
1086             curtemp > reftemp && (reftemp > 40 || curtemp > 60))
1087                 sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP;
1088 
1089         return (0);
1090 }
1091 
1092 static int
1093 logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
1094     int log_length)
1095 {
1096         int i, plen = 0;
1097         int entries = 0;
1098         ushort_t param_code;
1099         scsi_selftest_log_param_t *stp;
1100         nvlist_t *nvl;
1101 
1102         assert(sip->si_dsp->ds_testfail == NULL);
1103         if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0)
1104                 return (scsi_set_errno(sip, EDS_NOMEM));
1105         nvl = sip->si_dsp->ds_testfail;
1106 
1107         for (i = 0; i < log_length; i += plen, entries++) {
1108                 lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
1109                 param_code = BE_16(lphp->lph_param);
1110                 stp = (scsi_selftest_log_param_t *)lphp;
1111 
1112                 if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE &&
1113                     param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE &&
1114                     lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) {
1115                         /*
1116                          * We always log the last result, or the result of the
1117                          * last completed test.
1118                          */
1119                         if ((param_code == 1 ||
1120                             SELFTEST_COMPLETE(stp->st_results))) {
1121                                 if (nvlist_add_uint8(nvl,
1122                                     FM_EREPORT_PAYLOAD_SCSI_RESULTCODE,
1123                                     stp->st_results) != 0 ||
1124                                     nvlist_add_uint16(nvl,
1125                                     FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP,
1126                                     BE_16(stp->st_timestamp)) != 0 ||
1127                                     nvlist_add_uint8(nvl,
1128                                     FM_EREPORT_PAYLOAD_SCSI_SEGMENT,
1129                                     stp->st_number) != 0 ||
1130                                     nvlist_add_uint64(nvl,
1131                                     FM_EREPORT_PAYLOAD_SCSI_ADDRESS,
1132                                     BE_64(stp->st_lba)) != 0)
1133                                         return (scsi_set_errno(sip,
1134                                             EDS_NOMEM));
1135 
1136                                 if (SELFTEST_COMPLETE(stp->st_results)) {
1137                                         if (stp->st_results != SELFTEST_OK)
1138                                                 sip->si_dsp->ds_faults |=
1139                                                     DS_FAULT_TESTFAIL;
1140                                         return (0);
1141                                 }
1142                         }
1143                 }
1144 
1145                 plen = lphp->lph_length +
1146                     sizeof (scsi_log_parameter_header_t);
1147         }
1148 
1149         return (0);
1150 }
1151 
1152 /*
1153  * Analyze the IE mode sense page explicitly.  This is only needed if the IE log
1154  * page is not supported.
1155  */
1156 static int
1157 analyze_ie_sense(ds_scsi_info_t *sip)
1158 {
1159         uint_t skey, asc, ascq;
1160         nvlist_t *nvl;
1161 
1162         /*
1163          * Don't bother checking if we weren't able to set our MRIE correctly.
1164          */
1165         if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST)
1166                 return (0);
1167 
1168         if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) {
1169                 dprintf("failed to request IE page (KEY=0x%x ASC=0x%x "
1170                     "ASCQ=0x%x)\n", skey, asc, ascq);
1171                 return (scsi_set_errno(sip, EDS_IO));
1172         } else if (skey == KEY_NO_SENSE) {
1173                 assert(sip->si_dsp->ds_predfail == NULL);
1174                 if (nvlist_alloc(&sip->si_dsp->ds_predfail,
1175                     NV_UNIQUE_NAME, 0) != 0)
1176                         return (scsi_set_errno(sip, EDS_NOMEM));
1177                 nvl = sip->si_dsp->ds_predfail;
1178 
1179                 if (nvlist_add_uint8(nvl,
1180                     FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 ||
1181                     nvlist_add_uint8(nvl,
1182                     FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) {
1183                         nvlist_free(nvl);
1184                         return (scsi_set_errno(sip, EDS_NOMEM));
1185                 }
1186 
1187                 if (asc != 0)
1188                         sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL;
1189         }
1190 
1191         return (0);
1192 }
1193 
1194 /*
1195  * Clean up the scsi-specific information structure.
1196  */
1197 static void
1198 ds_scsi_close(void *arg)
1199 {
1200         ds_scsi_info_t *sip = arg;
1201         if (sip->si_sim)
1202                 (void) dlclose(sip->si_sim);
1203 
1204         free(sip);
1205 }
1206 
1207 /*
1208  * Initialize a single disk.  Initialization consists of:
1209  *
1210  * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
1211  *    Control page (page 0x1C).
1212  *
1213  * 2. If the IE page is available, try to set the following parameters:
1214  *
1215  *      DEXCPT          0       Enable exceptions
1216  *      MRIE            6       Only report IE information on request
1217  *      EWASC           1       Enable warning reporting
1218  *      REPORT COUNT    1       Only report an IE exception once
1219  *      LOGERR          1       Enable logging of errors
1220  *
1221  *    The remaining fields are left as-is, preserving the current values.  If we
1222  *    cannot set some of these fields, then we do our best.  Some drives may
1223  *    have a static configuration which still allows for some monitoring.
1224  *
1225  * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
1226  *    LOG SENSE command.
1227  *
1228  * 4. Check to see if the self-test log page (page 0x10) is supported.
1229  *
1230  * 5. Check to see if the temperature log page (page 0x0D) is supported, and
1231  *    contains a reference temperature.
1232  *
1233  * 6. Clear the GLTSD bit in control mode page 0xA.  This will allow the drive
1234  *    to save each of the log pages described above to nonvolatile storage.
1235  *    This is essential if the drive is to remember its failures across
1236  *    loss of power.
1237  */
1238 static void *
1239 ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip)
1240 {
1241         boolean_t changed;
1242 
1243         sip->si_dsp = dsp;
1244 
1245         /* Load and validate mode pages */
1246         if (load_modepages(sip) != 0) {
1247                 ds_scsi_close(sip);
1248                 return (NULL);
1249         }
1250 
1251         /* Load and validate log pages */
1252         if (load_logpages(sip) != 0) {
1253                 ds_scsi_close(sip);
1254                 return (NULL);
1255         }
1256 
1257         /* Load IE state */
1258         if (load_ie_modepage(sip) != 0 ||
1259             scsi_enable_ie(sip, &changed) != 0 ||
1260             (changed && load_ie_modepage(sip) != 0)) {
1261                 ds_scsi_close(sip);
1262                 return (NULL);
1263         }
1264 
1265         /* Clear the GLTSD bit in the control page */
1266         if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) {
1267                 ds_scsi_close(sip);
1268                 return (NULL);
1269         }
1270 
1271         return (sip);
1272 }
1273 
1274 static void *
1275 ds_scsi_open_uscsi(disk_status_t *dsp)
1276 {
1277         ds_scsi_info_t *sip;
1278 
1279         if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
1280                 (void) ds_set_errno(dsp, EDS_NOMEM);
1281                 return (NULL);
1282         }
1283 
1284         return (ds_scsi_open_common(dsp, sip));
1285 }
1286 
1287 static void *
1288 ds_scsi_open_sim(disk_status_t *dsp)
1289 {
1290         ds_scsi_info_t *sip;
1291 
1292         if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
1293                 (void) ds_set_errno(dsp, EDS_NOMEM);
1294                 return (NULL);
1295         }
1296 
1297         if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) {
1298                 (void) ds_set_errno(dsp, EDS_NO_TRANSPORT);
1299                 free(sip);
1300                 return (NULL);
1301         }
1302 
1303         return (ds_scsi_open_common(dsp, sip));
1304 }
1305 
1306 
1307 /*
1308  * Scan for any faults.  The following steps are performed:
1309  *
1310  * 1. If the temperature log page is supported, check the current temperature
1311  *    and threshold.  If the current temperature exceeds the threshold, report
1312  *    and overtemp fault.
1313  *
1314  * 2. If the selftest log page is supported, check to the last completed self
1315  *    test.  If the last completed test resulted in failure, report a selftest
1316  *    fault.
1317  *
1318  * 3. If the IE log page is supported, check to see if failure is predicted.  If
1319  *    so, indicate a predictive failure fault.
1320  *
1321  * 4. If the IE log page is not supported, but the mode page supports report on
1322  *    request mode, then issue a REQUEST SENSE for the mode page.  Indicate a
1323  *    predictive failure fault if necessary.
1324  */
1325 static int
1326 ds_scsi_scan(void *arg)
1327 {
1328         ds_scsi_info_t *sip = arg;
1329         int i;
1330 
1331         for (i = 0; i < NLOG_VALIDATION; i++) {
1332                 if ((sip->si_supp_log & log_validation[i].ve_supported) == 0)
1333                         continue;
1334 
1335                 if (analyze_one_logpage(sip, &log_validation[i]) != 0)
1336                         return (-1);
1337         }
1338 
1339         if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) &&
1340             (sip->si_supp_mode & MODEPAGE_SUPP_IEC) &&
1341             analyze_ie_sense(sip) != 0)
1342                 return (-1);
1343 
1344         return (0);
1345 }
1346 
1347 ds_transport_t ds_scsi_uscsi_transport = {
1348         ds_scsi_open_uscsi,
1349         ds_scsi_close,
1350         ds_scsi_scan
1351 };
1352 
1353 ds_transport_t ds_scsi_sim_transport = {
1354         ds_scsi_open_sim,
1355         ds_scsi_close,
1356         ds_scsi_scan
1357 };