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