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