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