1/*- 2 * Copyright (c) 2000 Matthew Jacob 3 * Copyright (c) 2010 Spectra Logic Corporation 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28/** 29 * \file scsi_enc_ses.c 30 * 31 * Structures and routines specific && private to SES only 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: stable/11/sys/cam/scsi/scsi_enc_ses.c 361040 2020-05-14 18:19:28Z jhb $"); 36 37#include <sys/param.h> 38 39#include <sys/ctype.h> 40#include <sys/errno.h> 41#include <sys/kernel.h> 42#include <sys/lock.h> 43#include <sys/malloc.h> 44#include <sys/mutex.h> 45#include <sys/queue.h> 46#include <sys/sbuf.h> 47#include <sys/sx.h> 48#include <sys/systm.h> 49#include <sys/types.h> 50 51#include <cam/cam.h> 52#include <cam/cam_ccb.h> 53#include <cam/cam_xpt_periph.h> 54#include <cam/cam_periph.h> 55 56#include <cam/scsi/scsi_message.h> 57#include <cam/scsi/scsi_enc.h> 58#include <cam/scsi/scsi_enc_internal.h> 59 60/* SES Native Type Device Support */ 61 62/* SES Diagnostic Page Codes */ 63typedef enum { 64 SesSupportedPages = 0x0, 65 SesConfigPage = 0x1, 66 SesControlPage = 0x2, 67 SesStatusPage = SesControlPage, 68 SesHelpTxt = 0x3, 69 SesStringOut = 0x4, 70 SesStringIn = SesStringOut, 71 SesThresholdOut = 0x5, 72 SesThresholdIn = SesThresholdOut, 73 SesArrayControl = 0x6, /* Obsolete in SES v2 */ 74 SesArrayStatus = SesArrayControl, 75 SesElementDescriptor = 0x7, 76 SesShortStatus = 0x8, 77 SesEnclosureBusy = 0x9, 78 SesAddlElementStatus = 0xa 79} SesDiagPageCodes; 80 81typedef struct ses_type { 82 const struct ses_elm_type_desc *hdr; 83 const char *text; 84} ses_type_t; 85 86typedef struct ses_comstat { 87 uint8_t comstatus; 88 uint8_t comstat[3]; 89} ses_comstat_t; 90 91typedef union ses_addl_data { 92 struct ses_elm_sas_device_phy *sasdev_phys; 93 struct ses_elm_sas_expander_phy *sasexp_phys; 94 struct ses_elm_sas_port_phy *sasport_phys; 95 struct ses_fcobj_port *fc_ports; 96} ses_add_data_t; 97 98typedef struct ses_addl_status { 99 struct ses_elm_addlstatus_base_hdr *hdr; 100 union { 101 union ses_fcobj_hdr *fc; 102 union ses_elm_sas_hdr *sas; 103 struct ses_elm_ata_hdr *ata; 104 } proto_hdr; 105 union ses_addl_data proto_data; /* array sizes stored in header */ 106} ses_add_status_t; 107 108typedef struct ses_element { 109 uint8_t eip; /* eip bit is set */ 110 uint16_t descr_len; /* length of the descriptor */ 111 const char *descr; /* descriptor for this object */ 112 struct ses_addl_status addl; /* additional status info */ 113} ses_element_t; 114 115typedef struct ses_control_request { 116 int elm_idx; 117 ses_comstat_t elm_stat; 118 int result; 119 TAILQ_ENTRY(ses_control_request) links; 120} ses_control_request_t; 121TAILQ_HEAD(ses_control_reqlist, ses_control_request); 122typedef struct ses_control_reqlist ses_control_reqlist_t; 123enum { 124 SES_SETSTATUS_ENC_IDX = -1 125}; 126 127static void 128ses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result) 129{ 130 ses_control_request_t *req; 131 132 while ((req = TAILQ_FIRST(reqlist)) != NULL) { 133 TAILQ_REMOVE(reqlist, req, links); 134 req->result = result; 135 wakeup(req); 136 } 137} 138 139enum ses_iter_index_values { 140 /** 141 * \brief Value of an initialized but invalid index 142 * in a ses_iterator object. 143 * 144 * This value is used for the individual_element_index of 145 * overal status elements and for all index types when 146 * an iterator is first initialized. 147 */ 148 ITERATOR_INDEX_INVALID = -1, 149 150 /** 151 * \brief Value of an index in a ses_iterator object 152 * when the iterator has traversed past the last 153 * valid element.. 154 */ 155 ITERATOR_INDEX_END = INT_MAX 156}; 157 158/** 159 * \brief Structure encapsulating all data necessary to traverse the 160 * elements of a SES configuration. 161 * 162 * The ses_iterator object simplifies the task of iterating through all 163 * elements detected via the SES configuration page by tracking the numerous 164 * element indexes that, instead of memoizing in the softc, we calculate 165 * on the fly during the traversal of the element objects. The various 166 * indexes are necessary due to the varying needs of matching objects in 167 * the different SES pages. Some pages (e.g. Status/Control) contain all 168 * elements, while others (e.g. Additional Element Status) only contain 169 * individual elements (no overal status elements) of particular types. 170 * 171 * To use an iterator, initialize it with ses_iter_init(), and then 172 * use ses_iter_next() to traverse the elements (including the first) in 173 * the configuration. Once an iterator is initiailized with ses_iter_init(), 174 * you may also seek to any particular element by either it's global or 175 * individual element index via the ses_iter_seek_to() function. You may 176 * also return an iterator to the position just before the first element 177 * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset(). 178 */ 179struct ses_iterator { 180 /** 181 * \brief Backlink to the overal software configuration structure. 182 * 183 * This is included for convenience so the iteration functions 184 * need only take a single, struct ses_iterator *, argument. 185 */ 186 enc_softc_t *enc; 187 188 enc_cache_t *cache; 189 190 /** 191 * \brief Index of the type of the current element within the 192 * ses_cache's ses_types array. 193 */ 194 int type_index; 195 196 /** 197 * \brief The position (0 based) of this element relative to all other 198 * elements of this type. 199 * 200 * This index resets to zero every time the iterator transitions 201 * to elements of a new type in the configuration. 202 */ 203 int type_element_index; 204 205 /** 206 * \brief The position (0 based) of this element relative to all 207 * other individual status elements in the configuration. 208 * 209 * This index ranges from 0 through the number of individual 210 * elements in the configuration. When the iterator returns 211 * an overall status element, individual_element_index is 212 * set to ITERATOR_INDEX_INVALID, to indicate that it does 213 * not apply to the current element. 214 */ 215 int individual_element_index; 216 217 /** 218 * \brief The position (0 based) of this element relative to 219 * all elements in the configration. 220 * 221 * This index is appropriate for indexing into enc->ses_elm_map. 222 */ 223 int global_element_index; 224 225 /** 226 * \brief The last valid individual element index of this 227 * iterator. 228 * 229 * When an iterator traverses an overal status element, the 230 * individual element index is reset to ITERATOR_INDEX_INVALID 231 * to prevent unintential use of the individual_element_index 232 * field. The saved_individual_element_index allows the iterator 233 * to restore it's position in the individual elements upon 234 * reaching the next individual element. 235 */ 236 int saved_individual_element_index; 237}; 238 239typedef enum { 240 SES_UPDATE_NONE, 241 SES_UPDATE_PAGES, 242 SES_UPDATE_GETCONFIG, 243 SES_UPDATE_GETSTATUS, 244 SES_UPDATE_GETELMDESCS, 245 SES_UPDATE_GETELMADDLSTATUS, 246 SES_PROCESS_CONTROL_REQS, 247 SES_PUBLISH_PHYSPATHS, 248 SES_PUBLISH_CACHE, 249 SES_NUM_UPDATE_STATES 250} ses_update_action; 251 252static enc_softc_cleanup_t ses_softc_cleanup; 253 254#define SCSZ 0x8000 255 256static fsm_fill_handler_t ses_fill_rcv_diag_io; 257static fsm_fill_handler_t ses_fill_control_request; 258static fsm_done_handler_t ses_process_pages; 259static fsm_done_handler_t ses_process_config; 260static fsm_done_handler_t ses_process_status; 261static fsm_done_handler_t ses_process_elm_descs; 262static fsm_done_handler_t ses_process_elm_addlstatus; 263static fsm_done_handler_t ses_process_control_request; 264static fsm_done_handler_t ses_publish_physpaths; 265static fsm_done_handler_t ses_publish_cache; 266 267static struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = 268{ 269 { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, 270 { 271 "SES_UPDATE_PAGES", 272 SesSupportedPages, 273 SCSZ, 274 60 * 1000, 275 ses_fill_rcv_diag_io, 276 ses_process_pages, 277 enc_error 278 }, 279 { 280 "SES_UPDATE_GETCONFIG", 281 SesConfigPage, 282 SCSZ, 283 60 * 1000, 284 ses_fill_rcv_diag_io, 285 ses_process_config, 286 enc_error 287 }, 288 { 289 "SES_UPDATE_GETSTATUS", 290 SesStatusPage, 291 SCSZ, 292 60 * 1000, 293 ses_fill_rcv_diag_io, 294 ses_process_status, 295 enc_error 296 }, 297 { 298 "SES_UPDATE_GETELMDESCS", 299 SesElementDescriptor, 300 SCSZ, 301 60 * 1000, 302 ses_fill_rcv_diag_io, 303 ses_process_elm_descs, 304 enc_error 305 }, 306 { 307 "SES_UPDATE_GETELMADDLSTATUS", 308 SesAddlElementStatus, 309 SCSZ, 310 60 * 1000, 311 ses_fill_rcv_diag_io, 312 ses_process_elm_addlstatus, 313 enc_error 314 }, 315 { 316 "SES_PROCESS_CONTROL_REQS", 317 SesControlPage, 318 SCSZ, 319 60 * 1000, 320 ses_fill_control_request, 321 ses_process_control_request, 322 enc_error 323 }, 324 { 325 "SES_PUBLISH_PHYSPATHS", 326 0, 327 0, 328 0, 329 NULL, 330 ses_publish_physpaths, 331 NULL 332 }, 333 { 334 "SES_PUBLISH_CACHE", 335 0, 336 0, 337 0, 338 NULL, 339 ses_publish_cache, 340 NULL 341 } 342}; 343 344typedef struct ses_cache { 345 /* Source for all the configuration data pointers */ 346 const struct ses_cfg_page *cfg_page; 347 348 /* References into the config page. */ 349 int ses_nsubencs; 350 const struct ses_enc_desc * const *subencs; 351 int ses_ntypes; 352 const ses_type_t *ses_types; 353 354 /* Source for all the status pointers */ 355 const struct ses_status_page *status_page; 356 357 /* Source for all the object descriptor pointers */ 358 const struct ses_elem_descr_page *elm_descs_page; 359 360 /* Source for all the additional object status pointers */ 361 const struct ses_addl_elem_status_page *elm_addlstatus_page; 362 363} ses_cache_t; 364 365typedef struct ses_softc { 366 uint32_t ses_flags; 367#define SES_FLAG_TIMEDCOMP 0x01 368#define SES_FLAG_ADDLSTATUS 0x02 369#define SES_FLAG_DESC 0x04 370 371 ses_control_reqlist_t ses_requests; 372 ses_control_reqlist_t ses_pending_requests; 373} ses_softc_t; 374 375/** 376 * \brief Reset a SES iterator to just before the first element 377 * in the configuration. 378 * 379 * \param iter The iterator object to reset. 380 * 381 * The indexes within a reset iterator are invalid and will only 382 * become valid upon completion of a ses_iter_seek_to() or a 383 * ses_iter_next(). 384 */ 385static void 386ses_iter_reset(struct ses_iterator *iter) 387{ 388 /* 389 * Set our indexes to just before the first valid element 390 * of the first type (ITERATOR_INDEX_INVALID == -1). This 391 * simplifies the implementation of ses_iter_next(). 392 */ 393 iter->type_index = 0; 394 iter->type_element_index = ITERATOR_INDEX_INVALID; 395 iter->global_element_index = ITERATOR_INDEX_INVALID; 396 iter->individual_element_index = ITERATOR_INDEX_INVALID; 397 iter->saved_individual_element_index = ITERATOR_INDEX_INVALID; 398} 399 400/** 401 * \brief Initialize the storage of a SES iterator and reset it to 402 * the position just before the first element of the 403 * configuration. 404 * 405 * \param enc The SES softc for the SES instance whose configuration 406 * will be enumerated by this iterator. 407 * \param iter The iterator object to initialize. 408 */ 409static void 410ses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter) 411{ 412 iter->enc = enc; 413 iter->cache = cache; 414 ses_iter_reset(iter); 415} 416 417/** 418 * \brief Traverse the provided SES iterator to the next element 419 * within the configuraiton. 420 * 421 * \param iter The iterator to move. 422 * 423 * \return If a valid next element exists, a pointer to it's enc_element_t. 424 * Otherwise NULL. 425 */ 426static enc_element_t * 427ses_iter_next(struct ses_iterator *iter) 428{ 429 ses_cache_t *ses_cache; 430 const ses_type_t *element_type; 431 432 ses_cache = iter->cache->private; 433 434 /* 435 * Note: Treat nelms as signed, so we will hit this case 436 * and immediately terminate the iteration if the 437 * configuration has 0 objects. 438 */ 439 if (iter->global_element_index >= (int)iter->cache->nelms - 1) { 440 441 /* Elements exhausted. */ 442 iter->type_index = ITERATOR_INDEX_END; 443 iter->type_element_index = ITERATOR_INDEX_END; 444 iter->global_element_index = ITERATOR_INDEX_END; 445 iter->individual_element_index = ITERATOR_INDEX_END; 446 iter->saved_individual_element_index = ITERATOR_INDEX_END; 447 return (NULL); 448 } 449 450 KASSERT((iter->type_index < ses_cache->ses_ntypes), 451 ("Corrupted element iterator. %d not less than %d", 452 iter->type_index, ses_cache->ses_ntypes)); 453 454 element_type = &ses_cache->ses_types[iter->type_index]; 455 iter->global_element_index++; 456 iter->type_element_index++; 457 458 /* 459 * There is an object for overal type status in addition 460 * to one for each allowed element, but only if the element 461 * count is non-zero. 462 */ 463 if (iter->type_element_index > element_type->hdr->etype_maxelt) { 464 465 /* 466 * We've exhausted the elements of this type. 467 * This next element belongs to the next type. 468 */ 469 iter->type_index++; 470 iter->type_element_index = 0; 471 iter->individual_element_index = ITERATOR_INDEX_INVALID; 472 } 473 474 if (iter->type_element_index > 0) { 475 iter->individual_element_index = 476 ++iter->saved_individual_element_index; 477 } 478 479 return (&iter->cache->elm_map[iter->global_element_index]); 480} 481 482/** 483 * Element index types tracked by a SES iterator. 484 */ 485typedef enum { 486 /** 487 * Index relative to all elements (overall and individual) 488 * in the system. 489 */ 490 SES_ELEM_INDEX_GLOBAL, 491 492 /** 493 * \brief Index relative to all individual elements in the system. 494 * 495 * This index counts only individual elements, skipping overall 496 * status elements. This is the index space of the additional 497 * element status page (page 0xa). 498 */ 499 SES_ELEM_INDEX_INDIVIDUAL 500} ses_elem_index_type_t; 501 502/** 503 * \brief Move the provided iterator forwards or backwards to the object 504 * having the give index. 505 * 506 * \param iter The iterator on which to perform the seek. 507 * \param element_index The index of the element to find. 508 * \param index_type The type (global or individual) of element_index. 509 * 510 * \return If the element is found, a pointer to it's enc_element_t. 511 * Otherwise NULL. 512 */ 513static enc_element_t * 514ses_iter_seek_to(struct ses_iterator *iter, int element_index, 515 ses_elem_index_type_t index_type) 516{ 517 enc_element_t *element; 518 int *cur_index; 519 520 if (index_type == SES_ELEM_INDEX_GLOBAL) 521 cur_index = &iter->global_element_index; 522 else 523 cur_index = &iter->individual_element_index; 524 525 if (*cur_index == element_index) { 526 /* Already there. */ 527 return (&iter->cache->elm_map[iter->global_element_index]); 528 } 529 530 ses_iter_reset(iter); 531 while ((element = ses_iter_next(iter)) != NULL 532 && *cur_index != element_index) 533 ; 534 535 if (*cur_index != element_index) 536 return (NULL); 537 538 return (element); 539} 540 541#if 0 542static int ses_encode(enc_softc_t *, uint8_t *, int, int, 543 struct ses_comstat *); 544#endif 545static int ses_set_timed_completion(enc_softc_t *, uint8_t); 546#if 0 547static int ses_putstatus(enc_softc_t *, int, struct ses_comstat *); 548#endif 549 550static void ses_poll_status(enc_softc_t *); 551static void ses_print_addl_data(enc_softc_t *, enc_element_t *); 552 553/*=========================== SES cleanup routines ===========================*/ 554 555static void 556ses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache) 557{ 558 ses_cache_t *ses_cache; 559 ses_cache_t *other_ses_cache; 560 enc_element_t *cur_elm; 561 enc_element_t *last_elm; 562 563 ENC_DLOG(enc, "%s: enter\n", __func__); 564 ses_cache = cache->private; 565 if (ses_cache->elm_addlstatus_page == NULL) 566 return; 567 568 for (cur_elm = cache->elm_map, 569 last_elm = &cache->elm_map[cache->nelms]; 570 cur_elm != last_elm; cur_elm++) { 571 ses_element_t *elmpriv; 572 573 elmpriv = cur_elm->elm_private; 574 575 /* Clear references to the additional status page. */ 576 bzero(&elmpriv->addl, sizeof(elmpriv->addl)); 577 } 578 579 other_ses_cache = enc_other_cache(enc, cache)->private; 580 if (other_ses_cache->elm_addlstatus_page 581 != ses_cache->elm_addlstatus_page) 582 ENC_FREE(ses_cache->elm_addlstatus_page); 583 ses_cache->elm_addlstatus_page = NULL; 584} 585 586static void 587ses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache) 588{ 589 ses_cache_t *ses_cache; 590 ses_cache_t *other_ses_cache; 591 enc_element_t *cur_elm; 592 enc_element_t *last_elm; 593 594 ENC_DLOG(enc, "%s: enter\n", __func__); 595 ses_cache = cache->private; 596 if (ses_cache->elm_descs_page == NULL) 597 return; 598 599 for (cur_elm = cache->elm_map, 600 last_elm = &cache->elm_map[cache->nelms]; 601 cur_elm != last_elm; cur_elm++) { 602 ses_element_t *elmpriv; 603 604 elmpriv = cur_elm->elm_private; 605 elmpriv->descr_len = 0; 606 elmpriv->descr = NULL; 607 } 608 609 other_ses_cache = enc_other_cache(enc, cache)->private; 610 if (other_ses_cache->elm_descs_page 611 != ses_cache->elm_descs_page) 612 ENC_FREE(ses_cache->elm_descs_page); 613 ses_cache->elm_descs_page = NULL; 614} 615 616static void 617ses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache) 618{ 619 ses_cache_t *ses_cache; 620 ses_cache_t *other_ses_cache; 621 622 ENC_DLOG(enc, "%s: enter\n", __func__); 623 ses_cache = cache->private; 624 if (ses_cache->status_page == NULL) 625 return; 626 627 other_ses_cache = enc_other_cache(enc, cache)->private; 628 if (other_ses_cache->status_page != ses_cache->status_page) 629 ENC_FREE(ses_cache->status_page); 630 ses_cache->status_page = NULL; 631} 632 633static void 634ses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache) 635{ 636 enc_element_t *cur_elm; 637 enc_element_t *last_elm; 638 639 ENC_DLOG(enc, "%s: enter\n", __func__); 640 if (cache->elm_map == NULL) 641 return; 642 643 ses_cache_free_elm_descs(enc, cache); 644 ses_cache_free_elm_addlstatus(enc, cache); 645 for (cur_elm = cache->elm_map, 646 last_elm = &cache->elm_map[cache->nelms]; 647 cur_elm != last_elm; cur_elm++) { 648 649 ENC_FREE_AND_NULL(cur_elm->elm_private); 650 } 651 ENC_FREE_AND_NULL(cache->elm_map); 652 cache->nelms = 0; 653 ENC_DLOG(enc, "%s: exit\n", __func__); 654} 655 656static void 657ses_cache_free(enc_softc_t *enc, enc_cache_t *cache) 658{ 659 ses_cache_t *other_ses_cache; 660 ses_cache_t *ses_cache; 661 662 ENC_DLOG(enc, "%s: enter\n", __func__); 663 ses_cache_free_elm_addlstatus(enc, cache); 664 ses_cache_free_status(enc, cache); 665 ses_cache_free_elm_map(enc, cache); 666 667 ses_cache = cache->private; 668 ses_cache->ses_ntypes = 0; 669 670 other_ses_cache = enc_other_cache(enc, cache)->private; 671 if (other_ses_cache->subencs != ses_cache->subencs) 672 ENC_FREE(ses_cache->subencs); 673 ses_cache->subencs = NULL; 674 675 if (other_ses_cache->ses_types != ses_cache->ses_types) 676 ENC_FREE(ses_cache->ses_types); 677 ses_cache->ses_types = NULL; 678 679 if (other_ses_cache->cfg_page != ses_cache->cfg_page) 680 ENC_FREE(ses_cache->cfg_page); 681 ses_cache->cfg_page = NULL; 682 683 ENC_DLOG(enc, "%s: exit\n", __func__); 684} 685 686static void 687ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst) 688{ 689 ses_cache_t *dst_ses_cache; 690 ses_cache_t *src_ses_cache; 691 enc_element_t *src_elm; 692 enc_element_t *dst_elm; 693 enc_element_t *last_elm; 694 695 ses_cache_free(enc, dst); 696 src_ses_cache = src->private; 697 dst_ses_cache = dst->private; 698 699 /* 700 * The cloned enclosure cache and ses specific cache are 701 * mostly identical to the source. 702 */ 703 *dst = *src; 704 *dst_ses_cache = *src_ses_cache; 705 706 /* 707 * But the ses cache storage is still independent. Restore 708 * the pointer that was clobbered by the structure copy above. 709 */ 710 dst->private = dst_ses_cache; 711 712 /* 713 * The element map is independent even though it starts out 714 * pointing to the same constant page data. 715 */ 716 dst->elm_map = malloc(dst->nelms * sizeof(enc_element_t), 717 M_SCSIENC, M_WAITOK); 718 memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t)); 719 for (dst_elm = dst->elm_map, src_elm = src->elm_map, 720 last_elm = &src->elm_map[src->nelms]; 721 src_elm != last_elm; src_elm++, dst_elm++) { 722 723 dst_elm->elm_private = malloc(sizeof(ses_element_t), 724 M_SCSIENC, M_WAITOK); 725 memcpy(dst_elm->elm_private, src_elm->elm_private, 726 sizeof(ses_element_t)); 727 } 728} 729 730/* Structure accessors. These are strongly typed to avoid errors. */ 731 732int 733ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj) 734{ 735 return ((obj)->base_hdr.byte1 >> 6); 736} 737int 738ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr) 739{ 740 return ((hdr)->byte0 & 0xf); 741} 742int 743ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr) 744{ 745 return ((hdr)->byte0 >> 4) & 0x1; 746} 747int 748ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr) 749{ 750 return ((hdr)->byte0 >> 7); 751} 752int 753ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr) 754{ 755 return ((hdr)->type0_noneip.byte1 & 0x1); 756} 757int 758ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy) 759{ 760 return ((phy)->target_ports & 0x1); 761} 762int 763ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy) 764{ 765 return ((phy)->target_ports >> 7); 766} 767int 768ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy) 769{ 770 return (((phy)->byte0 >> 4) & 0x7); 771} 772 773/** 774 * \brief Verify that the cached configuration data in our softc 775 * is valid for processing the page data corresponding to 776 * the provided page header. 777 * 778 * \param ses_cache The SES cache to validate. 779 * \param gen_code The 4 byte generation code from a SES diagnostic 780 * page header. 781 * 782 * \return non-zero if true, 0 if false. 783 */ 784static int 785ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code) 786{ 787 uint32_t cache_gc; 788 uint32_t cur_gc; 789 790 if (ses_cache->cfg_page == NULL) 791 return (0); 792 793 cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code); 794 cur_gc = scsi_4btoul(gen_code); 795 return (cache_gc == cur_gc); 796} 797 798/** 799 * Function signature for consumers of the ses_devids_iter() interface. 800 */ 801typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *, 802 struct scsi_vpd_id_descriptor *, void *); 803 804/** 805 * \brief Iterate over and create vpd device id records from the 806 * additional element status data for elm, passing that data 807 * to the provided callback. 808 * 809 * \param enc SES instance containing elm 810 * \param elm Element for which to extract device ID data. 811 * \param callback The callback function to invoke on each generated 812 * device id descriptor for elm. 813 * \param callback_arg Argument passed through to callback on each invocation. 814 */ 815static void 816ses_devids_iter(enc_softc_t *enc, enc_element_t *elm, 817 ses_devid_callback_t *callback, void *callback_arg) 818{ 819 ses_element_t *elmpriv; 820 struct ses_addl_status *addl; 821 u_int i; 822 size_t devid_record_size; 823 824 elmpriv = elm->elm_private; 825 addl = &(elmpriv->addl); 826 827 devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN 828 + sizeof(struct scsi_vpd_id_naa_ieee_reg); 829 for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) { 830 uint8_t devid_buf[devid_record_size]; 831 struct scsi_vpd_id_descriptor *devid; 832 uint8_t *phy_addr; 833 834 devid = (struct scsi_vpd_id_descriptor *)devid_buf; 835 phy_addr = addl->proto_data.sasdev_phys[i].phy_addr; 836 devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT) 837 | SVPD_ID_CODESET_BINARY; 838 devid->id_type = SVPD_ID_PIV 839 | SVPD_ID_ASSOC_PORT 840 | SVPD_ID_TYPE_NAA; 841 devid->reserved = 0; 842 devid->length = sizeof(struct scsi_vpd_id_naa_ieee_reg); 843 memcpy(devid->identifier, phy_addr, devid->length); 844 845 callback(enc, elm, devid, callback_arg); 846 } 847} 848 849/** 850 * Function signature for consumers of the ses_paths_iter() interface. 851 */ 852typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *, 853 struct cam_path *, void *); 854 855/** 856 * Argument package passed through ses_devids_iter() by 857 * ses_paths_iter() to ses_path_iter_devid_callback(). 858 */ 859typedef struct ses_path_iter_args { 860 ses_path_callback_t *callback; 861 void *callback_arg; 862} ses_path_iter_args_t; 863 864/** 865 * ses_devids_iter() callback function used by ses_paths_iter() 866 * to map device ids to peripheral driver instances. 867 * 868 * \param enc SES instance containing elm 869 * \param elm Element on which device ID matching is active. 870 * \param periph A device ID corresponding to elm. 871 * \param arg Argument passed through to callback on each invocation. 872 */ 873static void 874ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem, 875 struct scsi_vpd_id_descriptor *devid, 876 void *arg) 877{ 878 struct ccb_dev_match cdm; 879 struct dev_match_pattern match_pattern; 880 struct dev_match_result match_result; 881 struct device_match_result *device_match; 882 struct device_match_pattern *device_pattern; 883 ses_path_iter_args_t *args; 884 struct cam_path *path; 885 886 args = (ses_path_iter_args_t *)arg; 887 match_pattern.type = DEV_MATCH_DEVICE; 888 device_pattern = &match_pattern.pattern.device_pattern; 889 device_pattern->flags = DEV_MATCH_DEVID; 890 device_pattern->data.devid_pat.id_len = 891 offsetof(struct scsi_vpd_id_descriptor, identifier) 892 + devid->length; 893 memcpy(device_pattern->data.devid_pat.id, devid, 894 device_pattern->data.devid_pat.id_len); 895 896 memset(&cdm, 0, sizeof(cdm)); 897 if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, 898 CAM_XPT_PATH_ID, 899 CAM_TARGET_WILDCARD, 900 CAM_LUN_WILDCARD) != CAM_REQ_CMP) 901 return; 902 903 cdm.ccb_h.func_code = XPT_DEV_MATCH; 904 cdm.num_patterns = 1; 905 cdm.patterns = &match_pattern; 906 cdm.pattern_buf_len = sizeof(match_pattern); 907 cdm.match_buf_len = sizeof(match_result); 908 cdm.matches = &match_result; 909 910 do { 911 xpt_action((union ccb *)&cdm); 912 913 if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP || 914 (cdm.status != CAM_DEV_MATCH_LAST && 915 cdm.status != CAM_DEV_MATCH_MORE) || 916 cdm.num_matches == 0) 917 break; 918 919 device_match = &match_result.result.device_result; 920 if (xpt_create_path(&path, /*periph*/NULL, 921 device_match->path_id, 922 device_match->target_id, 923 device_match->target_lun) == CAM_REQ_CMP) { 924 925 args->callback(enc, elem, path, args->callback_arg); 926 927 xpt_free_path(path); 928 } 929 } while (cdm.status == CAM_DEV_MATCH_MORE); 930 931 xpt_free_path(cdm.ccb_h.path); 932} 933 934/** 935 * \brief Iterate over and find the matching periph objects for the 936 * specified element. 937 * 938 * \param enc SES instance containing elm 939 * \param elm Element for which to perform periph object matching. 940 * \param callback The callback function to invoke with each matching 941 * periph object. 942 * \param callback_arg Argument passed through to callback on each invocation. 943 */ 944static void 945ses_paths_iter(enc_softc_t *enc, enc_element_t *elm, 946 ses_path_callback_t *callback, void *callback_arg) 947{ 948 ses_element_t *elmpriv; 949 struct ses_addl_status *addl; 950 951 elmpriv = elm->elm_private; 952 addl = &(elmpriv->addl); 953 954 if (addl->hdr == NULL) 955 return; 956 957 switch(ses_elm_addlstatus_proto(addl->hdr)) { 958 case SPSP_PROTO_SAS: 959 if (addl->proto_hdr.sas != NULL && 960 addl->proto_data.sasdev_phys != NULL) { 961 ses_path_iter_args_t args; 962 963 args.callback = callback; 964 args.callback_arg = callback_arg; 965 ses_devids_iter(enc, elm, ses_path_iter_devid_callback, 966 &args); 967 } 968 break; 969 case SPSP_PROTO_ATA: 970 if (addl->proto_hdr.ata != NULL) { 971 struct cam_path *path; 972 struct ccb_getdev cgd; 973 974 if (xpt_create_path(&path, /*periph*/NULL, 975 scsi_4btoul(addl->proto_hdr.ata->bus), 976 scsi_4btoul(addl->proto_hdr.ata->target), 0) 977 != CAM_REQ_CMP) 978 return; 979 980 xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL); 981 cgd.ccb_h.func_code = XPT_GDEV_TYPE; 982 xpt_action((union ccb *)&cgd); 983 if (cgd.ccb_h.status == CAM_REQ_CMP) 984 callback(enc, elm, path, callback_arg); 985 986 xpt_free_path(path); 987 } 988 break; 989 } 990} 991 992/** 993 * ses_paths_iter() callback function used by ses_get_elmdevname() 994 * to record periph driver instance strings corresponding to a SES 995 * element. 996 * 997 * \param enc SES instance containing elm 998 * \param elm Element on which periph matching is active. 999 * \param periph A periph instance that matches elm. 1000 * \param arg Argument passed through to callback on each invocation. 1001 */ 1002static void 1003ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem, 1004 struct cam_path *path, void *arg) 1005{ 1006 struct sbuf *sb; 1007 1008 sb = (struct sbuf *)arg; 1009 cam_periph_list(path, sb); 1010} 1011 1012/** 1013 * Argument package passed through ses_paths_iter() to 1014 * ses_getcampath_callback. 1015 */ 1016typedef struct ses_setphyspath_callback_args { 1017 struct sbuf *physpath; 1018 int num_set; 1019} ses_setphyspath_callback_args_t; 1020 1021/** 1022 * \brief ses_paths_iter() callback to set the physical path on the 1023 * CAM EDT entries corresponding to a given SES element. 1024 * 1025 * \param enc SES instance containing elm 1026 * \param elm Element on which periph matching is active. 1027 * \param periph A periph instance that matches elm. 1028 * \param arg Argument passed through to callback on each invocation. 1029 */ 1030static void 1031ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm, 1032 struct cam_path *path, void *arg) 1033{ 1034 struct ccb_dev_advinfo cdai; 1035 ses_setphyspath_callback_args_t *args; 1036 char *old_physpath; 1037 1038 args = (ses_setphyspath_callback_args_t *)arg; 1039 old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO); 1040 xpt_path_lock(path); 1041 xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 1042 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1043 cdai.buftype = CDAI_TYPE_PHYS_PATH; 1044 cdai.flags = CDAI_FLAG_NONE; 1045 cdai.bufsiz = MAXPATHLEN; 1046 cdai.buf = old_physpath; 1047 xpt_action((union ccb *)&cdai); 1048 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1049 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1050 1051 if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) { 1052 1053 xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 1054 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1055 cdai.buftype = CDAI_TYPE_PHYS_PATH; 1056 cdai.flags = CDAI_FLAG_STORE; 1057 cdai.bufsiz = sbuf_len(args->physpath); 1058 cdai.buf = sbuf_data(args->physpath); 1059 xpt_action((union ccb *)&cdai); 1060 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1061 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1062 if (cdai.ccb_h.status == CAM_REQ_CMP) 1063 args->num_set++; 1064 } 1065 xpt_path_unlock(path); 1066 free(old_physpath, M_SCSIENC); 1067} 1068 1069/** 1070 * \brief Set a device's physical path string in CAM XPT. 1071 * 1072 * \param enc SES instance containing elm 1073 * \param elm Element to publish physical path string for 1074 * \param iter Iterator whose state corresponds to elm 1075 * 1076 * \return 0 on success, errno otherwise. 1077 */ 1078static int 1079ses_set_physpath(enc_softc_t *enc, enc_element_t *elm, 1080 struct ses_iterator *iter) 1081{ 1082 struct ccb_dev_advinfo cdai; 1083 ses_setphyspath_callback_args_t args; 1084 int i, ret; 1085 struct sbuf sb; 1086 struct scsi_vpd_id_descriptor *idd; 1087 uint8_t *devid; 1088 ses_element_t *elmpriv; 1089 const char *c; 1090 1091 ret = EIO; 1092 devid = NULL; 1093 1094 elmpriv = elm->elm_private; 1095 if (elmpriv->addl.hdr == NULL) 1096 goto out; 1097 1098 /* 1099 * Assemble the components of the physical path starting with 1100 * the device ID of the enclosure itself. 1101 */ 1102 xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL); 1103 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1104 cdai.flags = CDAI_FLAG_NONE; 1105 cdai.buftype = CDAI_TYPE_SCSI_DEVID; 1106 cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN; 1107 cdai.buf = devid = malloc(cdai.bufsiz, M_SCSIENC, M_WAITOK|M_ZERO); 1108 cam_periph_lock(enc->periph); 1109 xpt_action((union ccb *)&cdai); 1110 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1111 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1112 cam_periph_unlock(enc->periph); 1113 if (cdai.ccb_h.status != CAM_REQ_CMP) 1114 goto out; 1115 1116 idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, 1117 cdai.provsiz, scsi_devid_is_naa_ieee_reg); 1118 if (idd == NULL) 1119 goto out; 1120 1121 if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) { 1122 ret = ENOMEM; 1123 goto out; 1124 } 1125 /* Next, generate the physical path string */ 1126 sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x", 1127 scsi_8btou64(idd->identifier), iter->type_index, 1128 iter->type_element_index); 1129 /* Append the element descriptor if one exists */ 1130 if (elmpriv->descr != NULL && elmpriv->descr_len > 0) { 1131 sbuf_cat(&sb, "/elmdesc@"); 1132 for (i = 0, c = elmpriv->descr; i < elmpriv->descr_len; 1133 i++, c++) { 1134 if (!isprint(*c) || isspace(*c) || *c == '/') 1135 sbuf_putc(&sb, '_'); 1136 else 1137 sbuf_putc(&sb, *c); 1138 } 1139 } 1140 sbuf_finish(&sb); 1141 1142 /* 1143 * Set this physical path on any CAM devices with a device ID 1144 * descriptor that matches one created from the SES additional 1145 * status data for this element. 1146 */ 1147 args.physpath= &sb; 1148 args.num_set = 0; 1149 ses_paths_iter(enc, elm, ses_setphyspath_callback, &args); 1150 sbuf_delete(&sb); 1151 1152 ret = args.num_set == 0 ? ENOENT : 0; 1153 1154out: 1155 if (devid != NULL) 1156 ENC_FREE(devid); 1157 return (ret); 1158} 1159 1160/** 1161 * \brief Helper to set the CDB fields appropriately. 1162 * 1163 * \param cdb Buffer containing the cdb. 1164 * \param pagenum SES diagnostic page to query for. 1165 * \param dir Direction of query. 1166 */ 1167static void 1168ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir) 1169{ 1170 1171 /* Ref: SPC-4 r25 Section 6.20 Table 223 */ 1172 if (dir == CAM_DIR_IN) { 1173 cdb[0] = RECEIVE_DIAGNOSTIC; 1174 cdb[1] = 1; /* Set page code valid bit */ 1175 cdb[2] = pagenum; 1176 } else { 1177 cdb[0] = SEND_DIAGNOSTIC; 1178 cdb[1] = 0x10; 1179 cdb[2] = pagenum; 1180 } 1181 cdb[3] = bufsiz >> 8; /* high bits */ 1182 cdb[4] = bufsiz & 0xff; /* low bits */ 1183 cdb[5] = 0; 1184} 1185 1186/** 1187 * \brief Discover whether this instance supports timed completion of a 1188 * RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status 1189 * page, and store the result in the softc, updating if necessary. 1190 * 1191 * \param enc SES instance to query and update. 1192 * \param tc_en Value of timed completion to set (see \return). 1193 * 1194 * \return 1 if timed completion enabled, 0 otherwise. 1195 */ 1196static int 1197ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en) 1198{ 1199 union ccb *ccb; 1200 struct cam_periph *periph; 1201 struct ses_mgmt_mode_page *mgmt; 1202 uint8_t *mode_buf; 1203 size_t mode_buf_len; 1204 ses_softc_t *ses; 1205 1206 periph = enc->periph; 1207 ses = enc->enc_private; 1208 ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 1209 1210 mode_buf_len = sizeof(struct ses_mgmt_mode_page); 1211 mode_buf = ENC_MALLOCZ(mode_buf_len); 1212 if (mode_buf == NULL) 1213 goto out; 1214 1215 scsi_mode_sense(&ccb->csio, /*retries*/4, NULL, MSG_SIMPLE_Q_TAG, 1216 /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE, 1217 mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000); 1218 1219 /* 1220 * Ignore illegal request errors, as they are quite common and we 1221 * will print something out in that case anyway. 1222 */ 1223 cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, 1224 ENC_FLAGS|SF_QUIET_IR, NULL); 1225 if (ccb->ccb_h.status != CAM_REQ_CMP) { 1226 ENC_VLOG(enc, "Timed Completion Unsupported\n"); 1227 goto release; 1228 } 1229 1230 /* Skip the mode select if the desired value is already set */ 1231 mgmt = (struct ses_mgmt_mode_page *)mode_buf; 1232 if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en) 1233 goto done; 1234 1235 /* Value is not what we wanted, set it */ 1236 if (tc_en) 1237 mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN; 1238 else 1239 mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN; 1240 /* SES2r20: a completion time of zero means as long as possible */ 1241 bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time)); 1242 1243 scsi_mode_select(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG, 1244 /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len, 1245 SSD_FULL_SIZE, /*timeout*/60 * 1000); 1246 1247 cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 1248 if (ccb->ccb_h.status != CAM_REQ_CMP) { 1249 ENC_VLOG(enc, "Timed Completion Set Failed\n"); 1250 goto release; 1251 } 1252 1253done: 1254 if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) { 1255 ENC_LOG(enc, "Timed Completion Enabled\n"); 1256 ses->ses_flags |= SES_FLAG_TIMEDCOMP; 1257 } else { 1258 ENC_LOG(enc, "Timed Completion Disabled\n"); 1259 ses->ses_flags &= ~SES_FLAG_TIMEDCOMP; 1260 } 1261release: 1262 ENC_FREE(mode_buf); 1263 xpt_release_ccb(ccb); 1264out: 1265 return (ses->ses_flags & SES_FLAG_TIMEDCOMP); 1266} 1267 1268/** 1269 * \brief Process the list of supported pages and update flags. 1270 * 1271 * \param enc SES device to query. 1272 * \param buf Buffer containing the config page. 1273 * \param xfer_len Length of the config page in the buffer. 1274 * 1275 * \return 0 on success, errno otherwise. 1276 */ 1277static int 1278ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, 1279 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1280{ 1281 ses_softc_t *ses; 1282 struct scsi_diag_page *page; 1283 int err, i, length; 1284 1285 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 1286 ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); 1287 ses = enc->enc_private; 1288 err = -1; 1289 1290 if (error != 0) { 1291 err = error; 1292 goto out; 1293 } 1294 if (xfer_len < sizeof(*page)) { 1295 ENC_VLOG(enc, "Unable to parse Diag Pages List Header\n"); 1296 err = EIO; 1297 goto out; 1298 } 1299 page = (struct scsi_diag_page *)*bufp; 1300 length = scsi_2btoul(page->length); 1301 if (length + offsetof(struct scsi_diag_page, params) > xfer_len) { 1302 ENC_VLOG(enc, "Diag Pages List Too Long\n"); 1303 goto out; 1304 } 1305 ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n", 1306 __func__, length, xfer_len); 1307 1308 err = 0; 1309 for (i = 0; i < length; i++) { 1310 if (page->params[i] == SesElementDescriptor) 1311 ses->ses_flags |= SES_FLAG_DESC; 1312 else if (page->params[i] == SesAddlElementStatus) 1313 ses->ses_flags |= SES_FLAG_ADDLSTATUS; 1314 } 1315 1316out: 1317 ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); 1318 return (err); 1319} 1320 1321/** 1322 * \brief Process the config page and update associated structures. 1323 * 1324 * \param enc SES device to query. 1325 * \param buf Buffer containing the config page. 1326 * \param xfer_len Length of the config page in the buffer. 1327 * 1328 * \return 0 on success, errno otherwise. 1329 */ 1330static int 1331ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, 1332 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1333{ 1334 struct ses_iterator iter; 1335 ses_softc_t *ses; 1336 enc_cache_t *enc_cache; 1337 ses_cache_t *ses_cache; 1338 uint8_t *buf; 1339 int length; 1340 int err; 1341 int nelm; 1342 int ntype; 1343 struct ses_cfg_page *cfg_page; 1344 struct ses_enc_desc *buf_subenc; 1345 const struct ses_enc_desc **subencs; 1346 const struct ses_enc_desc **cur_subenc; 1347 const struct ses_enc_desc **last_subenc; 1348 ses_type_t *ses_types; 1349 ses_type_t *sestype; 1350 const struct ses_elm_type_desc *cur_buf_type; 1351 const struct ses_elm_type_desc *last_buf_type; 1352 uint8_t *last_valid_byte; 1353 enc_element_t *element; 1354 const char *type_text; 1355 1356 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 1357 ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); 1358 ses = enc->enc_private; 1359 enc_cache = &enc->enc_daemon_cache; 1360 ses_cache = enc_cache->private; 1361 buf = *bufp; 1362 err = -1; 1363 1364 if (error != 0) { 1365 err = error; 1366 goto out; 1367 } 1368 if (xfer_len < sizeof(cfg_page->hdr)) { 1369 ENC_VLOG(enc, "Unable to parse SES Config Header\n"); 1370 err = EIO; 1371 goto out; 1372 } 1373 1374 cfg_page = (struct ses_cfg_page *)buf; 1375 length = ses_page_length(&cfg_page->hdr); 1376 if (length > xfer_len) { 1377 ENC_VLOG(enc, "Enclosure Config Page Too Long\n"); 1378 goto out; 1379 } 1380 last_valid_byte = &buf[length - 1]; 1381 1382 ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", 1383 __func__, length, xfer_len); 1384 1385 err = 0; 1386 if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) { 1387 1388 /* Our cache is still valid. Proceed to fetching status. */ 1389 goto out; 1390 } 1391 1392 /* Cache is no longer valid. Free old data to make way for new. */ 1393 ses_cache_free(enc, enc_cache); 1394 ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n", 1395 scsi_4btoul(cfg_page->hdr.gen_code), 1396 ses_cfg_page_get_num_subenc(cfg_page)); 1397 1398 /* Take ownership of the buffer. */ 1399 ses_cache->cfg_page = cfg_page; 1400 *bufp = NULL; 1401 1402 /* 1403 * Now waltz through all the subenclosures summing the number of 1404 * types available in each. 1405 */ 1406 subencs = malloc(ses_cfg_page_get_num_subenc(cfg_page) 1407 * sizeof(*subencs), M_SCSIENC, M_WAITOK|M_ZERO); 1408 /* 1409 * Sub-enclosure data is const after construction (i.e. when 1410 * accessed via our cache object. 1411 * 1412 * The cast here is not required in C++ but C99 is not so 1413 * sophisticated (see C99 6.5.16.1(1)). 1414 */ 1415 ses_cache->ses_nsubencs = ses_cfg_page_get_num_subenc(cfg_page); 1416 ses_cache->subencs = subencs; 1417 1418 buf_subenc = cfg_page->subencs; 1419 cur_subenc = subencs; 1420 last_subenc = &subencs[ses_cache->ses_nsubencs - 1]; 1421 ntype = 0; 1422 while (cur_subenc <= last_subenc) { 1423 1424 if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) { 1425 ENC_VLOG(enc, "Enclosure %d Beyond End of " 1426 "Descriptors\n", cur_subenc - subencs); 1427 err = EIO; 1428 goto out; 1429 } 1430 1431 ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, " 1432 "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id, 1433 buf_subenc->num_types, buf_subenc->length, 1434 &buf_subenc->byte0 - buf); 1435 ENC_VLOG(enc, "WWN: %jx\n", 1436 (uintmax_t)scsi_8btou64(buf_subenc->logical_id)); 1437 1438 ntype += buf_subenc->num_types; 1439 *cur_subenc = buf_subenc; 1440 cur_subenc++; 1441 buf_subenc = ses_enc_desc_next(buf_subenc); 1442 } 1443 1444 /* Process the type headers. */ 1445 ses_types = malloc(ntype * sizeof(*ses_types), 1446 M_SCSIENC, M_WAITOK|M_ZERO); 1447 /* 1448 * Type data is const after construction (i.e. when accessed via 1449 * our cache object. 1450 */ 1451 ses_cache->ses_ntypes = ntype; 1452 ses_cache->ses_types = ses_types; 1453 1454 cur_buf_type = (const struct ses_elm_type_desc *) 1455 (&(*last_subenc)->length + (*last_subenc)->length + 1); 1456 last_buf_type = cur_buf_type + ntype - 1; 1457 type_text = (const uint8_t *)(last_buf_type + 1); 1458 nelm = 0; 1459 sestype = ses_types; 1460 while (cur_buf_type <= last_buf_type) { 1461 if (&cur_buf_type->etype_txt_len > last_valid_byte) { 1462 ENC_VLOG(enc, "Runt Enclosure Type Header %d\n", 1463 sestype - ses_types); 1464 err = EIO; 1465 goto out; 1466 } 1467 sestype->hdr = cur_buf_type; 1468 sestype->text = type_text; 1469 type_text += cur_buf_type->etype_txt_len; 1470 ENC_VLOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " 1471 "%d, Text Length %d: %.*s\n", sestype - ses_types, 1472 sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt, 1473 sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len, 1474 sestype->hdr->etype_txt_len, sestype->text); 1475 1476 nelm += sestype->hdr->etype_maxelt 1477 + /*overall status element*/1; 1478 sestype++; 1479 cur_buf_type++; 1480 } 1481 1482 /* Create the object map. */ 1483 enc_cache->elm_map = malloc(nelm * sizeof(enc_element_t), 1484 M_SCSIENC, M_WAITOK|M_ZERO); 1485 enc_cache->nelms = nelm; 1486 1487 ses_iter_init(enc, enc_cache, &iter); 1488 while ((element = ses_iter_next(&iter)) != NULL) { 1489 const struct ses_elm_type_desc *thdr; 1490 1491 ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__, 1492 iter.global_element_index, iter.type_index, nelm, 1493 iter.type_element_index); 1494 thdr = ses_cache->ses_types[iter.type_index].hdr; 1495 element->elm_idx = iter.global_element_index; 1496 element->elm_type = thdr->etype_elm_type; 1497 element->subenclosure = thdr->etype_subenc; 1498 element->type_elm_idx = iter.type_element_index; 1499 element->elm_private = malloc(sizeof(ses_element_t), 1500 M_SCSIENC, M_WAITOK|M_ZERO); 1501 ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d " 1502 "type 0x%x\n", __func__, iter.global_element_index, 1503 iter.type_index, iter.type_element_index, 1504 thdr->etype_subenc, thdr->etype_elm_type); 1505 } 1506 1507 err = 0; 1508 1509out: 1510 if (err) 1511 ses_cache_free(enc, enc_cache); 1512 else { 1513 ses_poll_status(enc); 1514 enc_update_request(enc, SES_PUBLISH_CACHE); 1515 } 1516 ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); 1517 return (err); 1518} 1519 1520/** 1521 * \brief Update the status page and associated structures. 1522 * 1523 * \param enc SES softc to update for. 1524 * \param buf Buffer containing the status page. 1525 * \param bufsz Amount of data in the buffer. 1526 * 1527 * \return 0 on success, errno otherwise. 1528 */ 1529static int 1530ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state, 1531 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1532{ 1533 struct ses_iterator iter; 1534 enc_element_t *element; 1535 ses_softc_t *ses; 1536 enc_cache_t *enc_cache; 1537 ses_cache_t *ses_cache; 1538 uint8_t *buf; 1539 int err = -1; 1540 int length; 1541 struct ses_status_page *page; 1542 union ses_status_element *cur_stat; 1543 union ses_status_element *last_stat; 1544 1545 ses = enc->enc_private; 1546 enc_cache = &enc->enc_daemon_cache; 1547 ses_cache = enc_cache->private; 1548 buf = *bufp; 1549 1550 ENC_DLOG(enc, "%s: enter (%p, %p, %d)\n", __func__, enc, buf, xfer_len); 1551 page = (struct ses_status_page *)buf; 1552 length = ses_page_length(&page->hdr); 1553 1554 if (error != 0) { 1555 err = error; 1556 goto out; 1557 } 1558 /* 1559 * Make sure the length fits in the buffer. 1560 * 1561 * XXX all this means is that the page is larger than the space 1562 * we allocated. Since we use a statically sized buffer, this 1563 * could happen... Need to use dynamic discovery of the size. 1564 */ 1565 if (length > xfer_len) { 1566 ENC_VLOG(enc, "Enclosure Status Page Too Long\n"); 1567 goto out; 1568 } 1569 1570 /* Check for simple enclosure reporting short enclosure status. */ 1571 if (length >= 4 && page->hdr.page_code == SesShortStatus) { 1572 ENC_DLOG(enc, "Got Short Enclosure Status page\n"); 1573 ses->ses_flags &= ~(SES_FLAG_ADDLSTATUS | SES_FLAG_DESC); 1574 ses_cache_free(enc, enc_cache); 1575 enc_cache->enc_status = page->hdr.page_specific_flags; 1576 enc_update_request(enc, SES_PUBLISH_CACHE); 1577 err = 0; 1578 goto out; 1579 } 1580 1581 /* Make sure the length contains at least one header and status */ 1582 if (length < (sizeof(*page) + sizeof(*page->elements))) { 1583 ENC_VLOG(enc, "Enclosure Status Page Too Short\n"); 1584 goto out; 1585 } 1586 1587 if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) { 1588 ENC_DLOG(enc, "%s: Generation count change detected\n", 1589 __func__); 1590 enc_update_request(enc, SES_UPDATE_GETCONFIG); 1591 goto out; 1592 } 1593 1594 ses_cache_free_status(enc, enc_cache); 1595 ses_cache->status_page = page; 1596 *bufp = NULL; 1597 1598 enc_cache->enc_status = page->hdr.page_specific_flags; 1599 1600 /* 1601 * Read in individual element status. The element order 1602 * matches the order reported in the config page (i.e. the 1603 * order of an unfiltered iteration of the config objects).. 1604 */ 1605 ses_iter_init(enc, enc_cache, &iter); 1606 cur_stat = page->elements; 1607 last_stat = (union ses_status_element *) 1608 &buf[length - sizeof(*last_stat)]; 1609 ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", 1610 __func__, length, xfer_len); 1611 while (cur_stat <= last_stat 1612 && (element = ses_iter_next(&iter)) != NULL) { 1613 1614 ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n", 1615 __func__, iter.global_element_index, iter.type_index, 1616 iter.type_element_index, (uint8_t *)cur_stat - buf, 1617 scsi_4btoul(cur_stat->bytes)); 1618 1619 memcpy(&element->encstat, cur_stat, sizeof(element->encstat)); 1620 element->svalid = 1; 1621 cur_stat++; 1622 } 1623 1624 if (ses_iter_next(&iter) != NULL) { 1625 ENC_VLOG(enc, "Status page, length insufficient for " 1626 "expected number of objects\n"); 1627 } else { 1628 if (cur_stat <= last_stat) 1629 ENC_VLOG(enc, "Status page, exhausted objects before " 1630 "exhausing page\n"); 1631 enc_update_request(enc, SES_PUBLISH_CACHE); 1632 err = 0; 1633 } 1634out: 1635 ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err); 1636 return (err); 1637} 1638 1639typedef enum { 1640 /** 1641 * The enclosure should not provide additional element 1642 * status for this element type in page 0x0A. 1643 * 1644 * \note This status is returned for any types not 1645 * listed SES3r02. Further types added in a 1646 * future specification will be incorrectly 1647 * classified. 1648 */ 1649 TYPE_ADDLSTATUS_NONE, 1650 1651 /** 1652 * The element type provides additional element status 1653 * in page 0x0A. 1654 */ 1655 TYPE_ADDLSTATUS_MANDATORY, 1656 1657 /** 1658 * The element type may provide additional element status 1659 * in page 0x0A, but i 1660 */ 1661 TYPE_ADDLSTATUS_OPTIONAL 1662} ses_addlstatus_avail_t; 1663 1664/** 1665 * \brief Check to see whether a given type (as obtained via type headers) is 1666 * supported by the additional status command. 1667 * 1668 * \param enc SES softc to check. 1669 * \param typidx Type index to check for. 1670 * 1671 * \return An enumeration indicating if additional status is mandatory, 1672 * optional, or not required for this type. 1673 */ 1674static ses_addlstatus_avail_t 1675ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx) 1676{ 1677 enc_cache_t *enc_cache; 1678 ses_cache_t *ses_cache; 1679 1680 enc_cache = &enc->enc_daemon_cache; 1681 ses_cache = enc_cache->private; 1682 switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) { 1683 case ELMTYP_DEVICE: 1684 case ELMTYP_ARRAY_DEV: 1685 case ELMTYP_SAS_EXP: 1686 return (TYPE_ADDLSTATUS_MANDATORY); 1687 case ELMTYP_SCSI_INI: 1688 case ELMTYP_SCSI_TGT: 1689 case ELMTYP_ESCC: 1690 return (TYPE_ADDLSTATUS_OPTIONAL); 1691 default: 1692 /* No additional status information available. */ 1693 break; 1694 } 1695 return (TYPE_ADDLSTATUS_NONE); 1696} 1697 1698static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *, 1699 uint8_t *, int); 1700static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *, 1701 int, int, int, int); 1702static int ses_get_elm_addlstatus_ata(enc_softc_t *, enc_cache_t *, uint8_t *, 1703 int, int, int, int); 1704 1705/** 1706 * \brief Parse the additional status element data for each object. 1707 * 1708 * \param enc The SES softc to update. 1709 * \param buf The buffer containing the additional status 1710 * element response. 1711 * \param xfer_len Size of the buffer. 1712 * 1713 * \return 0 on success, errno otherwise. 1714 */ 1715static int 1716ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, 1717 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1718{ 1719 struct ses_iterator iter, titer; 1720 int eip; 1721 int err; 1722 int length; 1723 int offset; 1724 enc_cache_t *enc_cache; 1725 ses_cache_t *ses_cache; 1726 uint8_t *buf; 1727 ses_element_t *elmpriv; 1728 const struct ses_page_hdr *hdr; 1729 enc_element_t *element, *telement; 1730 1731 enc_cache = &enc->enc_daemon_cache; 1732 ses_cache = enc_cache->private; 1733 buf = *bufp; 1734 err = -1; 1735 1736 if (error != 0) { 1737 err = error; 1738 goto out; 1739 } 1740 ses_cache_free_elm_addlstatus(enc, enc_cache); 1741 ses_cache->elm_addlstatus_page = 1742 (struct ses_addl_elem_status_page *)buf; 1743 *bufp = NULL; 1744 1745 /* 1746 * The objects appear in the same order here as in Enclosure Status, 1747 * which itself is ordered by the Type Descriptors from the Config 1748 * page. However, it is necessary to skip elements that are not 1749 * supported by this page when counting them. 1750 */ 1751 hdr = &ses_cache->elm_addlstatus_page->hdr; 1752 length = ses_page_length(hdr); 1753 ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length); 1754 /* Make sure the length includes at least one header. */ 1755 if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) { 1756 ENC_VLOG(enc, "Runt Additional Element Status Page\n"); 1757 goto out; 1758 } 1759 if (length > xfer_len) { 1760 ENC_VLOG(enc, "Additional Element Status Page Too Long\n"); 1761 goto out; 1762 } 1763 1764 if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) { 1765 ENC_DLOG(enc, "%s: Generation count change detected\n", 1766 __func__); 1767 enc_update_request(enc, SES_UPDATE_GETCONFIG); 1768 goto out; 1769 } 1770 1771 offset = sizeof(struct ses_page_hdr); 1772 ses_iter_init(enc, enc_cache, &iter); 1773 while (offset < length 1774 && (element = ses_iter_next(&iter)) != NULL) { 1775 struct ses_elm_addlstatus_base_hdr *elm_hdr; 1776 int proto_info_len; 1777 ses_addlstatus_avail_t status_type; 1778 1779 /* 1780 * Additional element status is only provided for 1781 * individual elements (i.e. overal status elements 1782 * are excluded) and those of the types specified 1783 * in the SES spec. 1784 */ 1785 status_type = ses_typehasaddlstatus(enc, iter.type_index); 1786 if (iter.individual_element_index == ITERATOR_INDEX_INVALID 1787 || status_type == TYPE_ADDLSTATUS_NONE) 1788 continue; 1789 1790 elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset]; 1791 eip = ses_elm_addlstatus_eip(elm_hdr); 1792 if (eip) { 1793 struct ses_elm_addlstatus_eip_hdr *eip_hdr; 1794 int expected_index, index; 1795 ses_elem_index_type_t index_type; 1796 1797 eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr; 1798 if (eip_hdr->byte2 & SES_ADDL_EIP_EIIOE) { 1799 index_type = SES_ELEM_INDEX_GLOBAL; 1800 expected_index = iter.global_element_index; 1801 } else { 1802 index_type = SES_ELEM_INDEX_INDIVIDUAL; 1803 expected_index = iter.individual_element_index; 1804 } 1805 if (eip_hdr->element_index < expected_index) { 1806 ENC_VLOG(enc, "%s: provided %selement index " 1807 "%d is lower then expected %d\n", 1808 __func__, (eip_hdr->byte2 & 1809 SES_ADDL_EIP_EIIOE) ? "global " : "", 1810 eip_hdr->element_index, expected_index); 1811 goto badindex; 1812 } 1813 titer = iter; 1814 telement = ses_iter_seek_to(&titer, 1815 eip_hdr->element_index, index_type); 1816 if (telement == NULL) { 1817 ENC_VLOG(enc, "%s: provided %selement index " 1818 "%d does not exist\n", __func__, 1819 (eip_hdr->byte2 & SES_ADDL_EIP_EIIOE) ? 1820 "global " : "", eip_hdr->element_index); 1821 goto badindex; 1822 } 1823 if (ses_typehasaddlstatus(enc, titer.type_index) == 1824 TYPE_ADDLSTATUS_NONE) { 1825 ENC_VLOG(enc, "%s: provided %selement index " 1826 "%d can't have additional status\n", 1827 __func__, 1828 (eip_hdr->byte2 & SES_ADDL_EIP_EIIOE) ? 1829 "global " : "", eip_hdr->element_index); 1830badindex: 1831 /* 1832 * If we expected mandatory element, we may 1833 * guess it was just a wrong index and we may 1834 * use the status. If element was optional, 1835 * then we have no idea where status belongs. 1836 */ 1837 if (status_type == TYPE_ADDLSTATUS_OPTIONAL) 1838 break; 1839 } else { 1840 iter = titer; 1841 element = telement; 1842 } 1843 1844 if (eip_hdr->byte2 & SES_ADDL_EIP_EIIOE) 1845 index = iter.global_element_index; 1846 else 1847 index = iter.individual_element_index; 1848 if (index > expected_index 1849 && status_type == TYPE_ADDLSTATUS_MANDATORY) { 1850 ENC_VLOG(enc, "%s: provided %s element" 1851 "index %d skips mandatory status " 1852 " element at index %d\n", 1853 __func__, (eip_hdr->byte2 & 1854 SES_ADDL_EIP_EIIOE) ? "global " : "", 1855 index, expected_index); 1856 } 1857 } 1858 elmpriv = element->elm_private; 1859 ENC_DLOG(enc, "%s: global element index=%d, type index=%d " 1860 "type element index=%d, offset=0x%x, " 1861 "byte0=0x%x, length=0x%x\n", __func__, 1862 iter.global_element_index, iter.type_index, 1863 iter.type_element_index, offset, elm_hdr->byte0, 1864 elm_hdr->length); 1865 1866 /* Skip to after the length field */ 1867 offset += sizeof(struct ses_elm_addlstatus_base_hdr); 1868 1869 /* Make sure the descriptor is within bounds */ 1870 if ((offset + elm_hdr->length) > length) { 1871 ENC_VLOG(enc, "Element %d Beyond End " 1872 "of Additional Element Status Descriptors\n", 1873 iter.global_element_index); 1874 break; 1875 } 1876 1877 /* Skip elements marked as invalid. */ 1878 if (ses_elm_addlstatus_invalid(elm_hdr)) { 1879 offset += elm_hdr->length; 1880 continue; 1881 } 1882 elmpriv->addl.hdr = elm_hdr; 1883 1884 /* Advance to the protocol data, skipping eip bytes if needed */ 1885 offset += (eip * SES_EIP_HDR_EXTRA_LEN); 1886 proto_info_len = elm_hdr->length 1887 - (eip * SES_EIP_HDR_EXTRA_LEN); 1888 1889 /* Errors in this block are ignored as they are non-fatal */ 1890 switch(ses_elm_addlstatus_proto(elm_hdr)) { 1891 case SPSP_PROTO_FC: 1892 if (elm_hdr->length == 0) 1893 break; 1894 ses_get_elm_addlstatus_fc(enc, enc_cache, 1895 &buf[offset], proto_info_len); 1896 break; 1897 case SPSP_PROTO_SAS: 1898 if (elm_hdr->length <= 2) 1899 break; 1900 ses_get_elm_addlstatus_sas(enc, enc_cache, 1901 &buf[offset], 1902 proto_info_len, 1903 eip, iter.type_index, 1904 iter.global_element_index); 1905 break; 1906 case SPSP_PROTO_ATA: 1907 ses_get_elm_addlstatus_ata(enc, enc_cache, 1908 &buf[offset], 1909 proto_info_len, 1910 eip, iter.type_index, 1911 iter.global_element_index); 1912 break; 1913 default: 1914 ENC_VLOG(enc, "Element %d: Unknown Additional Element " 1915 "Protocol 0x%x\n", iter.global_element_index, 1916 ses_elm_addlstatus_proto(elm_hdr)); 1917 break; 1918 } 1919 1920 offset += proto_info_len; 1921 } 1922 err = 0; 1923out: 1924 if (err) 1925 ses_cache_free_elm_addlstatus(enc, enc_cache); 1926 enc_update_request(enc, SES_PUBLISH_PHYSPATHS); 1927 enc_update_request(enc, SES_PUBLISH_CACHE); 1928 return (err); 1929} 1930 1931static int 1932ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, 1933 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1934{ 1935 ses_softc_t *ses; 1936 1937 ses = enc->enc_private; 1938 /* 1939 * Possible errors: 1940 * o Generation count wrong. 1941 * o Some SCSI status error. 1942 */ 1943 ses_terminate_control_requests(&ses->ses_pending_requests, error); 1944 ses_poll_status(enc); 1945 return (0); 1946} 1947 1948static int 1949ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, 1950 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1951{ 1952 struct ses_iterator iter; 1953 enc_cache_t *enc_cache; 1954 enc_element_t *element; 1955 1956 enc_cache = &enc->enc_daemon_cache; 1957 1958 ses_iter_init(enc, enc_cache, &iter); 1959 while ((element = ses_iter_next(&iter)) != NULL) { 1960 /* 1961 * ses_set_physpath() returns success if we changed 1962 * the physpath of any element. This allows us to 1963 * only announce devices once regardless of how 1964 * many times we process additional element status. 1965 */ 1966 if (ses_set_physpath(enc, element, &iter) == 0) 1967 ses_print_addl_data(enc, element); 1968 } 1969 1970 return (0); 1971} 1972 1973static int 1974ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, 1975 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1976{ 1977 1978 sx_xlock(&enc->enc_cache_lock); 1979 ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache, 1980 /*dst*/&enc->enc_cache); 1981 sx_xunlock(&enc->enc_cache_lock); 1982 1983 return (0); 1984} 1985 1986/* 1987 * \brief Sanitize an element descriptor 1988 * 1989 * The SES4r3 standard, sections 3.1.2 and 6.1.10, specifies that element 1990 * descriptors may only contain ASCII characters in the range 0x20 to 0x7e. 1991 * But some vendors violate that rule. Ensure that we only expose compliant 1992 * descriptors to userland. 1993 * 1994 * \param desc SES element descriptor as reported by the hardware 1995 * \param len Length of desc in bytes, not necessarily including 1996 * trailing NUL. It will be modified if desc is invalid. 1997 */ 1998static const char* 1999ses_sanitize_elm_desc(const char *desc, uint16_t *len) 2000{ 2001 const char *invalid = "<invalid>"; 2002 int i; 2003 2004 for (i = 0; i < *len; i++) { 2005 if (desc[i] == 0) { 2006 break; 2007 } else if (desc[i] < 0x20 || desc[i] > 0x7e) { 2008 *len = strlen(invalid); 2009 return (invalid); 2010 } 2011 } 2012 return (desc); 2013} 2014 2015/** 2016 * \brief Parse the descriptors for each object. 2017 * 2018 * \param enc The SES softc to update. 2019 * \param buf The buffer containing the descriptor list response. 2020 * \param xfer_len Size of the buffer. 2021 * 2022 * \return 0 on success, errno otherwise. 2023 */ 2024static int 2025ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, 2026 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 2027{ 2028 ses_softc_t *ses; 2029 struct ses_iterator iter; 2030 enc_element_t *element; 2031 int err; 2032 int offset; 2033 u_long length, plength; 2034 enc_cache_t *enc_cache; 2035 ses_cache_t *ses_cache; 2036 uint8_t *buf; 2037 ses_element_t *elmpriv; 2038 const struct ses_page_hdr *phdr; 2039 const struct ses_elm_desc_hdr *hdr; 2040 2041 ses = enc->enc_private; 2042 enc_cache = &enc->enc_daemon_cache; 2043 ses_cache = enc_cache->private; 2044 buf = *bufp; 2045 err = -1; 2046 2047 if (error != 0) { 2048 err = error; 2049 goto out; 2050 } 2051 ses_cache_free_elm_descs(enc, enc_cache); 2052 ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf; 2053 *bufp = NULL; 2054 2055 phdr = &ses_cache->elm_descs_page->hdr; 2056 plength = ses_page_length(phdr); 2057 if (xfer_len < sizeof(struct ses_page_hdr)) { 2058 ENC_VLOG(enc, "Runt Element Descriptor Page\n"); 2059 goto out; 2060 } 2061 if (plength > xfer_len) { 2062 ENC_VLOG(enc, "Element Descriptor Page Too Long\n"); 2063 goto out; 2064 } 2065 2066 if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) { 2067 ENC_VLOG(enc, "%s: Generation count change detected\n", 2068 __func__); 2069 enc_update_request(enc, SES_UPDATE_GETCONFIG); 2070 goto out; 2071 } 2072 2073 offset = sizeof(struct ses_page_hdr); 2074 2075 ses_iter_init(enc, enc_cache, &iter); 2076 while (offset < plength 2077 && (element = ses_iter_next(&iter)) != NULL) { 2078 2079 if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) { 2080 ENC_VLOG(enc, "Element %d Descriptor Header Past " 2081 "End of Buffer\n", iter.global_element_index); 2082 goto out; 2083 } 2084 hdr = (struct ses_elm_desc_hdr *)&buf[offset]; 2085 length = scsi_2btoul(hdr->length); 2086 ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__, 2087 iter.global_element_index, iter.type_index, 2088 iter.type_element_index, length, offset); 2089 if ((offset + sizeof(*hdr) + length) > plength) { 2090 ENC_VLOG(enc, "Element%d Descriptor Past " 2091 "End of Buffer\n", iter.global_element_index); 2092 goto out; 2093 } 2094 offset += sizeof(*hdr); 2095 2096 if (length > 0) { 2097 elmpriv = element->elm_private; 2098 elmpriv->descr_len = length; 2099 elmpriv->descr = ses_sanitize_elm_desc(&buf[offset], 2100 &elmpriv->descr_len); 2101 } 2102 2103 /* skip over the descriptor itself */ 2104 offset += length; 2105 } 2106 2107 err = 0; 2108out: 2109 if (err == 0) { 2110 if (ses->ses_flags & SES_FLAG_ADDLSTATUS) 2111 enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); 2112 } 2113 enc_update_request(enc, SES_PUBLISH_CACHE); 2114 return (err); 2115} 2116 2117static int 2118ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state, 2119 union ccb *ccb, uint8_t *buf) 2120{ 2121 2122 if (enc->enc_type == ENC_SEMB_SES) { 2123 semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5, 2124 NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1, 2125 state->page_code, buf, state->buf_size, 2126 state->timeout); 2127 } else { 2128 scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, 2129 NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1, 2130 state->page_code, buf, state->buf_size, 2131 SSD_FULL_SIZE, state->timeout); 2132 } 2133 return (0); 2134} 2135 2136/** 2137 * \brief Encode the object status into the response buffer, which is 2138 * expected to contain the current enclosure status. This function 2139 * turns off all the 'select' bits for the objects except for the 2140 * object specified, then sends it back to the enclosure. 2141 * 2142 * \param enc SES enclosure the change is being applied to. 2143 * \param buf Buffer containing the current enclosure status response. 2144 * \param amt Length of the response in the buffer. 2145 * \param req The control request to be applied to buf. 2146 * 2147 * \return 0 on success, errno otherwise. 2148 */ 2149static int 2150ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req) 2151{ 2152 struct ses_iterator iter; 2153 enc_element_t *element; 2154 int offset; 2155 struct ses_control_page_hdr *hdr; 2156 2157 ses_iter_init(enc, &enc->enc_cache, &iter); 2158 hdr = (struct ses_control_page_hdr *)buf; 2159 if (req->elm_idx == -1) { 2160 /* for enclosure status, at least 2 bytes are needed */ 2161 if (amt < 2) 2162 return EIO; 2163 hdr->control_flags = 2164 req->elm_stat.comstatus & SES_SET_STATUS_MASK; 2165 ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags); 2166 return (0); 2167 } 2168 2169 element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL); 2170 if (element == NULL) 2171 return (ENXIO); 2172 2173 /* 2174 * Seek to the type set that corresponds to the requested object. 2175 * The +1 is for the overall status element for the type. 2176 */ 2177 offset = sizeof(struct ses_control_page_hdr) 2178 + (iter.global_element_index * sizeof(struct ses_comstat)); 2179 2180 /* Check for buffer overflow. */ 2181 if (offset + sizeof(struct ses_comstat) > amt) 2182 return (EIO); 2183 2184 /* Set the status. */ 2185 memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat)); 2186 2187 ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n", 2188 iter.type_index, iter.global_element_index, offset, 2189 req->elm_stat.comstatus, req->elm_stat.comstat[0], 2190 req->elm_stat.comstat[1], req->elm_stat.comstat[2]); 2191 2192 return (0); 2193} 2194 2195static int 2196ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, 2197 union ccb *ccb, uint8_t *buf) 2198{ 2199 ses_softc_t *ses; 2200 enc_cache_t *enc_cache; 2201 ses_cache_t *ses_cache; 2202 struct ses_control_page_hdr *hdr; 2203 ses_control_request_t *req; 2204 size_t plength; 2205 size_t offset; 2206 2207 ses = enc->enc_private; 2208 enc_cache = &enc->enc_daemon_cache; 2209 ses_cache = enc_cache->private; 2210 hdr = (struct ses_control_page_hdr *)buf; 2211 2212 if (ses_cache->status_page == NULL) { 2213 ses_terminate_control_requests(&ses->ses_requests, EIO); 2214 return (EIO); 2215 } 2216 2217 plength = ses_page_length(&ses_cache->status_page->hdr); 2218 memcpy(buf, ses_cache->status_page, plength); 2219 2220 /* Disable the select bits in all status entries. */ 2221 offset = sizeof(struct ses_control_page_hdr); 2222 for (offset = sizeof(struct ses_control_page_hdr); 2223 offset < plength; offset += sizeof(struct ses_comstat)) { 2224 buf[offset] &= ~SESCTL_CSEL; 2225 } 2226 2227 /* And make sure the INVOP bit is clear. */ 2228 hdr->control_flags &= ~SES_ENCSTAT_INVOP; 2229 2230 /* Apply incoming requests. */ 2231 while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) { 2232 2233 TAILQ_REMOVE(&ses->ses_requests, req, links); 2234 req->result = ses_encode(enc, buf, plength, req); 2235 if (req->result != 0) { 2236 wakeup(req); 2237 continue; 2238 } 2239 TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links); 2240 } 2241 2242 if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0) 2243 return (ENOENT); 2244 2245 /* Fill out the ccb */ 2246 if (enc->enc_type == ENC_SEMB_SES) { 2247 semb_send_diagnostic(&ccb->ataio, /*retries*/5, NULL, 2248 MSG_SIMPLE_Q_TAG, 2249 buf, ses_page_length(&ses_cache->status_page->hdr), 2250 state->timeout); 2251 } else { 2252 scsi_send_diagnostic(&ccb->csio, /*retries*/5, NULL, 2253 MSG_SIMPLE_Q_TAG, /*unit_offline*/0, 2254 /*device_offline*/0, /*self_test*/0, 2255 /*page_format*/1, /*self_test_code*/0, 2256 buf, ses_page_length(&ses_cache->status_page->hdr), 2257 SSD_FULL_SIZE, state->timeout); 2258 } 2259 return (0); 2260} 2261 2262static int 2263ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache, 2264 uint8_t *buf, int bufsiz) 2265{ 2266 ENC_VLOG(enc, "FC Device Support Stubbed in Additional Status Page\n"); 2267 return (ENODEV); 2268} 2269 2270#define SES_PRINT_PORTS(p, type) do { \ 2271 if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) != 0) { \ 2272 sbuf_printf(sbp, " %s (", type); \ 2273 if ((p) & SES_SASOBJ_DEV_PHY_SMP) \ 2274 sbuf_printf(sbp, " SMP"); \ 2275 if ((p) & SES_SASOBJ_DEV_PHY_STP) \ 2276 sbuf_printf(sbp, " STP"); \ 2277 if ((p) & SES_SASOBJ_DEV_PHY_SSP) \ 2278 sbuf_printf(sbp, " SSP"); \ 2279 sbuf_printf(sbp, " )"); \ 2280 } \ 2281} while(0) 2282 2283/** 2284 * \brief Print the additional element status data for this object, for SAS 2285 * type 0 objects. See SES2 r20 Section 6.1.13.3.2. 2286 * 2287 * \param sesname SES device name associated with the object. 2288 * \param sbp Sbuf to print to. 2289 * \param obj The object to print the data for. 2290 */ 2291static void 2292ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp, 2293 enc_element_t *obj) 2294{ 2295 int i; 2296 ses_element_t *elmpriv; 2297 struct ses_addl_status *addl; 2298 struct ses_elm_sas_device_phy *phy; 2299 2300 elmpriv = obj->elm_private; 2301 addl = &(elmpriv->addl); 2302 sbuf_printf(sbp, ", SAS Slot: %d%s phys", 2303 addl->proto_hdr.sas->base_hdr.num_phys, 2304 ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas) ? "+" : ""); 2305 if (ses_elm_addlstatus_eip(addl->hdr)) 2306 sbuf_printf(sbp, " at slot %d", 2307 addl->proto_hdr.sas->type0_eip.dev_slot_num); 2308 sbuf_printf(sbp, "\n"); 2309 if (addl->proto_data.sasdev_phys == NULL) 2310 return; 2311 for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) { 2312 phy = &addl->proto_data.sasdev_phys[i]; 2313 sbuf_printf(sbp, "%s: phy %d:", sesname, i); 2314 if (ses_elm_sas_dev_phy_sata_dev(phy)) 2315 /* Spec says all other fields are specific values */ 2316 sbuf_printf(sbp, " SATA device\n"); 2317 else { 2318 sbuf_printf(sbp, " SAS device type %d phy %d", 2319 ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id); 2320 SES_PRINT_PORTS(phy->initiator_ports, "Initiator"); 2321 SES_PRINT_PORTS(phy->target_ports, "Target"); 2322 sbuf_printf(sbp, "\n"); 2323 } 2324 sbuf_printf(sbp, "%s: phy %d: parent %jx addr %jx\n", 2325 sesname, i, 2326 (uintmax_t)scsi_8btou64(phy->parent_addr), 2327 (uintmax_t)scsi_8btou64(phy->phy_addr)); 2328 } 2329} 2330#undef SES_PRINT_PORTS 2331 2332/** 2333 * \brief Print the additional element status data for this object, for SAS 2334 * type 1 objects. See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4. 2335 * 2336 * \param sesname SES device name associated with the object. 2337 * \param sbp Sbuf to print to. 2338 * \param obj The object to print the data for. 2339 */ 2340static void 2341ses_print_addl_data_sas_type1(char *sesname, struct sbuf *sbp, 2342 enc_element_t *obj) 2343{ 2344 int i, num_phys; 2345 ses_element_t *elmpriv; 2346 struct ses_addl_status *addl; 2347 struct ses_elm_sas_expander_phy *exp_phy; 2348 struct ses_elm_sas_port_phy *port_phy; 2349 2350 elmpriv = obj->elm_private; 2351 addl = &(elmpriv->addl); 2352 sbuf_printf(sbp, ", SAS "); 2353 if (obj->elm_type == ELMTYP_SAS_EXP) { 2354 num_phys = addl->proto_hdr.sas->base_hdr.num_phys; 2355 sbuf_printf(sbp, "Expander: %d phys", num_phys); 2356 if (addl->proto_data.sasexp_phys == NULL) 2357 return; 2358 for (i = 0;i < num_phys;i++) { 2359 exp_phy = &addl->proto_data.sasexp_phys[i]; 2360 sbuf_printf(sbp, "%s: phy %d: connector %d other %d\n", 2361 sesname, i, exp_phy->connector_index, 2362 exp_phy->other_index); 2363 } 2364 } else { 2365 num_phys = addl->proto_hdr.sas->base_hdr.num_phys; 2366 sbuf_printf(sbp, "Port: %d phys", num_phys); 2367 if (addl->proto_data.sasport_phys == NULL) 2368 return; 2369 for (i = 0;i < num_phys;i++) { 2370 port_phy = &addl->proto_data.sasport_phys[i]; 2371 sbuf_printf(sbp, 2372 "%s: phy %d: id %d connector %d other %d\n", 2373 sesname, i, port_phy->phy_id, 2374 port_phy->connector_index, port_phy->other_index); 2375 sbuf_printf(sbp, "%s: phy %d: addr %jx\n", sesname, i, 2376 (uintmax_t)scsi_8btou64(port_phy->phy_addr)); 2377 } 2378 } 2379} 2380 2381/** 2382 * \brief Print the additional element status data for this object, for 2383 * ATA objects. 2384 * 2385 * \param sbp Sbuf to print to. 2386 * \param obj The object to print the data for. 2387 */ 2388static void 2389ses_print_addl_data_ata(struct sbuf *sbp, enc_element_t *obj) 2390{ 2391 ses_element_t *elmpriv = obj->elm_private; 2392 struct ses_addl_status *addl = &elmpriv->addl; 2393 struct ses_elm_ata_hdr *ata = addl->proto_hdr.ata; 2394 2395 sbuf_printf(sbp, ", SATA Slot: scbus%d target %d\n", 2396 scsi_4btoul(ata->bus), scsi_4btoul(ata->target)); 2397} 2398 2399/** 2400 * \brief Print the additional element status data for this object. 2401 * 2402 * \param enc SES softc associated with the object. 2403 * \param obj The object to print the data for. 2404 */ 2405static void 2406ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj) 2407{ 2408 ses_element_t *elmpriv; 2409 struct ses_addl_status *addl; 2410 struct sbuf sesname, name, out; 2411 2412 elmpriv = obj->elm_private; 2413 if (elmpriv == NULL) 2414 return; 2415 2416 addl = &(elmpriv->addl); 2417 if (addl->hdr == NULL) 2418 return; 2419 2420 sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND); 2421 sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND); 2422 sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND); 2423 ses_paths_iter(enc, obj, ses_elmdevname_callback, &name); 2424 if (sbuf_len(&name) == 0) 2425 sbuf_printf(&name, "(none)"); 2426 sbuf_finish(&name); 2427 sbuf_printf(&sesname, "%s%d", enc->periph->periph_name, 2428 enc->periph->unit_number); 2429 sbuf_finish(&sesname); 2430 sbuf_printf(&out, "%s: %s in ", sbuf_data(&sesname), sbuf_data(&name)); 2431 if (elmpriv->descr != NULL) 2432 sbuf_printf(&out, "'%s'", elmpriv->descr); 2433 else { 2434 if (obj->elm_type <= ELMTYP_LAST) 2435 sbuf_cat(&out, elm_type_names[obj->elm_type]); 2436 else 2437 sbuf_printf(&out, "<Type 0x%02x>", obj->elm_type); 2438 sbuf_printf(&out, " %d", obj->type_elm_idx); 2439 if (obj->subenclosure != 0) 2440 sbuf_printf(&out, " of subenc %d", obj->subenclosure); 2441 } 2442 switch(ses_elm_addlstatus_proto(addl->hdr)) { 2443 case SPSP_PROTO_FC: 2444 goto noaddl; /* stubbed for now */ 2445 case SPSP_PROTO_SAS: 2446 if (addl->proto_hdr.sas == NULL) 2447 goto noaddl; 2448 switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) { 2449 case SES_SASOBJ_TYPE_SLOT: 2450 ses_print_addl_data_sas_type0(sbuf_data(&sesname), 2451 &out, obj); 2452 break; 2453 case SES_SASOBJ_TYPE_OTHER: 2454 ses_print_addl_data_sas_type1(sbuf_data(&sesname), 2455 &out, obj); 2456 break; 2457 default: 2458 goto noaddl; 2459 } 2460 break; 2461 case SPSP_PROTO_ATA: 2462 if (addl->proto_hdr.ata == NULL) 2463 goto noaddl; 2464 ses_print_addl_data_ata(&out, obj); 2465 break; 2466 default: 2467noaddl: 2468 sbuf_cat(&out, "\n"); 2469 break; 2470 } 2471 sbuf_finish(&out); 2472 printf("%s", sbuf_data(&out)); 2473 sbuf_delete(&out); 2474 sbuf_delete(&name); 2475 sbuf_delete(&sesname); 2476} 2477 2478/** 2479 * \brief Update the softc with the additional element status data for this 2480 * object, for SAS type 0 objects. 2481 * 2482 * \param enc SES softc to be updated. 2483 * \param buf The additional element status response buffer. 2484 * \param bufsiz Size of the response buffer. 2485 * \param eip The EIP bit value. 2486 * \param nobj Number of objects attached to the SES softc. 2487 * 2488 * \return 0 on success, errno otherwise. 2489 */ 2490static int 2491ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache, 2492 uint8_t *buf, int bufsiz, int eip, int nobj) 2493{ 2494 int err, offset, physz; 2495 enc_element_t *obj; 2496 ses_element_t *elmpriv; 2497 struct ses_addl_status *addl; 2498 2499 err = offset = 0; 2500 2501 /* basic object setup */ 2502 obj = &(enc_cache->elm_map[nobj]); 2503 elmpriv = obj->elm_private; 2504 addl = &(elmpriv->addl); 2505 2506 addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; 2507 2508 /* Don't assume this object has any phys */ 2509 bzero(&addl->proto_data, sizeof(addl->proto_data)); 2510 if (addl->proto_hdr.sas->base_hdr.num_phys == 0) 2511 goto out; 2512 2513 /* Skip forward to the phy list */ 2514 if (eip) 2515 offset += sizeof(struct ses_elm_sas_type0_eip_hdr); 2516 else 2517 offset += sizeof(struct ses_elm_sas_type0_base_hdr); 2518 2519 /* Make sure the phy list fits in the buffer */ 2520 physz = addl->proto_hdr.sas->base_hdr.num_phys; 2521 physz *= sizeof(struct ses_elm_sas_device_phy); 2522 if (physz > (bufsiz - offset + 4)) { 2523 ENC_VLOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n", 2524 nobj); 2525 err = EIO; 2526 goto out; 2527 } 2528 2529 /* Point to the phy list */ 2530 addl->proto_data.sasdev_phys = 2531 (struct ses_elm_sas_device_phy *)&buf[offset]; 2532 2533out: 2534 return (err); 2535} 2536 2537/** 2538 * \brief Update the softc with the additional element status data for this 2539 * object, for SAS type 1 objects. 2540 * 2541 * \param enc SES softc to be updated. 2542 * \param buf The additional element status response buffer. 2543 * \param bufsiz Size of the response buffer. 2544 * \param eip The EIP bit value. 2545 * \param nobj Number of objects attached to the SES softc. 2546 * 2547 * \return 0 on success, errno otherwise. 2548 */ 2549static int 2550ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache, 2551 uint8_t *buf, int bufsiz, int eip, int nobj) 2552{ 2553 int err, offset, physz; 2554 enc_element_t *obj; 2555 ses_element_t *elmpriv; 2556 struct ses_addl_status *addl; 2557 2558 err = offset = 0; 2559 2560 /* basic object setup */ 2561 obj = &(enc_cache->elm_map[nobj]); 2562 elmpriv = obj->elm_private; 2563 addl = &(elmpriv->addl); 2564 2565 addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; 2566 2567 /* Don't assume this object has any phys */ 2568 bzero(&addl->proto_data, sizeof(addl->proto_data)); 2569 if (addl->proto_hdr.sas->base_hdr.num_phys == 0) 2570 goto out; 2571 2572 /* Process expanders differently from other type1 cases */ 2573 if (obj->elm_type == ELMTYP_SAS_EXP) { 2574 offset += sizeof(struct ses_elm_sas_type1_expander_hdr); 2575 physz = addl->proto_hdr.sas->base_hdr.num_phys * 2576 sizeof(struct ses_elm_sas_expander_phy); 2577 if (physz > (bufsiz - offset)) { 2578 ENC_VLOG(enc, "Element %d: Expander Phy List Beyond " 2579 "End Of Buffer\n", nobj); 2580 err = EIO; 2581 goto out; 2582 } 2583 addl->proto_data.sasexp_phys = 2584 (struct ses_elm_sas_expander_phy *)&buf[offset]; 2585 } else { 2586 offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr); 2587 physz = addl->proto_hdr.sas->base_hdr.num_phys * 2588 sizeof(struct ses_elm_sas_port_phy); 2589 if (physz > (bufsiz - offset + 4)) { 2590 ENC_VLOG(enc, "Element %d: Port Phy List Beyond End " 2591 "Of Buffer\n", nobj); 2592 err = EIO; 2593 goto out; 2594 } 2595 addl->proto_data.sasport_phys = 2596 (struct ses_elm_sas_port_phy *)&buf[offset]; 2597 } 2598 2599out: 2600 return (err); 2601} 2602 2603/** 2604 * \brief Update the softc with the additional element status data for this 2605 * object, for SAS objects. 2606 * 2607 * \param enc SES softc to be updated. 2608 * \param buf The additional element status response buffer. 2609 * \param bufsiz Size of the response buffer. 2610 * \param eip The EIP bit value. 2611 * \param tidx Type index for this object. 2612 * \param nobj Number of objects attached to the SES softc. 2613 * 2614 * \return 0 on success, errno otherwise. 2615 */ 2616static int 2617ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, 2618 uint8_t *buf, int bufsiz, int eip, int tidx, 2619 int nobj) 2620{ 2621 int dtype, err; 2622 ses_cache_t *ses_cache; 2623 union ses_elm_sas_hdr *hdr; 2624 2625 /* Need to be able to read the descriptor type! */ 2626 if (bufsiz < sizeof(union ses_elm_sas_hdr)) { 2627 err = EIO; 2628 goto out; 2629 } 2630 2631 ses_cache = enc_cache->private; 2632 2633 hdr = (union ses_elm_sas_hdr *)buf; 2634 dtype = ses_elm_sas_descr_type(hdr); 2635 switch(dtype) { 2636 case SES_SASOBJ_TYPE_SLOT: 2637 switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { 2638 case ELMTYP_DEVICE: 2639 case ELMTYP_ARRAY_DEV: 2640 break; 2641 default: 2642 ENC_VLOG(enc, "Element %d has Additional Status type 0, " 2643 "invalid for SES element type 0x%x\n", nobj, 2644 ses_cache->ses_types[tidx].hdr->etype_elm_type); 2645 err = ENODEV; 2646 goto out; 2647 } 2648 err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache, 2649 buf, bufsiz, eip, 2650 nobj); 2651 break; 2652 case SES_SASOBJ_TYPE_OTHER: 2653 switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { 2654 case ELMTYP_SAS_EXP: 2655 case ELMTYP_SCSI_INI: 2656 case ELMTYP_SCSI_TGT: 2657 case ELMTYP_ESCC: 2658 break; 2659 default: 2660 ENC_VLOG(enc, "Element %d has Additional Status type 1, " 2661 "invalid for SES element type 0x%x\n", nobj, 2662 ses_cache->ses_types[tidx].hdr->etype_elm_type); 2663 err = ENODEV; 2664 goto out; 2665 } 2666 err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf, 2667 bufsiz, eip, nobj); 2668 break; 2669 default: 2670 ENC_VLOG(enc, "Element %d of type 0x%x has Additional Status " 2671 "of unknown type 0x%x\n", nobj, 2672 ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype); 2673 err = ENODEV; 2674 break; 2675 } 2676 2677out: 2678 return (err); 2679} 2680 2681/** 2682 * \brief Update the softc with the additional element status data for this 2683 * object, for ATA objects. 2684 * 2685 * \param enc SES softc to be updated. 2686 * \param buf The additional element status response buffer. 2687 * \param bufsiz Size of the response buffer. 2688 * \param eip The EIP bit value. 2689 * \param tidx Type index for this object. 2690 * \param nobj Number of objects attached to the SES softc. 2691 * 2692 * \return 0 on success, errno otherwise. 2693 */ 2694static int 2695ses_get_elm_addlstatus_ata(enc_softc_t *enc, enc_cache_t *enc_cache, 2696 uint8_t *buf, int bufsiz, int eip, int tidx, 2697 int nobj) 2698{ 2699 int err; 2700 ses_cache_t *ses_cache; 2701 2702 if (bufsiz < sizeof(struct ses_elm_ata_hdr)) { 2703 err = EIO; 2704 goto out; 2705 } 2706 2707 ses_cache = enc_cache->private; 2708 switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { 2709 case ELMTYP_DEVICE: 2710 case ELMTYP_ARRAY_DEV: 2711 break; 2712 default: 2713 ENC_VLOG(enc, "Element %d has Additional Status, " 2714 "invalid for SES element type 0x%x\n", nobj, 2715 ses_cache->ses_types[tidx].hdr->etype_elm_type); 2716 err = ENODEV; 2717 goto out; 2718 } 2719 2720 ((ses_element_t *)enc_cache->elm_map[nobj].elm_private) 2721 ->addl.proto_hdr.ata = (struct ses_elm_ata_hdr *)buf; 2722 err = 0; 2723 2724out: 2725 return (err); 2726} 2727 2728static void 2729ses_softc_invalidate(enc_softc_t *enc) 2730{ 2731 ses_softc_t *ses; 2732 2733 ses = enc->enc_private; 2734 ses_terminate_control_requests(&ses->ses_requests, ENXIO); 2735} 2736 2737static void 2738ses_softc_cleanup(enc_softc_t *enc) 2739{ 2740 2741 ses_cache_free(enc, &enc->enc_cache); 2742 ses_cache_free(enc, &enc->enc_daemon_cache); 2743 ENC_FREE_AND_NULL(enc->enc_private); 2744 ENC_FREE_AND_NULL(enc->enc_cache.private); 2745 ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); 2746} 2747 2748static int 2749ses_init_enc(enc_softc_t *enc) 2750{ 2751 return (0); 2752} 2753 2754static int 2755ses_get_enc_status(enc_softc_t *enc, int slpflag) 2756{ 2757 /* Automatically updated, caller checks enc_cache->encstat itself */ 2758 return (0); 2759} 2760 2761static int 2762ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) 2763{ 2764 ses_control_request_t req; 2765 ses_softc_t *ses; 2766 2767 ses = enc->enc_private; 2768 req.elm_idx = SES_SETSTATUS_ENC_IDX; 2769 req.elm_stat.comstatus = encstat & 0xf; 2770 2771 TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); 2772 enc_update_request(enc, SES_PROCESS_CONTROL_REQS); 2773 cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); 2774 2775 return (req.result); 2776} 2777 2778static int 2779ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) 2780{ 2781 unsigned int i = elms->elm_idx; 2782 2783 memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4); 2784 return (0); 2785} 2786 2787static int 2788ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) 2789{ 2790 ses_control_request_t req; 2791 ses_softc_t *ses; 2792 2793 /* If this is clear, we don't do diddly. */ 2794 if ((elms->cstat[0] & SESCTL_CSEL) == 0) 2795 return (0); 2796 2797 ses = enc->enc_private; 2798 req.elm_idx = elms->elm_idx; 2799 memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); 2800 2801 TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); 2802 enc_update_request(enc, SES_PROCESS_CONTROL_REQS); 2803 cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); 2804 2805 return (req.result); 2806} 2807 2808static int 2809ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd) 2810{ 2811 int i = (int)elmd->elm_idx; 2812 ses_element_t *elmpriv; 2813 2814 /* Assume caller has already checked obj_id validity */ 2815 elmpriv = enc->enc_cache.elm_map[i].elm_private; 2816 /* object might not have a descriptor */ 2817 if (elmpriv == NULL || elmpriv->descr == NULL) { 2818 elmd->elm_desc_len = 0; 2819 return (0); 2820 } 2821 if (elmd->elm_desc_len > elmpriv->descr_len) 2822 elmd->elm_desc_len = elmpriv->descr_len; 2823 copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len); 2824 return (0); 2825} 2826 2827/** 2828 * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the 2829 * given object id if one is available. 2830 * 2831 * \param enc SES softc to examine. 2832 * \param objdn ioctl structure to read/write device name info. 2833 * 2834 * \return 0 on success, errno otherwise. 2835 */ 2836static int 2837ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn) 2838{ 2839 struct sbuf sb; 2840 int len; 2841 2842 len = elmdn->elm_names_size; 2843 if (len < 0) 2844 return (EINVAL); 2845 2846 cam_periph_unlock(enc->periph); 2847 sbuf_new(&sb, NULL, len, SBUF_FIXEDLEN); 2848 ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx], 2849 ses_elmdevname_callback, &sb); 2850 sbuf_finish(&sb); 2851 elmdn->elm_names_len = sbuf_len(&sb); 2852 copyout(sbuf_data(&sb), elmdn->elm_devnames, elmdn->elm_names_len + 1); 2853 sbuf_delete(&sb); 2854 cam_periph_lock(enc->periph); 2855 return (elmdn->elm_names_len > 0 ? 0 : ENODEV); 2856} 2857 2858/** 2859 * \brief Send a string to the primary subenclosure using the String Out 2860 * SES diagnostic page. 2861 * 2862 * \param enc SES enclosure to run the command on. 2863 * \param sstr SES string structure to operate on 2864 * \param ioc Ioctl being performed 2865 * 2866 * \return 0 on success, errno otherwise. 2867 */ 2868static int 2869ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc) 2870{ 2871 ses_softc_t *ses; 2872 enc_cache_t *enc_cache; 2873 ses_cache_t *ses_cache; 2874 const struct ses_enc_desc *enc_desc; 2875 int amt, payload, ret; 2876 char cdb[6]; 2877 char str[32]; 2878 char vendor[9]; 2879 char product[17]; 2880 char rev[5]; 2881 uint8_t *buf; 2882 size_t size, rsize; 2883 2884 ses = enc->enc_private; 2885 enc_cache = &enc->enc_daemon_cache; 2886 ses_cache = enc_cache->private; 2887 2888 /* Implement SES2r20 6.1.6 */ 2889 if (sstr->bufsiz > 0xffff) 2890 return (EINVAL); /* buffer size too large */ 2891 2892 switch (ioc) { 2893 case ENCIOC_SETSTRING: 2894 payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */ 2895 amt = 0 - payload; 2896 buf = ENC_MALLOC(payload); 2897 if (buf == NULL) 2898 return (ENOMEM); 2899 ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT); 2900 /* Construct the page request */ 2901 buf[0] = SesStringOut; 2902 buf[1] = 0; 2903 buf[2] = sstr->bufsiz >> 8; 2904 buf[3] = sstr->bufsiz & 0xff; 2905 ret = copyin(sstr->buf, &buf[4], sstr->bufsiz); 2906 if (ret != 0) { 2907 ENC_FREE(buf); 2908 return (ret); 2909 } 2910 break; 2911 case ENCIOC_GETSTRING: 2912 payload = sstr->bufsiz; 2913 amt = payload; 2914 buf = ENC_MALLOC(payload); 2915 if (buf == NULL) 2916 return (ENOMEM); 2917 ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN); 2918 break; 2919 case ENCIOC_GETENCNAME: 2920 if (ses_cache->ses_nsubencs < 1) 2921 return (ENODEV); 2922 enc_desc = ses_cache->subencs[0]; 2923 cam_strvis(vendor, enc_desc->vendor_id, 2924 sizeof(enc_desc->vendor_id), sizeof(vendor)); 2925 cam_strvis(product, enc_desc->product_id, 2926 sizeof(enc_desc->product_id), sizeof(product)); 2927 cam_strvis(rev, enc_desc->product_rev, 2928 sizeof(enc_desc->product_rev), sizeof(rev)); 2929 rsize = snprintf(str, sizeof(str), "%s %s %s", 2930 vendor, product, rev) + 1; 2931 if (rsize > sizeof(str)) 2932 rsize = sizeof(str); 2933 copyout(&rsize, &sstr->bufsiz, sizeof(rsize)); 2934 size = rsize; 2935 if (size > sstr->bufsiz) 2936 size = sstr->bufsiz; 2937 copyout(str, sstr->buf, size); 2938 return (size == rsize ? 0 : ENOMEM); 2939 case ENCIOC_GETENCID: 2940 if (ses_cache->ses_nsubencs < 1) 2941 return (ENODEV); 2942 enc_desc = ses_cache->subencs[0]; 2943 rsize = snprintf(str, sizeof(str), "%16jx", 2944 scsi_8btou64(enc_desc->logical_id)) + 1; 2945 if (rsize > sizeof(str)) 2946 rsize = sizeof(str); 2947 copyout(&rsize, &sstr->bufsiz, sizeof(rsize)); 2948 size = rsize; 2949 if (size > sstr->bufsiz) 2950 size = sstr->bufsiz; 2951 copyout(str, sstr->buf, size); 2952 return (size == rsize ? 0 : ENOMEM); 2953 default: 2954 return (EINVAL); 2955 } 2956 ret = enc_runcmd(enc, cdb, 6, buf, &amt); 2957 if (ret == 0 && ioc == ENCIOC_GETSTRING) 2958 ret = copyout(buf, sstr->buf, sstr->bufsiz); 2959 if (ioc == ENCIOC_SETSTRING || ioc == ENCIOC_GETSTRING) 2960 ENC_FREE(buf); 2961 return (ret); 2962} 2963 2964/** 2965 * \invariant Called with cam_periph mutex held. 2966 */ 2967static void 2968ses_poll_status(enc_softc_t *enc) 2969{ 2970 ses_softc_t *ses; 2971 2972 ses = enc->enc_private; 2973 enc_update_request(enc, SES_UPDATE_GETSTATUS); 2974 if (ses->ses_flags & SES_FLAG_DESC) 2975 enc_update_request(enc, SES_UPDATE_GETELMDESCS); 2976 if (ses->ses_flags & SES_FLAG_ADDLSTATUS) 2977 enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); 2978} 2979 2980/** 2981 * \brief Notification received when CAM detects a new device in the 2982 * SCSI domain in which this SEP resides. 2983 * 2984 * \param enc SES enclosure instance. 2985 */ 2986static void 2987ses_device_found(enc_softc_t *enc) 2988{ 2989 ses_poll_status(enc); 2990 enc_update_request(enc, SES_PUBLISH_PHYSPATHS); 2991} 2992 2993static struct enc_vec ses_enc_vec = 2994{ 2995 .softc_invalidate = ses_softc_invalidate, 2996 .softc_cleanup = ses_softc_cleanup, 2997 .init_enc = ses_init_enc, 2998 .get_enc_status = ses_get_enc_status, 2999 .set_enc_status = ses_set_enc_status, 3000 .get_elm_status = ses_get_elm_status, 3001 .set_elm_status = ses_set_elm_status, 3002 .get_elm_desc = ses_get_elm_desc, 3003 .get_elm_devnames = ses_get_elm_devnames, 3004 .handle_string = ses_handle_string, 3005 .device_found = ses_device_found, 3006 .poll_status = ses_poll_status 3007}; 3008 3009/** 3010 * \brief Initialize a new SES instance. 3011 * 3012 * \param enc SES softc structure to set up the instance in. 3013 * \param doinit Do the initialization (see main driver). 3014 * 3015 * \return 0 on success, errno otherwise. 3016 */ 3017int 3018ses_softc_init(enc_softc_t *enc) 3019{ 3020 ses_softc_t *ses_softc; 3021 3022 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 3023 ("entering enc_softc_init(%p)\n", enc)); 3024 3025 enc->enc_vec = ses_enc_vec; 3026 enc->enc_fsm_states = enc_fsm_states; 3027 3028 if (enc->enc_private == NULL) 3029 enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t)); 3030 if (enc->enc_cache.private == NULL) 3031 enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t)); 3032 if (enc->enc_daemon_cache.private == NULL) 3033 enc->enc_daemon_cache.private = 3034 ENC_MALLOCZ(sizeof(ses_cache_t)); 3035 3036 if (enc->enc_private == NULL 3037 || enc->enc_cache.private == NULL 3038 || enc->enc_daemon_cache.private == NULL) { 3039 ENC_FREE_AND_NULL(enc->enc_private); 3040 ENC_FREE_AND_NULL(enc->enc_cache.private); 3041 ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); 3042 return (ENOMEM); 3043 } 3044 3045 ses_softc = enc->enc_private; 3046 TAILQ_INIT(&ses_softc->ses_requests); 3047 TAILQ_INIT(&ses_softc->ses_pending_requests); 3048 3049 enc_update_request(enc, SES_UPDATE_PAGES); 3050 3051 // XXX: Move this to the FSM so it doesn't hang init 3052 if (0) (void) ses_set_timed_completion(enc, 1); 3053 3054 return (0); 3055} 3056 3057