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
scsi_translate_error(struct scsi_extended_sense * rq,uint_t * skeyp,uint_t * ascp,uint_t * ascqp)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
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*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
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*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
scsi_request_sense(ds_scsi_info_t * sip,uint_t * skp,uint_t * ascp,uint_t * ascqp)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
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*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
mode_page_present(uchar_t * pgdata,uint_t pgdatalen,uchar_t pagecode)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
load_modepages(ds_scsi_info_t * sip)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
verify_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * lp)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
load_logpages(ds_scsi_info_t * sip)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
logpage_ie_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)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
logpage_temp_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)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
logpage_selftest_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)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
load_ie_modepage(ds_scsi_info_t * sip)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
scsi_enable_ie(ds_scsi_info_t * sip,boolean_t * changed)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
clear_gltsd(ds_scsi_info_t * sip)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
analyze_one_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * entry)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
logpage_ie_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)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
logpage_temp_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)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
logpage_selftest_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)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
analyze_ie_sense(ds_scsi_info_t * sip)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
ds_scsi_close(void * arg)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 *
ds_scsi_open_common(disk_status_t * dsp,ds_scsi_info_t * sip)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 *
ds_scsi_open_uscsi(disk_status_t * dsp)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 *
ds_scsi_open_sim(disk_status_t * dsp)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
ds_scsi_scan(void * arg)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