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