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