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