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