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