1*6673Seschrock /*
2*6673Seschrock  * CDDL HEADER START
3*6673Seschrock  *
4*6673Seschrock  * The contents of this file are subject to the terms of the
5*6673Seschrock  * Common Development and Distribution License (the "License").
6*6673Seschrock  * You may not use this file except in compliance with the License.
7*6673Seschrock  *
8*6673Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*6673Seschrock  * or http://www.opensolaris.org/os/licensing.
10*6673Seschrock  * See the License for the specific language governing permissions
11*6673Seschrock  * and limitations under the License.
12*6673Seschrock  *
13*6673Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14*6673Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*6673Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16*6673Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17*6673Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18*6673Seschrock  *
19*6673Seschrock  * CDDL HEADER END
20*6673Seschrock  */
21*6673Seschrock 
22*6673Seschrock /*
23*6673Seschrock  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*6673Seschrock  * Use is subject to license terms.
25*6673Seschrock  */
26*6673Seschrock 
27*6673Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*6673Seschrock 
29*6673Seschrock #include <string.h>
30*6673Seschrock #include <strings.h>
31*6673Seschrock 
32*6673Seschrock #include <scsi/libses.h>
33*6673Seschrock #include <scsi/libses_plugin.h>
34*6673Seschrock 
35*6673Seschrock #include <scsi/plugins/ses/framework/ses2_impl.h>
36*6673Seschrock 
37*6673Seschrock static int
sun_loki_fix_bay(ses_plugin_t * sp,ses_node_t * np)38*6673Seschrock sun_loki_fix_bay(ses_plugin_t *sp, ses_node_t *np)
39*6673Seschrock {
40*6673Seschrock 	ses2_aes_descr_eip_impl_t *dep;
41*6673Seschrock 	ses2_aes_descr_sas0_eip_impl_t *s0ep;
42*6673Seschrock 	size_t len;
43*6673Seschrock 	int nverr;
44*6673Seschrock 	nvlist_t *props = ses_node_props(np);
45*6673Seschrock 
46*6673Seschrock 	/*
47*6673Seschrock 	 * The spec conveniently defines the bay number as part of the
48*6673Seschrock 	 * additional element status descriptor.  However, the AES descriptor
49*6673Seschrock 	 * is technically only valid if the device is inserted.  This is a
50*6673Seschrock 	 * problem for loki because the bay numbers don't match the element
51*6673Seschrock 	 * class index, so when a device is removed we have no way of knowing
52*6673Seschrock 	 * *which* bay is empty.  Thankfully, loki defines this value even if
53*6673Seschrock 	 * the invalid bit is set, so we override this value, even for empty
54*6673Seschrock 	 * bays.
55*6673Seschrock 	 */
56*6673Seschrock 	if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
57*6673Seschrock 	    SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
58*6673Seschrock 		return (0);
59*6673Seschrock 
60*6673Seschrock 	if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS ||
61*6673Seschrock 	    !dep->sadei_eip || !dep->sadei_invalid)
62*6673Seschrock 		return (0);
63*6673Seschrock 
64*6673Seschrock 	s0ep = (ses2_aes_descr_sas0_eip_impl_t *)dep->sadei_protocol_specific;
65*6673Seschrock 
66*6673Seschrock 	SES_NV_ADD(uint64, nverr, props, SES_PROP_BAY_NUMBER,
67*6673Seschrock 	    s0ep->sadsi_bay_number);
68*6673Seschrock 
69*6673Seschrock 	return (0);
70*6673Seschrock }
71*6673Seschrock 
72*6673Seschrock static int
sun_loki_parse_node(ses_plugin_t * sp,ses_node_t * np)73*6673Seschrock sun_loki_parse_node(ses_plugin_t *sp, ses_node_t *np)
74*6673Seschrock {
75*6673Seschrock 	ses_node_t *encp;
76*6673Seschrock 	nvlist_t *props = ses_node_props(np);
77*6673Seschrock 	nvlist_t *encprops;
78*6673Seschrock 	uint8_t *stringin;
79*6673Seschrock 	uint_t len;
80*6673Seschrock 	nvlist_t *lid;
81*6673Seschrock 	int nverr;
82*6673Seschrock 	char serial[17];
83*6673Seschrock 	uint8_t fieldlen;
84*6673Seschrock 	char *field;
85*6673Seschrock 	uint64_t wwn;
86*6673Seschrock 	uint64_t type, index;
87*6673Seschrock 	int i;
88*6673Seschrock 
89*6673Seschrock 	if (ses_node_type(np) != SES_NODE_ENCLOSURE &&
90*6673Seschrock 	    ses_node_type(np) != SES_NODE_ELEMENT)
91*6673Seschrock 		return (0);
92*6673Seschrock 
93*6673Seschrock 	if (ses_node_type(np) == SES_NODE_ELEMENT) {
94*6673Seschrock 		VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
95*6673Seschrock 		    &type) == 0);
96*6673Seschrock 
97*6673Seschrock 		if (type == SES_ET_ARRAY_DEVICE)
98*6673Seschrock 			return (sun_loki_fix_bay(sp, np));
99*6673Seschrock 
100*6673Seschrock 		if (type != SES_ET_COOLING &&
101*6673Seschrock 		    type != SES_ET_POWER_SUPPLY)
102*6673Seschrock 			return (0);
103*6673Seschrock 
104*6673Seschrock 		VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
105*6673Seschrock 		    &index) == 0);
106*6673Seschrock 	}
107*6673Seschrock 
108*6673Seschrock 	/*
109*6673Seschrock 	 * Find the containing enclosure node and extract the STRING IN
110*6673Seschrock 	 * information.
111*6673Seschrock 	 */
112*6673Seschrock 	for (encp = np; ses_node_type(encp) != SES_NODE_ENCLOSURE;
113*6673Seschrock 	    encp = ses_node_parent(encp))
114*6673Seschrock 		;
115*6673Seschrock 
116*6673Seschrock 	encprops = ses_node_props(encp);
117*6673Seschrock 	if (nvlist_lookup_byte_array(encprops, SES_EN_PROP_STRING,
118*6673Seschrock 	    &stringin, &len) != 0 || len < 4)
119*6673Seschrock 		return (0);
120*6673Seschrock 
121*6673Seschrock 	/*
122*6673Seschrock 	 * If this is an enclosure, then calculate the chassis WWN by masking
123*6673Seschrock 	 * off the bottom 8 bits of the WWN.
124*6673Seschrock 	 */
125*6673Seschrock 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
126*6673Seschrock 		VERIFY(nvlist_lookup_nvlist(props, SES_EN_PROP_LID, &lid) == 0);
127*6673Seschrock 		VERIFY(nvlist_lookup_uint64(lid, SPC3_NAA_INT, &wwn) == 0);
128*6673Seschrock 		(void) snprintf(serial, sizeof (serial), "%llx",
129*6673Seschrock 		    wwn & ~0xFFULL);
130*6673Seschrock 		SES_NV_ADD(string, nverr, props, LIBSES_EN_PROP_CSN, serial);
131*6673Seschrock 	}
132*6673Seschrock 
133*6673Seschrock 	/*
134*6673Seschrock 	 * The STRING IN data is organized into a series of variable-length
135*6673Seschrock 	 * fields, where each field can be either a key ("Fan PartNUM") or a
136*6673Seschrock 	 * value.  If the field length is less than our shortest expected
137*6673Seschrock 	 * identifier, then something has gone awry and we assume that the data
138*6673Seschrock 	 * is corrupt.
139*6673Seschrock 	 */
140*6673Seschrock 	fieldlen = stringin[3];
141*6673Seschrock 	if (fieldlen < 11)
142*6673Seschrock 		return (0);
143*6673Seschrock 
144*6673Seschrock 	for (field = (char *)stringin + 4;
145*6673Seschrock 	    field + fieldlen <= (char *)stringin + len; field += fieldlen) {
146*6673Seschrock 		if (strncmp(field, "Storage J4500", 13) == 0) {
147*6673Seschrock 			/*
148*6673Seschrock 			 * This is the part number for the enclosure itself.
149*6673Seschrock 			 */
150*6673Seschrock 			if (ses_node_type(np) != SES_NODE_ENCLOSURE)
151*6673Seschrock 				continue;
152*6673Seschrock 
153*6673Seschrock 			field += fieldlen;
154*6673Seschrock 			if (field + fieldlen > (char *)stringin + len)
155*6673Seschrock 				break;
156*6673Seschrock 
157*6673Seschrock 			if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
158*6673Seschrock 				SES_NV_ADD(fixed_string_trunc, nverr, props,
159*6673Seschrock 				    LIBSES_PROP_PART, field, fieldlen);
160*6673Seschrock 				return (0);
161*6673Seschrock 			}
162*6673Seschrock 
163*6673Seschrock 		} else if (strncmp(field, "Fan PartNUM", 11) == 0) {
164*6673Seschrock 			/*
165*6673Seschrock 			 * Part numbers for the fans, of which there are 5.
166*6673Seschrock 			 */
167*6673Seschrock 			if (ses_node_type(np) != SES_NODE_ELEMENT ||
168*6673Seschrock 			    type != SES_ET_COOLING) {
169*6673Seschrock 				field += fieldlen * 5;
170*6673Seschrock 				continue;
171*6673Seschrock 			}
172*6673Seschrock 
173*6673Seschrock 			field += fieldlen;
174*6673Seschrock 
175*6673Seschrock 			for (i = 0; i < 5 &&
176*6673Seschrock 			    field + fieldlen <= (char *)stringin + len;
177*6673Seschrock 			    i++, field += fieldlen) {
178*6673Seschrock 				if (index == i &&
179*6673Seschrock 				    strncmp(field, "Unknown", 7) != 0 &&
180*6673Seschrock 				    strncmp(field, "Not Installed", 13) != 0) {
181*6673Seschrock 					SES_NV_ADD(fixed_string_trunc, nverr,
182*6673Seschrock 					    props, LIBSES_PROP_PART,
183*6673Seschrock 					    field, fieldlen);
184*6673Seschrock 					return (0);
185*6673Seschrock 				}
186*6673Seschrock 			}
187*6673Seschrock 
188*6673Seschrock 		} else if (strncmp(field, "PS PartNUM", 10) == 0) {
189*6673Seschrock 			/*
190*6673Seschrock 			 * Part numbers for the power supplies, of which there
191*6673Seschrock 			 * are 2.
192*6673Seschrock 			 */
193*6673Seschrock 			if (ses_node_type(np) != SES_NODE_ELEMENT ||
194*6673Seschrock 			    type != SES_ET_POWER_SUPPLY) {
195*6673Seschrock 				field += fieldlen * 2;
196*6673Seschrock 				continue;
197*6673Seschrock 			}
198*6673Seschrock 
199*6673Seschrock 			field += fieldlen;
200*6673Seschrock 
201*6673Seschrock 			for (i = 0; i < 2 &&
202*6673Seschrock 			    field + fieldlen <= (char *)stringin + len;
203*6673Seschrock 			    i++, field += fieldlen) {
204*6673Seschrock 				if (index == i &&
205*6673Seschrock 				    strncmp(field, "Unknown", 7) != 0 &&
206*6673Seschrock 				    strncmp(field, "Not Installed", 13) != 0) {
207*6673Seschrock 					SES_NV_ADD(fixed_string_trunc, nverr,
208*6673Seschrock 					    props, LIBSES_PROP_PART,
209*6673Seschrock 					    field, fieldlen);
210*6673Seschrock 					return (0);
211*6673Seschrock 				}
212*6673Seschrock 			}
213*6673Seschrock 		}
214*6673Seschrock 	}
215*6673Seschrock 
216*6673Seschrock 	return (0);
217*6673Seschrock }
218*6673Seschrock 
219*6673Seschrock int
_ses_init(ses_plugin_t * sp)220*6673Seschrock _ses_init(ses_plugin_t *sp)
221*6673Seschrock {
222*6673Seschrock 	ses_plugin_config_t config = {
223*6673Seschrock 		.spc_node_parse = sun_loki_parse_node
224*6673Seschrock 	};
225*6673Seschrock 
226*6673Seschrock 	return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION,
227*6673Seschrock 	    &config) != 0);
228*6673Seschrock }
229