1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
14 */
15
16 #include <stddef.h>
17 #include <libnvpair.h>
18 #include <scsi/libses.h>
19 #include <scsi/libses_plugin.h>
20 #include <scsi/plugins/ses/framework/ses2_impl.h>
21
22 /*
23 * This module is an almost identical copy of hp
24 */
25
26 /*
27 * Override bay number if the invalid bit is set for the AES descriptor
28 * Same approach used for Lenovo and Sun Loki, with a minor change
29 * to add 1 to the bay number to address the requirement for the bay numbering
30 * on the Ericsson SRU-0101 to start with 1 rather than 0.
31 */
32 static int
33 ercsn_0101_fix_bay(ses_plugin_t *sp, ses_node_t *np)
34 {
35 ses2_aes_descr_eip_impl_t *dep;
36 ses2_aes_descr_sas0_eip_impl_t *s0ep;
37 size_t len;
38 int nverr;
39 nvlist_t *props = ses_node_props(np);
40
41 /*
42 * The spec conveniently defines the bay number as part of the
43 * additional element status descriptor. However, the AES descriptor
44 * is technically only valid if the device is inserted.
45 * Thankfully, the enclosure defines this value even if
46 * the invalid bit is set, so we override bay value, even for empty
47 * bays.
48 */
49 if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
50 SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
51 return (0);
52
53 if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS ||
54 !dep->sadei_eip || !dep->sadei_invalid)
55 return (0);
56
57 s0ep = (ses2_aes_descr_sas0_eip_impl_t *)dep->sadei_protocol_specific;
58
59 /*
60 * This nested macro can return -1 on error
61 */
62 SES_NV_ADD(uint64, nverr, props, SES_PROP_BAY_NUMBER,
63 s0ep->sadsi_bay_number + 1);
64
65 return (0);
66 }
67
68 /*
69 * FC protocol specific parsing for the given (fp) AES element descriptor.
70 * Copy-pasted elem_parse_aes_fc() from ses2_elements.c with a minor change
71 * to add 1 to the bay number to address the requirement for the bay numbering
72 * on the Ericsson SRU-0101 to start with 1 rather than 0.
73 */
74 static int
75 ercsn_0101_elem_parse_aes_fc(const ses2_aes_descr_fc_eip_impl_t *fp,
76 nvlist_t *nvl, size_t len)
77 {
78 int nverr, i;
79 nvlist_t **nva;
80 int nports;
81
82 if (len < offsetof(ses2_aes_descr_fc_eip_impl_t,
83 sadfi_ports))
84 return (0);
85
86 SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
87 fp->sadfi_bay_number + 1);
88 SES_NV_ADD(uint64, nverr, nvl, SES_FC_PROP_NODE_NAME,
89 SCSI_READ64(&fp->sadfi_node_name));
90
91 nports = MIN(fp->sadfi_n_ports,
92 (len - offsetof(ses2_aes_descr_fc_eip_impl_t,
93 sadfi_ports)) / sizeof (ses2_aes_port_descr_impl_t));
94
95 if (nports == 0)
96 return (0);
97
98 nva = ses_zalloc(nports * sizeof (nvlist_t *));
99 if (nva == NULL)
100 return (-1);
101
102 for (i = 0; i < nports; i++) {
103 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
104 goto fail;
105 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_LOOP_POS,
106 fp->sadfi_ports[i].sapdi_port_loop_position)) != 0)
107 goto fail;
108 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_REQ_HARDADDR,
109 fp->sadfi_ports[i].sapdi_port_requested_hard_address)) != 0)
110 goto fail;
111 nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_ID,
112 SCSI_READ24(fp->sadfi_ports[i].sapdi_n_port_identifier));
113 if (nverr != 0)
114 goto fail;
115 if ((nverr = nvlist_add_uint64(nva[i], SES_FC_PROP_N_PORT_NAME,
116 SCSI_READ64(&fp->sadfi_ports[i].sapdi_n_port_name))) != 0)
117 goto fail;
118 }
119
120 if ((nverr = nvlist_add_nvlist_array(nvl, SES_FC_PROP_PORTS,
121 nva, nports)) != 0)
122 goto fail;
123
124 for (i = 0; i < nports && nva[i] != NULL; i++)
125 nvlist_free(nva[i]);
126 ses_free(nva);
127 return (0);
128
129 fail:
130 for (i = 0; i < nports && nva[i] != NULL; i++)
131 nvlist_free(nva[i]);
132 ses_free(nva);
133 return (ses_set_nverrno(nverr, NULL));
134 }
135
136 /*
137 * Parse AES descriptor for the given element (dep).
138 * Copy-pasted elem_parse_aes_device() from ses2_elements.c with a minor change
139 * to add 1 to the bay number to address the requirement for the bay numbering
140 * on the Ericsson SRU-0101 to start with 1 rather than 0.
141 */
142 static int
143 ercsn_0101_elem_parse_aes_device(const ses2_aes_descr_eip_impl_t *dep, nvlist_t *nvl,
144 size_t len)
145 {
146 ses2_aes_descr_fc_eip_impl_t *fp;
147 ses2_aes_descr_sas0_eip_impl_t *s0ep;
148 ses2_aes_descr_sas0_impl_t *s0p;
149 ses2_aes_descr_impl_t *dip;
150 nvlist_t **nva;
151 int nverr, i;
152 size_t nphy;
153
154 if (dep->sadei_eip) {
155 s0ep = (ses2_aes_descr_sas0_eip_impl_t *)
156 dep->sadei_protocol_specific;
157 s0p = (ses2_aes_descr_sas0_impl_t *)
158 dep->sadei_protocol_specific;
159 } else {
160 dip = (ses2_aes_descr_impl_t *)dep;
161 s0ep = NULL;
162 s0p = (ses2_aes_descr_sas0_impl_t *)
163 dip->sadei_protocol_specific;
164 }
165
166 if (dep->sadei_invalid)
167 return (0);
168
169 if (dep->sadei_protocol_identifier == SPC4_PROTO_FIBRE_CHANNEL) {
170 fp = (ses2_aes_descr_fc_eip_impl_t *)
171 dep->sadei_protocol_specific;
172
173 if (!SES_WITHIN_PAGE_STRUCT(fp, dep, len))
174 return (0);
175
176 return (ercsn_0101_elem_parse_aes_fc(fp, nvl, len -
177 offsetof(ses2_aes_descr_eip_impl_t,
178 sadei_protocol_specific)));
179 } else if (dep->sadei_protocol_identifier != SPC4_PROTO_SAS) {
180 return (0);
181 }
182
183 if (s0p->sadsi_descriptor_type != SES2_AESD_SAS_DEVICE)
184 return (0);
185
186 SES_NV_ADD(boolean_value, nverr, nvl, SES_DEV_PROP_SAS_NOT_ALL_PHYS,
187 s0p->sadsi_not_all_phys);
188 if (s0ep != NULL) {
189 SES_NV_ADD(uint64, nverr, nvl, SES_PROP_BAY_NUMBER,
190 s0ep->sadsi_bay_number + 1);
191 nphy = MIN(s0ep->sadsi_n_phy_descriptors,
192 (len - offsetof(ses2_aes_descr_sas0_eip_impl_t,
193 sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
194 } else {
195 nphy = MIN(s0p->sadsi_n_phy_descriptors,
196 (len - offsetof(ses2_aes_descr_sas0_impl_t,
197 sadsi_phys)) / sizeof (ses2_aes_phy0_descr_impl_t));
198 }
199
200 if (nphy == 0)
201 return (0);
202
203 nva = ses_zalloc(nphy * sizeof (nvlist_t *));
204 if (nva == NULL)
205 return (-1);
206
207 for (i = 0; i < nphy; i++) {
208 ses2_aes_phy0_descr_impl_t *pp;
209 pp = s0ep != NULL ? &s0ep->sadsi_phys[i] : &s0p->sadsi_phys[i];
210 if ((nverr = nvlist_alloc(&nva[i], NV_UNIQUE_NAME, 0)) != 0)
211 goto fail;
212 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_DEVICE_TYPE,
213 pp->sapdi_device_type)) != 0)
214 goto fail;
215 if ((nverr = nvlist_add_boolean_value(nva[i],
216 SES_SAS_PROP_SMPI_PORT, pp->sapdi_smp_initiator_port)) != 0)
217 goto fail;
218 if ((nverr = nvlist_add_boolean_value(nva[i],
219 SES_SAS_PROP_STPI_PORT, pp->sapdi_stp_initiator_port)) != 0)
220 goto fail;
221 if ((nverr = nvlist_add_boolean_value(nva[i],
222 SES_SAS_PROP_SSPI_PORT, pp->sapdi_ssp_initiator_port)) != 0)
223 goto fail;
224 if ((nverr = nvlist_add_boolean_value(nva[i],
225 SES_SAS_PROP_SATA_DEVICE, pp->sapdi_sata_device)) != 0)
226 goto fail;
227 if ((nverr = nvlist_add_boolean_value(nva[i],
228 SES_SAS_PROP_SMPT_PORT, pp->sapdi_smp_target_port)) != 0)
229 goto fail;
230 if ((nverr = nvlist_add_boolean_value(nva[i],
231 SES_SAS_PROP_STPT_PORT, pp->sapdi_stp_target_port)) != 0)
232 goto fail;
233 if ((nverr = nvlist_add_boolean_value(nva[i],
234 SES_SAS_PROP_SSPT_PORT, pp->sapdi_ssp_target_port)) != 0)
235 goto fail;
236 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ATT_ADDR,
237 SCSI_READ64(&pp->sapdi_attached_sas_address));
238 if (nverr != 0)
239 goto fail;
240 nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_ADDR,
241 SCSI_READ64(&pp->sapdi_sas_address));
242 if (nverr != 0)
243 goto fail;
244 if ((nverr = nvlist_add_uint64(nva[i], SES_SAS_PROP_PHY_ID,
245 pp->sapdi_phy_identifier)) != 0)
246 goto fail;
247 }
248
249 if ((nverr = nvlist_add_nvlist_array(nvl, SES_SAS_PROP_PHYS,
250 nva, nphy)) != 0)
251 goto fail;
252
253 for (i = 0; i < nphy && nva[i] != NULL; i++)
254 nvlist_free(nva[i]);
255 ses_free(nva);
256 return (0);
257
258 fail:
259 for (i = 0; i < nphy && nva[i] != NULL; i++)
260 nvlist_free(nva[i]);
261 ses_free(nva);
262 return (ses_set_nverrno(nverr, NULL));
263 }
264
265 /*
266 * Ericcson SRU-0101 specific ses node parsing is needed to correct
267 * libses assumptions about index numbering.
268 */
269 static int
270 ercsn_0101_parse_node(ses_plugin_t *sp, ses_node_t *np)
271 {
272 uint64_t i, type;
273 int nverr;
274 size_t len;
275 nvlist_t *props;
276 ses2_aes_descr_eip_impl_t *dep;
277
278
279 if (ses_node_type(np) != SES_NODE_ELEMENT)
280 return (0);
281
282 props = ses_node_props(np);
283 type = fnvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE);
284 if (type != SES_ET_ARRAY_DEVICE)
285 return (0);
286
287 /*
288 * Fix bay numbering oddity
289 */
290 if (ercsn_0101_fix_bay(sp, np) != 0)
291 return (-1);
292
293 if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX, &i) != 0)
294 return (0);
295
296 /*
297 * We populated the element-only-index in ses_build_snap_skel().
298 * This index starts at zero and is used internally by libses to match
299 * device element indexes to the indexes obtained from the AES page (see
300 * ses2_aes_index()).
301 * Ericsson starts their element index at one so we have an off by one
302 * error that we are correcting here
303 */
304 SES_NV_ADD(uint64, nverr, props, SES_PROP_ELEMENT_ONLY_INDEX, i + 1);
305
306 /* now that we've fixed the index we need to redo the AES parsing */
307 if ((dep = ses_plugin_page_lookup(sp, ses_node_snapshot(np),
308 SES2_DIAGPAGE_ADDL_ELEM_STATUS, np, &len)) == NULL)
309 return (0);
310
311 return (ercsn_0101_elem_parse_aes_device(dep, props, len));
312 }
313
314 int
315 _ses_init(ses_plugin_t *sp)
316 {
317 ses_plugin_config_t config = {
318 .spc_node_parse = ercsn_0101_parse_node
319 };
320
321 return (ses_plugin_register(sp, LIBSES_PLUGIN_VERSION, &config) != 0);
322 }