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 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 * 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 * 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 * 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 * 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 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 * 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 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 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 * 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 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 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 * 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 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