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