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