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() + drv_sectohz(wait_time);
 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 }