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
sun_loki_fix_bay(ses_plugin_t * sp,ses_node_t * np)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
sun_loki_parse_node(ses_plugin_t * sp,ses_node_t * np)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
_ses_init(ses_plugin_t * sp)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