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