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