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