124db4641Seschrock /*
224db4641Seschrock  * CDDL HEADER START
324db4641Seschrock  *
424db4641Seschrock  * The contents of this file are subject to the terms of the
524db4641Seschrock  * Common Development and Distribution License (the "License").
624db4641Seschrock  * You may not use this file except in compliance with the License.
724db4641Seschrock  *
824db4641Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
924db4641Seschrock  * or http://www.opensolaris.org/os/licensing.
1024db4641Seschrock  * See the License for the specific language governing permissions
1124db4641Seschrock  * and limitations under the License.
1224db4641Seschrock  *
1324db4641Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
1424db4641Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1524db4641Seschrock  * If applicable, add the following below this CDDL HEADER, with the
1624db4641Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
1724db4641Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
1824db4641Seschrock  *
1924db4641Seschrock  * CDDL HEADER END
2024db4641Seschrock  */
2124db4641Seschrock /*
2224db4641Seschrock  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2324db4641Seschrock  * Use is subject to license terms.
240244979bSAlek Pinchuk  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
2524db4641Seschrock  */
2624db4641Seschrock 
2724db4641Seschrock #include <assert.h>
2824db4641Seschrock #include <errno.h>
2924db4641Seschrock #include <libdiskstatus.h>
3024db4641Seschrock #include <limits.h>
3124db4641Seschrock #include <stdlib.h>
3224db4641Seschrock #include <strings.h>
3324db4641Seschrock #include <sys/fm/io/scsi.h>
3424db4641Seschrock 
3524db4641Seschrock #include "ds_scsi.h"
3624db4641Seschrock #include "ds_scsi_sim.h"
3724db4641Seschrock #include "ds_scsi_uscsi.h"
3824db4641Seschrock 
3924db4641Seschrock typedef struct ds_scsi_info {
4024db4641Seschrock 	disk_status_t		*si_dsp;
4124db4641Seschrock 	void			*si_sim;
4224db4641Seschrock 	int			si_cdblen;
4324db4641Seschrock 	int			si_supp_mode;
4424db4641Seschrock 	int			si_supp_log;
4524db4641Seschrock 	int			si_extensions;
4624db4641Seschrock 	int			si_reftemp;
4724db4641Seschrock 	scsi_ms_hdrs_t		si_hdrs;
4824db4641Seschrock 	scsi_ie_page_t		si_iec_current;
4924db4641Seschrock 	scsi_ie_page_t		si_iec_changeable;
5024db4641Seschrock 	nvlist_t		*si_state_modepage;
5124db4641Seschrock 	nvlist_t		*si_state_logpage;
5224db4641Seschrock 	nvlist_t		*si_state_iec;
5324db4641Seschrock } ds_scsi_info_t;
5424db4641Seschrock 
5524db4641Seschrock #define	scsi_set_errno(sip, errno)	(ds_set_errno((sip)->si_dsp, (errno)))
5624db4641Seschrock 
5724db4641Seschrock /*
5824db4641Seschrock  * Table to validate log pages
5924db4641Seschrock  */
6024db4641Seschrock typedef int (*logpage_validation_fn_t)(ds_scsi_info_t *,
6124db4641Seschrock     scsi_log_parameter_header_t *, int, nvlist_t *);
6224db4641Seschrock typedef int (*logpage_analyze_fn_t)(ds_scsi_info_t *,
6324db4641Seschrock     scsi_log_parameter_header_t *, int);
6424db4641Seschrock 
6524db4641Seschrock typedef struct logpage_validation_entry {
6624db4641Seschrock 	uchar_t			ve_code;
6724db4641Seschrock 	int			ve_supported;
6824db4641Seschrock 	const char		*ve_desc;
6924db4641Seschrock 	logpage_validation_fn_t	ve_validate;
7024db4641Seschrock 	logpage_analyze_fn_t	ve_analyze;
7124db4641Seschrock } logpage_validation_entry_t;
7224db4641Seschrock 
7324db4641Seschrock static int logpage_ie_verify(ds_scsi_info_t *,
7424db4641Seschrock     scsi_log_parameter_header_t *, int, nvlist_t *);
7524db4641Seschrock static int logpage_temp_verify(ds_scsi_info_t *,
7624db4641Seschrock     scsi_log_parameter_header_t *, int, nvlist_t *);
7724db4641Seschrock static int logpage_selftest_verify(ds_scsi_info_t *,
7824db4641Seschrock     scsi_log_parameter_header_t *, int, nvlist_t *);
790244979bSAlek Pinchuk static int logpage_ssm_verify(ds_scsi_info_t *,
800244979bSAlek Pinchuk     scsi_log_parameter_header_t *, int, nvlist_t *);
8124db4641Seschrock 
8224db4641Seschrock static int logpage_ie_analyze(ds_scsi_info_t *,
8324db4641Seschrock     scsi_log_parameter_header_t *, int);
8424db4641Seschrock static int logpage_temp_analyze(ds_scsi_info_t *,
8524db4641Seschrock     scsi_log_parameter_header_t *, int);
8624db4641Seschrock static int logpage_selftest_analyze(ds_scsi_info_t *,
8724db4641Seschrock     scsi_log_parameter_header_t *, int);
880244979bSAlek Pinchuk static int logpage_ssm_analyze(ds_scsi_info_t *,
890244979bSAlek Pinchuk     scsi_log_parameter_header_t *, int);
9024db4641Seschrock 
9124db4641Seschrock static struct logpage_validation_entry log_validation[] = {
9224db4641Seschrock 	{ LOGPAGE_IE,		LOGPAGE_SUPP_IE,
9324db4641Seschrock 	    "informational-exceptions",
9424db4641Seschrock 	    logpage_ie_verify,	logpage_ie_analyze },
9524db4641Seschrock 	{ LOGPAGE_TEMP,		LOGPAGE_SUPP_TEMP,
9624db4641Seschrock 	    "temperature",
9724db4641Seschrock 	    logpage_temp_verify, logpage_temp_analyze },
9824db4641Seschrock 	{ LOGPAGE_SELFTEST,	LOGPAGE_SUPP_SELFTEST,
9924db4641Seschrock 	    "self-test",
1000244979bSAlek Pinchuk 	    logpage_selftest_verify, logpage_selftest_analyze },
1010244979bSAlek Pinchuk 	{ LOGPAGE_SSM,		LOGPAGE_SUPP_SSM,
1020244979bSAlek Pinchuk 	    FM_EREPORT_SCSI_SSMWEAROUT,
1030244979bSAlek Pinchuk 	    logpage_ssm_verify, logpage_ssm_analyze }
10424db4641Seschrock };
10524db4641Seschrock 
10624db4641Seschrock #define	NLOG_VALIDATION	(sizeof (log_validation) / sizeof (log_validation[0]))
10724db4641Seschrock 
10824db4641Seschrock /*
10924db4641Seschrock  * Given an extended sense page, retrieves the sense key, as well as the
11024db4641Seschrock  * additional sense code information.
11124db4641Seschrock  */
11224db4641Seschrock static void
scsi_translate_error(struct scsi_extended_sense * rq,uint_t * skeyp,uint_t * ascp,uint_t * ascqp)11324db4641Seschrock scsi_translate_error(struct scsi_extended_sense *rq, uint_t *skeyp,
11424db4641Seschrock     uint_t *ascp, uint_t *ascqp)
11524db4641Seschrock {
11624db4641Seschrock 	struct scsi_descr_sense_hdr *sdsp =
11724db4641Seschrock 	    (struct scsi_descr_sense_hdr *)rq;
11824db4641Seschrock 
11924db4641Seschrock 	*skeyp = rq->es_key;
12024db4641Seschrock 
12124db4641Seschrock 	/*
12224db4641Seschrock 	 * Get asc, ascq and info field from sense data.  There are two
12324db4641Seschrock 	 * possible formats (fixed sense data and descriptor sense data)
12424db4641Seschrock 	 * depending on the value of es_code.
12524db4641Seschrock 	 */
12624db4641Seschrock 	switch (rq->es_code) {
12724db4641Seschrock 	case CODE_FMT_DESCR_CURRENT:
12824db4641Seschrock 	case CODE_FMT_DESCR_DEFERRED:
12924db4641Seschrock 
13024db4641Seschrock 		*ascp = sdsp->ds_add_code;
13124db4641Seschrock 		*ascqp = sdsp->ds_qual_code;
13224db4641Seschrock 		break;
13324db4641Seschrock 
13424db4641Seschrock 	case CODE_FMT_FIXED_CURRENT:
13524db4641Seschrock 	case CODE_FMT_FIXED_DEFERRED:
13624db4641Seschrock 	default:
13724db4641Seschrock 
13824db4641Seschrock 		if (rq->es_add_len >= 6) {
13924db4641Seschrock 			*ascp = rq->es_add_code;
14024db4641Seschrock 			*ascqp = rq->es_qual_code;
14124db4641Seschrock 		} else {
14224db4641Seschrock 			*ascp = 0xff;
14324db4641Seschrock 			*ascqp = 0xff;
14424db4641Seschrock 		}
14524db4641Seschrock 		break;
14624db4641Seschrock 	}
14724db4641Seschrock }
14824db4641Seschrock 
14924db4641Seschrock /*
15024db4641Seschrock  * Routines built atop the bare uscsi commands, which take into account the
15124db4641Seschrock  * command length, automatically translate any scsi errors, and transparently
15224db4641Seschrock  * call into the simulator if active.
15324db4641Seschrock  */
15424db4641Seschrock static int
scsi_mode_select(ds_scsi_info_t * sip,uchar_t page_code,int options,void * buf,uint_t buflen,scsi_ms_hdrs_t * headers,uint_t * skp,uint_t * ascp,uint_t * ascqp)15524db4641Seschrock scsi_mode_select(ds_scsi_info_t *sip, uchar_t page_code, int options,
15624db4641Seschrock     void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
15724db4641Seschrock     uint_t *ascp, uint_t *ascqp)
15824db4641Seschrock {
15924db4641Seschrock 	int result;
16024db4641Seschrock 	struct scsi_extended_sense sense;
16124db4641Seschrock 	int senselen = sizeof (struct scsi_extended_sense);
16224db4641Seschrock 	struct mode_page *mp = (struct mode_page *)buf;
16324db4641Seschrock 
16424db4641Seschrock 	assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
16524db4641Seschrock 	    sip->si_cdblen == MODE_CMD_LEN_10);
16624db4641Seschrock 	assert(headers->ms_length == sip->si_cdblen);
16724db4641Seschrock 
16824db4641Seschrock 	bzero(&sense, sizeof (struct scsi_extended_sense));
16924db4641Seschrock 
17024db4641Seschrock 	if (mp->ps) {
17124db4641Seschrock 		options |= MODE_SELECT_SP;
17224db4641Seschrock 		mp->ps = 0;
17324db4641Seschrock 	} else {
17424db4641Seschrock 		options &= ~MODE_SELECT_SP;
17524db4641Seschrock 	}
17624db4641Seschrock 
17724db4641Seschrock 	if (sip->si_cdblen == MODE_CMD_LEN_6) {
17824db4641Seschrock 		/* The following fields are reserved during mode select: */
17924db4641Seschrock 		headers->ms_hdr.g0.ms_header.length = 0;
18024db4641Seschrock 		headers->ms_hdr.g0.ms_header.device_specific = 0;
18124db4641Seschrock 
18224db4641Seschrock 		if (sip->si_sim)
18324db4641Seschrock 			result = simscsi_mode_select(sip->si_sim,
18424db4641Seschrock 			    page_code, options, buf, buflen,
18524db4641Seschrock 			    &headers->ms_hdr.g0, &sense, &senselen);
18624db4641Seschrock 		else
18724db4641Seschrock 			result = uscsi_mode_select(sip->si_dsp->ds_fd,
18824db4641Seschrock 			    page_code, options, buf, buflen,
18924db4641Seschrock 			    &headers->ms_hdr.g0, &sense, &senselen);
19024db4641Seschrock 	} else {
19124db4641Seschrock 		/* The following fields are reserved during mode select: */
19224db4641Seschrock 		headers->ms_hdr.g1.ms_header.length = 0;
19324db4641Seschrock 		headers->ms_hdr.g1.ms_header.device_specific = 0;
19424db4641Seschrock 
19524db4641Seschrock 		if (sip->si_sim)
19624db4641Seschrock 			result = simscsi_mode_select_10(sip->si_sim,
19724db4641Seschrock 			    page_code, options, buf, buflen,
19824db4641Seschrock 			    &headers->ms_hdr.g1, &sense, &senselen);
19924db4641Seschrock 		else
20024db4641Seschrock 			result = uscsi_mode_select_10(sip->si_dsp->ds_fd,
20124db4641Seschrock 			    page_code, options, buf, buflen,
20224db4641Seschrock 			    &headers->ms_hdr.g1, &sense, &senselen);
20324db4641Seschrock 	}
20424db4641Seschrock 
20524db4641Seschrock 	if (result != 0)
20624db4641Seschrock 		scsi_translate_error(&sense, skp, ascp, ascqp);
20724db4641Seschrock 
20824db4641Seschrock 	return (result);
20924db4641Seschrock }
21024db4641Seschrock 
21124db4641Seschrock static int
scsi_mode_sense(ds_scsi_info_t * sip,uchar_t page_code,uchar_t pc,void * buf,uint_t buflen,scsi_ms_hdrs_t * headers,uint_t * skp,uint_t * ascp,uint_t * ascqp)21224db4641Seschrock scsi_mode_sense(ds_scsi_info_t *sip, uchar_t page_code, uchar_t pc,
21324db4641Seschrock     void *buf, uint_t buflen, scsi_ms_hdrs_t *headers, uint_t *skp,
21424db4641Seschrock     uint_t *ascp, uint_t *ascqp)
21524db4641Seschrock {
21624db4641Seschrock 	int result;
21724db4641Seschrock 	struct scsi_extended_sense sense;
21824db4641Seschrock 	int senselen = sizeof (struct scsi_extended_sense);
21924db4641Seschrock 
22024db4641Seschrock 	assert(sip->si_cdblen == MODE_CMD_LEN_6 ||
22124db4641Seschrock 	    sip->si_cdblen == MODE_CMD_LEN_10);
22224db4641Seschrock 
22324db4641Seschrock 	bzero(&sense, sizeof (struct scsi_extended_sense));
22424db4641Seschrock 
22524db4641Seschrock 	bzero(headers, sizeof (scsi_ms_hdrs_t));
22624db4641Seschrock 	headers->ms_length = sip->si_cdblen;
22724db4641Seschrock 
22824db4641Seschrock 	if (sip->si_cdblen == MODE_CMD_LEN_6) {
22924db4641Seschrock 		if (sip->si_sim)
23024db4641Seschrock 			result = simscsi_mode_sense(sip->si_sim,
23124db4641Seschrock 			    page_code, pc, buf, buflen, &headers->ms_hdr.g0,
23224db4641Seschrock 			    &sense, &senselen);
23324db4641Seschrock 		else
23424db4641Seschrock 			result = uscsi_mode_sense(sip->si_dsp->ds_fd, page_code,
23524db4641Seschrock 			    pc, buf, buflen, &headers->ms_hdr.g0, &sense,
23624db4641Seschrock 			    &senselen);
23724db4641Seschrock 	} else {
23824db4641Seschrock 		if (sip->si_sim)
23924db4641Seschrock 			result = simscsi_mode_sense_10(sip->si_sim,
24024db4641Seschrock 			    page_code, pc, buf, buflen, &headers->ms_hdr.g1,
24124db4641Seschrock 			    &sense, &senselen);
24224db4641Seschrock 		else
24324db4641Seschrock 			result = uscsi_mode_sense_10(sip->si_dsp->ds_fd,
24424db4641Seschrock 			    page_code, pc, buf, buflen, &headers->ms_hdr.g1,
24524db4641Seschrock 			    &sense, &senselen);
24624db4641Seschrock 	}
24724db4641Seschrock 
24824db4641Seschrock 	if (result != 0)
24924db4641Seschrock 		scsi_translate_error(&sense, skp, ascp, ascqp);
25024db4641Seschrock 
25124db4641Seschrock 	return (result);
25224db4641Seschrock }
25324db4641Seschrock 
25424db4641Seschrock static int
scsi_request_sense(ds_scsi_info_t * sip,uint_t * skp,uint_t * ascp,uint_t * ascqp)25524db4641Seschrock scsi_request_sense(ds_scsi_info_t *sip, uint_t *skp, uint_t *ascp,
25624db4641Seschrock     uint_t *ascqp)
25724db4641Seschrock {
25824db4641Seschrock 	struct scsi_extended_sense sense, sensebuf;
25924db4641Seschrock 	int senselen = sizeof (struct scsi_extended_sense);
26024db4641Seschrock 	int sensebuflen = sizeof (struct scsi_extended_sense);
26124db4641Seschrock 	int result;
26224db4641Seschrock 
26324db4641Seschrock 	bzero(&sense, sizeof (struct scsi_extended_sense));
26424db4641Seschrock 	bzero(&sensebuf, sizeof (struct scsi_extended_sense));
26524db4641Seschrock 
26624db4641Seschrock 	if (sip->si_sim)
26724db4641Seschrock 		result = simscsi_request_sense(sip->si_sim,
26824db4641Seschrock 		    (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
26924db4641Seschrock 	else
27024db4641Seschrock 		result = uscsi_request_sense(sip->si_dsp->ds_fd,
27124db4641Seschrock 		    (caddr_t)&sensebuf, sensebuflen, &sense, &senselen);
27224db4641Seschrock 
27324db4641Seschrock 	if (result == 0)
27424db4641Seschrock 		scsi_translate_error(&sensebuf, skp, ascp, ascqp);
27524db4641Seschrock 	else
27624db4641Seschrock 		scsi_translate_error(&sense, skp, ascp, ascqp);
27724db4641Seschrock 
27824db4641Seschrock 	return (result);
27924db4641Seschrock }
28024db4641Seschrock 
28124db4641Seschrock static int
scsi_log_sense(ds_scsi_info_t * sip,int page_code,int page_control,caddr_t page_data,int page_size,uint_t * skp,uint_t * ascp,uint_t * ascqp)28224db4641Seschrock scsi_log_sense(ds_scsi_info_t *sip, int page_code, int page_control,
28324db4641Seschrock     caddr_t page_data, int page_size, uint_t *skp, uint_t *ascp, uint_t *ascqp)
28424db4641Seschrock {
28524db4641Seschrock 	int result;
28624db4641Seschrock 	struct scsi_extended_sense sense;
28724db4641Seschrock 	int senselen = sizeof (struct scsi_extended_sense);
28824db4641Seschrock 
28924db4641Seschrock 	if (sip->si_sim)
29024db4641Seschrock 		result = simscsi_log_sense(sip->si_sim,
29124db4641Seschrock 		    page_code, page_control, page_data, page_size, &sense,
29224db4641Seschrock 		    &senselen);
29324db4641Seschrock 	else
29424db4641Seschrock 		result = uscsi_log_sense(sip->si_dsp->ds_fd,
29524db4641Seschrock 		    page_code, page_control, page_data, page_size, &sense,
29624db4641Seschrock 		    &senselen);
29724db4641Seschrock 
29824db4641Seschrock 	if (result != 0)
29924db4641Seschrock 		scsi_translate_error(&sense, skp, ascp, ascqp);
30024db4641Seschrock 
30124db4641Seschrock 	return (result);
30224db4641Seschrock }
30324db4641Seschrock 
30424db4641Seschrock /*
30524db4641Seschrock  * Given a list of supported mode pages, determine if the given page is present.
30624db4641Seschrock  */
30724db4641Seschrock static boolean_t
mode_page_present(uchar_t * pgdata,uint_t pgdatalen,uchar_t pagecode)30824db4641Seschrock mode_page_present(uchar_t *pgdata, uint_t pgdatalen, uchar_t pagecode)
30924db4641Seschrock {
31024db4641Seschrock 	uint_t i = 0;
31124db4641Seschrock 	struct mode_page *pg;
31224db4641Seschrock 	boolean_t found = B_FALSE;
31324db4641Seschrock 
31424db4641Seschrock 	/*
31524db4641Seschrock 	 * The mode page list contains all mode pages supported by the device,
31624db4641Seschrock 	 * one after the other.
31724db4641Seschrock 	 */
31824db4641Seschrock 	while (i < pgdatalen) {
31924db4641Seschrock 		pg = (struct mode_page *)&pgdata[i];
32024db4641Seschrock 
32124db4641Seschrock 		if (pg->code == pagecode) {
32224db4641Seschrock 			found = B_TRUE;
32324db4641Seschrock 			break;
32424db4641Seschrock 		}
32524db4641Seschrock 
32624db4641Seschrock 		i += MODESENSE_PAGE_LEN(pg);
32724db4641Seschrock 	}
32824db4641Seschrock 
32924db4641Seschrock 	return (found);
33024db4641Seschrock }
33124db4641Seschrock 
33224db4641Seschrock /*
33324db4641Seschrock  * Load mode pages and check that the appropriate pages are supported.
33424db4641Seschrock  *
33524db4641Seschrock  * As part of this process, we determine which form of the MODE SENSE / MODE
33624db4641Seschrock  * SELECT command to use (the 6-byte or 10-byte version) by executing a MODE
33724db4641Seschrock  * SENSE command for a page that should be implemented by the device.
33824db4641Seschrock  */
33924db4641Seschrock static int
load_modepages(ds_scsi_info_t * sip)34024db4641Seschrock load_modepages(ds_scsi_info_t *sip)
34124db4641Seschrock {
34224db4641Seschrock 	int allpages_buflen;
34324db4641Seschrock 	uchar_t *allpages;
34424db4641Seschrock 	scsi_ms_hdrs_t headers;
34524db4641Seschrock 	int result;
34624db4641Seschrock 	uint_t skey, asc, ascq;
34724db4641Seschrock 	int datalength = 0;
34824db4641Seschrock 	scsi_ms_header_t *smh = &headers.ms_hdr.g0;
34924db4641Seschrock 	scsi_ms_header_g1_t *smh_g1 = &headers.ms_hdr.g1;
35024db4641Seschrock 	nvlist_t *nvl;
35124db4641Seschrock 
35224db4641Seschrock 	allpages_buflen = MAX_BUFLEN(scsi_ms_header_g1_t);
35324db4641Seschrock 	if ((allpages = calloc(allpages_buflen, 1)) == NULL)
35424db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
35524db4641Seschrock 
35624db4641Seschrock 	bzero(&headers, sizeof (headers));
35724db4641Seschrock 
35824db4641Seschrock 	/*
35924db4641Seschrock 	 * Attempt a mode sense(6).  If that fails, try a mode sense(10)
36024db4641Seschrock 	 *
36124db4641Seschrock 	 * allpages is allocated to be of the maximum size for either a mode
36224db4641Seschrock 	 * sense(6) or mode sense(10) MODEPAGE_ALLPAGES response.
36324db4641Seschrock 	 *
36424db4641Seschrock 	 * Note that the length passed into uscsi_mode_sense should be set to
36524db4641Seschrock 	 * the maximum size of the parameter response, which in this case is
36624db4641Seschrock 	 * UCHAR_MAX - the size of the headers/block descriptors.
36724db4641Seschrock 	 */
36824db4641Seschrock 	sip->si_cdblen = MODE_CMD_LEN_6;
36924db4641Seschrock 	if ((result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES, PC_CURRENT,
37024db4641Seschrock 	    (caddr_t)allpages, UCHAR_MAX - sizeof (scsi_ms_header_t),
37124db4641Seschrock 	    &headers, &skey, &asc, &ascq)) == 0) {
37224db4641Seschrock 		/*
37324db4641Seschrock 		 * Compute the data length of the page that contains all mode
37424db4641Seschrock 		 * sense pages.  This is a bit tricky because the format of the
37524db4641Seschrock 		 * response from the lun is:
37624db4641Seschrock 		 *
37724db4641Seschrock 		 * header: <length> <medium type byte> <dev specific byte>
37824db4641Seschrock 		 *	   <block descriptor length>
37924db4641Seschrock 		 *	   [<optional block descriptor>]
38024db4641Seschrock 		 * data:   [<mode page data> <mode page data> ...]
38124db4641Seschrock 		 *
38224db4641Seschrock 		 * Since the length field in the header describes the length of
38324db4641Seschrock 		 * the entire response.  This includes the header, but NOT
38424db4641Seschrock 		 * the length field itself, which is 1 or 2 bytes depending on
38524db4641Seschrock 		 * which mode sense type (6- or 10- byte) is being executed.
38624db4641Seschrock 		 *
38724db4641Seschrock 		 * So, the data length equals the length value in the header
38824db4641Seschrock 		 * plus 1 (because the length byte was not included in the
38924db4641Seschrock 		 * length count), minus [[the sum of the length of the header
39024db4641Seschrock 		 * and the length of the block descriptor]].
39124db4641Seschrock 		 */
39224db4641Seschrock 		datalength = (smh->ms_header.length +
39324db4641Seschrock 		    sizeof (smh->ms_header.length)) -
39424db4641Seschrock 		    (sizeof (struct mode_header) +
39524db4641Seschrock 		    smh->ms_header.bdesc_length);
39624db4641Seschrock 	} else if (SCSI_INVALID_OPCODE(skey, asc, ascq)) {
39724db4641Seschrock 		/*
39824db4641Seschrock 		 * Fallback and try the 10-byte version of the command.
39924db4641Seschrock 		 */
40024db4641Seschrock 		sip->si_cdblen = MODE_CMD_LEN_10;
40124db4641Seschrock 		result = scsi_mode_sense(sip, MODEPAGE_ALLPAGES,
40224db4641Seschrock 		    PC_CURRENT, (caddr_t)allpages, allpages_buflen,
40324db4641Seschrock 		    &headers, &skey, &asc, &ascq);
40424db4641Seschrock 
40524db4641Seschrock 		if (result == 0) {
40624db4641Seschrock 			datalength = (BE_16(smh_g1->ms_header.length) +
40724db4641Seschrock 			    sizeof (smh_g1->ms_header.length)) -
40824db4641Seschrock 			    (sizeof (struct mode_header_g1) +
40924db4641Seschrock 			    BE_16(smh_g1->ms_header.bdesc_length));
41024db4641Seschrock 
41124db4641Seschrock 		}
41224db4641Seschrock 	}
41324db4641Seschrock 
41424db4641Seschrock 	if (result == 0 && datalength >= 0) {
41524db4641Seschrock 		if (nvlist_add_int8(sip->si_dsp->ds_state, "command-length",
41624db4641Seschrock 		    sip->si_cdblen == MODE_CMD_LEN_6 ? 6 : 10) != 0) {
41724db4641Seschrock 			free(allpages);
41824db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
41924db4641Seschrock 		}
42024db4641Seschrock 
42124db4641Seschrock 		nvl = NULL;
42224db4641Seschrock 		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
42324db4641Seschrock 		    nvlist_add_nvlist(sip->si_dsp->ds_state, "modepages",
42424db4641Seschrock 		    nvl) != 0) {
42524db4641Seschrock 			free(allpages);
42624db4641Seschrock 			nvlist_free(nvl);
42724db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
42824db4641Seschrock 		}
42924db4641Seschrock 
43024db4641Seschrock 		nvlist_free(nvl);
43124db4641Seschrock 		result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
43224db4641Seschrock 		    "modepages", &sip->si_state_modepage);
43324db4641Seschrock 		assert(result == 0);
43424db4641Seschrock 
43524db4641Seschrock 		/*
43624db4641Seschrock 		 * One of the sets of the commands (above) succeeded, so now
43724db4641Seschrock 		 * look for the mode pages we need and record them appropriately
43824db4641Seschrock 		 */
43924db4641Seschrock 		if (mode_page_present(allpages, datalength,
44024db4641Seschrock 		    MODEPAGE_INFO_EXCPT)) {
44124db4641Seschrock 
44224db4641Seschrock 			nvl = NULL;
44324db4641Seschrock 			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
44424db4641Seschrock 			    nvlist_add_nvlist(sip->si_state_modepage,
44524db4641Seschrock 			    "informational-exceptions", nvl) != 0) {
44624db4641Seschrock 				free(allpages);
44724db4641Seschrock 				nvlist_free(nvl);
44824db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
44924db4641Seschrock 			}
45024db4641Seschrock 			nvlist_free(nvl);
45124db4641Seschrock 			sip->si_supp_mode |= MODEPAGE_SUPP_IEC;
45224db4641Seschrock 			result = nvlist_lookup_nvlist(sip->si_state_modepage,
45324db4641Seschrock 			    "informational-exceptions", &sip->si_state_iec);
45424db4641Seschrock 			assert(result == 0);
45524db4641Seschrock 		}
45624db4641Seschrock 
45724db4641Seschrock 	} else {
45824db4641Seschrock 		/*
45924db4641Seschrock 		 * If the device failed to respond to one of the basic commands,
46024db4641Seschrock 		 * then assume it's not a SCSI device or otherwise doesn't
46124db4641Seschrock 		 * support the necessary transport.
46224db4641Seschrock 		 */
46324db4641Seschrock 		if (datalength < 0)
46424db4641Seschrock 			dprintf("command returned invalid data length (%d)\n",
46524db4641Seschrock 			    datalength);
46624db4641Seschrock 		else
46724db4641Seschrock 			dprintf("failed to load modepages (KEY=0x%x "
46824db4641Seschrock 			    "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
46924db4641Seschrock 
47024db4641Seschrock 		result = scsi_set_errno(sip, EDS_NO_TRANSPORT);
47124db4641Seschrock 	}
47224db4641Seschrock 
47324db4641Seschrock 	free(allpages);
47424db4641Seschrock 	return (result);
47524db4641Seschrock }
47624db4641Seschrock 
47724db4641Seschrock /*
47824db4641Seschrock  * Verify a single logpage.  This will do some generic validation and then call
47924db4641Seschrock  * the logpage-specific function for further verification.
48024db4641Seschrock  */
48124db4641Seschrock static int
verify_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * lp)48224db4641Seschrock verify_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *lp)
48324db4641Seschrock {
48424db4641Seschrock 	scsi_log_header_t *lhp;
48524db4641Seschrock 	struct scsi_extended_sense sense;
48624db4641Seschrock 	int buflen;
48724db4641Seschrock 	int log_length;
48824db4641Seschrock 	int result = 0;
48924db4641Seschrock 	uint_t kp, asc, ascq;
49024db4641Seschrock 	nvlist_t *nvl;
49124db4641Seschrock 
49224db4641Seschrock 	buflen = MAX_BUFLEN(scsi_log_header_t);
49324db4641Seschrock 	if ((lhp = calloc(buflen, 1)) == NULL)
49424db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
49524db4641Seschrock 	bzero(&sense, sizeof (struct scsi_extended_sense));
49624db4641Seschrock 
49724db4641Seschrock 	nvl = NULL;
49824db4641Seschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
49924db4641Seschrock 	    nvlist_add_nvlist(sip->si_state_logpage, lp->ve_desc, nvl) != 0) {
50024db4641Seschrock 		nvlist_free(nvl);
50124db4641Seschrock 		free(lhp);
50224db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
50324db4641Seschrock 	}
50424db4641Seschrock 	nvlist_free(nvl);
50524db4641Seschrock 	result = nvlist_lookup_nvlist(sip->si_state_logpage, lp->ve_desc, &nvl);
50624db4641Seschrock 	assert(result == 0);
50724db4641Seschrock 
50824db4641Seschrock 	result = scsi_log_sense(sip, lp->ve_code,
50924db4641Seschrock 	    PC_CUMULATIVE, (caddr_t)lhp, buflen, &kp, &asc, &ascq);
51024db4641Seschrock 
51124db4641Seschrock 	if (result == 0) {
51224db4641Seschrock 		log_length = BE_16(lhp->lh_length);
51324db4641Seschrock 		if (nvlist_add_uint16(nvl, "length", log_length) != 0) {
51424db4641Seschrock 			free(lhp);
51524db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
51624db4641Seschrock 		}
51724db4641Seschrock 
51824db4641Seschrock 		if (lp->ve_validate(sip, (scsi_log_parameter_header_t *)
51924db4641Seschrock 		    (((char *)lhp) + sizeof (scsi_log_header_t)),
52024db4641Seschrock 		    log_length, nvl) != 0) {
52124db4641Seschrock 			free(lhp);
52224db4641Seschrock 			return (-1);
52324db4641Seschrock 		}
52424db4641Seschrock 	} else {
52524db4641Seschrock 		dprintf("failed to load %s log page (KEY=0x%x "
52624db4641Seschrock 		    "ASC=0x%x ASCQ=0x%x)\n", lp->ve_desc, kp, asc, ascq);
52724db4641Seschrock 	}
52824db4641Seschrock 
52924db4641Seschrock 	free(lhp);
53024db4641Seschrock 	return (0);
53124db4641Seschrock }
53224db4641Seschrock 
53324db4641Seschrock /*
53424db4641Seschrock  * Load log pages and determine which pages are supported.
53524db4641Seschrock  */
53624db4641Seschrock static int
load_logpages(ds_scsi_info_t * sip)53724db4641Seschrock load_logpages(ds_scsi_info_t *sip)
53824db4641Seschrock {
53924db4641Seschrock 	int buflen;
54024db4641Seschrock 	scsi_supported_log_pages_t *sp;
54124db4641Seschrock 	struct scsi_extended_sense sense;
54224db4641Seschrock 	int result;
54324db4641Seschrock 	uint_t sk, asc, ascq;
54424db4641Seschrock 	int i, j;
54524db4641Seschrock 	nvlist_t *nvl;
54624db4641Seschrock 
54724db4641Seschrock 	buflen = MAX_BUFLEN(scsi_log_header_t);
54824db4641Seschrock 	if ((sp = calloc(buflen, 1)) == NULL)
54924db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
55024db4641Seschrock 
55124db4641Seschrock 	bzero(&sense, sizeof (struct scsi_extended_sense));
55224db4641Seschrock 
55324db4641Seschrock 	if ((result = scsi_log_sense(sip, LOGPAGE_SUPP_LIST,
55424db4641Seschrock 	    PC_CUMULATIVE, (caddr_t)sp, buflen, &sk, &asc, &ascq)) == 0) {
55524db4641Seschrock 		int pagecount = BE_16(sp->slp_hdr.lh_length);
55624db4641Seschrock 
55724db4641Seschrock 		for (i = 0; i < pagecount; i++) {
55824db4641Seschrock 			for (j = 0; j < NLOG_VALIDATION; j++) {
55924db4641Seschrock 				if (log_validation[j].ve_code ==
56024db4641Seschrock 				    sp->slp_pages[i])
56124db4641Seschrock 					sip->si_supp_log |=
56224db4641Seschrock 					    log_validation[j].ve_supported;
56324db4641Seschrock 			}
56424db4641Seschrock 		}
56524db4641Seschrock 	}
56624db4641Seschrock 
56724db4641Seschrock 	free(sp);
56824db4641Seschrock 	if (result == 0) {
56924db4641Seschrock 		nvl = NULL;
57024db4641Seschrock 		if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
57124db4641Seschrock 		    nvlist_add_nvlist(sip->si_dsp->ds_state, "logpages",
57224db4641Seschrock 		    nvl) != 0) {
57324db4641Seschrock 			nvlist_free(nvl);
57424db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
57524db4641Seschrock 		}
57624db4641Seschrock 
57724db4641Seschrock 		nvlist_free(nvl);
57824db4641Seschrock 		result = nvlist_lookup_nvlist(sip->si_dsp->ds_state,
57924db4641Seschrock 		    "logpages", &sip->si_state_logpage);
58024db4641Seschrock 		assert(result == 0);
58124db4641Seschrock 
58224db4641Seschrock 		/*
58324db4641Seschrock 		 * Validate the logpage contents.
58424db4641Seschrock 		 */
58524db4641Seschrock 		for (i = 0; i < NLOG_VALIDATION; i++) {
58624db4641Seschrock 			if ((sip->si_supp_log &
58724db4641Seschrock 			    log_validation[i].ve_supported) == 0)
58824db4641Seschrock 				continue;
58924db4641Seschrock 
59024db4641Seschrock 			/*
59124db4641Seschrock 			 * verify_logpage will clear the supported bit if
59224db4641Seschrock 			 * verification fails.
59324db4641Seschrock 			 */
59424db4641Seschrock 			if (verify_logpage(sip, &log_validation[i]) != 0)
59524db4641Seschrock 				return (-1);
59624db4641Seschrock 		}
59724db4641Seschrock 
59824db4641Seschrock 	} else {
59924db4641Seschrock 		dprintf("failed to get log pages "
60024db4641Seschrock 		    "(KEY=0x%x ASC=0x%x ASCq=0x%x)\n", sk, asc, ascq);
60124db4641Seschrock 	}
60224db4641Seschrock 
60324db4641Seschrock 	/*
60424db4641Seschrock 	 * We always return 0 here, even if the required log pages aren't
60524db4641Seschrock 	 * supported.
60624db4641Seschrock 	 */
60724db4641Seschrock 	return (0);
60824db4641Seschrock }
60924db4641Seschrock 
61024db4641Seschrock /*
61124db4641Seschrock  * Verify that the IE log page is sane.  This log page is potentially chock-full
61224db4641Seschrock  * of vendor specific information that we do not know how to access.  All we can
61324db4641Seschrock  * do is check for the generic predictive failure bit.  If this log page is not
61424db4641Seschrock  * well-formed, then bail out.
61524db4641Seschrock  */
61624db4641Seschrock static int
logpage_ie_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)61724db4641Seschrock logpage_ie_verify(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
61824db4641Seschrock     int log_length, nvlist_t *nvl)
61924db4641Seschrock {
62024db4641Seschrock 	int i, plen = 0;
62124db4641Seschrock 	boolean_t seen = B_FALSE;
62224db4641Seschrock 	scsi_ie_log_param_t *iep =
62324db4641Seschrock 	    (scsi_ie_log_param_t *)lphp;
62424db4641Seschrock 
62524db4641Seschrock 	for (i = 0; i < log_length; i += plen) {
62624db4641Seschrock 		iep = (scsi_ie_log_param_t *)((char *)iep + plen);
62724db4641Seschrock 
62824db4641Seschrock 		if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE) {
62924db4641Seschrock 			if (nvlist_add_boolean_value(nvl, "general",
63024db4641Seschrock 			    B_TRUE) != 0)
63124db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
63224db4641Seschrock 
63324db4641Seschrock 			if (lphp->lph_length < LOGPARAM_IE_MIN_LEN) {
63424db4641Seschrock 				if (nvlist_add_uint8(nvl,
63524db4641Seschrock 				    "invalid-length", lphp->lph_length) != 0)
63624db4641Seschrock 					return (scsi_set_errno(sip, EDS_NOMEM));
63724db4641Seschrock 			} else {
63824db4641Seschrock 				seen = B_TRUE;
63924db4641Seschrock 			}
64024db4641Seschrock 			break;
64124db4641Seschrock 		}
64224db4641Seschrock 
64324db4641Seschrock 		plen = iep->ie_hdr.lph_length +
64424db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
64524db4641Seschrock 	}
64624db4641Seschrock 
64724db4641Seschrock 	if (!seen) {
64824db4641Seschrock 		sip->si_supp_log &= ~LOGPAGE_SUPP_IE;
64924db4641Seschrock 		dprintf("IE logpage validation failed\n");
65024db4641Seschrock 	}
65124db4641Seschrock 
65224db4641Seschrock 	return (0);
65324db4641Seschrock }
65424db4641Seschrock 
65524db4641Seschrock /*
65624db4641Seschrock  * Verify the contents of the temperature log page.  The temperature log page
65724db4641Seschrock  * contains two log parameters: the current temperature, and (optionally) the
65824db4641Seschrock  * reference temperature.  For the verification phase, we check that the two
65924db4641Seschrock  * parameters we care about are well-formed.  If there is no reference
66024db4641Seschrock  * temperature, then we cannot use the page for monitoring purposes.
66124db4641Seschrock  */
66224db4641Seschrock static int
logpage_temp_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)66324db4641Seschrock logpage_temp_verify(ds_scsi_info_t *sip,
66424db4641Seschrock     scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
66524db4641Seschrock {
66624db4641Seschrock 	int i, plen = 0;
66724db4641Seschrock 	boolean_t has_reftemp = B_FALSE;
66824db4641Seschrock 	boolean_t bad_length = B_FALSE;
66924db4641Seschrock 	ushort_t param_code;
67024db4641Seschrock 
67124db4641Seschrock 	for (i = 0; i < log_length; i += plen) {
67224db4641Seschrock 		lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
67324db4641Seschrock 		param_code = BE_16(lphp->lph_param);
67424db4641Seschrock 
67524db4641Seschrock 		switch (param_code) {
67624db4641Seschrock 		case LOGPARAM_TEMP_CURTEMP:
67724db4641Seschrock 			if (nvlist_add_boolean_value(nvl, "current-temperature",
67824db4641Seschrock 			    B_TRUE) != 0)
67924db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
68024db4641Seschrock 			if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
68124db4641Seschrock 				if (nvlist_add_uint8(nvl,
68224db4641Seschrock 				    "invalid-length", lphp->lph_length) != 0)
68324db4641Seschrock 					return (scsi_set_errno(sip, EDS_NOMEM));
68424db4641Seschrock 				bad_length = B_TRUE;
68524db4641Seschrock 			}
68624db4641Seschrock 			break;
68724db4641Seschrock 
68824db4641Seschrock 		case LOGPARAM_TEMP_REFTEMP:
68924db4641Seschrock 			if (nvlist_add_boolean_value(nvl,
69024db4641Seschrock 			    "reference-temperature", B_TRUE) != 0)
69124db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
69224db4641Seschrock 			if (lphp->lph_length != LOGPARAM_TEMP_LEN) {
69324db4641Seschrock 				if (nvlist_add_uint8(nvl,
69424db4641Seschrock 				    "invalid-length", lphp->lph_length) != 0)
69524db4641Seschrock 					return (scsi_set_errno(sip, EDS_NOMEM));
69624db4641Seschrock 				bad_length = B_TRUE;
69724db4641Seschrock 			}
69824db4641Seschrock 			has_reftemp = B_TRUE;
69924db4641Seschrock 			break;
70024db4641Seschrock 		}
70124db4641Seschrock 
70224db4641Seschrock 		plen = lphp->lph_length +
70324db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
70424db4641Seschrock 	}
70524db4641Seschrock 
70624db4641Seschrock 	if (bad_length || !has_reftemp) {
70724db4641Seschrock 		sip->si_supp_log &= ~LOGPAGE_SUPP_TEMP;
70824db4641Seschrock 		dprintf("temperature logpage validation failed\n");
70924db4641Seschrock 	}
71024db4641Seschrock 
71124db4641Seschrock 	return (0);
71224db4641Seschrock }
71324db4641Seschrock 
71424db4641Seschrock /*
71524db4641Seschrock  * Verify the contents of the self test log page.  The log supports a maximum of
71624db4641Seschrock  * 20 entries, where each entry's parameter code is its index in the log.  We
71724db4641Seschrock  * check that the parameter codes fall within this range, and that the size of
71824db4641Seschrock  * each page is what we expect.  It's perfectly acceptable for there to be no
71924db4641Seschrock  * entries in this log, so we must also be sure to validate the contents as part
72024db4641Seschrock  * of the analysis phase.
72124db4641Seschrock  */
72224db4641Seschrock static int
logpage_selftest_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)72324db4641Seschrock logpage_selftest_verify(ds_scsi_info_t *sip,
72424db4641Seschrock     scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
72524db4641Seschrock {
72624db4641Seschrock 	int i, plen = 0;
72724db4641Seschrock 	boolean_t bad = B_FALSE;
72824db4641Seschrock 	int entries = 0;
72924db4641Seschrock 	ushort_t param_code;
73024db4641Seschrock 
73124db4641Seschrock 	for (i = 0; i < log_length; i += plen, entries++) {
73224db4641Seschrock 		lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
73324db4641Seschrock 		param_code = BE_16(lphp->lph_param);
73424db4641Seschrock 
73524db4641Seschrock 		if (param_code < LOGPAGE_SELFTEST_MIN_PARAM_CODE ||
73624db4641Seschrock 		    param_code > LOGPAGE_SELFTEST_MAX_PARAM_CODE) {
73724db4641Seschrock 			if (nvlist_add_uint16(nvl, "invalid-param-code",
73824db4641Seschrock 			    param_code) != 0)
73924db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
74024db4641Seschrock 			bad = B_TRUE;
74124db4641Seschrock 			break;
74224db4641Seschrock 		}
74324db4641Seschrock 
74424db4641Seschrock 		if (lphp->lph_length != LOGPAGE_SELFTEST_PARAM_LEN) {
74524db4641Seschrock 			if (nvlist_add_uint8(nvl, "invalid-length",
74624db4641Seschrock 			    lphp->lph_length) != 0)
74724db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
74824db4641Seschrock 			bad = B_TRUE;
74924db4641Seschrock 			break;
75024db4641Seschrock 
75124db4641Seschrock 		}
75224db4641Seschrock 
75324db4641Seschrock 		plen = lphp->lph_length +
75424db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
75524db4641Seschrock 	}
75624db4641Seschrock 
75724db4641Seschrock 	if (bad) {
75824db4641Seschrock 		sip->si_supp_log &= ~LOGPAGE_SUPP_SELFTEST;
75924db4641Seschrock 		dprintf("selftest logpage validation failed\n");
76024db4641Seschrock 	}
76124db4641Seschrock 
76224db4641Seschrock 	return (0);
76324db4641Seschrock }
76424db4641Seschrock 
7650244979bSAlek Pinchuk /*
7660244979bSAlek Pinchuk  * Verify the contents of the Solid State Media (SSM) log page.
7670244979bSAlek Pinchuk  * As of SBC3r36 SSM log page contains one log parameter:
7680244979bSAlek Pinchuk  * "Percentage Used Endurance Indicator" which is mandatory.
7690244979bSAlek Pinchuk  * For the verification phase, we sanity check this parameter
7700244979bSAlek Pinchuk  * by making sure it's present and it's length is set to 0x04.
7710244979bSAlek Pinchuk  */
7720244979bSAlek Pinchuk static int
logpage_ssm_verify(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length,nvlist_t * nvl)7730244979bSAlek Pinchuk logpage_ssm_verify(ds_scsi_info_t *sip,
7740244979bSAlek Pinchuk     scsi_log_parameter_header_t *lphp, int log_length, nvlist_t *nvl)
7750244979bSAlek Pinchuk {
7760244979bSAlek Pinchuk 	ushort_t param_code;
7770244979bSAlek Pinchuk 	int i, plen = 0;
7780244979bSAlek Pinchuk 
7790244979bSAlek Pinchuk 	for (i = 0; i < log_length; i += plen) {
7800244979bSAlek Pinchuk 		lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
7810244979bSAlek Pinchuk 		param_code = BE_16(lphp->lph_param);
7820244979bSAlek Pinchuk 
7830244979bSAlek Pinchuk 		switch (param_code) {
7840244979bSAlek Pinchuk 		case LOGPARAM_PRCNT_USED:
7850244979bSAlek Pinchuk 			if (nvlist_add_boolean_value(nvl,
7860244979bSAlek Pinchuk 			    FM_EREPORT_SCSI_SSMWEAROUT, B_TRUE) != 0)
7870244979bSAlek Pinchuk 				return (scsi_set_errno(sip, EDS_NOMEM));
7880244979bSAlek Pinchuk 			if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN) {
7890244979bSAlek Pinchuk 				if (nvlist_add_uint8(nvl,
7900244979bSAlek Pinchuk 				    "invalid-length", lphp->lph_length) != 0)
7910244979bSAlek Pinchuk 					return (scsi_set_errno(sip, EDS_NOMEM));
7920244979bSAlek Pinchuk 
7930244979bSAlek Pinchuk 				dprintf("solid state media logpage bad len\n");
7940244979bSAlek Pinchuk 				break;
7950244979bSAlek Pinchuk 			}
7960244979bSAlek Pinchuk 
7970244979bSAlek Pinchuk 			/* verification succeded */
7980244979bSAlek Pinchuk 			return (0);
7990244979bSAlek Pinchuk 		}
8000244979bSAlek Pinchuk 
8010244979bSAlek Pinchuk 		plen = lphp->lph_length +
8020244979bSAlek Pinchuk 		    sizeof (scsi_log_parameter_header_t);
8030244979bSAlek Pinchuk 	}
8040244979bSAlek Pinchuk 
8050244979bSAlek Pinchuk 	/* verification failed */
8060244979bSAlek Pinchuk 	sip->si_supp_log &= ~LOGPAGE_SUPP_SSM;
8070244979bSAlek Pinchuk 	return (0);
8080244979bSAlek Pinchuk }
8090244979bSAlek Pinchuk 
81024db4641Seschrock /*
81124db4641Seschrock  * Load the current IE mode pages
81224db4641Seschrock  */
81324db4641Seschrock static int
load_ie_modepage(ds_scsi_info_t * sip)81424db4641Seschrock load_ie_modepage(ds_scsi_info_t *sip)
81524db4641Seschrock {
81624db4641Seschrock 	struct scsi_ms_hdrs junk_hdrs;
81724db4641Seschrock 	int result;
81824db4641Seschrock 	uint_t skey, asc, ascq;
81924db4641Seschrock 
82024db4641Seschrock 	if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
82124db4641Seschrock 		return (0);
82224db4641Seschrock 
82324db4641Seschrock 	bzero(&sip->si_iec_current, sizeof (sip->si_iec_current));
82424db4641Seschrock 	bzero(&sip->si_iec_changeable, sizeof (sip->si_iec_changeable));
82524db4641Seschrock 
82624db4641Seschrock 	if ((result = scsi_mode_sense(sip,
82724db4641Seschrock 	    MODEPAGE_INFO_EXCPT, PC_CURRENT, &sip->si_iec_current,
82824db4641Seschrock 	    MODEPAGE_INFO_EXCPT_LEN, &sip->si_hdrs, &skey, &asc,
82924db4641Seschrock 	    &ascq)) == 0) {
83024db4641Seschrock 		result = scsi_mode_sense(sip,
83124db4641Seschrock 		    MODEPAGE_INFO_EXCPT, PC_CHANGEABLE,
83224db4641Seschrock 		    &sip->si_iec_changeable,
83324db4641Seschrock 		    MODEPAGE_INFO_EXCPT_LEN, &junk_hdrs, &skey, &asc, &ascq);
83424db4641Seschrock 	}
83524db4641Seschrock 
83624db4641Seschrock 	if (result != 0) {
83724db4641Seschrock 		dprintf("failed to get IEC modepage (KEY=0x%x "
83824db4641Seschrock 		    "ASC=0x%x ASCQ=0x%x)", skey, asc, ascq);
83924db4641Seschrock 		sip->si_supp_mode &= ~MODEPAGE_SUPP_IEC;
84024db4641Seschrock 	} else  {
84124db4641Seschrock 		if (nvlist_add_boolean_value(sip->si_state_iec,
84224db4641Seschrock 		    "dexcpt", sip->si_iec_current.ie_dexcpt) != 0 ||
84324db4641Seschrock 		    nvlist_add_boolean_value(sip->si_state_iec,
84424db4641Seschrock 		    "logerr", sip->si_iec_current.ie_logerr) != 0 ||
84524db4641Seschrock 		    nvlist_add_uint8(sip->si_state_iec,
84624db4641Seschrock 		    "mrie", sip->si_iec_current.ie_mrie) != 0 ||
84724db4641Seschrock 		    nvlist_add_boolean_value(sip->si_state_iec,
84824db4641Seschrock 		    "test", sip->si_iec_current.ie_test) != 0 ||
84924db4641Seschrock 		    nvlist_add_boolean_value(sip->si_state_iec,
85024db4641Seschrock 		    "ewasc", sip->si_iec_current.ie_ewasc) != 0 ||
85124db4641Seschrock 		    nvlist_add_boolean_value(sip->si_state_iec,
85224db4641Seschrock 		    "perf", sip->si_iec_current.ie_perf) != 0 ||
85324db4641Seschrock 		    nvlist_add_boolean_value(sip->si_state_iec,
85424db4641Seschrock 		    "ebf", sip->si_iec_current.ie_ebf) != 0 ||
85524db4641Seschrock 		    nvlist_add_uint32(sip->si_state_iec,
85624db4641Seschrock 		    "interval-timer",
85724db4641Seschrock 		    BE_32(sip->si_iec_current.ie_interval_timer)) != 0 ||
85824db4641Seschrock 		    nvlist_add_uint32(sip->si_state_iec,
85924db4641Seschrock 		    "report-count",
86024db4641Seschrock 		    BE_32(sip->si_iec_current.ie_report_count)) != 0)
86124db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
86224db4641Seschrock 	}
86324db4641Seschrock 
86424db4641Seschrock 	return (0);
86524db4641Seschrock }
86624db4641Seschrock 
86724db4641Seschrock /*
86824db4641Seschrock  * Enable IE reporting.  We prefer the following settings:
86924db4641Seschrock  *
87024db4641Seschrock  * (1) DEXCPT = 0
87124db4641Seschrock  * (3) MRIE = 6 (IE_REPORT_ON_REQUEST)
87224db4641Seschrock  * (4) EWASC = 1
87324db4641Seschrock  * (6) REPORT COUNT = 0x00000001
87424db4641Seschrock  * (7) LOGERR = 1
87524db4641Seschrock  *
87624db4641Seschrock  * However, not all drives support changing these values, and the current state
87724db4641Seschrock  * may be useful enough as-is.  For example, some drives support IE logging, but
87824db4641Seschrock  * don't support changing the MRIE.  In this case, we can still use the
87924db4641Seschrock  * information provided by the log page.
88024db4641Seschrock  */
88124db4641Seschrock static int
scsi_enable_ie(ds_scsi_info_t * sip,boolean_t * changed)88224db4641Seschrock scsi_enable_ie(ds_scsi_info_t *sip, boolean_t *changed)
88324db4641Seschrock {
88424db4641Seschrock 	scsi_ie_page_t new_iec_page;
88524db4641Seschrock 	scsi_ms_hdrs_t hdrs;
88624db4641Seschrock 	uint_t skey, asc, ascq;
88724db4641Seschrock 
88824db4641Seschrock 	if (!(sip->si_supp_mode & MODEPAGE_SUPP_IEC))
88924db4641Seschrock 		return (0);
89024db4641Seschrock 
89124db4641Seschrock 	bzero(&new_iec_page, sizeof (new_iec_page));
89224db4641Seschrock 	bzero(&hdrs, sizeof (hdrs));
89324db4641Seschrock 
89424db4641Seschrock 	(void) memcpy(&new_iec_page, &sip->si_iec_current,
89524db4641Seschrock 	    sizeof (new_iec_page));
89624db4641Seschrock 
89724db4641Seschrock 	if (IEC_IE_CHANGEABLE(sip->si_iec_changeable))
89824db4641Seschrock 		new_iec_page.ie_dexcpt = 0;
89924db4641Seschrock 
90024db4641Seschrock 	if (IEC_MRIE_CHANGEABLE(sip->si_iec_changeable))
90124db4641Seschrock 		new_iec_page.ie_mrie = IE_REPORT_ON_REQUEST;
90224db4641Seschrock 
90324db4641Seschrock 	/*
90424db4641Seschrock 	 * We only want to enable warning reporting if we are able to change the
90524db4641Seschrock 	 * mrie to report on request.  Otherwise, we risk unnecessarily
90624db4641Seschrock 	 * interrupting normal SCSI commands with a CHECK CONDITION code.
90724db4641Seschrock 	 */
90824db4641Seschrock 	if (IEC_EWASC_CHANGEABLE(sip->si_iec_changeable)) {
90924db4641Seschrock 		if (new_iec_page.ie_mrie == IE_REPORT_ON_REQUEST)
91024db4641Seschrock 			new_iec_page.ie_ewasc = 1;
91124db4641Seschrock 		else
91224db4641Seschrock 			new_iec_page.ie_ewasc = 0;
91324db4641Seschrock 	}
91424db4641Seschrock 
91524db4641Seschrock 	if (IEC_RPTCNT_CHANGEABLE(sip->si_iec_changeable))
91624db4641Seschrock 		new_iec_page.ie_report_count = BE_32(1);
91724db4641Seschrock 
91824db4641Seschrock 	if (IEC_LOGERR_CHANGEABLE(sip->si_iec_changeable))
91924db4641Seschrock 		new_iec_page.ie_logerr = 1;
92024db4641Seschrock 
92124db4641Seschrock 	/*
92224db4641Seschrock 	 * Now compare the new mode page with the existing one.
92324db4641Seschrock 	 * if there's no difference, there's no need for a mode select
92424db4641Seschrock 	 */
92524db4641Seschrock 	if (memcmp(&new_iec_page, &sip->si_iec_current,
92624db4641Seschrock 	    MODEPAGE_INFO_EXCPT_LEN) == 0) {
92724db4641Seschrock 		*changed = B_FALSE;
92824db4641Seschrock 	} else {
92924db4641Seschrock 		(void) memcpy(&hdrs, &sip->si_hdrs, sizeof (sip->si_hdrs));
93024db4641Seschrock 
93124db4641Seschrock 		if (scsi_mode_select(sip,
93224db4641Seschrock 		    MODEPAGE_INFO_EXCPT, MODE_SELECT_PF, &new_iec_page,
93324db4641Seschrock 		    MODEPAGE_INFO_EXCPT_LEN, &hdrs, &skey, &asc, &ascq) == 0) {
93424db4641Seschrock 			*changed = B_TRUE;
93524db4641Seschrock 		} else {
93624db4641Seschrock 			dprintf("failed to enable IE (KEY=0x%x "
93724db4641Seschrock 			    "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
93824db4641Seschrock 			*changed = B_FALSE;
93924db4641Seschrock 		}
94024db4641Seschrock 	}
94124db4641Seschrock 
94224db4641Seschrock 	if (nvlist_add_boolean_value(sip->si_state_iec, "changed",
94324db4641Seschrock 	    *changed) != 0)
94424db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
94524db4641Seschrock 
94624db4641Seschrock 	return (0);
94724db4641Seschrock }
94824db4641Seschrock 
94924db4641Seschrock /*
95024db4641Seschrock  * Clear the GLTSD bit, indicating log pages should be saved to non-volatile
95124db4641Seschrock  * storage.
95224db4641Seschrock  */
95324db4641Seschrock static int
clear_gltsd(ds_scsi_info_t * sip)95424db4641Seschrock clear_gltsd(ds_scsi_info_t *sip)
95524db4641Seschrock {
95624db4641Seschrock 	scsi_ms_hdrs_t hdrs, junk_hdrs;
95724db4641Seschrock 	struct mode_control_scsi3 control_pg_cur, control_pg_chg;
95824db4641Seschrock 	int result;
95924db4641Seschrock 	uint_t skey, asc, ascq;
96024db4641Seschrock 
96124db4641Seschrock 	bzero(&hdrs, sizeof (hdrs));
96224db4641Seschrock 	bzero(&control_pg_cur, sizeof (control_pg_cur));
96324db4641Seschrock 	bzero(&control_pg_chg, sizeof (control_pg_chg));
96424db4641Seschrock 
96524db4641Seschrock 	result = scsi_mode_sense(sip,
96624db4641Seschrock 	    MODEPAGE_CTRL_MODE, PC_CURRENT, &control_pg_cur,
96724db4641Seschrock 	    MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
96824db4641Seschrock 
96924db4641Seschrock 	if (result != 0) {
97024db4641Seschrock 		dprintf("failed to read Control mode page (KEY=0x%x "
97124db4641Seschrock 		    "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
97224db4641Seschrock 	} else if (control_pg_cur.mode_page.length !=
97324db4641Seschrock 	    PAGELENGTH_MODE_CONTROL_SCSI3) {
97424db4641Seschrock 		dprintf("SCSI-3 control mode page not supported\n");
97524db4641Seschrock 	} else if ((result = scsi_mode_sense(sip,
97624db4641Seschrock 	    MODEPAGE_CTRL_MODE, PC_CHANGEABLE, &control_pg_chg,
97724db4641Seschrock 	    MODEPAGE_CTRL_MODE_LEN, &junk_hdrs, &skey, &asc, &ascq))
97824db4641Seschrock 	    != 0) {
97924db4641Seschrock 		dprintf("failed to read changeable Control mode page (KEY=0x%x "
98024db4641Seschrock 		    "ASC=0x%x ASCQ=0x%x)\n", skey, asc, ascq);
98124db4641Seschrock 	} else if (control_pg_cur.gltsd && !GLTSD_CHANGEABLE(control_pg_chg)) {
98224db4641Seschrock 		dprintf("gltsd is set and not changeable\n");
98324db4641Seschrock 		if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
98424db4641Seschrock 		    "gltsd", control_pg_cur.gltsd) != 0)
98524db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
98624db4641Seschrock 	} else if (control_pg_cur.gltsd) {
98724db4641Seschrock 		control_pg_cur.gltsd = 0;
98824db4641Seschrock 		result = scsi_mode_select(sip,
98924db4641Seschrock 		    MODEPAGE_CTRL_MODE, MODE_SELECT_PF, &control_pg_cur,
99024db4641Seschrock 		    MODEPAGE_CTRL_MODE_LEN, &hdrs, &skey, &asc, &ascq);
99124db4641Seschrock 		if (result != 0)
99224db4641Seschrock 			dprintf("failed to enable GLTSD (KEY=0x%x "
99324db4641Seschrock 			    "ASC=0x%x ASCQ=0x%x\n", skey, asc, ascq);
99424db4641Seschrock 		if (nvlist_add_boolean_value(sip->si_dsp->ds_state,
99524db4641Seschrock 		    "gltsd", control_pg_cur.gltsd) != 0)
99624db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
99724db4641Seschrock 	}
99824db4641Seschrock 
99924db4641Seschrock 	return (0);
100024db4641Seschrock }
100124db4641Seschrock 
100224db4641Seschrock /*
100324db4641Seschrock  * Fetch the contents of the logpage, and then call the logpage-specific
100424db4641Seschrock  * analysis function.  The analysis function is responsible for detecting any
100524db4641Seschrock  * faults and filling in the details.
100624db4641Seschrock  */
100724db4641Seschrock static int
analyze_one_logpage(ds_scsi_info_t * sip,logpage_validation_entry_t * entry)100824db4641Seschrock analyze_one_logpage(ds_scsi_info_t *sip, logpage_validation_entry_t *entry)
100924db4641Seschrock {
101024db4641Seschrock 	scsi_log_header_t *lhp;
101124db4641Seschrock 	scsi_log_parameter_header_t *lphp;
101224db4641Seschrock 	int buflen;
101324db4641Seschrock 	int log_length;
101424db4641Seschrock 	uint_t skey, asc, ascq;
101524db4641Seschrock 	int result;
101624db4641Seschrock 
101724db4641Seschrock 	buflen = MAX_BUFLEN(scsi_log_header_t);
101824db4641Seschrock 	if ((lhp = calloc(buflen, 1)) == NULL)
101924db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
102024db4641Seschrock 
102124db4641Seschrock 	result = scsi_log_sense(sip, entry->ve_code,
102224db4641Seschrock 	    PC_CUMULATIVE, (caddr_t)lhp, buflen, &skey, &asc, &ascq);
102324db4641Seschrock 
102424db4641Seschrock 	if (result == 0) {
102524db4641Seschrock 		log_length = BE_16(lhp->lh_length);
102624db4641Seschrock 		lphp = (scsi_log_parameter_header_t *)(((uchar_t *)lhp) +
102724db4641Seschrock 		    sizeof (scsi_log_header_t));
102824db4641Seschrock 
102924db4641Seschrock 		result = entry->ve_analyze(sip, lphp, log_length);
103024db4641Seschrock 	} else {
103124db4641Seschrock 		result = scsi_set_errno(sip, EDS_IO);
103224db4641Seschrock 	}
103324db4641Seschrock 
103424db4641Seschrock 	free(lhp);
103524db4641Seschrock 	return (result);
103624db4641Seschrock }
103724db4641Seschrock 
103824db4641Seschrock /*
103924db4641Seschrock  * Analyze the IE logpage.  If we find an IE log record with a non-zero 'asc',
104024db4641Seschrock  * then we have a fault.
104124db4641Seschrock  */
104224db4641Seschrock static int
logpage_ie_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)104324db4641Seschrock logpage_ie_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
104424db4641Seschrock     int log_length)
104524db4641Seschrock {
104624db4641Seschrock 	int i, plen = 0;
104724db4641Seschrock 	scsi_ie_log_param_t *iep = (scsi_ie_log_param_t *)lphp;
104824db4641Seschrock 	nvlist_t *nvl;
104924db4641Seschrock 
105024db4641Seschrock 	assert(sip->si_dsp->ds_predfail == NULL);
105124db4641Seschrock 	if (nvlist_alloc(&sip->si_dsp->ds_predfail, NV_UNIQUE_NAME, 0) != 0)
105224db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
105324db4641Seschrock 	nvl = sip->si_dsp->ds_predfail;
105424db4641Seschrock 
105524db4641Seschrock 	for (i = 0; i < log_length; i += plen) {
105624db4641Seschrock 		iep = (scsi_ie_log_param_t *)((char *)iep + plen);
105724db4641Seschrock 
105824db4641Seschrock 		/*
105924db4641Seschrock 		 * Even though we validated the length during the initial phase,
106024db4641Seschrock 		 * never trust the device.
106124db4641Seschrock 		 */
106224db4641Seschrock 		if (BE_16(iep->ie_hdr.lph_param) == LOGPARAM_IE &&
106324db4641Seschrock 		    iep->ie_hdr.lph_length >= LOGPARAM_IE_MIN_LEN) {
106424db4641Seschrock 			if (nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASC,
106524db4641Seschrock 			    iep->ie_asc) != 0 ||
106624db4641Seschrock 			    nvlist_add_uint8(nvl, FM_EREPORT_PAYLOAD_SCSI_ASCQ,
106724db4641Seschrock 			    iep->ie_ascq) != 0)
106824db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
106924db4641Seschrock 
107024db4641Seschrock 			if (iep->ie_asc != 0)
107124db4641Seschrock 				sip->si_dsp->ds_faults |=
107224db4641Seschrock 				    DS_FAULT_PREDFAIL;
107324db4641Seschrock 			break;
107424db4641Seschrock 		}
107524db4641Seschrock 		plen = iep->ie_hdr.lph_length +
107624db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
107724db4641Seschrock 	}
107824db4641Seschrock 
107924db4641Seschrock 	return (0);
108024db4641Seschrock }
108124db4641Seschrock 
108224db4641Seschrock static int
logpage_temp_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)108324db4641Seschrock logpage_temp_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
108424db4641Seschrock     int log_length)
108524db4641Seschrock {
108624db4641Seschrock 	int i, plen = 0;
108724db4641Seschrock 	uint8_t reftemp, curtemp;
108824db4641Seschrock 	ushort_t param_code;
108924db4641Seschrock 	scsi_temp_log_param_t *temp;
109024db4641Seschrock 	nvlist_t *nvl;
109124db4641Seschrock 
109224db4641Seschrock 	assert(sip->si_dsp->ds_overtemp == NULL);
109324db4641Seschrock 	if (nvlist_alloc(&sip->si_dsp->ds_overtemp, NV_UNIQUE_NAME, 0) != 0)
109424db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
109524db4641Seschrock 	nvl = sip->si_dsp->ds_overtemp;
109624db4641Seschrock 
109724db4641Seschrock 	reftemp = curtemp = INVALID_TEMPERATURE;
109824db4641Seschrock 	for (i = 0; i < log_length; i += plen) {
109924db4641Seschrock 		lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
110024db4641Seschrock 		param_code = BE_16(lphp->lph_param);
110124db4641Seschrock 		temp = (scsi_temp_log_param_t *)lphp;
110224db4641Seschrock 
110324db4641Seschrock 		switch (param_code) {
110424db4641Seschrock 		case LOGPARAM_TEMP_CURTEMP:
110524db4641Seschrock 			if (lphp->lph_length != LOGPARAM_TEMP_LEN)
110624db4641Seschrock 				break;
110724db4641Seschrock 
110824db4641Seschrock 			if (nvlist_add_uint8(nvl,
110924db4641Seschrock 			    FM_EREPORT_PAYLOAD_SCSI_CURTEMP,
111024db4641Seschrock 			    temp->t_temp) != 0)
111124db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
111224db4641Seschrock 			curtemp = temp->t_temp;
111324db4641Seschrock 			break;
111424db4641Seschrock 
111524db4641Seschrock 		case LOGPARAM_TEMP_REFTEMP:
111624db4641Seschrock 			if (lphp->lph_length != LOGPARAM_TEMP_LEN)
111724db4641Seschrock 				break;
111824db4641Seschrock 
111924db4641Seschrock 			if (nvlist_add_uint8(nvl,
112024db4641Seschrock 			    FM_EREPORT_PAYLOAD_SCSI_THRESHTEMP,
112124db4641Seschrock 			    temp->t_temp) != 0)
112224db4641Seschrock 				return (scsi_set_errno(sip, EDS_NOMEM));
112324db4641Seschrock 			reftemp = temp->t_temp;
112424db4641Seschrock 			break;
112524db4641Seschrock 		}
112624db4641Seschrock 
112724db4641Seschrock 		plen = lphp->lph_length +
112824db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
112924db4641Seschrock 	}
113024db4641Seschrock 
113124db4641Seschrock 	if (reftemp != INVALID_TEMPERATURE && curtemp != INVALID_TEMPERATURE &&
113224db4641Seschrock 	    curtemp > reftemp)
113324db4641Seschrock 		sip->si_dsp->ds_faults |= DS_FAULT_OVERTEMP;
113424db4641Seschrock 
113524db4641Seschrock 	return (0);
113624db4641Seschrock }
113724db4641Seschrock 
113824db4641Seschrock static int
logpage_selftest_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)113924db4641Seschrock logpage_selftest_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
114024db4641Seschrock     int log_length)
114124db4641Seschrock {
114224db4641Seschrock 	int i, plen = 0;
114324db4641Seschrock 	int entries = 0;
114424db4641Seschrock 	ushort_t param_code;
114524db4641Seschrock 	scsi_selftest_log_param_t *stp;
114624db4641Seschrock 	nvlist_t *nvl;
114724db4641Seschrock 
114824db4641Seschrock 	assert(sip->si_dsp->ds_testfail == NULL);
114924db4641Seschrock 	if (nvlist_alloc(&sip->si_dsp->ds_testfail, NV_UNIQUE_NAME, 0) != 0)
115024db4641Seschrock 		return (scsi_set_errno(sip, EDS_NOMEM));
115124db4641Seschrock 	nvl = sip->si_dsp->ds_testfail;
115224db4641Seschrock 
115324db4641Seschrock 	for (i = 0; i < log_length; i += plen, entries++) {
115424db4641Seschrock 		lphp = (scsi_log_parameter_header_t *)((char *)lphp + plen);
115524db4641Seschrock 		param_code = BE_16(lphp->lph_param);
115624db4641Seschrock 		stp = (scsi_selftest_log_param_t *)lphp;
115724db4641Seschrock 
115824db4641Seschrock 		if (param_code >= LOGPAGE_SELFTEST_MIN_PARAM_CODE &&
115924db4641Seschrock 		    param_code <= LOGPAGE_SELFTEST_MAX_PARAM_CODE &&
116024db4641Seschrock 		    lphp->lph_length >= LOGPAGE_SELFTEST_PARAM_LEN) {
116124db4641Seschrock 			/*
116224db4641Seschrock 			 * We always log the last result, or the result of the
116324db4641Seschrock 			 * last completed test.
116424db4641Seschrock 			 */
116524db4641Seschrock 			if ((param_code == 1 ||
116624db4641Seschrock 			    SELFTEST_COMPLETE(stp->st_results))) {
116724db4641Seschrock 				if (nvlist_add_uint8(nvl,
116824db4641Seschrock 				    FM_EREPORT_PAYLOAD_SCSI_RESULTCODE,
116924db4641Seschrock 				    stp->st_results) != 0 ||
117024db4641Seschrock 				    nvlist_add_uint16(nvl,
117124db4641Seschrock 				    FM_EREPORT_PAYLOAD_SCSI_TIMESTAMP,
117224db4641Seschrock 				    BE_16(stp->st_timestamp)) != 0 ||
117324db4641Seschrock 				    nvlist_add_uint8(nvl,
117424db4641Seschrock 				    FM_EREPORT_PAYLOAD_SCSI_SEGMENT,
117524db4641Seschrock 				    stp->st_number) != 0 ||
117624db4641Seschrock 				    nvlist_add_uint64(nvl,
117724db4641Seschrock 				    FM_EREPORT_PAYLOAD_SCSI_ADDRESS,
117824db4641Seschrock 				    BE_64(stp->st_lba)) != 0)
117924db4641Seschrock 					return (scsi_set_errno(sip,
118024db4641Seschrock 					    EDS_NOMEM));
118124db4641Seschrock 
118224db4641Seschrock 				if (SELFTEST_COMPLETE(stp->st_results)) {
118324db4641Seschrock 					if (stp->st_results != SELFTEST_OK)
118424db4641Seschrock 						sip->si_dsp->ds_faults |=
118524db4641Seschrock 						    DS_FAULT_TESTFAIL;
118624db4641Seschrock 					return (0);
118724db4641Seschrock 				}
118824db4641Seschrock 			}
118924db4641Seschrock 		}
119024db4641Seschrock 
119124db4641Seschrock 		plen = lphp->lph_length +
119224db4641Seschrock 		    sizeof (scsi_log_parameter_header_t);
119324db4641Seschrock 	}
119424db4641Seschrock 
119524db4641Seschrock 	return (0);
119624db4641Seschrock }
119724db4641Seschrock 
11980244979bSAlek Pinchuk /*
11990244979bSAlek Pinchuk  * Analyze the contents of the Solid State Media (SSM) log page's
12000244979bSAlek Pinchuk  * "Percentage Used Endurance Indicator" log parameter.
12010244979bSAlek Pinchuk  * We generate a fault if the percentage used is equal to or over
12020244979bSAlek Pinchuk  * PRCNT_USED_FAULT_THRSH
12030244979bSAlek Pinchuk  */
12040244979bSAlek Pinchuk static int
logpage_ssm_analyze(ds_scsi_info_t * sip,scsi_log_parameter_header_t * lphp,int log_length)12050244979bSAlek Pinchuk logpage_ssm_analyze(ds_scsi_info_t *sip, scsi_log_parameter_header_t *lphp,
12060244979bSAlek Pinchuk     int log_length)
12070244979bSAlek Pinchuk {
12080244979bSAlek Pinchuk 	uint16_t param_code;
12090244979bSAlek Pinchuk 	scsi_ssm_log_param_t *ssm;
12100244979bSAlek Pinchuk 	nvlist_t *nvl;
12110244979bSAlek Pinchuk 	int i, plen = 0;
12120244979bSAlek Pinchuk 
1213*a37235a3SAlek Pinchuk 	assert(sip->si_dsp->ds_ssmwearout == NULL);
1214*a37235a3SAlek Pinchuk 	if (nvlist_alloc(&sip->si_dsp->ds_ssmwearout, NV_UNIQUE_NAME, 0) != 0)
12150244979bSAlek Pinchuk 		return (scsi_set_errno(sip, EDS_NOMEM));
1216*a37235a3SAlek Pinchuk 	nvl = sip->si_dsp->ds_ssmwearout;
12170244979bSAlek Pinchuk 
12180244979bSAlek Pinchuk 	for (i = 0; i < log_length; i += plen) {
12190244979bSAlek Pinchuk 		lphp = (scsi_log_parameter_header_t *)((uint8_t *)lphp + plen);
12200244979bSAlek Pinchuk 		param_code = BE_16(lphp->lph_param);
12210244979bSAlek Pinchuk 		ssm = (scsi_ssm_log_param_t *)lphp;
12220244979bSAlek Pinchuk 
12230244979bSAlek Pinchuk 		switch (param_code) {
12240244979bSAlek Pinchuk 		case LOGPARAM_PRCNT_USED:
12250244979bSAlek Pinchuk 			if (lphp->lph_length != LOGPARAM_PRCNT_USED_PARAM_LEN)
12260244979bSAlek Pinchuk 				break;
12270244979bSAlek Pinchuk 
12280244979bSAlek Pinchuk 			if ((nvlist_add_uint8(nvl,
12290244979bSAlek Pinchuk 			    FM_EREPORT_PAYLOAD_SCSI_CURSSMWEAROUT,
12300244979bSAlek Pinchuk 			    ssm->ssm_prcnt_used) != 0) ||
12310244979bSAlek Pinchuk 			    (nvlist_add_uint8(nvl,
12320244979bSAlek Pinchuk 			    FM_EREPORT_PAYLOAD_SCSI_THRSHSSMWEAROUT,
12330244979bSAlek Pinchuk 			    PRCNT_USED_FAULT_THRSH) != 0))
12340244979bSAlek Pinchuk 				return (scsi_set_errno(sip, EDS_NOMEM));
12350244979bSAlek Pinchuk 
12360244979bSAlek Pinchuk 			if (ssm->ssm_prcnt_used >= PRCNT_USED_FAULT_THRSH)
12370244979bSAlek Pinchuk 				sip->si_dsp->ds_faults |= DS_FAULT_SSMWEAROUT;
12380244979bSAlek Pinchuk 
12390244979bSAlek Pinchuk 			return (0);
12400244979bSAlek Pinchuk 		}
12410244979bSAlek Pinchuk 
12420244979bSAlek Pinchuk 		plen = lphp->lph_length +
12430244979bSAlek Pinchuk 		    sizeof (scsi_log_parameter_header_t);
12440244979bSAlek Pinchuk 	}
12450244979bSAlek Pinchuk 
12460244979bSAlek Pinchuk 	/*
12470244979bSAlek Pinchuk 	 * If we got this far we didn't see LOGPARAM_PRCNT_USED
12480244979bSAlek Pinchuk 	 * which is strange since we verified that it's there
12490244979bSAlek Pinchuk 	 */
12500244979bSAlek Pinchuk 	dprintf("solid state media logpage analyze failed\n");
12510244979bSAlek Pinchuk #if DEBUG
12520244979bSAlek Pinchuk 	abort();
12530244979bSAlek Pinchuk #endif
12540244979bSAlek Pinchuk 	return (scsi_set_errno(sip, EDS_NOT_SUPPORTED));
12550244979bSAlek Pinchuk }
12560244979bSAlek Pinchuk 
125724db4641Seschrock /*
125824db4641Seschrock  * Analyze the IE mode sense page explicitly.  This is only needed if the IE log
125924db4641Seschrock  * page is not supported.
126024db4641Seschrock  */
126124db4641Seschrock static int
analyze_ie_sense(ds_scsi_info_t * sip)126224db4641Seschrock analyze_ie_sense(ds_scsi_info_t *sip)
126324db4641Seschrock {
126424db4641Seschrock 	uint_t skey, asc, ascq;
126524db4641Seschrock 	nvlist_t *nvl;
126624db4641Seschrock 
126724db4641Seschrock 	/*
126824db4641Seschrock 	 * Don't bother checking if we weren't able to set our MRIE correctly.
126924db4641Seschrock 	 */
127024db4641Seschrock 	if (sip->si_iec_current.ie_mrie != IE_REPORT_ON_REQUEST)
127124db4641Seschrock 		return (0);
127224db4641Seschrock 
127324db4641Seschrock 	if (scsi_request_sense(sip, &skey, &asc, &ascq) != 0) {
127424db4641Seschrock 		dprintf("failed to request IE page (KEY=0x%x ASC=0x%x "
127524db4641Seschrock 		    "ASCQ=0x%x)\n", skey, asc, ascq);
127624db4641Seschrock 		return (scsi_set_errno(sip, EDS_IO));
127724db4641Seschrock 	} else if (skey == KEY_NO_SENSE) {
127824db4641Seschrock 		assert(sip->si_dsp->ds_predfail == NULL);
127924db4641Seschrock 		if (nvlist_alloc(&sip->si_dsp->ds_predfail,
128024db4641Seschrock 		    NV_UNIQUE_NAME, 0) != 0)
128124db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
128224db4641Seschrock 		nvl = sip->si_dsp->ds_predfail;
128324db4641Seschrock 
128424db4641Seschrock 		if (nvlist_add_uint8(nvl,
128524db4641Seschrock 		    FM_EREPORT_PAYLOAD_SCSI_ASC, asc) != 0 ||
128624db4641Seschrock 		    nvlist_add_uint8(nvl,
128724db4641Seschrock 		    FM_EREPORT_PAYLOAD_SCSI_ASCQ, ascq) != 0) {
128824db4641Seschrock 			nvlist_free(nvl);
128924db4641Seschrock 			return (scsi_set_errno(sip, EDS_NOMEM));
129024db4641Seschrock 		}
129124db4641Seschrock 
129224db4641Seschrock 		if (asc != 0)
129324db4641Seschrock 			sip->si_dsp->ds_faults |= DS_FAULT_PREDFAIL;
129424db4641Seschrock 	}
129524db4641Seschrock 
129624db4641Seschrock 	return (0);
129724db4641Seschrock }
129824db4641Seschrock 
129924db4641Seschrock /*
130024db4641Seschrock  * Clean up the scsi-specific information structure.
130124db4641Seschrock  */
130224db4641Seschrock static void
ds_scsi_close(void * arg)130324db4641Seschrock ds_scsi_close(void *arg)
130424db4641Seschrock {
130524db4641Seschrock 	ds_scsi_info_t *sip = arg;
130624db4641Seschrock 	if (sip->si_sim)
130724db4641Seschrock 		(void) dlclose(sip->si_sim);
130824db4641Seschrock 
130924db4641Seschrock 	free(sip);
131024db4641Seschrock }
131124db4641Seschrock 
131224db4641Seschrock /*
131324db4641Seschrock  * Initialize a single disk.  Initialization consists of:
131424db4641Seschrock  *
131524db4641Seschrock  * 1. Check to see if the IE mechanism is enabled via MODE SENSE for the IE
131624db4641Seschrock  *    Control page (page 0x1C).
131724db4641Seschrock  *
131824db4641Seschrock  * 2. If the IE page is available, try to set the following parameters:
131924db4641Seschrock  *
132024db4641Seschrock  *    	DEXCPT		0	Enable exceptions
132124db4641Seschrock  *    	MRIE		6	Only report IE information on request
132224db4641Seschrock  *    	EWASC		1	Enable warning reporting
132324db4641Seschrock  *    	REPORT COUNT	1	Only report an IE exception once
132424db4641Seschrock  *    	LOGERR		1	Enable logging of errors
132524db4641Seschrock  *
132624db4641Seschrock  *    The remaining fields are left as-is, preserving the current values.  If we
132724db4641Seschrock  *    cannot set some of these fields, then we do our best.  Some drives may
132824db4641Seschrock  *    have a static configuration which still allows for some monitoring.
132924db4641Seschrock  *
133024db4641Seschrock  * 3. Check to see if the IE log page (page 0x2F) is supported by issuing a
133124db4641Seschrock  *    LOG SENSE command.
133224db4641Seschrock  *
133324db4641Seschrock  * 4. Check to see if the self-test log page (page 0x10) is supported.
133424db4641Seschrock  *
133524db4641Seschrock  * 5. Check to see if the temperature log page (page 0x0D) is supported, and
133624db4641Seschrock  *    contains a reference temperature.
133724db4641Seschrock  *
133824db4641Seschrock  * 6. Clear the GLTSD bit in control mode page 0xA.  This will allow the drive
133924db4641Seschrock  *    to save each of the log pages described above to nonvolatile storage.
134024db4641Seschrock  *    This is essential if the drive is to remember its failures across
134124db4641Seschrock  *    loss of power.
134224db4641Seschrock  */
134324db4641Seschrock static void *
ds_scsi_open_common(disk_status_t * dsp,ds_scsi_info_t * sip)134424db4641Seschrock ds_scsi_open_common(disk_status_t *dsp, ds_scsi_info_t *sip)
134524db4641Seschrock {
134624db4641Seschrock 	boolean_t changed;
134724db4641Seschrock 
134824db4641Seschrock 	sip->si_dsp = dsp;
134924db4641Seschrock 
135024db4641Seschrock 	/* Load and validate mode pages */
135124db4641Seschrock 	if (load_modepages(sip) != 0) {
135224db4641Seschrock 		ds_scsi_close(sip);
135324db4641Seschrock 		return (NULL);
135424db4641Seschrock 	}
135524db4641Seschrock 
135624db4641Seschrock 	/* Load and validate log pages */
135724db4641Seschrock 	if (load_logpages(sip) != 0) {
135824db4641Seschrock 		ds_scsi_close(sip);
135924db4641Seschrock 		return (NULL);
136024db4641Seschrock 	}
136124db4641Seschrock 
136224db4641Seschrock 	/* Load IE state */
136324db4641Seschrock 	if (load_ie_modepage(sip) != 0 ||
136424db4641Seschrock 	    scsi_enable_ie(sip, &changed) != 0 ||
136524db4641Seschrock 	    (changed && load_ie_modepage(sip) != 0)) {
136624db4641Seschrock 		ds_scsi_close(sip);
136724db4641Seschrock 		return (NULL);
136824db4641Seschrock 	}
136924db4641Seschrock 
137024db4641Seschrock 	/* Clear the GLTSD bit in the control page */
137124db4641Seschrock 	if (sip->si_supp_log != 0 && clear_gltsd(sip) != 0) {
137224db4641Seschrock 		ds_scsi_close(sip);
137324db4641Seschrock 		return (NULL);
137424db4641Seschrock 	}
137524db4641Seschrock 
137624db4641Seschrock 	return (sip);
137724db4641Seschrock }
137824db4641Seschrock 
137924db4641Seschrock static void *
ds_scsi_open_uscsi(disk_status_t * dsp)138024db4641Seschrock ds_scsi_open_uscsi(disk_status_t *dsp)
138124db4641Seschrock {
138224db4641Seschrock 	ds_scsi_info_t *sip;
138324db4641Seschrock 
138424db4641Seschrock 	if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
138524db4641Seschrock 		(void) ds_set_errno(dsp, EDS_NOMEM);
138624db4641Seschrock 		return (NULL);
138724db4641Seschrock 	}
138824db4641Seschrock 
138924db4641Seschrock 	return (ds_scsi_open_common(dsp, sip));
139024db4641Seschrock }
139124db4641Seschrock 
139224db4641Seschrock static void *
ds_scsi_open_sim(disk_status_t * dsp)139324db4641Seschrock ds_scsi_open_sim(disk_status_t *dsp)
139424db4641Seschrock {
139524db4641Seschrock 	ds_scsi_info_t *sip;
139624db4641Seschrock 
139724db4641Seschrock 	if ((sip = calloc(sizeof (ds_scsi_info_t), 1)) == NULL) {
139824db4641Seschrock 		(void) ds_set_errno(dsp, EDS_NOMEM);
139924db4641Seschrock 		return (NULL);
140024db4641Seschrock 	}
140124db4641Seschrock 
140224db4641Seschrock 	if ((sip->si_sim = dlopen(dsp->ds_path, RTLD_LAZY)) == NULL) {
140324db4641Seschrock 		(void) ds_set_errno(dsp, EDS_NO_TRANSPORT);
140424db4641Seschrock 		free(sip);
140524db4641Seschrock 		return (NULL);
140624db4641Seschrock 	}
140724db4641Seschrock 
140824db4641Seschrock 	return (ds_scsi_open_common(dsp, sip));
140924db4641Seschrock }
141024db4641Seschrock 
141124db4641Seschrock 
141224db4641Seschrock /*
141324db4641Seschrock  * Scan for any faults.  The following steps are performed:
141424db4641Seschrock  *
141524db4641Seschrock  * 1. If the temperature log page is supported, check the current temperature
141624db4641Seschrock  *    and threshold.  If the current temperature exceeds the threshold, report
141724db4641Seschrock  *    and overtemp fault.
141824db4641Seschrock  *
141924db4641Seschrock  * 2. If the selftest log page is supported, check to the last completed self
142024db4641Seschrock  *    test.  If the last completed test resulted in failure, report a selftest
142124db4641Seschrock  *    fault.
142224db4641Seschrock  *
142324db4641Seschrock  * 3. If the IE log page is supported, check to see if failure is predicted.  If
142424db4641Seschrock  *    so, indicate a predictive failure fault.
142524db4641Seschrock  *
142624db4641Seschrock  * 4. If the IE log page is not supported, but the mode page supports report on
142724db4641Seschrock  *    request mode, then issue a REQUEST SENSE for the mode page.  Indicate a
142824db4641Seschrock  *    predictive failure fault if necessary.
142924db4641Seschrock  */
143024db4641Seschrock static int
ds_scsi_scan(void * arg)143124db4641Seschrock ds_scsi_scan(void *arg)
143224db4641Seschrock {
143324db4641Seschrock 	ds_scsi_info_t *sip = arg;
143424db4641Seschrock 	int i;
143524db4641Seschrock 
143624db4641Seschrock 	for (i = 0; i < NLOG_VALIDATION; i++) {
143724db4641Seschrock 		if ((sip->si_supp_log & log_validation[i].ve_supported) == 0)
143824db4641Seschrock 			continue;
143924db4641Seschrock 
144024db4641Seschrock 		if (analyze_one_logpage(sip, &log_validation[i]) != 0)
144124db4641Seschrock 			return (-1);
144224db4641Seschrock 	}
144324db4641Seschrock 
144424db4641Seschrock 	if (!(sip->si_supp_log & LOGPAGE_SUPP_IE) &&
144524db4641Seschrock 	    (sip->si_supp_mode & MODEPAGE_SUPP_IEC) &&
144624db4641Seschrock 	    analyze_ie_sense(sip) != 0)
144724db4641Seschrock 		return (-1);
144824db4641Seschrock 
144924db4641Seschrock 	return (0);
145024db4641Seschrock }
145124db4641Seschrock 
145224db4641Seschrock ds_transport_t ds_scsi_uscsi_transport = {
145324db4641Seschrock 	ds_scsi_open_uscsi,
145424db4641Seschrock 	ds_scsi_close,
145524db4641Seschrock 	ds_scsi_scan
145624db4641Seschrock };
145724db4641Seschrock 
145824db4641Seschrock ds_transport_t ds_scsi_sim_transport = {
145924db4641Seschrock 	ds_scsi_open_sim,
146024db4641Seschrock 	ds_scsi_close,
146124db4641Seschrock 	ds_scsi_scan
146224db4641Seschrock };
1463