xref: /onnv-gate/usr/src/lib/fm/libdiskstatus/common/ds_scsi.c (revision 4198:6bdfb19526db)
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