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