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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <stddef.h>
27 #include <strings.h>
28 
29 #include <scsi/libses.h>
30 #include <scsi/libses_plugin.h>
31 #include <scsi/plugins/ses/framework/ses2.h>
32 
33 #include "ses2_impl.h"
34 
35 static int
36 ses2_ctl_common_setdef(ses_node_t *np, ses2_diag_page_t page, void *data)
37 {
38 	ses2_cmn_elem_ctl_impl_t *eip = data;
39 	nvlist_t *props = ses_node_props(np);
40 
41 	if (page != SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS)
42 		return (0);
43 
44 	SES_NV_CTLBOOL_INVERT(props, SES_PROP_SWAP, eip->seci_rst_swap);
45 	SES_NV_CTLBOOL(props, SES_PROP_DISABLED, eip->seci_disable);
46 	SES_NV_CTLBOOL(props, SES_PROP_PRDFAIL, eip->seci_prdfail);
47 
48 	eip->seci_select = 1;
49 
50 	return (0);
51 }
52 
53 /*ARGSUSED*/
54 static void *
55 ses2_aes_index(ses_plugin_t *sp, ses_node_t *np, void *data, size_t pagelen,
56     size_t *len)
57 {
58 	ses2_aes_page_impl_t *apip = data;
59 	uint64_t index, type;
60 	nvlist_t *props = ses_node_props(np);
61 	ses2_aes_descr_eip_impl_t *dep;
62 	size_t desclen;
63 	int i, pos;
64 	ses_node_t *uncle;
65 
66 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
67 	    &index) == 0);
68 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
69 	    &type) == 0);
70 
71 	if (pagelen < offsetof(ses2_aes_page_impl_t, sapi_data))
72 		return (0);
73 
74 	/*
75 	 * Because, according to 6.1.13.1, the element index "does not
76 	 * include the OVERALL STATUS fields", we have to go recompute the
77 	 * index of this element each time.  This, naturally, is a linear
78 	 * exercise as well.
79 	 */
80 	for (uncle = ses_node_parent(np); uncle != NULL;
81 	    uncle = ses_node_prev_sibling(uncle))
82 		--index;
83 
84 	for (dep = (ses2_aes_descr_eip_impl_t *)apip->sapi_data, pos = 0, i = 0;
85 	    pos < SCSI_READ16(&apip->sapi_page_length);
86 	    dep = (ses2_aes_descr_eip_impl_t *)(apip->sapi_data + pos), i++) {
87 		if (!SES_WITHIN_PAGE_STRUCT(dep, data, pagelen))
88 			break;
89 
90 		desclen = dep->sadei_length +
91 		    offsetof(ses2_aes_descr_eip_impl_t, sadei_length) +
92 		    sizeof (dep->sadei_length);
93 
94 		if (!SES_WITHIN_PAGE(dep, desclen, data, pagelen))
95 			break;
96 
97 		pos += desclen;
98 		if (!dep->sadei_eip &&
99 		    type != SES_ET_DEVICE &&
100 		    type != SES_ET_ARRAY_DEVICE) {
101 			/*
102 			 * We can't really do anything with this, because
103 			 * while the standard requires that these descriptors
104 			 * be in the same order as those in the status page,
105 			 * some element types may optionally include AES
106 			 * data.  This means we cannot know which element
107 			 * this descriptor refers to unless EIP is 1.  Sadly,
108 			 * the standard only says that this "should" be true.
109 			 * It's impossible to guess what use this is supposed
110 			 * to have otherwise.  See 6.1.13.1.
111 			 */
112 			continue;
113 		} else if (dep->sadei_eip &&
114 		    dep->sadei_element_index != index) {
115 			continue;
116 		} else if (dep->sadei_eip || i == index) {
117 			*len = desclen;
118 			return (dep);
119 		}
120 	}
121 
122 	return (NULL);
123 }
124 
125 /*ARGSUSED*/
126 static void *
127 ses2_threshold_index(ses_plugin_t *sp, ses_node_t *np, void *data,
128     size_t pagelen, size_t *len)
129 {
130 	uint64_t index;
131 	nvlist_t *props = ses_node_props(np);
132 	ses2_threshold_in_page_impl_t *tpip = data;
133 	ses2_threshold_impl_t *tp;
134 
135 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
136 	    &index) == 0);
137 
138 	*len = sizeof (ses2_threshold_impl_t);
139 	tp = &tpip->stipi_thresholds[index];
140 
141 	if (!SES_WITHIN_PAGE_STRUCT(tp, data, pagelen))
142 		return (NULL);
143 
144 	return (&tpip->stipi_thresholds[index]);
145 }
146 
147 /*ARGSUSED*/
148 static void *
149 ses2_element_index(ses_plugin_t *sp, ses_node_t *np, void *data,
150     size_t pagelen, size_t *len)
151 {
152 	uint64_t index;
153 	nvlist_t *props = ses_node_props(np);
154 	ses2_elem_desc_page_impl_t *edip = data;
155 	ses2_elem_descriptor_impl_t *dp;
156 	int i;
157 	uint16_t dlen;
158 
159 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX, &index) != 0)
160 		return (NULL);
161 
162 	if (!SES_WITHIN_PAGE(data, sizeof (*dp), data, pagelen))
163 		return (NULL);
164 
165 	/*
166 	 * This variable-length list of variable-length strings format sucks
167 	 * for performance; we ALWAYS have to walk the whole bloody thing to
168 	 * find a particular node's entry.
169 	 */
170 	for (i = 0, dp = (ses2_elem_descriptor_impl_t *)edip->sedpi_data;
171 	    i < index; i++) {
172 
173 		if (!SES_WITHIN_PAGE_STRUCT(dp, data, pagelen))
174 			return (NULL);
175 
176 		dlen = SCSI_READ16(&dp->sedi_descriptor_length);
177 
178 		dp = (ses2_elem_descriptor_impl_t *)
179 		    ((uint8_t *)dp->sedi_descriptor + dlen);
180 	}
181 
182 	if (!SES_WITHIN_PAGE_STRUCT(dp, data, pagelen))
183 		return (NULL);
184 
185 	*len = SCSI_READ16(&dp->sedi_descriptor_length);
186 
187 	if (!SES_WITHIN_PAGE(dp,
188 	    *len + offsetof(ses2_elem_descriptor_impl_t, sedi_descriptor),
189 	    data, pagelen))
190 		return (NULL);
191 
192 	return (dp->sedi_descriptor);
193 }
194 
195 /*ARGSUSED*/
196 static void *
197 ses2_status_index(ses_plugin_t *sp, ses_node_t *np, void *data,
198     size_t pagelen, size_t *len)
199 {
200 	uint64_t index;
201 	nvlist_t *props = ses_node_props(np);
202 	ses2_status_page_impl_t *spip = data;
203 
204 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
205 	    &index) != 0)
206 		return (NULL);
207 
208 	if ((index + 1) * sizeof (ses2_elem_status_impl_t) +
209 	    offsetof(ses2_status_page_impl_t, sspi_data) > pagelen)
210 		return (NULL);
211 
212 	*len = sizeof (ses2_elem_status_impl_t);
213 	return ((ses2_elem_status_impl_t *)spip->sspi_data + index);
214 }
215 
216 /*ARGSUSED*/
217 static size_t
218 ses2_ctl_len(uint_t nelem, int page, size_t datalen)
219 {
220 	ASSERT(page == SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS);
221 
222 	return (nelem * sizeof (ses2_elem_ctl_impl_t) +
223 	    offsetof(ses2_control_page_impl_t, scpi_data[0]));
224 }
225 
226 /*ARGSUSED*/
227 static void *
228 ses2_ctl_fill(ses_plugin_t *sp, void *pagedata, size_t pagelen,
229     ses_node_t *np)
230 {
231 	uint64_t index;
232 	nvlist_t *props = ses_node_props(np);
233 	ses2_control_page_impl_t *pip = pagedata;
234 	void *data;
235 	ses2_diag_page_t page = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS;
236 
237 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
238 	    &index) != 0) {
239 		(void) ses_error(ESES_BAD_RESPONSE, "missing element index "
240 		    "for enclosure node");
241 		return (NULL);
242 	}
243 
244 	data = &pip->scpi_data[index];
245 
246 	if (ses2_ctl_common_setdef(np, page, data) != 0 ||
247 	    ses2_element_setdef(np, page, data) != 0 ||
248 	    ses2_enclosure_setdef(np, page, data) != 0)
249 		return (NULL);
250 
251 	return (data);
252 }
253 
254 /*ARGSUSED*/
255 static size_t
256 ses2_stringout_len(uint_t nelem, int page, size_t datalen)
257 {
258 	ASSERT(page == SES2_DIAGPAGE_STRING_IO);
259 
260 	return (datalen + offsetof(ses2_string_out_page_impl_t, ssopi_data[0]));
261 }
262 
263 /*ARGSUSED*/
264 static size_t
265 ses2_threshout_len(uint_t nelem, int page, size_t datalen)
266 {
267 	ASSERT(page == SES2_DIAGPAGE_THRESHOLD_IO);
268 
269 	return (nelem * sizeof (ses2_threshold_impl_t) +
270 	    offsetof(ses2_threshold_out_page_impl_t, stopi_thresholds[0]));
271 }
272 
273 /*ARGSUSED*/
274 static void *
275 ses2_threshout_ctl_fill(ses_plugin_t *sp, void *pagedata, size_t pagelen,
276     ses_node_t *np)
277 {
278 	uint64_t index;
279 	nvlist_t *props = ses_node_props(np);
280 	ses2_threshold_out_page_impl_t *pip = pagedata;
281 	ses2_diag_page_t page = SES2_DIAGPAGE_THRESHOLD_IO;
282 	void *data;
283 
284 	VERIFY(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_INDEX,
285 	    &index) == 0);
286 
287 	data = &pip[index];
288 
289 	if (ses2_ctl_common_setdef(np, page, data) != 0 ||
290 	    ses2_element_setdef(np, page, data) != 0 ||
291 	    ses2_enclosure_setdef(np, page, data) != 0)
292 		return (NULL);
293 
294 	return (data);
295 }
296 
297 /*ARGSUSED*/
298 static size_t
299 ses2_substrout_len(uint_t nelem, int page, size_t datalen)
300 {
301 	ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO);
302 
303 	return (datalen +
304 	    offsetof(ses2_substring_out_page_impl_t, ssopi_data[0]));
305 }
306 
307 /*ARGSUSED*/
308 static size_t
309 ses2_ucodeout_len(uint_t nelem, int page, size_t datalen)
310 {
311 	size_t len;
312 
313 	ASSERT(page == SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS);
314 
315 	len = datalen +
316 	    offsetof(ses2_ucode_ctl_page_impl_t, sucpi_ucode_data[0]);
317 
318 	return (P2ROUNDUP(len, 4));
319 }
320 
321 /*ARGSUSED*/
322 static void *
323 ses2_ucodeout_ctl_fill(ses_plugin_t *sp, void *data, size_t pagelen,
324     ses_node_t *np)
325 {
326 	ses_snap_t *snap = ses_node_snapshot(np);
327 	nvlist_t *props = ses_node_props(np);
328 	ses2_ucode_ctl_page_impl_t *uip = data;
329 	uint64_t eid;
330 
331 	if (ses_node_type(np) != SES_NODE_ENCLOSURE) {
332 		(void) ses_error(ESES_BAD_TYPE,
333 		    "microcode download page only valid for enclosure "
334 		    "nodes");
335 		return (NULL);
336 	}
337 
338 	VERIFY(nvlist_lookup_uint64(props, SES_EN_PROP_EID, &eid) == 0);
339 
340 	SCSI_WRITE32(&uip->sucpi_generation_code,
341 	    ses_snap_generation(snap));
342 	uip->sucpi_subenclosure_identifier = eid;
343 
344 	return (data);
345 }
346 
347 /*ARGSUSED*/
348 static size_t
349 ses2_subnickout_len(uint_t nelem, int page, size_t datalen)
350 {
351 	ASSERT(page == SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS);
352 
353 	return (sizeof (ses2_subnick_ctl_page_impl_t));
354 }
355 
356 ses_pagedesc_t ses2_pages[] = {
357 {
358 	.spd_pagenum = SES2_DIAGPAGE_SUPPORTED_PAGES,
359 	.spd_req = SES_REQ_MANDATORY_ALL,
360 	.spd_gcoff = -1
361 },
362 {
363 	.spd_pagenum = SES2_DIAGPAGE_CONFIG,
364 	.spd_req = SES_REQ_MANDATORY_STANDARD,
365 	.spd_gcoff = offsetof(ses2_config_page_impl_t, scpi_generation_code)
366 },
367 {
368 	.spd_pagenum = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
369 	.spd_req = SES_REQ_MANDATORY_STANDARD,
370 	.spd_index = ses2_status_index,
371 	.spd_gcoff = offsetof(ses2_status_page_impl_t, sspi_generation_code)
372 },
373 {
374 	.spd_pagenum = SES2_DIAGPAGE_HELP_TEXT,
375 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
376 	.spd_gcoff = -1
377 },
378 {
379 	.spd_pagenum = SES2_DIAGPAGE_STRING_IO,
380 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
381 	.spd_gcoff = -1
382 },
383 {
384 	.spd_pagenum = SES2_DIAGPAGE_THRESHOLD_IO,
385 	.spd_index = ses2_threshold_index,
386 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
387 	.spd_gcoff =
388 	    offsetof(ses2_threshold_in_page_impl_t, stipi_generation_code)
389 },
390 {
391 	.spd_pagenum = SES2_DIAGPAGE_ELEMENT_DESC,
392 	.spd_index = ses2_element_index,
393 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
394 	.spd_gcoff = offsetof(ses2_elem_desc_page_impl_t, sedpi_generation_code)
395 },
396 {
397 	.spd_pagenum = SES2_DIAGPAGE_ADDL_ELEM_STATUS,
398 	.spd_index = ses2_aes_index,
399 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
400 	.spd_gcoff = offsetof(ses2_aes_page_impl_t, sapi_generation_code)
401 },
402 {
403 	.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_HELP_TEXT,
404 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
405 	.spd_gcoff = offsetof(ses2_subhelp_page_impl_t, sspi_generation_code)
406 },
407 {
408 	.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO,
409 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
410 	.spd_gcoff =
411 	    offsetof(ses2_substring_in_page_impl_t, ssipi_generation_code)
412 },
413 {
414 	.spd_pagenum = SES2_DIAGPAGE_SUPPORTED_SES_PAGES,
415 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
416 	.spd_gcoff = -1
417 },
418 {
419 	.spd_pagenum = SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS,
420 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
421 	.spd_gcoff =
422 	    offsetof(ses2_ucode_status_page_impl_t, suspi_generation_code)
423 },
424 {
425 	.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
426 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
427 	.spd_gcoff =
428 	    offsetof(ses2_subnick_status_page_impl_t, sspci_generation_code)
429 },
430 /* Control pages */
431 {
432 	.spd_pagenum = SES2_DIAGPAGE_ENCLOSURE_CTL_STATUS,
433 	.spd_ctl_len = ses2_ctl_len,
434 	.spd_ctl_fill = ses2_ctl_fill,
435 	.spd_req = SES_REQ_MANDATORY_STANDARD,
436 	.spd_gcoff = offsetof(ses2_control_page_impl_t, scpi_generation_code)
437 },
438 {
439 	.spd_pagenum = SES2_DIAGPAGE_STRING_IO,
440 	.spd_ctl_len = ses2_stringout_len,
441 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
442 	.spd_gcoff = -1
443 },
444 {
445 	.spd_pagenum = SES2_DIAGPAGE_THRESHOLD_IO,
446 	.spd_ctl_len = ses2_threshout_len,
447 	.spd_ctl_fill = ses2_threshout_ctl_fill,
448 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
449 	.spd_gcoff =
450 	    offsetof(ses2_threshold_out_page_impl_t, stopi_generation_code)
451 },
452 {
453 	.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_STRING_IO,
454 	.spd_ctl_len = ses2_substrout_len,
455 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
456 	.spd_gcoff =
457 	    offsetof(ses2_substring_out_page_impl_t, ssopi_generation_code)
458 },
459 {
460 	.spd_pagenum = SES2_DIAGPAGE_DL_MICROCODE_CTL_STATUS,
461 	.spd_ctl_len = ses2_ucodeout_len,
462 	.spd_ctl_fill = ses2_ucodeout_ctl_fill,
463 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
464 	.spd_gcoff =
465 	    offsetof(ses2_ucode_ctl_page_impl_t, sucpi_generation_code)
466 },
467 {
468 	.spd_pagenum = SES2_DIAGPAGE_SUBENCLOSURE_NICKNAME_CTL_STATUS,
469 	.spd_ctl_len = ses2_subnickout_len,
470 	.spd_req = SES_REQ_OPTIONAL_STANDARD,
471 	.spd_gcoff =
472 	    offsetof(ses2_subnick_ctl_page_impl_t, sspci_generation_code)
473 },
474 {
475 	.spd_pagenum = -1,
476 	.spd_gcoff = -1
477 }
478 };
479