1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Interface for Serengeti IOSRAM mailbox 29 * OS <-> SC communication protocol 30 */ 31 32 #include <sys/types.h> 33 #include <sys/systm.h> 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/kmem.h> 37 #include <sys/uadmin.h> 38 #include <sys/machsystm.h> 39 #include <sys/disp.h> 40 #include <sys/taskq.h> 41 42 #include <sys/sgevents.h> 43 #include <sys/sgsbbc_priv.h> 44 #include <sys/sgsbbc_iosram_priv.h> 45 #include <sys/sgsbbc_mailbox_priv.h> 46 #include <sys/plat_ecc_unum.h> 47 #include <sys/plat_ecc_dimm.h> 48 #include <sys/serengeti.h> 49 #include <sys/fm/util.h> 50 #include <sys/promif.h> 51 #include <sys/plat_datapath.h> 52 53 sbbc_mailbox_t *master_mbox = NULL; 54 55 /* 56 * Panic Shutdown event support 57 */ 58 static kmutex_t panic_hdlr_lock; 59 60 /* 61 * The ID of the soft interrupt which triggers the bringing down of a Domain 62 * when a PANIC_SHUTDOWN event is received. 63 */ 64 static ddi_softintr_t panic_softintr_id = 0; 65 66 static sg_panic_shutdown_t panic_payload; 67 static sbbc_msg_t panic_payload_msg; 68 69 /* 70 * A queue for making sure outgoing messages are in order as ScApp 71 * does not support interleaving messages. 72 */ 73 static kcondvar_t outbox_queue; 74 static kmutex_t outbox_queue_lock; 75 76 /* 77 * Handle unsolicited capability message. 78 */ 79 static plat_capability_data_t cap_payload; 80 static sbbc_msg_t cap_payload_msg; 81 static kmutex_t cap_msg_hdlr_lock; 82 83 /* 84 * Datapath error and fault messages arrive unsolicited. The message data 85 * is contained in a plat_datapath_info_t structure. 86 */ 87 typedef struct { 88 uint8_t type; /* CDS, DX, CP */ 89 uint8_t pad; /* for alignment */ 90 uint16_t cpuid; /* Safari ID of base CPU */ 91 uint32_t t_value; /* SERD timeout threshold (seconds) */ 92 } plat_datapath_info_t; 93 94 /* 95 * Unsolicited datapath error messages are processed via a soft interrupt, 96 * triggered in unsolicited interrupt processing. 97 */ 98 static ddi_softintr_t dp_softintr_id = 0; 99 static kmutex_t dp_hdlr_lock; 100 101 static plat_datapath_info_t dp_payload; 102 static sbbc_msg_t dp_payload_msg; 103 104 static char *dperrtype[] = { 105 DP_ERROR_CDS, 106 DP_ERROR_DX, 107 DP_ERROR_RP 108 }; 109 110 /* 111 * Variable indicating if we are already processing requests. 112 * Setting this value must be protected by outbox_queue_lock. 113 */ 114 static int outbox_busy = 0; 115 116 /* 117 * local stuff 118 */ 119 static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t); 120 static int sbbc_mbox_recv_msg(); 121 static int mbox_write(struct sbbc_mbox_header *, 122 struct sbbc_fragment *, sbbc_msg_t *); 123 static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *, 124 sbbc_msg_t *); 125 static int mbox_has_free_space(struct sbbc_mbox_header *); 126 static void mbox_skip_next_msg(struct sbbc_mbox_header *); 127 static int mbox_read_header(uint32_t, struct sbbc_mbox_header *); 128 static void mbox_update_header(uint32_t, struct sbbc_mbox_header *); 129 static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *); 130 static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t); 131 static void wakeup_next(void); 132 static uint_t sbbc_panic_shutdown_handler(char *arg); 133 static uint_t sbbc_do_fast_shutdown(char *arg); 134 static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp); 135 static uint_t cap_ecc_msg_handler(char *); 136 static uint_t sbbc_datapath_error_msg_handler(char *arg); 137 static uint_t sbbc_datapath_fault_msg_handler(char *arg); 138 static uint_t sbbc_dp_trans_event(char *arg); 139 140 141 /* 142 * Interrupt handlers 143 */ 144 static int sbbc_mbox_msgin(void); 145 static int sbbc_mbox_msgout(void); 146 static int sbbc_mbox_spacein(void); 147 static int sbbc_mbox_spaceout(void); 148 149 /* 150 * ECC event mailbox message taskq and parameters 151 */ 152 static taskq_t *sbbc_ecc_mbox_taskq = NULL; 153 static int sbbc_ecc_mbox_taskq_errs = 0; 154 static int sbbc_ecc_mbox_send_errs = 0; 155 static int sbbc_ecc_mbox_inval_errs = 0; 156 static int sbbc_ecc_mbox_other_errs = 0; 157 int sbbc_ecc_mbox_err_throttle = ECC_MBOX_TASKQ_ERR_THROTTLE; 158 159 /* 160 * Called when SBBC driver is loaded 161 * Initialise global mailbox stuff, etc 162 */ 163 void 164 sbbc_mbox_init() 165 { 166 int i; 167 168 master_mbox = kmem_zalloc(sizeof (sbbc_mailbox_t), KM_NOSLEEP); 169 if (master_mbox == NULL) { 170 cmn_err(CE_PANIC, "Can't allocate memory for mailbox\n"); 171 } 172 173 /* 174 * mutex'es for the wait-lists 175 */ 176 for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) { 177 mutex_init(&master_mbox->mbox_wait_lock[i], 178 NULL, MUTEX_DEFAULT, NULL); 179 master_mbox->mbox_wait_list[i] = NULL; 180 } 181 182 for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) 183 master_mbox->intrs[i] = NULL; 184 185 /* 186 * Two mailbox channels SC -> OS , read-only 187 * OS -> SC, read/write 188 */ 189 master_mbox->mbox_in = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP); 190 if (master_mbox->mbox_in == NULL) { 191 cmn_err(CE_PANIC, 192 "Can't allocate memory for inbound mailbox\n"); 193 } 194 195 master_mbox->mbox_out = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP); 196 if (master_mbox->mbox_out == NULL) { 197 cmn_err(CE_PANIC, 198 "Can't allocate memory for outbound mailbox\n"); 199 } 200 201 mutex_init(&master_mbox->mbox_in->mb_lock, NULL, 202 MUTEX_DEFAULT, NULL); 203 mutex_init(&master_mbox->mbox_out->mb_lock, NULL, 204 MUTEX_DEFAULT, NULL); 205 206 /* 207 * Add PANIC_SHUTDOWN Event mutex 208 */ 209 mutex_init(&panic_hdlr_lock, NULL, MUTEX_DEFAULT, NULL); 210 211 /* Initialize datapath error message handler mutex */ 212 mutex_init(&dp_hdlr_lock, NULL, MUTEX_DEFAULT, NULL); 213 214 /* Initialize capability message handler event mutex */ 215 mutex_init(&cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL); 216 217 /* 218 * NOT USED YET 219 */ 220 master_mbox->mbox_in->mb_type = 221 master_mbox->mbox_out->mb_type = 0; 222 223 cv_init(&outbox_queue, NULL, CV_DEFAULT, NULL); 224 mutex_init(&outbox_queue_lock, NULL, MUTEX_DEFAULT, NULL); 225 226 } 227 228 /* 229 * called when the SBBC driver is unloaded 230 */ 231 void 232 sbbc_mbox_fini() 233 { 234 int i; 235 int err; 236 237 /* 238 * destroy ECC event mailbox taskq 239 */ 240 if (sbbc_ecc_mbox_taskq != NULL) { 241 taskq_destroy(sbbc_ecc_mbox_taskq); 242 sbbc_ecc_mbox_taskq = NULL; 243 sbbc_ecc_mbox_taskq_errs = 0; 244 } 245 246 /* 247 * unregister interrupts 248 */ 249 (void) iosram_unreg_intr(SBBC_MAILBOX_IN); 250 (void) iosram_unreg_intr(SBBC_MAILBOX_IN); 251 (void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_IN); 252 (void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_OUT); 253 254 /* 255 * Remove Panic Shutdown and Datapath Error event support. 256 * 257 * NOTE: If we have not added the soft interrupt handlers for these 258 * then we know that we have not registered the event handlers either. 259 */ 260 if (panic_softintr_id != 0) { 261 ddi_remove_softintr(panic_softintr_id); 262 263 err = sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN, 264 sbbc_panic_shutdown_handler); 265 if (err != 0) { 266 cmn_err(CE_WARN, "Failed to unreg Panic Shutdown " 267 "handler. Err=%d", err); 268 } 269 } 270 if (dp_softintr_id != 0) { 271 ddi_remove_softintr(dp_softintr_id); 272 273 err = sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR, 274 sbbc_datapath_error_msg_handler); 275 err |= sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT, 276 sbbc_datapath_fault_msg_handler); 277 if (err != 0) { 278 cmn_err(CE_WARN, "Failed to unreg Datapath Error " 279 "handler. Err=%d", err); 280 } 281 } 282 283 /* 284 * destroy all its mutex'es, lists etc 285 */ 286 287 /* 288 * mutex'es for the wait-lists 289 */ 290 for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) { 291 mutex_destroy(&master_mbox->mbox_wait_lock[i]); 292 } 293 294 mutex_destroy(&master_mbox->mbox_in->mb_lock); 295 mutex_destroy(&master_mbox->mbox_out->mb_lock); 296 297 mutex_destroy(&panic_hdlr_lock); 298 mutex_destroy(&dp_hdlr_lock); 299 300 kmem_free(master_mbox->mbox_in, sizeof (sbbc_mbox_t)); 301 kmem_free(master_mbox->mbox_out, sizeof (sbbc_mbox_t)); 302 kmem_free(master_mbox, sizeof (sbbc_mailbox_t)); 303 304 cv_destroy(&outbox_queue); 305 mutex_destroy(&outbox_queue_lock); 306 307 err = sbbc_mbox_unreg_intr(INFO_MBOX, cap_ecc_msg_handler); 308 if (err != 0) { 309 cmn_err(CE_WARN, "Failed to unregister capability message " 310 "handler. Err=%d", err); 311 } 312 313 mutex_destroy(&cap_msg_hdlr_lock); 314 } 315 316 /* 317 * Update iosram_sbbc to the new softstate after a tunnel switch. 318 * Move software interrupts from the old dip to the new dip. 319 */ 320 int 321 sbbc_mbox_switch(sbbc_softstate_t *softsp) 322 { 323 sbbc_intrs_t *intr; 324 int msg_type; 325 int rc = 0; 326 int err; 327 328 if (master_mbox == NULL) 329 return (ENXIO); 330 331 ASSERT(MUTEX_HELD(&master_iosram->iosram_lock)); 332 333 for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) { 334 335 for (intr = master_mbox->intrs[msg_type]; intr != NULL; 336 intr = intr->sbbc_intr_next) { 337 338 if (intr->sbbc_intr_id) { 339 ddi_remove_softintr(intr->sbbc_intr_id); 340 341 if (ddi_add_softintr(softsp->dip, 342 DDI_SOFTINT_HIGH, 343 &intr->sbbc_intr_id, NULL, NULL, 344 intr->sbbc_handler, intr->sbbc_arg) 345 != DDI_SUCCESS) { 346 347 cmn_err(CE_WARN, 348 "Can't add SBBC mailbox " 349 "softint for msg_type %x\n", 350 msg_type); 351 rc = ENXIO; 352 } 353 } 354 } 355 } 356 357 /* 358 * Add PANIC_SHUTDOWN Event handler 359 */ 360 if (panic_softintr_id) { 361 ddi_remove_softintr(panic_softintr_id); 362 363 err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, 364 &panic_softintr_id, NULL, NULL, 365 sbbc_do_fast_shutdown, NULL); 366 367 if (err != DDI_SUCCESS) { 368 cmn_err(CE_WARN, "Failed to register Panic " 369 "Shutdown handler. Err=%d", err); 370 (void) sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN, 371 sbbc_panic_shutdown_handler); 372 rc = ENXIO; 373 } 374 375 } 376 /* 377 * Add Datapath Error Event handler 378 */ 379 if (dp_softintr_id) { 380 ddi_remove_softintr(dp_softintr_id); 381 382 err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, 383 &dp_softintr_id, NULL, NULL, 384 sbbc_dp_trans_event, NULL); 385 386 if (err != DDI_SUCCESS) { 387 cmn_err(CE_WARN, "Failed to register Datapath " 388 "Error Event handler. Err=%d", err); 389 (void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR, 390 sbbc_datapath_error_msg_handler); 391 (void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT, 392 sbbc_datapath_fault_msg_handler); 393 rc = ENXIO; 394 } 395 396 } 397 398 return (rc); 399 } 400 401 /* 402 * Called when the IOSRAM tunnel is created for the 'chosen' node. 403 * 404 * Read the mailbox header from the IOSRAM 405 * tunnel[SBBC_MAILBOX_KEY] 406 * Register the mailbox interrupt handlers 407 * for messages in/space etc 408 */ 409 int 410 sbbc_mbox_create(sbbc_softstate_t *softsp) 411 { 412 struct sbbc_mbox_header header; 413 414 int i; 415 int err; 416 int rc = 0; 417 418 /* 419 * This function should only be called once when 420 * the chosen node is initialized. 421 */ 422 ASSERT(MUTEX_HELD(&chosen_lock)); 423 424 if (master_mbox == NULL) 425 return (ENXIO); 426 427 /* 428 * read the header at offset 0 429 * check magic/version etc 430 */ 431 if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)&header, 432 sizeof (struct sbbc_mbox_header))) { 433 434 return (rc); 435 } 436 437 /* 438 * add the interrupt handlers for the mailbox 439 * interrupts 440 */ 441 for (i = 0; i < MBOX_INTRS; i++) { 442 sbbc_intrfunc_t intr_handler; 443 uint_t *state; 444 kmutex_t *lock; 445 uint32_t intr_num; 446 447 switch (i) { 448 case MBOX_MSGIN_INTR: 449 intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgin; 450 intr_num = SBBC_MAILBOX_IN; 451 break; 452 case MBOX_MSGOUT_INTR: 453 intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgout; 454 intr_num = SBBC_MAILBOX_OUT; 455 break; 456 case MBOX_SPACEIN_INTR: 457 intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spacein; 458 intr_num = SBBC_MAILBOX_SPACE_IN; 459 break; 460 case MBOX_SPACEOUT_INTR: 461 intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spaceout; 462 intr_num = SBBC_MAILBOX_SPACE_OUT; 463 break; 464 } 465 state = (uint_t *)&master_mbox->intr_state[i].mbox_intr_state; 466 lock = &master_mbox->intr_state[i].mbox_intr_lock; 467 if (iosram_reg_intr(intr_num, intr_handler, (caddr_t)NULL, 468 state, lock)) { 469 470 cmn_err(CE_WARN, 471 "Can't register Mailbox interrupts \n"); 472 } 473 } 474 475 /* 476 * Add PANIC_SHUTDOWN Event handler 477 */ 478 panic_payload_msg.msg_buf = (caddr_t)&panic_payload; 479 panic_payload_msg.msg_len = sizeof (panic_payload); 480 481 err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &panic_softintr_id, 482 NULL, NULL, sbbc_do_fast_shutdown, NULL); 483 484 if (err == DDI_SUCCESS) { 485 err = sbbc_mbox_reg_intr(MBOX_EVENT_PANIC_SHUTDOWN, 486 sbbc_panic_shutdown_handler, &panic_payload_msg, 487 NULL, &panic_hdlr_lock); 488 if (err != 0) { 489 cmn_err(CE_WARN, "Failed to register Panic " 490 "Shutdown handler. Err=%d", err); 491 } 492 493 } else { 494 cmn_err(CE_WARN, "Failed to add Panic Shutdown " 495 "softintr handler"); 496 } 497 498 /* 499 * Add Unsolicited Datapath Error Events handler 500 */ 501 dp_payload_msg.msg_buf = (caddr_t)&dp_payload; 502 dp_payload_msg.msg_len = sizeof (dp_payload); 503 504 err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &dp_softintr_id, 505 NULL, NULL, sbbc_dp_trans_event, NULL); 506 507 if (err == DDI_SUCCESS) { 508 err = sbbc_mbox_reg_intr(MBOX_EVENT_DP_ERROR, 509 sbbc_datapath_error_msg_handler, &dp_payload_msg, 510 NULL, &dp_hdlr_lock); 511 err |= sbbc_mbox_reg_intr(MBOX_EVENT_DP_FAULT, 512 sbbc_datapath_fault_msg_handler, &dp_payload_msg, 513 NULL, &dp_hdlr_lock); 514 if (err != 0) { 515 cmn_err(CE_WARN, "Failed to register Datapath " 516 "error handler. Err=%d", err); 517 } 518 519 } else { 520 cmn_err(CE_WARN, "Failed to add Datapath error " 521 "softintr handler"); 522 } 523 524 /* 525 * Register an interrupt handler with the sgbbc driver for the 526 * unsolicited INFO_MBOX response for the capability bitmap. 527 * This message is expected whenever the SC is (re)booted or 528 * failed over. 529 */ 530 cap_payload_msg.msg_buf = (caddr_t)&cap_payload; 531 cap_payload_msg.msg_len = sizeof (cap_payload); 532 533 err = sbbc_mbox_reg_intr(INFO_MBOX, cap_ecc_msg_handler, 534 &cap_payload_msg, NULL, &cap_msg_hdlr_lock); 535 if (err != 0) { 536 cmn_err(CE_WARN, "Failed to register capability message" 537 " handler with Err=%d", err); 538 } 539 540 /* 541 * Now is the opportunity to register 542 * the deferred mbox intrs. 543 */ 544 sbbc_mbox_post_reg(softsp); 545 546 return (rc); 547 } 548 549 /* 550 * Called when chosen IOSRAM is initialized 551 * to register the deferred mbox intrs. 552 */ 553 static void 554 sbbc_mbox_post_reg(sbbc_softstate_t *softsp) 555 { 556 uint32_t msg_type; 557 sbbc_intrs_t *intr; 558 559 ASSERT(master_mbox); 560 for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) { 561 intr = master_mbox->intrs[msg_type]; 562 while (intr != NULL) { 563 if (!intr->registered) { 564 SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_post_reg: " 565 "postreg for msgtype=%x\n", msg_type); 566 if (ddi_add_softintr(softsp->dip, 567 DDI_SOFTINT_HIGH, &intr->sbbc_intr_id, 568 NULL, NULL, intr->sbbc_handler, 569 (caddr_t)intr->sbbc_arg) 570 != DDI_SUCCESS) { 571 cmn_err(CE_WARN, "Can't add SBBC " 572 "deferred mailbox softint \n"); 573 } else 574 intr->registered = 1; 575 } 576 intr = intr->sbbc_intr_next; 577 } 578 } 579 } 580 581 /* 582 * Register a handler for a message type 583 * NB NB NB 584 * arg must be either NULL or the address of a sbbc_fragment 585 * pointer 586 */ 587 int 588 sbbc_mbox_reg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler, 589 sbbc_msg_t *arg, uint_t *state, kmutex_t *lock) 590 { 591 sbbc_intrs_t *intr, *previntr; 592 int rc = 0; 593 594 /* 595 * Validate arguments 596 */ 597 if (msg_type >= SBBC_MBOX_MSG_TYPES) 598 return (EINVAL); 599 600 /* 601 * Verify that we have already set up the master sbbc 602 */ 603 if (master_iosram == NULL || master_mbox == NULL) 604 return (ENXIO); 605 606 mutex_enter(&master_iosram->iosram_lock); 607 msg_type &= SBBC_MSG_TYPE_MASK; 608 previntr = intr = master_mbox->intrs[msg_type]; 609 610 /* Find the end of the link list */ 611 while (intr != NULL && intr->sbbc_handler != intr_handler) { 612 613 previntr = intr; 614 intr = intr->sbbc_intr_next; 615 } 616 617 /* Return if the handler has been registered */ 618 if (intr != NULL) { 619 mutex_exit(&master_iosram->iosram_lock); 620 return (EBUSY); 621 } 622 623 /* 624 * The requested handler has not been installed. 625 * Allocate some memory. 626 */ 627 intr = kmem_zalloc(sizeof (sbbc_intrs_t), KM_SLEEP); 628 629 intr->sbbc_handler = intr_handler; 630 intr->sbbc_arg = (caddr_t)arg; 631 intr->sbbc_intr_state = state; 632 intr->sbbc_intr_lock = lock; 633 intr->sbbc_intr_next = NULL; 634 /* not registered yet */ 635 intr->registered = 0; 636 637 if (previntr != NULL) 638 previntr->sbbc_intr_next = intr; 639 else 640 master_mbox->intrs[msg_type] = intr; 641 642 /* 643 * register only if the chosen IOSRAM is 644 * initialized, otherwise defer the registration 645 * until IOSRAM initialization. 646 */ 647 if (master_iosram->iosram_sbbc) { 648 if (ddi_add_softintr(master_iosram->iosram_sbbc->dip, 649 DDI_SOFTINT_HIGH, 650 &intr->sbbc_intr_id, NULL, NULL, 651 intr_handler, (caddr_t)arg) != DDI_SUCCESS) { 652 cmn_err(CE_WARN, "Can't add SBBC mailbox softint \n"); 653 rc = ENXIO; 654 } else 655 intr->registered = 1; 656 } else { 657 SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_reg_intr: " 658 "deferring msg=%x registration\n", msg_type); 659 } 660 661 mutex_exit(&master_iosram->iosram_lock); 662 663 return (rc); 664 } 665 666 /* 667 * Unregister a handler for a message type 668 */ 669 int 670 sbbc_mbox_unreg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler) 671 { 672 sbbc_intrs_t *intr, *previntr, *nextintr; 673 674 /* 675 * Verify that we have already set up the master sbbc 676 */ 677 if (master_iosram == NULL || master_mbox == NULL) 678 return (ENXIO); 679 680 msg_type &= SBBC_MSG_TYPE_MASK; 681 682 if (msg_type >= SBBC_MBOX_MSG_TYPES || 683 intr_handler == (sbbc_intrfunc_t)NULL) { 684 685 return (EINVAL); 686 } 687 688 mutex_enter(&master_iosram->iosram_lock); 689 690 previntr = intr = master_mbox->intrs[msg_type]; 691 692 /* 693 * No handlers installed 694 */ 695 if (intr == NULL) { 696 mutex_exit(&master_iosram->iosram_lock); 697 return (EINVAL); 698 } 699 700 while (intr != NULL) { 701 702 /* Save the next pointer */ 703 nextintr = intr->sbbc_intr_next; 704 705 /* Found a match. Remove it from the link list */ 706 if (intr->sbbc_handler == intr_handler) { 707 708 if (intr->sbbc_intr_id) 709 ddi_remove_softintr(intr->sbbc_intr_id); 710 711 kmem_free(intr, sizeof (sbbc_intrs_t)); 712 713 if (previntr != master_mbox->intrs[msg_type]) 714 previntr->sbbc_intr_next = nextintr; 715 else 716 master_mbox->intrs[msg_type] = nextintr; 717 718 break; 719 } 720 721 /* update pointers */ 722 previntr = intr; 723 intr = nextintr; 724 } 725 726 mutex_exit(&master_iosram->iosram_lock); 727 728 return (0); 729 } 730 /* 731 * Interrupt handlers - one for each mailbox 732 * interrupt type 733 */ 734 735 /* 736 * mailbox message received 737 */ 738 static int 739 sbbc_mbox_msgin() 740 { 741 mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock); 742 master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_state = 743 SBBC_INTR_RUNNING; 744 mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock); 745 746 /* 747 * We are only locking the InBox here, not the whole 748 * mailbox. This is based on the assumption of 749 * complete separation of mailboxes - outbox is 750 * read/write, inbox is read-only. 751 * We only ever update the producer for the 752 * outbox and the consumer for the inbox. 753 */ 754 mutex_enter(&master_mbox->mbox_in->mb_lock); 755 756 for (;;) { 757 /* 758 * Get as many incoming messages as possible 759 */ 760 while (sbbc_mbox_recv_msg() == 0) 761 /* empty */; 762 763 /* 764 * send interrupt to SC to let it know that 765 * space is available over here 766 */ 767 (void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN); 768 769 mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR]. 770 mbox_intr_lock); 771 /* 772 * Read the inbox one more time to see if new messages 773 * has come in after we exit the loop. 774 */ 775 if (sbbc_mbox_recv_msg() == 0) { 776 mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR]. 777 mbox_intr_lock); 778 } else { 779 master_mbox->intr_state[MBOX_MSGIN_INTR]. 780 mbox_intr_state = SBBC_INTR_IDLE; 781 mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR]. 782 mbox_intr_lock); 783 break; 784 } 785 } 786 787 mutex_exit(&master_mbox->mbox_in->mb_lock); 788 789 return (DDI_INTR_CLAIMED); 790 } 791 792 /* 793 * mailbox message sent 794 */ 795 static int 796 sbbc_mbox_msgout() 797 { 798 /* 799 * Should never get this 800 */ 801 802 return (DDI_INTR_CLAIMED); 803 } 804 805 /* 806 * space in the inbox 807 */ 808 static int 809 sbbc_mbox_spacein() 810 { 811 /* 812 * Should never get this 813 */ 814 815 return (DDI_INTR_CLAIMED); 816 } 817 818 /* 819 * space in the outbox 820 */ 821 static int 822 sbbc_mbox_spaceout() 823 { 824 /* 825 * cv_broadcast() the threads waiting on the 826 * outbox's mb_full 827 */ 828 829 mutex_enter(&master_mbox->mbox_out->mb_lock); 830 831 cv_broadcast(&master_mbox->mbox_out->mb_full); 832 833 mutex_exit(&master_mbox->mbox_out->mb_lock); 834 835 return (DDI_INTR_CLAIMED); 836 } 837 838 /* 839 * Client Interface 840 * 841 * The main interface will be 842 * 843 * sbbc_mbox_request_response(sbbc_msg_t *request, 844 * sbbc_msg_t *response, time_t wait_time) 845 * 846 * 1) the client calls request_response 847 * 2) a new unique msg ID is assigned for that msg 848 * 3) if there is space available in the outbox 849 * - the request msg is written to the mbox_out mailbox 850 * and the mailbox info updated. 851 * - allocate a sbbc_msg_waiter struct for this 852 * message, initialise the w_cv condvar. 853 * - get the mailbox mbox_wait_lock mutex for this 854 * message type 855 * - the response msg is put on the mbox_wait_list for 856 * that message type to await the SC's response 857 * - wait on the w_cv condvar protected by the 858 * mbox_wait_lock 859 * - SBBC_MAILBOX_OUT interrupt is sent to the SC 860 * 861 * 4) if no space in the outbox, 862 * - the request message blocks waiting 863 * for a SBBC_MAILBOX_SPACE_OUT interrupt 864 * It will block on the mailbox mb_full condvar. 865 * - go to (3) above 866 * 5) When we get a SBBC_MAILBOX_IN interrupt. 867 * - read the message ID of the next message (FIFO) 868 * - find that ID on the wait list 869 * - no wait list entry => unsolicited message. If theres 870 * a handler, trigger it 871 * - if someone is waiting, read the message in from 872 * SRAM, handling fragmentation, wraparound, etc 873 * - if the whole message has been read, signal 874 * the waiter 875 * - read next message until mailbox empty 876 * - send SBBC_MAILBOX_SPACE_IN interrupt to the SC 877 * 878 * 6) If a response is required and none is received, the client 879 * will timeout after <wait_time> seconds and the message 880 * status will be set to ETIMEDOUT. 881 */ 882 int 883 sbbc_mbox_request_response(sbbc_msg_t *request, 884 sbbc_msg_t *response, time_t wait_time) 885 { 886 887 struct sbbc_msg_waiter *waiter; 888 uint_t msg_id; 889 int rc = 0; 890 int flags; 891 uint16_t msg_type; 892 clock_t stop_time; 893 clock_t clockleft; 894 kmutex_t *mbox_wait_lock; 895 kmutex_t *mb_lock; 896 static fn_t f = "sbbc_mbox_request_response"; 897 898 if ((request == NULL) || 899 (request->msg_type.type >= SBBC_MBOX_MSG_TYPES) || 900 ((response != NULL) && 901 (response->msg_type.type >= SBBC_MBOX_MSG_TYPES))) 902 return (EINVAL); 903 904 msg_type = request->msg_type.type; 905 906 /* 907 * Verify that we have already set up the master sbbc 908 */ 909 if (master_mbox == NULL) 910 return (ENXIO); 911 mbox_wait_lock = &master_mbox->mbox_wait_lock[msg_type]; 912 913 flags = WAIT_FOR_REPLY|WAIT_FOR_SPACE; 914 915 /* 916 * We want to place a lower limit on the shortest amount of time we 917 * will wait before timing out while communicating with the SC via 918 * the mailbox. 919 */ 920 if (wait_time < sbbc_mbox_min_timeout) 921 wait_time = sbbc_mbox_default_timeout; 922 923 stop_time = ddi_get_lbolt() + wait_time * drv_usectohz(MICROSEC); 924 925 /* 926 * If there is a message being processed, sleep until it is our turn. 927 */ 928 mutex_enter(&outbox_queue_lock); 929 930 /* 931 * allocate an ID for this message, let it wrap 932 * around transparently. 933 * msg_id == 0 is unsolicited message 934 */ 935 msg_id = ++(master_mbox->mbox_msg_id); 936 if (msg_id == 0) 937 msg_id = ++(master_mbox->mbox_msg_id); 938 939 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n", 940 f, msg_id, request->msg_len); 941 942 /* 943 * A new message can actually grab the lock before the thread 944 * that has just been signaled. Therefore, we need to double 945 * check to make sure that outbox_busy is not already set 946 * after we wake up. 947 * 948 * Potentially this could mean starvation for certain unfortunate 949 * threads that keep getting woken up and putting back to sleep. 950 * But the window of such contention is very small to begin with. 951 */ 952 while (outbox_busy) { 953 954 clockleft = cv_timedwait(&outbox_queue, &outbox_queue_lock, 955 stop_time); 956 957 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up\n", f, msg_id); 958 959 /* 960 * If we have timed out, set status to ETIMEOUT and return. 961 */ 962 if (clockleft < 0) { 963 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n", 964 f, msg_id); 965 cmn_err(CE_NOTE, 966 "Timed out obtaining SBBC outbox lock"); 967 request->msg_status = ETIMEDOUT; 968 if (response != NULL) 969 response->msg_status = ETIMEDOUT; 970 mutex_exit(&outbox_queue_lock); 971 return (ETIMEDOUT); 972 } 973 } 974 975 outbox_busy = 1; 976 mutex_exit(&outbox_queue_lock); 977 978 /* 979 * We are only locking the OutBox from here, not the whole 980 * mailbox. This is based on the assumption of 981 * complete separation of mailboxes - outbox is 982 * read/write, inbox is read-only. 983 * We only ever update the producer for the 984 * outbox and the consumer for the inbox. 985 */ 986 mb_lock = &master_mbox->mbox_out->mb_lock; 987 mutex_enter(mb_lock); 988 989 /* 990 * No response expected ? Just send the message and return 991 */ 992 if (response == NULL) { 993 rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, 994 stop_time); 995 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n", 996 f, msg_id, rc); 997 998 wakeup_next(); 999 1000 mutex_exit(mb_lock); 1001 request->msg_status = rc; 1002 return (rc); 1003 } 1004 1005 /* 1006 * allocate/initialise a waiter 1007 */ 1008 waiter = kmem_zalloc(sizeof (struct sbbc_msg_waiter), KM_NOSLEEP); 1009 1010 if (waiter == (struct sbbc_msg_waiter *)NULL) { 1011 cmn_err(CE_WARN, "SBBC Mailbox can't allocate waiter\n"); 1012 1013 wakeup_next(); 1014 1015 mutex_exit(mb_lock); 1016 return (ENOMEM); 1017 } 1018 1019 waiter->w_id = 0; /* Until we get an ID from the send */ 1020 waiter->w_msg = response; 1021 waiter->w_msg->msg_status = EINPROGRESS; 1022 1023 cv_init(&waiter->w_cv, NULL, CV_DEFAULT, NULL); 1024 1025 rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, stop_time); 1026 1027 wakeup_next(); 1028 1029 if (rc != 0) { 1030 1031 request->msg_status = response->msg_status = rc; 1032 mutex_exit(mb_lock); 1033 1034 /* Free the waiter */ 1035 cv_destroy(&waiter->w_cv); 1036 kmem_free(waiter, sizeof (struct sbbc_msg_waiter)); 1037 1038 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n", 1039 f, msg_id, rc); 1040 1041 return (rc); 1042 } 1043 1044 waiter->w_id = msg_id; 1045 1046 /* 1047 * Lock this waiter list and add the waiter 1048 */ 1049 mutex_enter(mbox_wait_lock); 1050 1051 if (master_mbox->mbox_wait_list[msg_type] == NULL) { 1052 master_mbox->mbox_wait_list[msg_type] = waiter; 1053 waiter->w_next = NULL; 1054 } else { 1055 struct sbbc_msg_waiter *tmp; 1056 tmp = master_mbox->mbox_wait_list[msg_type]; 1057 master_mbox->mbox_wait_list[msg_type] = waiter; 1058 waiter->w_next = tmp; 1059 } 1060 1061 mutex_exit(mb_lock); 1062 1063 /* 1064 * wait here for a response to our message 1065 * holding the mbox_wait_lock for the list ensures 1066 * that the interrupt handler can't get in before 1067 * we block. 1068 * NOTE: We use the request msg_type for the 1069 * the wait_list. This ensures that the 1070 * msg_type won't change. 1071 */ 1072 clockleft = cv_timedwait(&waiter->w_cv, mbox_wait_lock, stop_time); 1073 1074 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n", 1075 f, msg_id); 1076 1077 /* 1078 * If we have timed out, set msg_status to ETIMEDOUT, 1079 * and remove the waiter from the waiter list. 1080 */ 1081 if (clockleft < 0) { 1082 /* 1083 * Remove the waiter from the waiter list. 1084 * If we can't find the waiter in the list, 1085 * 1. msg_status == EINPROGRESS 1086 * It is being processed. We will give it 1087 * a chance to finish. 1088 * 2. msg_status != EINPROGRESS 1089 * It is done processing. We can safely 1090 * remove it. 1091 * If we can find the waiter, it has timed out. 1092 */ 1093 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n", 1094 f, msg_id); 1095 if (mbox_find_waiter(msg_type, msg_id) == NULL) { 1096 if (waiter->w_msg->msg_status == EINPROGRESS) { 1097 SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x " 1098 "complete.\n", f, msg_id); 1099 cv_wait(&waiter->w_cv, mbox_wait_lock); 1100 } 1101 } else { 1102 SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x " 1103 "to ETIMEDOUT\n", f, msg_id); 1104 cmn_err(CE_NOTE, "Timed out waiting for SC response"); 1105 rc = waiter->w_msg->msg_status = ETIMEDOUT; 1106 } 1107 } 1108 1109 /* 1110 * lose the waiter 1111 */ 1112 cv_destroy(&waiter->w_cv); 1113 kmem_free(waiter, sizeof (struct sbbc_msg_waiter)); 1114 1115 mutex_exit(mbox_wait_lock); 1116 1117 return (rc); 1118 1119 } 1120 1121 static void 1122 wakeup_next() 1123 { 1124 /* 1125 * Done sending the current message or encounter an error. 1126 * Wake up the one request in the outbox_queue. 1127 */ 1128 mutex_enter(&outbox_queue_lock); 1129 outbox_busy = 0; 1130 cv_signal(&outbox_queue); 1131 mutex_exit(&outbox_queue_lock); 1132 } 1133 1134 1135 /* ARGSUSED */ 1136 int 1137 sbbc_mbox_send_msg(sbbc_msg_t *msg, int flags, uint_t msg_id, 1138 time_t wait_time, clock_t stop_time) 1139 { 1140 struct sbbc_mbox_header header; 1141 struct sbbc_fragment frag; 1142 int rc = 0; 1143 int bytes_written; 1144 uint32_t intr_enabled; 1145 clock_t clockleft; 1146 static fn_t f = "sbbc_mbox_send_msg"; 1147 1148 /* 1149 * First check that the SC has enabled its mailbox 1150 */ 1151 rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0, 1152 (caddr_t)&intr_enabled, sizeof (intr_enabled)); 1153 1154 if (rc) 1155 return (rc); 1156 1157 if (!(intr_enabled & SBBC_MAILBOX_OUT)) 1158 return (ENOTSUP); 1159 1160 /* 1161 * read the mailbox header 1162 */ 1163 if (rc = mbox_read_header(SBBC_OUTBOX, &header)) 1164 return (rc); 1165 1166 /* 1167 * Allocate/initialise a fragment for this message 1168 */ 1169 frag.f_id = msg_id; 1170 frag.f_type = msg->msg_type; 1171 frag.f_status = 0; 1172 frag.f_total_len = msg->msg_len; 1173 frag.f_frag_offset = 0; 1174 /* 1175 * Throw in the message data 1176 */ 1177 bcopy(&msg->msg_data, &frag.f_data, sizeof (msg->msg_data)); 1178 1179 /* 1180 * If not enough space is available 1181 * write what we can and wait for 1182 * an interrupt to tell us that more 1183 * space is available 1184 */ 1185 1186 bytes_written = 0; 1187 do { 1188 rc = mbox_write(&header, &frag, msg); 1189 1190 if (rc != 0 && rc != ENOSPC) { 1191 return (rc); 1192 } 1193 1194 if (rc == 0) { 1195 /* 1196 * Always tell the SC when there is a message. 1197 * Ignore returned value as not being able to 1198 * signal the SC about space available does 1199 * not stop the SC from processing input. 1200 */ 1201 (void) iosram_send_intr(SBBC_MAILBOX_OUT); 1202 } 1203 1204 bytes_written += frag.f_frag_len; 1205 frag.f_frag_offset += frag.f_frag_len; 1206 if ((bytes_written < msg->msg_len) || (rc == ENOSPC)) { 1207 1208 if (mbox_has_free_space(&header) <= 1209 sizeof (struct sbbc_fragment)) { 1210 1211 int tmprc; 1212 1213 clockleft = cv_timedwait( 1214 &master_mbox->mbox_out->mb_full, 1215 &master_mbox->mbox_out->mb_lock, 1216 stop_time); 1217 1218 /* Return ETIMEDOUT if we timed out */ 1219 if (clockleft < 0) { 1220 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x " 1221 "has timed out\n", f, msg_id); 1222 cmn_err(CE_NOTE, 1223 "Timed out sending message " 1224 "to SC"); 1225 return (ETIMEDOUT); 1226 } 1227 1228 /* Read updated header from IOSRAM */ 1229 if (tmprc = mbox_read_header(SBBC_OUTBOX, 1230 &header)) { 1231 1232 return (tmprc); 1233 } 1234 } 1235 } 1236 1237 SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, " 1238 "msg_len = 0x%x\n", f, 1239 msg_id, bytes_written, msg->msg_len); 1240 } while ((bytes_written < msg->msg_len) || (rc == ENOSPC)); 1241 1242 /* 1243 * this could be a spurious interrupt 1244 * as the SC may be merrily readings its 1245 * mail even as send, but what can you do ? No 1246 * synchronization method between SC <-> OS 1247 * SRAM data eaters means that this is inevitable. 1248 * It would take a bigger brain to fix this. 1249 * 1250 */ 1251 (void) iosram_send_intr(SBBC_MAILBOX_OUT); 1252 1253 return (rc); 1254 } 1255 1256 1257 /* 1258 * get next message 1259 * Read the next message from SRAM 1260 * Check if theres an entry on the wait queue 1261 * for this message 1262 * If yes, read the message in and signal 1263 * the waiter (if all the message has been received) 1264 * No, its unsolicited, if theres a handler installed for 1265 * this message type trigger it, otherwise toss 1266 * the message 1267 */ 1268 int 1269 sbbc_mbox_recv_msg() 1270 { 1271 struct sbbc_mbox_header header; 1272 struct sbbc_fragment frag; 1273 sbbc_msg_t tmpmsg; /* Temporary msg storage */ 1274 int rc = 0, i, first_hdlr, last_hdlr; 1275 uint32_t intr_enabled; 1276 sbbc_intrs_t *intr; 1277 struct sbbc_msg_waiter *waiter; 1278 uint16_t type; /* frag.f_type.type */ 1279 uint32_t f_id; /* frag.f_id */ 1280 uint32_t f_frag_offset, f_frag_len; 1281 kmutex_t *mbox_wait_lock; 1282 static fn_t f = "sbbc_mbox_recv_msg"; 1283 1284 /* 1285 * First check that the OS has enabled its mailbox 1286 */ 1287 rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0, 1288 (caddr_t)&intr_enabled, sizeof (intr_enabled)); 1289 1290 if (rc) { 1291 return (rc); 1292 } 1293 1294 if (!(intr_enabled & SBBC_MAILBOX_IN)) 1295 return (ENOTSUP); 1296 1297 /* 1298 * read the mailbox header 1299 */ 1300 if (rc = mbox_read_header(SBBC_INBOX, &header)) 1301 return (rc); 1302 1303 /* 1304 * check if any messages available. If 1305 * consumer == producer then no more 1306 * messages 1307 */ 1308 if ((header.mailboxes[SBBC_INBOX].mbox_consumer == 1309 header.mailboxes[SBBC_INBOX].mbox_producer)) { 1310 1311 return (-1); 1312 } 1313 1314 /* 1315 * read the fragment header for this message 1316 */ 1317 if (rc = mbox_read_frag(&header, &frag)) { 1318 1319 return (rc); 1320 } 1321 1322 /* Save to local variable for easy reading */ 1323 type = frag.f_type.type; 1324 f_id = frag.f_id; 1325 1326 SGSBBC_DBG_MBOX("%s: f_id = 0x%x\n", f, f_id); 1327 1328 /* 1329 * check the message type. If its invalid, we will 1330 * just toss the message 1331 */ 1332 if (type >= SBBC_MBOX_MSG_TYPES) { 1333 goto done; 1334 } 1335 1336 /* 1337 * if theres no waiters for this message type, and theres 1338 * no message handler installed, toss it. 1339 * 1340 * Unsolicited messages (f_id == 0) are tricky because we won't know 1341 * when the handler has finished so that we can 1342 * remove the message, so, given the small brains in operation 1343 * here, what we do is restrict junk mail to zero-length 1344 * messages, then we allocate a fragment using kmem, 1345 * make a copy of the fragment in this memory, 1346 * pass this pointer to the fragment, then skip the message. 1347 * So even if there is data associated with the junkmail, 1348 * the message handler doesn't get to see it 1349 * We expect the mesaage handler to free the memory. 1350 */ 1351 if (type == SBBC_BROADCAST_MSG) { 1352 /* 1353 * Broadcast message, trigger all handlers 1354 */ 1355 first_hdlr = 0; 1356 last_hdlr = SBBC_MBOX_MSG_TYPES - 1; 1357 } else if ((master_mbox->mbox_wait_list[type] == NULL) || (f_id == 0)) { 1358 /* 1359 * Theres no waiters, or its unsolicited anyway 1360 */ 1361 first_hdlr = last_hdlr = type; 1362 } else { 1363 /* 1364 * check the fragment message type, look at the wait list for 1365 * that type to find its associated message 1366 * 1367 * First find the message. If we get it, take it off 1368 * the waiter list and read the data. We will 1369 * put it back on the list if necessary. 1370 * This avoids the problem of a second message-in 1371 * interrupt playing with this waiter. 1372 * This will cut down on mutex spinning on the wait 1373 * list locks, also, expect the next fragment to be 1374 * for this messageso we might as well have it at the 1375 * start of the list. 1376 * 1377 * its possible that a return message has a different type, 1378 * (possible but not recommended!). So, if we don't find 1379 * it on the list pointed to by the request type, 1380 * go look at all the other lists 1381 */ 1382 1383 mbox_wait_lock = &master_mbox->mbox_wait_lock[type]; 1384 1385 mutex_enter(mbox_wait_lock); 1386 if ((waiter = mbox_find_waiter(type, f_id)) == NULL) { 1387 for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) { 1388 if (i == type) 1389 continue; 1390 if ((waiter = mbox_find_waiter(i, f_id)) 1391 != NULL) 1392 break; 1393 } 1394 } 1395 mutex_exit(mbox_wait_lock); 1396 1397 if (waiter == NULL) { 1398 rc = -1; 1399 /* 1400 * there's no waiter for this message, but that 1401 * could mean that this message is the start of 1402 * a send/receive to us, and every 'first' request 1403 * must by definition be unsolicited, 1404 * so trigger the handler 1405 */ 1406 first_hdlr = last_hdlr = type; 1407 } else { 1408 SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, " 1409 "msg_len = 0x%x\n", 1410 f, f_id, waiter->w_id, 1411 waiter->w_msg->msg_len); 1412 1413 rc = mbox_read(&header, &frag, waiter->w_msg); 1414 1415 SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, " 1416 "len = 0x%x, total_len = 0x%x\n", 1417 f, frag.f_id, frag.f_frag_offset, 1418 frag.f_frag_len, frag.f_total_len); 1419 1420 if (rc || ((frag.f_frag_offset + frag.f_frag_len) == 1421 frag.f_total_len)) { 1422 /* 1423 * failed or all the message has been read in 1424 */ 1425 mutex_enter(mbox_wait_lock); 1426 waiter->w_msg->msg_status = (rc == ENOMEM)? 1427 rc : frag.f_status; 1428 SGSBBC_DBG_MBOX("%s: msg_status = %d\n", 1429 f, waiter->w_msg->msg_status); 1430 cv_signal(&waiter->w_cv); 1431 mutex_exit(mbox_wait_lock); 1432 1433 } else { 1434 /* 1435 * back on the wait list 1436 */ 1437 mutex_enter(mbox_wait_lock); 1438 if (waiter->w_msg->msg_status == ETIMEDOUT) { 1439 cv_signal(&waiter->w_cv); 1440 mutex_exit(mbox_wait_lock); 1441 goto done; 1442 } 1443 1444 if (master_mbox->mbox_wait_list[type] == NULL) { 1445 master_mbox->mbox_wait_list[type] = 1446 waiter; 1447 waiter->w_next = NULL; 1448 } else { 1449 struct sbbc_msg_waiter *tmp; 1450 tmp = master_mbox->mbox_wait_list[type]; 1451 master_mbox->mbox_wait_list[type] = 1452 waiter; 1453 waiter->w_next = tmp; 1454 } 1455 mutex_exit(mbox_wait_lock); 1456 } 1457 goto done; 1458 } 1459 } 1460 1461 /* 1462 * Set msg_len to f_frag_len so msg_buf will be large enough 1463 * to contain what is in the fragment. 1464 */ 1465 f_frag_len = tmpmsg.msg_len = frag.f_frag_len; 1466 /* 1467 * Save the f_frag_offset for copying into client's space. 1468 * Set frag.f_frag_offset to 0 so we don't have to allocate 1469 * too much space for reading in the message. 1470 */ 1471 f_frag_offset = frag.f_frag_offset; 1472 frag.f_frag_offset = 0; 1473 1474 /* Allocate space for msg_buf */ 1475 if (f_frag_len != 0 && (tmpmsg.msg_buf = 1476 kmem_alloc(f_frag_len, KM_NOSLEEP)) == NULL) { 1477 1478 rc = ENOMEM; 1479 cmn_err(CE_WARN, "Can't allocate memory" 1480 " for unsolicited messages\n"); 1481 } else { 1482 /* Save the incoming message in tmpmsg */ 1483 rc = mbox_read(&header, &frag, &tmpmsg); 1484 1485 for (i = first_hdlr; rc == 0 && i <= last_hdlr; i++) { 1486 1487 intr = master_mbox->intrs[i]; 1488 if ((intr == NULL) || (intr->sbbc_intr_id == 0)) { 1489 continue; 1490 } 1491 1492 while (intr != NULL) { 1493 /* 1494 * If the client has allocated enough space 1495 * for incoming message, copy into the 1496 * client buffer. 1497 */ 1498 sbbc_msg_t *arg = (sbbc_msg_t *)intr->sbbc_arg; 1499 if (arg != (void *)NULL) { 1500 if (arg->msg_len >= frag.f_total_len) { 1501 if (f_frag_len > 0) 1502 bcopy(tmpmsg.msg_buf, 1503 arg->msg_buf + 1504 f_frag_offset, 1505 f_frag_len); 1506 } else { 1507 arg->msg_status = ENOMEM; 1508 } 1509 } 1510 1511 /* 1512 * Only trigger the interrupt when we 1513 * have received the whole message. 1514 */ 1515 if (f_frag_offset + f_frag_len == 1516 frag.f_total_len) { 1517 1518 ddi_trigger_softintr( 1519 intr->sbbc_intr_id); 1520 } 1521 intr = intr->sbbc_intr_next; 1522 } 1523 } 1524 1525 if (f_frag_len != 0) { 1526 /* Don't forget to free the buffer */ 1527 kmem_free(tmpmsg.msg_buf, f_frag_len); 1528 } 1529 } 1530 done: 1531 mbox_skip_next_msg(&header); 1532 return (rc); 1533 } 1534 1535 /* 1536 * available free space in the outbox 1537 */ 1538 static int 1539 mbox_has_free_space(struct sbbc_mbox_header *header) 1540 { 1541 uint32_t space = 0; 1542 1543 ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock)); 1544 1545 if (header->mailboxes[SBBC_OUTBOX].mbox_producer == 1546 header->mailboxes[SBBC_OUTBOX].mbox_consumer) { 1547 /* 1548 * mailbox is empty 1549 */ 1550 space += header->mailboxes[SBBC_OUTBOX].mbox_len - 1551 header->mailboxes[SBBC_OUTBOX].mbox_producer; 1552 space += 1553 header->mailboxes[SBBC_OUTBOX].mbox_producer; 1554 } else if (header->mailboxes[SBBC_OUTBOX].mbox_producer > 1555 header->mailboxes[SBBC_OUTBOX].mbox_consumer) { 1556 space += header->mailboxes[SBBC_OUTBOX].mbox_len - 1557 header->mailboxes[SBBC_OUTBOX].mbox_producer; 1558 space += header->mailboxes[SBBC_OUTBOX].mbox_consumer; 1559 } else { 1560 /* 1561 * mailbox wrapped around 1562 */ 1563 space += header->mailboxes[SBBC_OUTBOX].mbox_consumer - 1564 header->mailboxes[SBBC_OUTBOX].mbox_producer; 1565 } 1566 1567 /* 1568 * Need to make sure that the mailbox never 1569 * gets completely full, as consumer == producer is 1570 * our test for empty, so we drop MBOX_ALIGN_BYTES. 1571 */ 1572 1573 if (space >= MBOX_ALIGN_BYTES) 1574 space -= MBOX_ALIGN_BYTES; 1575 else 1576 space = 0; 1577 1578 return (space); 1579 1580 } 1581 /* 1582 * Write the data to IOSRAM 1583 * Update the SRAM mailbox header 1584 * Update the local mailbox pointers 1585 * Only write a single fragment. If possible, 1586 * put the whole message into a fragment. 1587 * 1588 * Note: We assume that there is no 'max' message 1589 * size. We will just keep fragmenting. 1590 * Note: We always write to SBBC_OUTBOX and 1591 * read from SBBC_INBOX 1592 * 1593 * If we get an error at any time, return immediately 1594 * without updating the mailbox header in SRAM 1595 */ 1596 static int 1597 mbox_write(struct sbbc_mbox_header *header, 1598 struct sbbc_fragment *frag, sbbc_msg_t *msg) 1599 { 1600 int bytes_written, bytes_remaining, free_space; 1601 int rc = 0; 1602 caddr_t src; 1603 uint32_t sram_dst; 1604 int space_at_end, space_at_start; 1605 uint32_t mbox_offset, mbox_len; 1606 uint32_t mbox_producer, mbox_consumer; 1607 uint32_t f_total_len, f_frag_offset; 1608 uint32_t frag_header_size; 1609 static fn_t f = "mbox_write"; 1610 1611 ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock)); 1612 1613 /* 1614 * Save to local variables to make code more readable 1615 */ 1616 mbox_offset = header->mailboxes[SBBC_OUTBOX].mbox_offset; 1617 mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len; 1618 mbox_producer = header->mailboxes[SBBC_OUTBOX].mbox_producer; 1619 mbox_consumer = header->mailboxes[SBBC_OUTBOX].mbox_consumer; 1620 f_total_len = frag->f_total_len; 1621 f_frag_offset = frag->f_frag_offset; 1622 frag_header_size = sizeof (struct sbbc_fragment); 1623 1624 SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, " 1625 "mbox_producer = 0x%x\n", f, mbox_consumer, mbox_producer); 1626 1627 /* 1628 * Write pointer in SRAM 1629 */ 1630 sram_dst = mbox_offset + mbox_producer; 1631 1632 /* 1633 * NB We assume that the consumer stays constant 1634 * during the write. It may not necessarily 1635 * be the case but it won't cause us any problems, just means 1636 * we fragment more than is absolutely necessary 1637 * 1638 * possible cases 1639 * 1) consumer == producer, mailbox empty 1640 * space_at_end == mailbox end - producer 1641 * space_at_start == producer - MBOX_ALIGN_BYTES 1642 * 2) producer < consumer 1643 * space_at_end = (consumer - producer - MBOX_ALIGN_BYTES) 1644 * space_at_start == 0 1645 * 3) producer > consumer 1646 * space_at_end = mailbox end - producer 1647 * space_at_start = consumer - MBOX_ALIGN_BYTES 1648 * 1649 * (space - MBOX_ALIGN_BYTES) because we need to avoid the 1650 * scenario where the producer wraps around completely and 1651 * producer == consumer, as this is our test for 'empty'. 1652 * Also we want it to be 8-byte aligned. 1653 * Note: start is assumed = 0 1654 */ 1655 if (mbox_producer < mbox_consumer) { 1656 space_at_end = mbox_consumer - mbox_producer - MBOX_ALIGN_BYTES; 1657 if (space_at_end < 0) 1658 space_at_end = 0; 1659 space_at_start = 0; 1660 } else { 1661 space_at_end = mbox_len - mbox_producer; 1662 if (mbox_consumer == 0) 1663 space_at_end -= MBOX_ALIGN_BYTES; 1664 space_at_start = mbox_consumer - MBOX_ALIGN_BYTES; 1665 if (space_at_start < 0) 1666 space_at_start = 0; 1667 } 1668 1669 SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n", 1670 f, space_at_end, space_at_start); 1671 1672 free_space = space_at_end + space_at_start; 1673 1674 if (free_space < frag_header_size) { 1675 /* 1676 * can't even write a fragment header, so just return 1677 * the caller will block waiting for space 1678 */ 1679 frag->f_frag_len = 0; 1680 return (ENOSPC); 1681 } 1682 1683 /* 1684 * How many bytes will be in the fragment ? 1685 */ 1686 bytes_remaining = f_total_len - f_frag_offset; 1687 frag->f_frag_len = min(bytes_remaining, free_space - frag_header_size); 1688 1689 SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n", 1690 f, sram_dst); 1691 1692 /* 1693 * we can write the fragment header and some data 1694 * First, the fragment header 1695 */ 1696 if (space_at_end >= frag_header_size) { 1697 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, (caddr_t)frag, 1698 frag_header_size); 1699 if (rc) 1700 return (rc); 1701 1702 sram_dst = (uint32_t)(sram_dst + frag_header_size); 1703 /* 1704 * Wrap around if we reach the end 1705 */ 1706 if (sram_dst >= (mbox_len + mbox_offset)) { 1707 sram_dst = mbox_offset; 1708 } 1709 space_at_end -= frag_header_size; 1710 } else { 1711 /* wraparound */ 1712 if (space_at_end) { 1713 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, 1714 (caddr_t)frag, space_at_end); 1715 if (rc) 1716 return (rc); 1717 sram_dst = (uint32_t)mbox_offset; 1718 } 1719 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, 1720 (caddr_t)((caddr_t)frag + space_at_end), 1721 (frag_header_size - space_at_end)); 1722 if (rc) 1723 return (rc); 1724 sram_dst += frag_header_size - space_at_end; 1725 space_at_start -= (frag_header_size - space_at_end); 1726 space_at_end = 0; 1727 } 1728 1729 SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n", 1730 f, space_at_end, space_at_start); 1731 1732 /* 1733 * Now the fragment data 1734 */ 1735 free_space -= frag_header_size; 1736 src = (caddr_t)(msg->msg_buf + f_frag_offset); 1737 bytes_written = 0; 1738 if (space_at_end) { 1739 SGSBBC_DBG_MBOX("%s: writing data:sram_dst = 0x%x, " 1740 "bytes_remaining = 0x%x\n", 1741 f, sram_dst, bytes_remaining); 1742 1743 if (space_at_end < bytes_remaining) 1744 bytes_written = space_at_end; 1745 else 1746 bytes_written = bytes_remaining; 1747 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src, 1748 bytes_written); 1749 if (rc) 1750 return (rc); 1751 1752 sram_dst = (uint32_t)(sram_dst + bytes_written); 1753 /* 1754 * Wrap around if we reach the end 1755 */ 1756 if (sram_dst >= (mbox_len + mbox_offset)) { 1757 sram_dst = mbox_offset; 1758 } 1759 src = (caddr_t)(src + bytes_written); 1760 bytes_remaining -= bytes_written; 1761 } 1762 1763 if ((bytes_remaining > 0) && space_at_start) { 1764 SGSBBC_DBG_MBOX("%s: writing the rest:sram_dst = 0x%x, " 1765 "bytes_remaining = 0x%x\n", 1766 f, sram_dst, bytes_remaining); 1767 if (space_at_start < bytes_remaining) { 1768 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src, 1769 space_at_start); 1770 bytes_written += space_at_start; 1771 } else { 1772 rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src, 1773 bytes_remaining); 1774 bytes_written += bytes_remaining; 1775 } 1776 if (rc) 1777 return (rc); 1778 } 1779 1780 frag->f_frag_len = bytes_written; 1781 1782 /* 1783 * update header->mbox_producer (bytes_written + frag_size) 1784 */ 1785 sram_dst = mbox_producer + bytes_written + frag_header_size; 1786 if (sram_dst >= mbox_len) { 1787 sram_dst = sram_dst % mbox_len; 1788 } 1789 1790 SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, " 1791 "bytes_written = 0x%x\n", f, sram_dst, bytes_written); 1792 1793 header->mailboxes[SBBC_OUTBOX].mbox_producer = sram_dst; 1794 1795 mbox_update_header(SBBC_OUTBOX, header); 1796 1797 1798 return (rc); 1799 } 1800 1801 1802 /* 1803 * Get the next frag from IOSRAM. 1804 * Write it to the corresponding msg buf. 1805 * The caller must update the SRAM pointers etc. 1806 */ 1807 static int 1808 mbox_read(struct sbbc_mbox_header *header, 1809 struct sbbc_fragment *frag, sbbc_msg_t *msg) 1810 { 1811 int rc = 0; 1812 uint32_t sram_src, sram_end; 1813 caddr_t msg_buf; 1814 int bytes_at_start, bytes_at_end; 1815 int bytes_to_read; 1816 uint32_t frag_header_size, frag_total_size; 1817 uint32_t f_frag_offset, f_frag_len; 1818 uint32_t mbox_producer, mbox_consumer; 1819 uint32_t mbox_len, mbox_offset; 1820 static fn_t f = "mbox_read"; 1821 1822 ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock)); 1823 1824 /* 1825 * Save to local variables to make code more readable 1826 */ 1827 mbox_producer = header->mailboxes[SBBC_INBOX].mbox_producer; 1828 mbox_consumer = header->mailboxes[SBBC_INBOX].mbox_consumer; 1829 mbox_len = header->mailboxes[SBBC_INBOX].mbox_len; 1830 mbox_offset = header->mailboxes[SBBC_INBOX].mbox_offset; 1831 frag_header_size = sizeof (struct sbbc_fragment); 1832 f_frag_offset = frag->f_frag_offset; 1833 f_frag_len = frag->f_frag_len; 1834 frag_total_size = frag_header_size + f_frag_len; 1835 1836 /* 1837 * If the message buffer size is smaller than the fragment 1838 * size, return an error. 1839 */ 1840 if (msg->msg_len < f_frag_len) { 1841 rc = ENOMEM; 1842 goto done; 1843 } 1844 1845 msg_buf = (caddr_t)(msg->msg_buf + f_frag_offset); 1846 1847 /* 1848 * Throw in the message data 1849 */ 1850 bcopy(&frag->f_data, &msg->msg_data, sizeof (msg->msg_data)); 1851 1852 /* 1853 * We have it all, waiter, message, so lets 1854 * go get that puppy! 1855 * Message could be in one or two chunks - 1856 * consumer < producer: 1 chunk, (producer - consumer) 1857 * consumer > producer: 2 chunks, (end - consumer) 1858 * (producer - start) 1859 */ 1860 sram_end = (uint32_t)(mbox_offset + mbox_len); 1861 sram_src = (uint32_t)(mbox_offset + mbox_consumer + frag_header_size); 1862 1863 /* 1864 * wraparound 1865 */ 1866 if (sram_src >= sram_end) 1867 sram_src -= mbox_len; 1868 1869 /* 1870 * find where the data is 1871 * possible cases 1872 * 1) consumer == producer, mailbox empty 1873 * error 1874 * 2) producer < consumer 1875 * bytes_at_end = mailbox end - consumer 1876 * bytes_at_start = producer 1877 * 3) producer > consumer 1878 * bytes_at_end = producer - consumer 1879 * bytes_at_start = 0 1880 */ 1881 1882 SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, " 1883 "frag_len = 0x%x\n", 1884 f, mbox_consumer, mbox_producer, f_frag_len); 1885 1886 if (mbox_producer == mbox_consumer) { 1887 bytes_at_end = bytes_at_start = 0; 1888 } else if (mbox_producer < mbox_consumer) { 1889 bytes_at_end = mbox_len - mbox_consumer; 1890 bytes_at_start = mbox_producer; 1891 } else { 1892 bytes_at_end = mbox_producer - mbox_consumer; 1893 bytes_at_start = 0; 1894 } 1895 1896 SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, " 1897 "bytes_at_start = 0x%x\n", f, bytes_at_end, bytes_at_start); 1898 1899 if ((bytes_at_end + bytes_at_start) < frag_total_size) { 1900 1901 /* 1902 * mailbox is corrupt 1903 * but what to do ? 1904 */ 1905 cmn_err(CE_PANIC, "Corrupt INBOX!\n" 1906 "producer = %x, consumer = %x, bytes_at_start = %x, " 1907 "bytes_at_end = %x\n", mbox_producer, mbox_consumer, 1908 bytes_at_start, bytes_at_end); 1909 } 1910 1911 /* 1912 * If bytes_at_end is greater than header size, read the 1913 * part at the end of the mailbox, and then update the 1914 * pointers and bytes_to_read. 1915 */ 1916 if (bytes_at_end > frag_header_size) { 1917 /* 1918 * We are only interested in the data segment. 1919 */ 1920 bytes_at_end -= frag_header_size; 1921 bytes_to_read = (bytes_at_end >= f_frag_len)? 1922 f_frag_len : bytes_at_end; 1923 SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, " 1924 "bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read); 1925 rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf, 1926 bytes_to_read); 1927 if (rc) { 1928 goto done; 1929 } 1930 1931 /* 1932 * Update pointers in SRAM and message buffer. 1933 */ 1934 sram_src = (uint32_t)mbox_offset; 1935 msg_buf = (caddr_t)(msg_buf + bytes_to_read); 1936 bytes_to_read = f_frag_len - bytes_to_read; 1937 } else { 1938 bytes_to_read = f_frag_len; 1939 } 1940 1941 /* 1942 * wraparound to start of mailbox 1943 */ 1944 if (bytes_to_read > 0) { 1945 SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, " 1946 "bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read); 1947 rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf, 1948 bytes_to_read); 1949 } 1950 1951 done: 1952 msg->msg_bytes += f_frag_len; 1953 1954 return (rc); 1955 } 1956 1957 /* 1958 * move past the next message in the inbox 1959 */ 1960 static void 1961 mbox_skip_next_msg(struct sbbc_mbox_header *header) 1962 { 1963 struct sbbc_fragment frag; 1964 uint32_t next_msg; 1965 1966 ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock)); 1967 1968 if (mbox_read_frag(header, &frag)) { 1969 cmn_err(CE_PANIC, "INBOX is Corrupt !\n"); 1970 } 1971 1972 /* 1973 * Move on to the next message 1974 */ 1975 next_msg = header->mailboxes[SBBC_INBOX].mbox_consumer; 1976 next_msg += sizeof (struct sbbc_fragment); 1977 next_msg += frag.f_frag_len; 1978 if (next_msg >= header->mailboxes[SBBC_INBOX].mbox_len) { 1979 next_msg = (next_msg + 1980 header->mailboxes[SBBC_INBOX].mbox_len) % 1981 header->mailboxes[SBBC_INBOX].mbox_len; 1982 } 1983 header->mailboxes[SBBC_INBOX].mbox_consumer = 1984 next_msg; 1985 1986 mbox_update_header(SBBC_INBOX, header); 1987 1988 return; 1989 1990 } 1991 1992 static struct sbbc_msg_waiter * 1993 mbox_find_waiter(uint16_t msg_type, uint32_t msg_id) 1994 { 1995 struct sbbc_msg_waiter *waiter, *prev; 1996 1997 prev = NULL; 1998 for (waiter = master_mbox->mbox_wait_list[msg_type]; 1999 waiter != NULL; waiter = waiter->w_next) { 2000 2001 if (waiter->w_id == msg_id) { 2002 if (prev != NULL) { 2003 prev->w_next = waiter->w_next; 2004 } else { 2005 master_mbox->mbox_wait_list[msg_type] = 2006 waiter->w_next; 2007 } 2008 break; 2009 } 2010 prev = waiter; 2011 } 2012 2013 return (waiter); 2014 } 2015 2016 static int 2017 mbox_read_header(uint32_t mailbox, struct sbbc_mbox_header *header) 2018 { 2019 struct sbbc_mbox_header *hd; 2020 uint32_t offset; 2021 int rc; 2022 2023 /* 2024 * Initialize a sbbc_mbox_header pointer to 0 so that we 2025 * can use it to calculate the offsets of fields inside 2026 * the structure. 2027 */ 2028 hd = (struct sbbc_mbox_header *)0; 2029 2030 if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)header, 2031 sizeof (struct sbbc_mbox_header))) 2032 return (rc); 2033 2034 /* 2035 * Since the header is read in a byte-by-byte fashion 2036 * using ddi_rep_get8, we need to re-read the producer 2037 * or consumer pointer as integer in case it has changed 2038 * after part of the previous value has been read. 2039 */ 2040 switch (mailbox) { 2041 2042 case SBBC_INBOX: 2043 offset = (uint32_t)(uintptr_t) 2044 (&hd->mailboxes[SBBC_INBOX].mbox_producer); 2045 rc = iosram_read(SBBC_MAILBOX_KEY, offset, 2046 (caddr_t)&header->mailboxes[SBBC_INBOX].mbox_producer, 2047 sizeof (uint32_t)); 2048 break; 2049 case SBBC_OUTBOX: 2050 offset = (uint32_t)(uintptr_t) 2051 (&hd->mailboxes[SBBC_OUTBOX].mbox_consumer); 2052 rc = iosram_read(SBBC_MAILBOX_KEY, offset, 2053 (caddr_t)&header->mailboxes[SBBC_OUTBOX].mbox_consumer, 2054 sizeof (uint32_t)); 2055 break; 2056 default: 2057 cmn_err(CE_PANIC, "Invalid Mbox header type\n"); 2058 break; 2059 2060 } 2061 2062 return (rc); 2063 } 2064 2065 /* 2066 * There are only two fields updated by the domain, 2067 * the inbox consumer field and the outbox producer 2068 * field. These fields are protected by the respective 2069 * mbox_{in|out}->mb_lock so that accesses will 2070 * be serialised. The only coherency issue is writing 2071 * back the header, so we do it here after grabbing 2072 * the global mailbox lock. 2073 */ 2074 static void 2075 mbox_update_header(uint32_t mailbox, struct sbbc_mbox_header *header) 2076 { 2077 struct sbbc_mbox_header *hd; 2078 uint32_t value, offset, mbox_len; 2079 2080 /* 2081 * Initialize a sbbc_mbox_header pointer to 0 so that we 2082 * can use it to calculate the offsets of fields inside 2083 * the structure. 2084 */ 2085 hd = (struct sbbc_mbox_header *)0; 2086 2087 switch (mailbox) { 2088 2089 case SBBC_INBOX: 2090 value = header->mailboxes[SBBC_INBOX].mbox_consumer; 2091 offset = (uint32_t)(uintptr_t) 2092 (&hd->mailboxes[SBBC_INBOX].mbox_consumer); 2093 2094 mbox_len = header->mailboxes[SBBC_INBOX].mbox_len; 2095 break; 2096 case SBBC_OUTBOX: 2097 value = header->mailboxes[SBBC_OUTBOX].mbox_producer; 2098 offset = (uint32_t)(uintptr_t) 2099 (&hd->mailboxes[SBBC_OUTBOX].mbox_producer); 2100 mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len; 2101 break; 2102 default: 2103 cmn_err(CE_PANIC, "Invalid Mbox header type\n"); 2104 break; 2105 2106 } 2107 2108 /* 2109 * If the last read/write would cause the next read/write 2110 * to be unaligned, we skip on modulo MBOX_ALIGN_BYTES. 2111 * This is OK because all the mailbox handlers will 2112 * conform to this. 2113 */ 2114 if (value % MBOX_ALIGN_BYTES) { 2115 value += (MBOX_ALIGN_BYTES - (value % MBOX_ALIGN_BYTES)); 2116 value %= mbox_len; 2117 } 2118 2119 if (iosram_write(SBBC_MAILBOX_KEY, offset, (caddr_t)&value, 2120 sizeof (uint32_t))) { 2121 cmn_err(CE_PANIC, "Mailbox Corrupt ! \n"); 2122 } 2123 2124 /* 2125 * Update internal pointers so they won't be out of sync with 2126 * the values in IOSRAM. 2127 */ 2128 switch (mailbox) { 2129 2130 case SBBC_INBOX: 2131 header->mailboxes[SBBC_INBOX].mbox_consumer = value; 2132 break; 2133 case SBBC_OUTBOX: 2134 header->mailboxes[SBBC_OUTBOX].mbox_producer = value; 2135 break; 2136 } 2137 } 2138 2139 static int 2140 mbox_read_frag(struct sbbc_mbox_header *header, 2141 struct sbbc_fragment *frag) 2142 { 2143 int rc = 0; 2144 uint32_t sram_src, bytes; 2145 caddr_t dst; 2146 2147 ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock)); 2148 /* 2149 * read the fragment header for this message 2150 */ 2151 sram_src = (uint32_t)(header->mailboxes[SBBC_INBOX].mbox_offset + 2152 header->mailboxes[SBBC_INBOX].mbox_consumer); 2153 2154 /* 2155 * wraparound ? 2156 */ 2157 if ((header->mailboxes[SBBC_INBOX].mbox_consumer + 2158 sizeof (struct sbbc_fragment)) >= 2159 header->mailboxes[SBBC_INBOX].mbox_len) { 2160 2161 dst = (caddr_t)frag; 2162 bytes = header->mailboxes[SBBC_INBOX].mbox_len - 2163 header->mailboxes[SBBC_INBOX].mbox_consumer; 2164 2165 if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, dst, bytes)) { 2166 return (rc); 2167 } 2168 2169 dst += bytes; 2170 sram_src = header->mailboxes[SBBC_INBOX].mbox_offset; 2171 bytes = (header->mailboxes[SBBC_INBOX].mbox_consumer + 2172 sizeof (struct sbbc_fragment)) % 2173 header->mailboxes[SBBC_INBOX].mbox_len; 2174 2175 if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, 2176 dst, bytes)) { 2177 return (rc); 2178 } 2179 } else { 2180 if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, (caddr_t)frag, 2181 sizeof (struct sbbc_fragment))) { 2182 return (rc); 2183 } 2184 } 2185 2186 return (0); 2187 } 2188 2189 2190 /* 2191 * This function is triggered by a soft interrupt and it's purpose is to call 2192 * to kadmin() to shutdown the Domain. 2193 */ 2194 /*ARGSUSED0*/ 2195 static uint_t 2196 sbbc_do_fast_shutdown(char *arg) 2197 { 2198 (void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred); 2199 2200 /* 2201 * If kadmin fails for some reason then we bring the system down 2202 * via power_down(), or failing that using halt(). 2203 */ 2204 power_down("kadmin() failed, trying power_down()"); 2205 2206 halt("power_down() failed, trying halt()"); 2207 2208 /* 2209 * We should never make it this far, so something must have gone 2210 * horribly, horribly wrong. 2211 */ 2212 /*NOTREACHED*/ 2213 return (DDI_INTR_UNCLAIMED); 2214 } 2215 2216 2217 /* 2218 * This function handles unsolicited PANIC_SHUTDOWN events 2219 */ 2220 static uint_t 2221 sbbc_panic_shutdown_handler(char *arg) 2222 { 2223 static fn_t f = "sbbc_panic_shutdown_handler()"; 2224 2225 sg_panic_shutdown_t *payload = NULL; 2226 sbbc_msg_t *msg = NULL; 2227 2228 if (arg == NULL) { 2229 SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f); 2230 return (DDI_INTR_UNCLAIMED); 2231 } 2232 2233 msg = (sbbc_msg_t *)arg; 2234 2235 if (msg->msg_buf == NULL) { 2236 SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f); 2237 return (DDI_INTR_UNCLAIMED); 2238 } 2239 2240 payload = (sg_panic_shutdown_t *)msg->msg_buf; 2241 2242 switch (*payload) { 2243 case SC_EVENT_PANIC_ENV: 2244 2245 /* 2246 * Let the user know why the domain is going down. 2247 */ 2248 cmn_err(CE_WARN, "%s", PANIC_ENV_EVENT_MSG); 2249 2250 /* 2251 * trigger sbbc_do_fast_shutdown(). 2252 */ 2253 ddi_trigger_softintr(panic_softintr_id); 2254 2255 /*NOTREACHED*/ 2256 break; 2257 2258 case SC_EVENT_PANIC_KEYSWITCH: 2259 /* 2260 * The SC warns a user if they try a destructive keyswitch 2261 * command on a Domain which is currently running Solaris. 2262 * If the user chooses to continue despite our best advise 2263 * then we bring down the Domain immediately without trying 2264 * to shut the system down gracefully. 2265 */ 2266 break; 2267 2268 default: 2269 SGSBBC_DBG_EVENT(CE_NOTE, "%s: Unknown payload:%d", f, 2270 *payload); 2271 return (DDI_INTR_UNCLAIMED); 2272 } 2273 2274 return (DDI_INTR_CLAIMED); 2275 } 2276 2277 /* 2278 * dp_get_cores() 2279 * 2280 * Checks cpu implementation for the input cpuid and returns 2281 * the number of cores. 2282 * If implementation cannot be determined, returns 1 2283 */ 2284 static int 2285 dp_get_cores(uint16_t cpuid) 2286 { 2287 int bd, ii, impl, nc; 2288 2289 bd = cpuid / 4; 2290 nc = SG_MAX_CPUS_PER_BD; 2291 2292 /* find first with valid implementation */ 2293 for (ii = 0; ii < nc; ii++) 2294 if (cpu[MAKE_CPUID(bd, ii)]) { 2295 impl = cpunodes[MAKE_CPUID(bd, ii)].implementation; 2296 break; 2297 } 2298 2299 if (IS_JAGUAR(impl) || IS_PANTHER(impl)) 2300 return (2); 2301 else 2302 return (1); 2303 } 2304 2305 /* 2306 * dp_payload_add_cpus() 2307 * 2308 * From datapath mailbox message, determines the number of and safari IDs 2309 * for affected cpus, then adds this info to the datapath ereport. 2310 * 2311 */ 2312 static int 2313 dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp) 2314 { 2315 int jj = 0, numcpus = 0; 2316 int bd, procpos, ii, num, ncores, ret; 2317 uint16_t *dparray, cpuid; 2318 uint64_t *snarray; 2319 2320 /* check for multiple core architectures */ 2321 ncores = dp_get_cores(dpmsg->cpuid); 2322 2323 switch (dpmsg->type) { 2324 case DP_CDS_TYPE: 2325 numcpus = ncores; 2326 break; 2327 2328 case DP_DX_TYPE: 2329 numcpus = 2 * ncores; 2330 break; 2331 2332 case DP_RP_TYPE: 2333 numcpus = SG_MAX_CPUS_PER_BD; 2334 break; 2335 2336 default: 2337 ASSERT(0); 2338 return (-1); 2339 } 2340 2341 num = numcpus; 2342 2343 /* 2344 * populate dparray with impacted cores (only those present) 2345 */ 2346 dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP); 2347 bd = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid)); 2348 procpos = SG_CPUID_TO_PORTID(dpmsg->cpuid) & 0x3; 2349 2350 mutex_enter(&cpu_lock); 2351 2352 switch (dpmsg->type) { 2353 2354 case DP_CDS_TYPE: 2355 /* 2356 * For a CDS error, it's the reporting cpuid 2357 * and it's other core (if present) 2358 */ 2359 cpuid = dpmsg->cpuid & 0x1FF; /* core 0 */ 2360 if (cpu[cpuid]) 2361 dparray[jj++] = cpuid; 2362 2363 cpuid = dpmsg->cpuid | SG_CORE_ID_MASK; /* core 1 */ 2364 if (cpu[cpuid]) 2365 dparray[jj++] = cpuid; 2366 break; 2367 2368 case DP_DX_TYPE: 2369 /* 2370 * For a DX error, it's the reporting cpuid (all 2371 * cores) and the other CPU sharing the same 2372 * DX<-->DCDS interface (all cores) 2373 */ 2374 2375 /* reporting cpuid */ 2376 cpuid = dpmsg->cpuid & 0x1FF; /* core 0 */ 2377 if (cpu[cpuid]) 2378 dparray[jj++] = cpuid; 2379 2380 cpuid = dpmsg->cpuid | SG_CORE_ID_MASK; /* core 1 */ 2381 if (cpu[cpuid]) 2382 dparray[jj++] = cpuid; 2383 2384 /* find partner cpuid */ 2385 if (procpos == 0 || procpos == 2) 2386 cpuid = dpmsg->cpuid + 1; 2387 else 2388 cpuid = dpmsg->cpuid - 1; 2389 2390 /* add partner cpuid */ 2391 cpuid &= 0x1FF; /* core 0 */ 2392 if (cpu[cpuid]) 2393 dparray[jj++] = cpuid; 2394 2395 cpuid |= SG_CORE_ID_MASK; /* core 1 */ 2396 if (cpu[cpuid]) 2397 dparray[jj++] = cpuid; 2398 break; 2399 2400 case DP_RP_TYPE: 2401 /* 2402 * For a RP error, it's all cpuids (all cores) on 2403 * the reporting board 2404 */ 2405 for (ii = 0; ii < SG_MAX_CMPS_PER_BD; ii++) { 2406 cpuid = MAKE_CPUID(bd, ii); 2407 if (cpu[cpuid]) /* core 0 */ 2408 dparray[jj++] = cpuid; 2409 cpuid |= SG_CORE_ID_MASK; 2410 if (cpu[cpuid]) /* core 1 */ 2411 dparray[jj++] = cpuid; 2412 } 2413 break; 2414 } 2415 2416 mutex_exit(&cpu_lock); 2417 2418 /* 2419 * The datapath message could not be associated with any 2420 * configured CPU. 2421 */ 2422 if (!jj) { 2423 kmem_free(dparray, num * sizeof (uint16_t *)); 2424 ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj); 2425 ASSERT(ret == 0); 2426 return (-1); 2427 } 2428 2429 snarray = kmem_zalloc(jj * sizeof (uint64_t), KM_SLEEP); 2430 for (ii = 0; ii < jj; ii++) 2431 snarray[ii] = cpunodes[dparray[ii]].device_id; 2432 2433 ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj); 2434 ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj); 2435 ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj); 2436 ASSERT(ret == 0); 2437 2438 kmem_free(dparray, num * sizeof (uint16_t *)); 2439 kmem_free(snarray, jj * sizeof (uint64_t *)); 2440 2441 return (0); 2442 } 2443 2444 /* 2445 * sbbc_dp_trans_event() - datapath message handler. 2446 * 2447 * Process datapath error and fault messages received from the SC. Checks 2448 * for, and disregards, messages associated with I/O boards. Otherwise, 2449 * extracts message info to produce a datapath ereport. 2450 */ 2451 /*ARGSUSED*/ 2452 static uint_t 2453 sbbc_dp_trans_event(char *arg) 2454 { 2455 const char *f = "sbbc_dp_trans_event()"; 2456 nvlist_t *erp, *detector, *hcelem; 2457 char buf[FM_MAX_CLASS]; 2458 int board; 2459 plat_datapath_info_t *dpmsg; 2460 sbbc_msg_t *msg; 2461 int msgtype; 2462 2463 /* set i/f message and payload pointers */ 2464 msg = &dp_payload_msg; 2465 dpmsg = &dp_payload; 2466 msgtype = msg->msg_type.type; 2467 2468 cmn_err(CE_NOTE, "%s: msgtype=0x%x\n", f, msgtype); 2469 cmn_err(CE_NOTE, "type=0x%x cpuid=0x%x t_value=0x%x\n", dpmsg->type, 2470 dpmsg->cpuid, dpmsg->t_value); 2471 2472 /* check for valid type */ 2473 if (dpmsg->type > DP_RP_TYPE) { 2474 cmn_err(CE_WARN, "%s: dpmsg type 0x%x invalid\n", 2475 f, dpmsg->type); 2476 return (DDI_INTR_CLAIMED); 2477 } 2478 2479 /* check for I/O board message - Schizo AIDs are 25 - 30 */ 2480 if (dpmsg->cpuid > 23) { 2481 cmn_err(CE_NOTE, "%s: ignore I/O board msg\n", f); 2482 return (DDI_INTR_CLAIMED); 2483 } 2484 2485 /* allocate space for ereport */ 2486 erp = fm_nvlist_create(NULL); 2487 2488 /* 2489 * Member Name Data Type Comments 2490 * ----------- --------- ----------- 2491 * version uint8 0 2492 * class string "asic" 2493 * ENA uint64 ENA Format 1 2494 * detector fmri aggregated ID data for SC-DE 2495 * 2496 * Datapath ereport subclasses and data payloads: 2497 * There will be two types of ereports (error and fault) which will be 2498 * identified by the "type" member. 2499 * 2500 * ereport.asic.serengeti.cds.cds-dp 2501 * ereport.asic.serengeti.dx.dx-dp (board) 2502 * ereport.asic.serengeti.rp.rp-dp (centerplane) 2503 * 2504 * Member Name Data Type Comments 2505 * ----------- --------- ----------- 2506 * erptype uint16 derived from message type: error or 2507 * fault 2508 * t-value uint32 SC's datapath SERD timeout threshold 2509 * dp-list-sz uint8 number of dp-list array elements 2510 * dp-list array of uint16 Safari IDs of affected cpus 2511 * sn-list array of uint64 Serial numbers of affected cpus 2512 */ 2513 2514 /* compose common ereport elements */ 2515 detector = fm_nvlist_create(NULL); 2516 2517 /* 2518 * Create legacy FMRI for the detector 2519 */ 2520 board = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid)); 2521 switch (dpmsg->type) { 2522 case DP_CDS_TYPE: 2523 case DP_DX_TYPE: 2524 (void) snprintf(buf, FM_MAX_CLASS, "SB%d", board); 2525 break; 2526 case DP_RP_TYPE: 2527 (void) snprintf(buf, FM_MAX_CLASS, "RP"); 2528 break; 2529 default: 2530 (void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN"); 2531 break; 2532 } 2533 2534 hcelem = fm_nvlist_create(NULL); 2535 2536 (void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC); 2537 (void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf); 2538 2539 (void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION); 2540 (void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC); 2541 (void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, ""); 2542 (void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1); 2543 (void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1); 2544 2545 /* build ereport class name */ 2546 (void) snprintf(buf, FM_MAX_CLASS, "asic.serengeti.%s.%s-%s", 2547 dperrtype[dpmsg->type], dperrtype[dpmsg->type], 2548 FM_ERROR_DATAPATH); 2549 2550 fm_ereport_set(erp, FM_EREPORT_VERSION, buf, 2551 fm_ena_generate(0, FM_ENA_FMT1), detector, NULL); 2552 2553 /* add payload elements */ 2554 if (msgtype == MBOX_EVENT_DP_ERROR) 2555 fm_payload_set(erp, 2556 DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL); 2557 else 2558 fm_payload_set(erp, 2559 DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL); 2560 2561 fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL); 2562 2563 (void) dp_payload_add_cpus(dpmsg, erp); 2564 2565 /* post ereport */ 2566 fm_ereport_post(erp, EVCH_SLEEP); 2567 2568 /* free ereport memory */ 2569 fm_nvlist_destroy(erp, FM_NVA_FREE); 2570 fm_nvlist_destroy(detector, FM_NVA_FREE); 2571 2572 return (DDI_INTR_CLAIMED); 2573 } 2574 2575 static uint_t 2576 sbbc_datapath_error_msg_handler(char *arg) 2577 { 2578 static fn_t f = "sbbc_datapath_error_msg_handler()"; 2579 sbbc_msg_t *msg = NULL; 2580 2581 if (arg == NULL) { 2582 SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f); 2583 return (DDI_INTR_UNCLAIMED); 2584 } 2585 2586 msg = (sbbc_msg_t *)arg; 2587 2588 if (msg->msg_buf == NULL) { 2589 SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f); 2590 return (DDI_INTR_UNCLAIMED); 2591 } 2592 2593 msg->msg_type.type = MBOX_EVENT_DP_ERROR; 2594 2595 /* trigger sbbc_dp_trans_event() */ 2596 ddi_trigger_softintr(dp_softintr_id); 2597 2598 return (DDI_INTR_CLAIMED); 2599 } 2600 2601 static uint_t 2602 sbbc_datapath_fault_msg_handler(char *arg) 2603 { 2604 2605 static fn_t f = "sbbc_datapath_fault_msg_handler()"; 2606 2607 sbbc_msg_t *msg = NULL; 2608 2609 if (arg == NULL) { 2610 SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f); 2611 return (DDI_INTR_UNCLAIMED); 2612 } 2613 2614 msg = (sbbc_msg_t *)arg; 2615 2616 if (msg->msg_buf == NULL) { 2617 SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f); 2618 return (DDI_INTR_UNCLAIMED); 2619 } 2620 2621 msg->msg_type.type = MBOX_EVENT_DP_FAULT; 2622 2623 /* trigger sbbc_dp_trans_event() */ 2624 ddi_trigger_softintr(dp_softintr_id); 2625 2626 return (DDI_INTR_CLAIMED); 2627 } 2628 2629 /* 2630 * Log an ECC event message to the SC. This is called from the 2631 * sbbc_ecc_mbox_taskq or directly from plat_send_ecc_mailbox_msg 2632 * for indictment messages. 2633 */ 2634 int 2635 sbbc_mbox_ecc_output(sbbc_ecc_mbox_t *msgp) 2636 { 2637 int rv; 2638 plat_capability_data_t *cap; 2639 plat_dimm_sid_board_data_t *ddata; 2640 plat_ecc_msg_hdr_t *hdr; 2641 2642 rv = sbbc_mbox_request_response(&msgp->ecc_req, &msgp->ecc_resp, 2643 sbbc_mbox_default_timeout); 2644 2645 if (rv != 0) { 2646 /* 2647 * Indictment messages use the return value to indicate a 2648 * problem in the mailbox. For Error mailbox messages, we'll 2649 * have to use a syslog message. 2650 */ 2651 if (msgp->ecc_log_error) { 2652 if (sbbc_ecc_mbox_send_errs == 0) { 2653 cmn_err(CE_NOTE, "!Solaris failed to send a " 2654 "message (0x%x/0x%x) to the System " 2655 "Controller. Error: %d, Message Status: %d", 2656 msgp->ecc_resp.msg_type.type, 2657 msgp->ecc_resp.msg_type.sub_type, 2658 rv, msgp->ecc_resp.msg_status); 2659 } 2660 2661 if (++sbbc_ecc_mbox_send_errs >= 2662 sbbc_ecc_mbox_err_throttle) { 2663 sbbc_ecc_mbox_send_errs = 0; 2664 } 2665 } 2666 2667 } else if (msgp->ecc_resp.msg_status != 0) { 2668 if (msgp->ecc_resp.msg_type.type == INFO_MBOX) { 2669 switch (msgp->ecc_resp.msg_type.sub_type) { 2670 case INFO_MBOX_ECC: 2671 hdr = (plat_ecc_msg_hdr_t *) 2672 msgp->ecc_req.msg_buf; 2673 if (hdr->emh_msg_type == 2674 PLAT_ECC_DIMM_SID_MESSAGE) { 2675 rv = msgp->ecc_resp.msg_status; 2676 break; 2677 } 2678 /*FALLTHROUGH*/ 2679 case INFO_MBOX_ECC_CAP: 2680 /* 2681 * The positive response comes only 2682 * from the AVL FS1 updated SC. 2683 * If the firmware is either downgraded 2684 * or failover to an older version, then 2685 * lets reset the SC capability to 2686 * default. 2687 */ 2688 plat_ecc_capability_sc_set 2689 (PLAT_ECC_CAPABILITY_SC_DEFAULT); 2690 break; 2691 default: 2692 break; 2693 } 2694 } 2695 if (msgp->ecc_log_error) { 2696 if (sbbc_ecc_mbox_inval_errs == 0) { 2697 cmn_err(CE_NOTE, "!An internal error (%d) " 2698 "occurred in the System Controller while " 2699 "processing this message (0x%x/0x%x)", 2700 msgp->ecc_resp.msg_status, 2701 msgp->ecc_resp.msg_type.type, 2702 msgp->ecc_resp.msg_type.sub_type); 2703 } 2704 if (msgp->ecc_resp.msg_status == EINVAL) { 2705 if (++sbbc_ecc_mbox_inval_errs >= 2706 sbbc_ecc_mbox_err_throttle) { 2707 sbbc_ecc_mbox_inval_errs = 0; 2708 } 2709 rv = ENOMSG; 2710 } else { 2711 if (++sbbc_ecc_mbox_other_errs >= 2712 sbbc_ecc_mbox_err_throttle) { 2713 sbbc_ecc_mbox_other_errs = 0; 2714 } 2715 rv = msgp->ecc_resp.msg_status; 2716 } 2717 } 2718 2719 } else { 2720 if (msgp->ecc_resp.msg_type.type == INFO_MBOX) { 2721 switch (msgp->ecc_resp.msg_type.sub_type) { 2722 case INFO_MBOX_ECC_CAP: 2723 /* 2724 * Successfully received the response 2725 * for the capability message, so updating 2726 * the SC ECC messaging capability. 2727 */ 2728 cap = (plat_capability_data_t *) 2729 msgp->ecc_resp.msg_buf; 2730 plat_ecc_capability_sc_set 2731 (cap->capd_capability); 2732 break; 2733 2734 case INFO_MBOX_ECC: 2735 hdr = (plat_ecc_msg_hdr_t *) 2736 msgp->ecc_resp.msg_buf; 2737 if (hdr && (hdr->emh_msg_type == 2738 PLAT_ECC_DIMM_SID_MESSAGE)) { 2739 /* 2740 * Successfully received a response 2741 * to a request for DIMM serial ids. 2742 */ 2743 ddata = (plat_dimm_sid_board_data_t *) 2744 msgp->ecc_resp.msg_buf; 2745 (void) plat_store_mem_sids(ddata); 2746 } 2747 break; 2748 2749 default: 2750 break; 2751 } 2752 } 2753 } 2754 2755 if (msgp->ecc_resp.msg_buf) 2756 kmem_free((void *)msgp->ecc_resp.msg_buf, 2757 (size_t)msgp->ecc_resp.msg_len); 2758 2759 kmem_free((void *)msgp->ecc_req.msg_buf, (size_t)msgp->ecc_req.msg_len); 2760 kmem_free(msgp, sizeof (sbbc_ecc_mbox_t)); 2761 return (rv); 2762 } 2763 2764 /* 2765 * Enqueue ECC event message on taskq to SC. This is invoked from 2766 * plat_send_ecc_mailbox_msg() for each ECC event generating a message. 2767 */ 2768 void 2769 sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t *sbbc_ecc_msgp) 2770 { 2771 /* 2772 * Create the ECC event mailbox taskq, if it does not yet exist. 2773 * This must be done here rather than in sbbc_mbox_init(). The 2774 * sgsbbc driver is loaded very early in the boot flow. Calling 2775 * taskq_create() from sbbc_mbox_init could lead to a boot deadlock. 2776 * 2777 * There might be a tiny probability that two ECC handlers on 2778 * different processors could arrive here simultaneously. If 2779 * the taskq has not been created previously, then these two 2780 * simultaneous events could cause the creation of an extra taskq. 2781 * Given the extremely small likelihood (if not outright impossibility) 2782 * of this occurrence, sbbc_ecc_mbox_taskq is not protected by a lock. 2783 */ 2784 2785 if (sbbc_ecc_mbox_taskq == NULL) { 2786 sbbc_ecc_mbox_taskq = taskq_create("ECC_event_mailbox", 1, 2787 minclsyspri, ECC_MBOX_TASKQ_MIN, ECC_MBOX_TASKQ_MAX, 2788 TASKQ_PREPOPULATE); 2789 if (sbbc_ecc_mbox_taskq == NULL) { 2790 if (sbbc_ecc_mbox_taskq_errs == 0) { 2791 cmn_err(CE_NOTE, "Unable to create mailbox " 2792 "task queue for ECC event logging to " 2793 "System Controller"); 2794 } 2795 if (++sbbc_ecc_mbox_taskq_errs >= 2796 sbbc_ecc_mbox_err_throttle) { 2797 sbbc_ecc_mbox_taskq_errs = 0; 2798 } 2799 2800 kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf, 2801 (size_t)sbbc_ecc_msgp->ecc_req.msg_len); 2802 kmem_free((void *)sbbc_ecc_msgp, 2803 sizeof (sbbc_ecc_mbox_t)); 2804 return; 2805 } 2806 2807 /* 2808 * Reset error counter so that first taskq_dispatch 2809 * error will be output 2810 */ 2811 sbbc_ecc_mbox_taskq_errs = 0; 2812 } 2813 2814 /* 2815 * Enqueue the message 2816 */ 2817 2818 if (taskq_dispatch(sbbc_ecc_mbox_taskq, 2819 (task_func_t *)sbbc_mbox_ecc_output, sbbc_ecc_msgp, 2820 TQ_NOSLEEP) == NULL) { 2821 2822 if (sbbc_ecc_mbox_taskq_errs == 0) { 2823 cmn_err(CE_NOTE, "Unable to send ECC event " 2824 "message to System Controller"); 2825 } 2826 if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) { 2827 sbbc_ecc_mbox_taskq_errs = 0; 2828 } 2829 2830 kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf, 2831 (size_t)sbbc_ecc_msgp->ecc_req.msg_len); 2832 kmem_free((void *)sbbc_ecc_msgp, sizeof (sbbc_ecc_mbox_t)); 2833 } 2834 } 2835 2836 static uint_t 2837 cap_ecc_msg_handler(char *addr) 2838 { 2839 sbbc_msg_t *msg = NULL; 2840 plat_capability_data_t *cap = NULL; 2841 static fn_t f = "cap_ecc_msg_handler"; 2842 2843 msg = (sbbc_msg_t *)addr; 2844 2845 if (msg == NULL) { 2846 SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with " 2847 "null addr"); 2848 return (DDI_INTR_CLAIMED); 2849 } 2850 2851 if (msg->msg_buf == NULL) { 2852 SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with " 2853 "null data buffer"); 2854 return (DDI_INTR_CLAIMED); 2855 } 2856 2857 cap = (plat_capability_data_t *)msg->msg_buf; 2858 switch (cap->capd_msg_type) { 2859 case PLAT_ECC_CAPABILITY_MESSAGE: 2860 SGSBBC_DBG_MBOX("%s: capability 0x%x\n", f, 2861 cap->capd_capability); 2862 plat_ecc_capability_sc_set(cap->capd_capability); 2863 break; 2864 default: 2865 SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f, 2866 cap->capd_msg_type); 2867 break; 2868 } 2869 2870 return (DDI_INTR_CLAIMED); 2871 } 2872