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