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