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