103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel /*
2803831d35Sstevel * Interface for Serengeti IOSRAM mailbox
2903831d35Sstevel * OS <-> SC communication protocol
3003831d35Sstevel */
3103831d35Sstevel
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/systm.h>
3403831d35Sstevel #include <sys/ddi.h>
3503831d35Sstevel #include <sys/sunddi.h>
3603831d35Sstevel #include <sys/kmem.h>
3703831d35Sstevel #include <sys/uadmin.h>
3803831d35Sstevel #include <sys/machsystm.h>
3903831d35Sstevel #include <sys/disp.h>
4003831d35Sstevel #include <sys/taskq.h>
4103831d35Sstevel
4203831d35Sstevel #include <sys/sgevents.h>
4303831d35Sstevel #include <sys/sgsbbc_priv.h>
4403831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
4503831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
4603831d35Sstevel #include <sys/plat_ecc_unum.h>
4703831d35Sstevel #include <sys/plat_ecc_dimm.h>
4803831d35Sstevel #include <sys/serengeti.h>
4903831d35Sstevel #include <sys/fm/util.h>
5003831d35Sstevel #include <sys/promif.h>
5103831d35Sstevel #include <sys/plat_datapath.h>
5203831d35Sstevel
5303831d35Sstevel sbbc_mailbox_t *master_mbox = NULL;
5403831d35Sstevel
5503831d35Sstevel /*
5603831d35Sstevel * Panic Shutdown event support
5703831d35Sstevel */
5803831d35Sstevel static kmutex_t panic_hdlr_lock;
5903831d35Sstevel
6003831d35Sstevel /*
6103831d35Sstevel * The ID of the soft interrupt which triggers the bringing down of a Domain
6203831d35Sstevel * when a PANIC_SHUTDOWN event is received.
6303831d35Sstevel */
6403831d35Sstevel static ddi_softintr_t panic_softintr_id = 0;
6503831d35Sstevel
6603831d35Sstevel static sg_panic_shutdown_t panic_payload;
6703831d35Sstevel static sbbc_msg_t panic_payload_msg;
6803831d35Sstevel
6903831d35Sstevel /*
7003831d35Sstevel * A queue for making sure outgoing messages are in order as ScApp
7103831d35Sstevel * does not support interleaving messages.
7203831d35Sstevel */
7303831d35Sstevel static kcondvar_t outbox_queue;
7403831d35Sstevel static kmutex_t outbox_queue_lock;
7503831d35Sstevel
7603831d35Sstevel /*
7703831d35Sstevel * Handle unsolicited capability message.
7803831d35Sstevel */
7903831d35Sstevel static plat_capability_data_t cap_payload;
8003831d35Sstevel static sbbc_msg_t cap_payload_msg;
8103831d35Sstevel static kmutex_t cap_msg_hdlr_lock;
8203831d35Sstevel
8303831d35Sstevel /*
8403831d35Sstevel * Datapath error and fault messages arrive unsolicited. The message data
8503831d35Sstevel * is contained in a plat_datapath_info_t structure.
8603831d35Sstevel */
8703831d35Sstevel typedef struct {
8803831d35Sstevel uint8_t type; /* CDS, DX, CP */
8903831d35Sstevel uint8_t pad; /* for alignment */
9003831d35Sstevel uint16_t cpuid; /* Safari ID of base CPU */
9103831d35Sstevel uint32_t t_value; /* SERD timeout threshold (seconds) */
9203831d35Sstevel } plat_datapath_info_t;
9303831d35Sstevel
9403831d35Sstevel /*
9503831d35Sstevel * Unsolicited datapath error messages are processed via a soft interrupt,
9603831d35Sstevel * triggered in unsolicited interrupt processing.
9703831d35Sstevel */
9803831d35Sstevel static ddi_softintr_t dp_softintr_id = 0;
9903831d35Sstevel static kmutex_t dp_hdlr_lock;
10003831d35Sstevel
10103831d35Sstevel static plat_datapath_info_t dp_payload;
10203831d35Sstevel static sbbc_msg_t dp_payload_msg;
10303831d35Sstevel
10403831d35Sstevel static char *dperrtype[] = {
10503831d35Sstevel DP_ERROR_CDS,
10603831d35Sstevel DP_ERROR_DX,
10703831d35Sstevel DP_ERROR_RP
10803831d35Sstevel };
10903831d35Sstevel
11003831d35Sstevel /*
11103831d35Sstevel * Variable indicating if we are already processing requests.
11203831d35Sstevel * Setting this value must be protected by outbox_queue_lock.
11303831d35Sstevel */
11403831d35Sstevel static int outbox_busy = 0;
11503831d35Sstevel
11603831d35Sstevel /*
11703831d35Sstevel * local stuff
11803831d35Sstevel */
11903831d35Sstevel static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t);
12003831d35Sstevel static int sbbc_mbox_recv_msg();
12103831d35Sstevel static int mbox_write(struct sbbc_mbox_header *,
12203831d35Sstevel struct sbbc_fragment *, sbbc_msg_t *);
12303831d35Sstevel static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *,
12403831d35Sstevel sbbc_msg_t *);
12503831d35Sstevel static int mbox_has_free_space(struct sbbc_mbox_header *);
12603831d35Sstevel static void mbox_skip_next_msg(struct sbbc_mbox_header *);
12703831d35Sstevel static int mbox_read_header(uint32_t, struct sbbc_mbox_header *);
12803831d35Sstevel static void mbox_update_header(uint32_t, struct sbbc_mbox_header *);
12903831d35Sstevel static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *);
13003831d35Sstevel static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t);
13103831d35Sstevel static void wakeup_next(void);
13203831d35Sstevel static uint_t sbbc_panic_shutdown_handler(char *arg);
13303831d35Sstevel static uint_t sbbc_do_fast_shutdown(char *arg);
13403831d35Sstevel static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp);
13503831d35Sstevel static uint_t cap_ecc_msg_handler(char *);
13603831d35Sstevel static uint_t sbbc_datapath_error_msg_handler(char *arg);
13703831d35Sstevel static uint_t sbbc_datapath_fault_msg_handler(char *arg);
13803831d35Sstevel static uint_t sbbc_dp_trans_event(char *arg);
13903831d35Sstevel
14003831d35Sstevel
14103831d35Sstevel /*
14203831d35Sstevel * Interrupt handlers
14303831d35Sstevel */
14403831d35Sstevel static int sbbc_mbox_msgin(void);
14503831d35Sstevel static int sbbc_mbox_msgout(void);
14603831d35Sstevel static int sbbc_mbox_spacein(void);
14703831d35Sstevel static int sbbc_mbox_spaceout(void);
14803831d35Sstevel
14903831d35Sstevel /*
15003831d35Sstevel * ECC event mailbox message taskq and parameters
15103831d35Sstevel */
15203831d35Sstevel static taskq_t *sbbc_ecc_mbox_taskq = NULL;
15303831d35Sstevel static int sbbc_ecc_mbox_taskq_errs = 0;
15403831d35Sstevel static int sbbc_ecc_mbox_send_errs = 0;
15503831d35Sstevel static int sbbc_ecc_mbox_inval_errs = 0;
15603831d35Sstevel static int sbbc_ecc_mbox_other_errs = 0;
15703831d35Sstevel int sbbc_ecc_mbox_err_throttle = ECC_MBOX_TASKQ_ERR_THROTTLE;
15803831d35Sstevel
15903831d35Sstevel /*
16003831d35Sstevel * Called when SBBC driver is loaded
16103831d35Sstevel * Initialise global mailbox stuff, etc
16203831d35Sstevel */
16303831d35Sstevel void
sbbc_mbox_init()16403831d35Sstevel sbbc_mbox_init()
16503831d35Sstevel {
16603831d35Sstevel int i;
16703831d35Sstevel
16803831d35Sstevel master_mbox = kmem_zalloc(sizeof (sbbc_mailbox_t), KM_NOSLEEP);
16903831d35Sstevel if (master_mbox == NULL) {
17003831d35Sstevel cmn_err(CE_PANIC, "Can't allocate memory for mailbox\n");
17103831d35Sstevel }
17203831d35Sstevel
17303831d35Sstevel /*
17403831d35Sstevel * mutex'es for the wait-lists
17503831d35Sstevel */
17603831d35Sstevel for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
17703831d35Sstevel mutex_init(&master_mbox->mbox_wait_lock[i],
17803831d35Sstevel NULL, MUTEX_DEFAULT, NULL);
17903831d35Sstevel master_mbox->mbox_wait_list[i] = NULL;
18003831d35Sstevel }
18103831d35Sstevel
18203831d35Sstevel for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++)
18303831d35Sstevel master_mbox->intrs[i] = NULL;
18403831d35Sstevel
18503831d35Sstevel /*
18603831d35Sstevel * Two mailbox channels SC -> OS , read-only
18703831d35Sstevel * OS -> SC, read/write
18803831d35Sstevel */
18903831d35Sstevel master_mbox->mbox_in = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
19003831d35Sstevel if (master_mbox->mbox_in == NULL) {
19103831d35Sstevel cmn_err(CE_PANIC,
19203831d35Sstevel "Can't allocate memory for inbound mailbox\n");
19303831d35Sstevel }
19403831d35Sstevel
19503831d35Sstevel master_mbox->mbox_out = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
19603831d35Sstevel if (master_mbox->mbox_out == NULL) {
19703831d35Sstevel cmn_err(CE_PANIC,
19803831d35Sstevel "Can't allocate memory for outbound mailbox\n");
19903831d35Sstevel }
20003831d35Sstevel
20103831d35Sstevel mutex_init(&master_mbox->mbox_in->mb_lock, NULL,
20203831d35Sstevel MUTEX_DEFAULT, NULL);
20303831d35Sstevel mutex_init(&master_mbox->mbox_out->mb_lock, NULL,
20403831d35Sstevel MUTEX_DEFAULT, NULL);
20503831d35Sstevel
20603831d35Sstevel /*
20703831d35Sstevel * Add PANIC_SHUTDOWN Event mutex
20803831d35Sstevel */
20903831d35Sstevel mutex_init(&panic_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21003831d35Sstevel
21103831d35Sstevel /* Initialize datapath error message handler mutex */
21203831d35Sstevel mutex_init(&dp_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21303831d35Sstevel
21403831d35Sstevel /* Initialize capability message handler event mutex */
21503831d35Sstevel mutex_init(&cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21603831d35Sstevel
21703831d35Sstevel /*
21803831d35Sstevel * NOT USED YET
21903831d35Sstevel */
22003831d35Sstevel master_mbox->mbox_in->mb_type =
22103831d35Sstevel master_mbox->mbox_out->mb_type = 0;
22203831d35Sstevel
22303831d35Sstevel cv_init(&outbox_queue, NULL, CV_DEFAULT, NULL);
22403831d35Sstevel mutex_init(&outbox_queue_lock, NULL, MUTEX_DEFAULT, NULL);
22503831d35Sstevel
22603831d35Sstevel }
22703831d35Sstevel
22803831d35Sstevel /*
22903831d35Sstevel * called when the SBBC driver is unloaded
23003831d35Sstevel */
23103831d35Sstevel void
sbbc_mbox_fini()23203831d35Sstevel sbbc_mbox_fini()
23303831d35Sstevel {
23403831d35Sstevel int i;
23503831d35Sstevel int err;
23603831d35Sstevel
23703831d35Sstevel /*
23803831d35Sstevel * destroy ECC event mailbox taskq
23903831d35Sstevel */
24003831d35Sstevel if (sbbc_ecc_mbox_taskq != NULL) {
24103831d35Sstevel taskq_destroy(sbbc_ecc_mbox_taskq);
24203831d35Sstevel sbbc_ecc_mbox_taskq = NULL;
24303831d35Sstevel sbbc_ecc_mbox_taskq_errs = 0;
24403831d35Sstevel }
24503831d35Sstevel
24603831d35Sstevel /*
24703831d35Sstevel * unregister interrupts
24803831d35Sstevel */
24903831d35Sstevel (void) iosram_unreg_intr(SBBC_MAILBOX_IN);
25003831d35Sstevel (void) iosram_unreg_intr(SBBC_MAILBOX_IN);
25103831d35Sstevel (void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_IN);
25203831d35Sstevel (void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_OUT);
25303831d35Sstevel
25403831d35Sstevel /*
25503831d35Sstevel * Remove Panic Shutdown and Datapath Error event support.
25603831d35Sstevel *
25703831d35Sstevel * NOTE: If we have not added the soft interrupt handlers for these
25803831d35Sstevel * then we know that we have not registered the event handlers either.
25903831d35Sstevel */
26003831d35Sstevel if (panic_softintr_id != 0) {
26103831d35Sstevel ddi_remove_softintr(panic_softintr_id);
26203831d35Sstevel
26303831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
26403831d35Sstevel sbbc_panic_shutdown_handler);
26503831d35Sstevel if (err != 0) {
26603831d35Sstevel cmn_err(CE_WARN, "Failed to unreg Panic Shutdown "
26703831d35Sstevel "handler. Err=%d", err);
26803831d35Sstevel }
26903831d35Sstevel }
27003831d35Sstevel if (dp_softintr_id != 0) {
27103831d35Sstevel ddi_remove_softintr(dp_softintr_id);
27203831d35Sstevel
27303831d35Sstevel err = sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
27403831d35Sstevel sbbc_datapath_error_msg_handler);
27503831d35Sstevel err |= sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
27603831d35Sstevel sbbc_datapath_fault_msg_handler);
27703831d35Sstevel if (err != 0) {
27803831d35Sstevel cmn_err(CE_WARN, "Failed to unreg Datapath Error "
27903831d35Sstevel "handler. Err=%d", err);
28003831d35Sstevel }
28103831d35Sstevel }
28203831d35Sstevel
28303831d35Sstevel /*
28403831d35Sstevel * destroy all its mutex'es, lists etc
28503831d35Sstevel */
28603831d35Sstevel
28703831d35Sstevel /*
28803831d35Sstevel * mutex'es for the wait-lists
28903831d35Sstevel */
29003831d35Sstevel for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
29103831d35Sstevel mutex_destroy(&master_mbox->mbox_wait_lock[i]);
29203831d35Sstevel }
29303831d35Sstevel
29403831d35Sstevel mutex_destroy(&master_mbox->mbox_in->mb_lock);
29503831d35Sstevel mutex_destroy(&master_mbox->mbox_out->mb_lock);
29603831d35Sstevel
29703831d35Sstevel mutex_destroy(&panic_hdlr_lock);
29803831d35Sstevel mutex_destroy(&dp_hdlr_lock);
29903831d35Sstevel
30003831d35Sstevel kmem_free(master_mbox->mbox_in, sizeof (sbbc_mbox_t));
30103831d35Sstevel kmem_free(master_mbox->mbox_out, sizeof (sbbc_mbox_t));
30203831d35Sstevel kmem_free(master_mbox, sizeof (sbbc_mailbox_t));
30303831d35Sstevel
30403831d35Sstevel cv_destroy(&outbox_queue);
30503831d35Sstevel mutex_destroy(&outbox_queue_lock);
30603831d35Sstevel
30703831d35Sstevel err = sbbc_mbox_unreg_intr(INFO_MBOX, cap_ecc_msg_handler);
30803831d35Sstevel if (err != 0) {
30903831d35Sstevel cmn_err(CE_WARN, "Failed to unregister capability message "
31003831d35Sstevel "handler. Err=%d", err);
31103831d35Sstevel }
31203831d35Sstevel
31303831d35Sstevel mutex_destroy(&cap_msg_hdlr_lock);
31403831d35Sstevel }
31503831d35Sstevel
31603831d35Sstevel /*
31703831d35Sstevel * Update iosram_sbbc to the new softstate after a tunnel switch.
31803831d35Sstevel * Move software interrupts from the old dip to the new dip.
31903831d35Sstevel */
32003831d35Sstevel int
sbbc_mbox_switch(sbbc_softstate_t * softsp)32103831d35Sstevel sbbc_mbox_switch(sbbc_softstate_t *softsp)
32203831d35Sstevel {
32303831d35Sstevel sbbc_intrs_t *intr;
32403831d35Sstevel int msg_type;
32503831d35Sstevel int rc = 0;
32603831d35Sstevel int err;
32703831d35Sstevel
32803831d35Sstevel if (master_mbox == NULL)
32903831d35Sstevel return (ENXIO);
33003831d35Sstevel
33103831d35Sstevel ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
33203831d35Sstevel
33303831d35Sstevel for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
33403831d35Sstevel
33503831d35Sstevel for (intr = master_mbox->intrs[msg_type]; intr != NULL;
33603831d35Sstevel intr = intr->sbbc_intr_next) {
33703831d35Sstevel
33803831d35Sstevel if (intr->sbbc_intr_id) {
33903831d35Sstevel ddi_remove_softintr(intr->sbbc_intr_id);
34003831d35Sstevel
34103831d35Sstevel if (ddi_add_softintr(softsp->dip,
34203831d35Sstevel DDI_SOFTINT_HIGH,
34303831d35Sstevel &intr->sbbc_intr_id, NULL, NULL,
34403831d35Sstevel intr->sbbc_handler, intr->sbbc_arg)
34503831d35Sstevel != DDI_SUCCESS) {
34603831d35Sstevel
34703831d35Sstevel cmn_err(CE_WARN,
34803831d35Sstevel "Can't add SBBC mailbox "
34903831d35Sstevel "softint for msg_type %x\n",
35003831d35Sstevel msg_type);
35103831d35Sstevel rc = ENXIO;
35203831d35Sstevel }
35303831d35Sstevel }
35403831d35Sstevel }
35503831d35Sstevel }
35603831d35Sstevel
35703831d35Sstevel /*
35803831d35Sstevel * Add PANIC_SHUTDOWN Event handler
35903831d35Sstevel */
36003831d35Sstevel if (panic_softintr_id) {
36103831d35Sstevel ddi_remove_softintr(panic_softintr_id);
36203831d35Sstevel
36303831d35Sstevel err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
36403831d35Sstevel &panic_softintr_id, NULL, NULL,
36503831d35Sstevel sbbc_do_fast_shutdown, NULL);
36603831d35Sstevel
36703831d35Sstevel if (err != DDI_SUCCESS) {
36803831d35Sstevel cmn_err(CE_WARN, "Failed to register Panic "
36903831d35Sstevel "Shutdown handler. Err=%d", err);
37003831d35Sstevel (void) sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
37103831d35Sstevel sbbc_panic_shutdown_handler);
37203831d35Sstevel rc = ENXIO;
37303831d35Sstevel }
37403831d35Sstevel
37503831d35Sstevel }
37603831d35Sstevel /*
37703831d35Sstevel * Add Datapath Error Event handler
37803831d35Sstevel */
37903831d35Sstevel if (dp_softintr_id) {
38003831d35Sstevel ddi_remove_softintr(dp_softintr_id);
38103831d35Sstevel
38203831d35Sstevel err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
38303831d35Sstevel &dp_softintr_id, NULL, NULL,
38403831d35Sstevel sbbc_dp_trans_event, NULL);
38503831d35Sstevel
38603831d35Sstevel if (err != DDI_SUCCESS) {
38703831d35Sstevel cmn_err(CE_WARN, "Failed to register Datapath "
38803831d35Sstevel "Error Event handler. Err=%d", err);
38903831d35Sstevel (void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
39003831d35Sstevel sbbc_datapath_error_msg_handler);
39103831d35Sstevel (void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
39203831d35Sstevel sbbc_datapath_fault_msg_handler);
39303831d35Sstevel rc = ENXIO;
39403831d35Sstevel }
39503831d35Sstevel
39603831d35Sstevel }
39703831d35Sstevel
39803831d35Sstevel return (rc);
39903831d35Sstevel }
40003831d35Sstevel
40103831d35Sstevel /*
40203831d35Sstevel * Called when the IOSRAM tunnel is created for the 'chosen' node.
40303831d35Sstevel *
40403831d35Sstevel * Read the mailbox header from the IOSRAM
40503831d35Sstevel * tunnel[SBBC_MAILBOX_KEY]
40603831d35Sstevel * Register the mailbox interrupt handlers
40703831d35Sstevel * for messages in/space etc
40803831d35Sstevel */
40903831d35Sstevel int
sbbc_mbox_create(sbbc_softstate_t * softsp)41003831d35Sstevel sbbc_mbox_create(sbbc_softstate_t *softsp)
41103831d35Sstevel {
41203831d35Sstevel struct sbbc_mbox_header header;
41303831d35Sstevel
41403831d35Sstevel int i;
41503831d35Sstevel int err;
41603831d35Sstevel int rc = 0;
41703831d35Sstevel
41803831d35Sstevel /*
41903831d35Sstevel * This function should only be called once when
42003831d35Sstevel * the chosen node is initialized.
42103831d35Sstevel */
42203831d35Sstevel ASSERT(MUTEX_HELD(&chosen_lock));
42303831d35Sstevel
42403831d35Sstevel if (master_mbox == NULL)
42503831d35Sstevel return (ENXIO);
42603831d35Sstevel
42703831d35Sstevel /*
42803831d35Sstevel * read the header at offset 0
42903831d35Sstevel * check magic/version etc
43003831d35Sstevel */
43103831d35Sstevel if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)&header,
43203831d35Sstevel sizeof (struct sbbc_mbox_header))) {
43303831d35Sstevel
43403831d35Sstevel return (rc);
43503831d35Sstevel }
43603831d35Sstevel
43703831d35Sstevel /*
43803831d35Sstevel * add the interrupt handlers for the mailbox
43903831d35Sstevel * interrupts
44003831d35Sstevel */
44103831d35Sstevel for (i = 0; i < MBOX_INTRS; i++) {
44203831d35Sstevel sbbc_intrfunc_t intr_handler;
44303831d35Sstevel uint_t *state;
44403831d35Sstevel kmutex_t *lock;
44503831d35Sstevel uint32_t intr_num;
44603831d35Sstevel
44703831d35Sstevel switch (i) {
44803831d35Sstevel case MBOX_MSGIN_INTR:
44903831d35Sstevel intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgin;
45003831d35Sstevel intr_num = SBBC_MAILBOX_IN;
45103831d35Sstevel break;
45203831d35Sstevel case MBOX_MSGOUT_INTR:
45303831d35Sstevel intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgout;
45403831d35Sstevel intr_num = SBBC_MAILBOX_OUT;
45503831d35Sstevel break;
45603831d35Sstevel case MBOX_SPACEIN_INTR:
45703831d35Sstevel intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spacein;
45803831d35Sstevel intr_num = SBBC_MAILBOX_SPACE_IN;
45903831d35Sstevel break;
46003831d35Sstevel case MBOX_SPACEOUT_INTR:
46103831d35Sstevel intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spaceout;
46203831d35Sstevel intr_num = SBBC_MAILBOX_SPACE_OUT;
46303831d35Sstevel break;
46403831d35Sstevel }
46503831d35Sstevel state = (uint_t *)&master_mbox->intr_state[i].mbox_intr_state;
46603831d35Sstevel lock = &master_mbox->intr_state[i].mbox_intr_lock;
46703831d35Sstevel if (iosram_reg_intr(intr_num, intr_handler, (caddr_t)NULL,
46803831d35Sstevel state, lock)) {
46903831d35Sstevel
47003831d35Sstevel cmn_err(CE_WARN,
47103831d35Sstevel "Can't register Mailbox interrupts \n");
47203831d35Sstevel }
47303831d35Sstevel }
47403831d35Sstevel
47503831d35Sstevel /*
47603831d35Sstevel * Add PANIC_SHUTDOWN Event handler
47703831d35Sstevel */
47803831d35Sstevel panic_payload_msg.msg_buf = (caddr_t)&panic_payload;
47903831d35Sstevel panic_payload_msg.msg_len = sizeof (panic_payload);
48003831d35Sstevel
48103831d35Sstevel err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &panic_softintr_id,
48203831d35Sstevel NULL, NULL, sbbc_do_fast_shutdown, NULL);
48303831d35Sstevel
48403831d35Sstevel if (err == DDI_SUCCESS) {
48503831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
48603831d35Sstevel sbbc_panic_shutdown_handler, &panic_payload_msg,
48703831d35Sstevel NULL, &panic_hdlr_lock);
48803831d35Sstevel if (err != 0) {
48903831d35Sstevel cmn_err(CE_WARN, "Failed to register Panic "
49003831d35Sstevel "Shutdown handler. Err=%d", err);
49103831d35Sstevel }
49203831d35Sstevel
49303831d35Sstevel } else {
49403831d35Sstevel cmn_err(CE_WARN, "Failed to add Panic Shutdown "
49503831d35Sstevel "softintr handler");
49603831d35Sstevel }
49703831d35Sstevel
49803831d35Sstevel /*
49903831d35Sstevel * Add Unsolicited Datapath Error Events handler
50003831d35Sstevel */
50103831d35Sstevel dp_payload_msg.msg_buf = (caddr_t)&dp_payload;
50203831d35Sstevel dp_payload_msg.msg_len = sizeof (dp_payload);
50303831d35Sstevel
50403831d35Sstevel err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &dp_softintr_id,
50503831d35Sstevel NULL, NULL, sbbc_dp_trans_event, NULL);
50603831d35Sstevel
50703831d35Sstevel if (err == DDI_SUCCESS) {
50803831d35Sstevel err = sbbc_mbox_reg_intr(MBOX_EVENT_DP_ERROR,
50903831d35Sstevel sbbc_datapath_error_msg_handler, &dp_payload_msg,
51003831d35Sstevel NULL, &dp_hdlr_lock);
51103831d35Sstevel err |= sbbc_mbox_reg_intr(MBOX_EVENT_DP_FAULT,
51203831d35Sstevel sbbc_datapath_fault_msg_handler, &dp_payload_msg,
51303831d35Sstevel NULL, &dp_hdlr_lock);
51403831d35Sstevel if (err != 0) {
51503831d35Sstevel cmn_err(CE_WARN, "Failed to register Datapath "
51603831d35Sstevel "error handler. Err=%d", err);
51703831d35Sstevel }
51803831d35Sstevel
51903831d35Sstevel } else {
52003831d35Sstevel cmn_err(CE_WARN, "Failed to add Datapath error "
52103831d35Sstevel "softintr handler");
52203831d35Sstevel }
52303831d35Sstevel
52403831d35Sstevel /*
52503831d35Sstevel * Register an interrupt handler with the sgbbc driver for the
52603831d35Sstevel * unsolicited INFO_MBOX response for the capability bitmap.
52703831d35Sstevel * This message is expected whenever the SC is (re)booted or
52803831d35Sstevel * failed over.
52903831d35Sstevel */
53003831d35Sstevel cap_payload_msg.msg_buf = (caddr_t)&cap_payload;
53103831d35Sstevel cap_payload_msg.msg_len = sizeof (cap_payload);
53203831d35Sstevel
53303831d35Sstevel err = sbbc_mbox_reg_intr(INFO_MBOX, cap_ecc_msg_handler,
53403831d35Sstevel &cap_payload_msg, NULL, &cap_msg_hdlr_lock);
53503831d35Sstevel if (err != 0) {
53603831d35Sstevel cmn_err(CE_WARN, "Failed to register capability message"
53703831d35Sstevel " handler with Err=%d", err);
53803831d35Sstevel }
53903831d35Sstevel
54003831d35Sstevel /*
54103831d35Sstevel * Now is the opportunity to register
54203831d35Sstevel * the deferred mbox intrs.
54303831d35Sstevel */
54403831d35Sstevel sbbc_mbox_post_reg(softsp);
54503831d35Sstevel
54603831d35Sstevel return (rc);
54703831d35Sstevel }
54803831d35Sstevel
54903831d35Sstevel /*
55003831d35Sstevel * Called when chosen IOSRAM is initialized
55103831d35Sstevel * to register the deferred mbox intrs.
55203831d35Sstevel */
55303831d35Sstevel static void
sbbc_mbox_post_reg(sbbc_softstate_t * softsp)55403831d35Sstevel sbbc_mbox_post_reg(sbbc_softstate_t *softsp)
55503831d35Sstevel {
55603831d35Sstevel uint32_t msg_type;
55703831d35Sstevel sbbc_intrs_t *intr;
55803831d35Sstevel
55903831d35Sstevel ASSERT(master_mbox);
56003831d35Sstevel for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
56103831d35Sstevel intr = master_mbox->intrs[msg_type];
56203831d35Sstevel while (intr != NULL) {
56303831d35Sstevel if (!intr->registered) {
56403831d35Sstevel SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_post_reg: "
56503831d35Sstevel "postreg for msgtype=%x\n", msg_type);
56603831d35Sstevel if (ddi_add_softintr(softsp->dip,
56703831d35Sstevel DDI_SOFTINT_HIGH, &intr->sbbc_intr_id,
56803831d35Sstevel NULL, NULL, intr->sbbc_handler,
56903831d35Sstevel (caddr_t)intr->sbbc_arg)
57003831d35Sstevel != DDI_SUCCESS) {
57103831d35Sstevel cmn_err(CE_WARN, "Can't add SBBC "
57203831d35Sstevel "deferred mailbox softint \n");
57303831d35Sstevel } else
57403831d35Sstevel intr->registered = 1;
57503831d35Sstevel }
57603831d35Sstevel intr = intr->sbbc_intr_next;
57703831d35Sstevel }
57803831d35Sstevel }
57903831d35Sstevel }
58003831d35Sstevel
58103831d35Sstevel /*
58203831d35Sstevel * Register a handler for a message type
58303831d35Sstevel * NB NB NB
58403831d35Sstevel * arg must be either NULL or the address of a sbbc_fragment
58503831d35Sstevel * pointer
58603831d35Sstevel */
58703831d35Sstevel int
sbbc_mbox_reg_intr(uint32_t msg_type,sbbc_intrfunc_t intr_handler,sbbc_msg_t * arg,uint_t * state,kmutex_t * lock)58803831d35Sstevel sbbc_mbox_reg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler,
58903831d35Sstevel sbbc_msg_t *arg, uint_t *state, kmutex_t *lock)
59003831d35Sstevel {
59103831d35Sstevel sbbc_intrs_t *intr, *previntr;
59203831d35Sstevel int rc = 0;
59303831d35Sstevel
59403831d35Sstevel /*
59503831d35Sstevel * Validate arguments
59603831d35Sstevel */
59703831d35Sstevel if (msg_type >= SBBC_MBOX_MSG_TYPES)
59803831d35Sstevel return (EINVAL);
59903831d35Sstevel
60003831d35Sstevel /*
60103831d35Sstevel * Verify that we have already set up the master sbbc
60203831d35Sstevel */
60303831d35Sstevel if (master_iosram == NULL || master_mbox == NULL)
60403831d35Sstevel return (ENXIO);
60503831d35Sstevel
60603831d35Sstevel mutex_enter(&master_iosram->iosram_lock);
60703831d35Sstevel msg_type &= SBBC_MSG_TYPE_MASK;
60803831d35Sstevel previntr = intr = master_mbox->intrs[msg_type];
60903831d35Sstevel
61003831d35Sstevel /* Find the end of the link list */
61103831d35Sstevel while (intr != NULL && intr->sbbc_handler != intr_handler) {
61203831d35Sstevel
61303831d35Sstevel previntr = intr;
61403831d35Sstevel intr = intr->sbbc_intr_next;
61503831d35Sstevel }
61603831d35Sstevel
61703831d35Sstevel /* Return if the handler has been registered */
61803831d35Sstevel if (intr != NULL) {
61903831d35Sstevel mutex_exit(&master_iosram->iosram_lock);
62003831d35Sstevel return (EBUSY);
62103831d35Sstevel }
62203831d35Sstevel
62303831d35Sstevel /*
62403831d35Sstevel * The requested handler has not been installed.
62503831d35Sstevel * Allocate some memory.
62603831d35Sstevel */
62703831d35Sstevel intr = kmem_zalloc(sizeof (sbbc_intrs_t), KM_SLEEP);
62803831d35Sstevel
62903831d35Sstevel intr->sbbc_handler = intr_handler;
63003831d35Sstevel intr->sbbc_arg = (caddr_t)arg;
63103831d35Sstevel intr->sbbc_intr_state = state;
63203831d35Sstevel intr->sbbc_intr_lock = lock;
63303831d35Sstevel intr->sbbc_intr_next = NULL;
63403831d35Sstevel /* not registered yet */
63503831d35Sstevel intr->registered = 0;
63603831d35Sstevel
63703831d35Sstevel if (previntr != NULL)
63803831d35Sstevel previntr->sbbc_intr_next = intr;
63903831d35Sstevel else
64003831d35Sstevel master_mbox->intrs[msg_type] = intr;
64103831d35Sstevel
64203831d35Sstevel /*
64303831d35Sstevel * register only if the chosen IOSRAM is
64403831d35Sstevel * initialized, otherwise defer the registration
64503831d35Sstevel * until IOSRAM initialization.
64603831d35Sstevel */
64703831d35Sstevel if (master_iosram->iosram_sbbc) {
64803831d35Sstevel if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
64903831d35Sstevel DDI_SOFTINT_HIGH,
65003831d35Sstevel &intr->sbbc_intr_id, NULL, NULL,
65103831d35Sstevel intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
65203831d35Sstevel cmn_err(CE_WARN, "Can't add SBBC mailbox softint \n");
65303831d35Sstevel rc = ENXIO;
65403831d35Sstevel } else
65503831d35Sstevel intr->registered = 1;
65603831d35Sstevel } else {
65703831d35Sstevel SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_reg_intr: "
65803831d35Sstevel "deferring msg=%x registration\n", msg_type);
65903831d35Sstevel }
66003831d35Sstevel
66103831d35Sstevel mutex_exit(&master_iosram->iosram_lock);
66203831d35Sstevel
66303831d35Sstevel return (rc);
66403831d35Sstevel }
66503831d35Sstevel
66603831d35Sstevel /*
66703831d35Sstevel * Unregister a handler for a message type
66803831d35Sstevel */
66903831d35Sstevel int
sbbc_mbox_unreg_intr(uint32_t msg_type,sbbc_intrfunc_t intr_handler)67003831d35Sstevel sbbc_mbox_unreg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler)
67103831d35Sstevel {
67203831d35Sstevel sbbc_intrs_t *intr, *previntr, *nextintr;
67303831d35Sstevel
67403831d35Sstevel /*
67503831d35Sstevel * Verify that we have already set up the master sbbc
67603831d35Sstevel */
67703831d35Sstevel if (master_iosram == NULL || master_mbox == NULL)
67803831d35Sstevel return (ENXIO);
67903831d35Sstevel
68003831d35Sstevel msg_type &= SBBC_MSG_TYPE_MASK;
68103831d35Sstevel
68203831d35Sstevel if (msg_type >= SBBC_MBOX_MSG_TYPES ||
68303831d35Sstevel intr_handler == (sbbc_intrfunc_t)NULL) {
68403831d35Sstevel
68503831d35Sstevel return (EINVAL);
68603831d35Sstevel }
68703831d35Sstevel
68803831d35Sstevel mutex_enter(&master_iosram->iosram_lock);
68903831d35Sstevel
69003831d35Sstevel previntr = intr = master_mbox->intrs[msg_type];
69103831d35Sstevel
69203831d35Sstevel /*
69303831d35Sstevel * No handlers installed
69403831d35Sstevel */
69503831d35Sstevel if (intr == NULL) {
69603831d35Sstevel mutex_exit(&master_iosram->iosram_lock);
69703831d35Sstevel return (EINVAL);
69803831d35Sstevel }
69903831d35Sstevel
70003831d35Sstevel while (intr != NULL) {
70103831d35Sstevel
70203831d35Sstevel /* Save the next pointer */
70303831d35Sstevel nextintr = intr->sbbc_intr_next;
70403831d35Sstevel
70503831d35Sstevel /* Found a match. Remove it from the link list */
70603831d35Sstevel if (intr->sbbc_handler == intr_handler) {
70703831d35Sstevel
70803831d35Sstevel if (intr->sbbc_intr_id)
70903831d35Sstevel ddi_remove_softintr(intr->sbbc_intr_id);
71003831d35Sstevel
71103831d35Sstevel kmem_free(intr, sizeof (sbbc_intrs_t));
71203831d35Sstevel
71303831d35Sstevel if (previntr != master_mbox->intrs[msg_type])
71403831d35Sstevel previntr->sbbc_intr_next = nextintr;
71503831d35Sstevel else
71603831d35Sstevel master_mbox->intrs[msg_type] = nextintr;
71703831d35Sstevel
71803831d35Sstevel break;
71903831d35Sstevel }
72003831d35Sstevel
72103831d35Sstevel /* update pointers */
72203831d35Sstevel previntr = intr;
72303831d35Sstevel intr = nextintr;
72403831d35Sstevel }
72503831d35Sstevel
72603831d35Sstevel mutex_exit(&master_iosram->iosram_lock);
72703831d35Sstevel
72803831d35Sstevel return (0);
72903831d35Sstevel }
73003831d35Sstevel /*
73103831d35Sstevel * Interrupt handlers - one for each mailbox
73203831d35Sstevel * interrupt type
73303831d35Sstevel */
73403831d35Sstevel
73503831d35Sstevel /*
73603831d35Sstevel * mailbox message received
73703831d35Sstevel */
73803831d35Sstevel static int
sbbc_mbox_msgin()73903831d35Sstevel sbbc_mbox_msgin()
74003831d35Sstevel {
74103831d35Sstevel mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
74203831d35Sstevel master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_state =
74303831d35Sstevel SBBC_INTR_RUNNING;
74403831d35Sstevel mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
74503831d35Sstevel
74603831d35Sstevel /*
74703831d35Sstevel * We are only locking the InBox here, not the whole
74803831d35Sstevel * mailbox. This is based on the assumption of
74903831d35Sstevel * complete separation of mailboxes - outbox is
75003831d35Sstevel * read/write, inbox is read-only.
75103831d35Sstevel * We only ever update the producer for the
75203831d35Sstevel * outbox and the consumer for the inbox.
75303831d35Sstevel */
75403831d35Sstevel mutex_enter(&master_mbox->mbox_in->mb_lock);
75503831d35Sstevel
75603831d35Sstevel for (;;) {
75703831d35Sstevel /*
75803831d35Sstevel * Get as many incoming messages as possible
75903831d35Sstevel */
76003831d35Sstevel while (sbbc_mbox_recv_msg() == 0)
76103831d35Sstevel /* empty */;
76203831d35Sstevel
76303831d35Sstevel /*
76403831d35Sstevel * send interrupt to SC to let it know that
76503831d35Sstevel * space is available over here
76603831d35Sstevel */
76703831d35Sstevel (void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN);
76803831d35Sstevel
76903831d35Sstevel mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].
77003831d35Sstevel mbox_intr_lock);
77103831d35Sstevel /*
77203831d35Sstevel * Read the inbox one more time to see if new messages
77303831d35Sstevel * has come in after we exit the loop.
77403831d35Sstevel */
77503831d35Sstevel if (sbbc_mbox_recv_msg() == 0) {
77603831d35Sstevel mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
77703831d35Sstevel mbox_intr_lock);
77803831d35Sstevel } else {
77903831d35Sstevel master_mbox->intr_state[MBOX_MSGIN_INTR].
78003831d35Sstevel mbox_intr_state = SBBC_INTR_IDLE;
78103831d35Sstevel mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
78203831d35Sstevel mbox_intr_lock);
78303831d35Sstevel break;
78403831d35Sstevel }
78503831d35Sstevel }
78603831d35Sstevel
78703831d35Sstevel mutex_exit(&master_mbox->mbox_in->mb_lock);
78803831d35Sstevel
78903831d35Sstevel return (DDI_INTR_CLAIMED);
79003831d35Sstevel }
79103831d35Sstevel
79203831d35Sstevel /*
79303831d35Sstevel * mailbox message sent
79403831d35Sstevel */
79503831d35Sstevel static int
sbbc_mbox_msgout()79603831d35Sstevel sbbc_mbox_msgout()
79703831d35Sstevel {
79803831d35Sstevel /*
79903831d35Sstevel * Should never get this
80003831d35Sstevel */
80103831d35Sstevel
80203831d35Sstevel return (DDI_INTR_CLAIMED);
80303831d35Sstevel }
80403831d35Sstevel
80503831d35Sstevel /*
80603831d35Sstevel * space in the inbox
80703831d35Sstevel */
80803831d35Sstevel static int
sbbc_mbox_spacein()80903831d35Sstevel sbbc_mbox_spacein()
81003831d35Sstevel {
81103831d35Sstevel /*
81203831d35Sstevel * Should never get this
81303831d35Sstevel */
81403831d35Sstevel
81503831d35Sstevel return (DDI_INTR_CLAIMED);
81603831d35Sstevel }
81703831d35Sstevel
81803831d35Sstevel /*
81903831d35Sstevel * space in the outbox
82003831d35Sstevel */
82103831d35Sstevel static int
sbbc_mbox_spaceout()82203831d35Sstevel sbbc_mbox_spaceout()
82303831d35Sstevel {
82403831d35Sstevel /*
82503831d35Sstevel * cv_broadcast() the threads waiting on the
82603831d35Sstevel * outbox's mb_full
82703831d35Sstevel */
82803831d35Sstevel
82903831d35Sstevel mutex_enter(&master_mbox->mbox_out->mb_lock);
83003831d35Sstevel
83103831d35Sstevel cv_broadcast(&master_mbox->mbox_out->mb_full);
83203831d35Sstevel
83303831d35Sstevel mutex_exit(&master_mbox->mbox_out->mb_lock);
83403831d35Sstevel
83503831d35Sstevel return (DDI_INTR_CLAIMED);
83603831d35Sstevel }
83703831d35Sstevel
83803831d35Sstevel /*
83903831d35Sstevel * Client Interface
84003831d35Sstevel *
84103831d35Sstevel * The main interface will be
84203831d35Sstevel *
84303831d35Sstevel * sbbc_mbox_request_response(sbbc_msg_t *request,
84403831d35Sstevel * sbbc_msg_t *response, time_t wait_time)
84503831d35Sstevel *
84603831d35Sstevel * 1) the client calls request_response
84703831d35Sstevel * 2) a new unique msg ID is assigned for that msg
84803831d35Sstevel * 3) if there is space available in the outbox
84903831d35Sstevel * - the request msg is written to the mbox_out mailbox
85003831d35Sstevel * and the mailbox info updated.
85103831d35Sstevel * - allocate a sbbc_msg_waiter struct for this
85203831d35Sstevel * message, initialise the w_cv condvar.
85303831d35Sstevel * - get the mailbox mbox_wait_lock mutex for this
85403831d35Sstevel * message type
85503831d35Sstevel * - the response msg is put on the mbox_wait_list for
85603831d35Sstevel * that message type to await the SC's response
85703831d35Sstevel * - wait on the w_cv condvar protected by the
85803831d35Sstevel * mbox_wait_lock
85903831d35Sstevel * - SBBC_MAILBOX_OUT interrupt is sent to the SC
86003831d35Sstevel *
86103831d35Sstevel * 4) if no space in the outbox,
86203831d35Sstevel * - the request message blocks waiting
86303831d35Sstevel * for a SBBC_MAILBOX_SPACE_OUT interrupt
86403831d35Sstevel * It will block on the mailbox mb_full condvar.
86503831d35Sstevel * - go to (3) above
86603831d35Sstevel * 5) When we get a SBBC_MAILBOX_IN interrupt.
86703831d35Sstevel * - read the message ID of the next message (FIFO)
86803831d35Sstevel * - find that ID on the wait list
86903831d35Sstevel * - no wait list entry => unsolicited message. If theres
87003831d35Sstevel * a handler, trigger it
87103831d35Sstevel * - if someone is waiting, read the message in from
87203831d35Sstevel * SRAM, handling fragmentation, wraparound, etc
87303831d35Sstevel * - if the whole message has been read, signal
87403831d35Sstevel * the waiter
87503831d35Sstevel * - read next message until mailbox empty
87603831d35Sstevel * - send SBBC_MAILBOX_SPACE_IN interrupt to the SC
87703831d35Sstevel *
87803831d35Sstevel * 6) If a response is required and none is received, the client
87903831d35Sstevel * will timeout after <wait_time> seconds and the message
88003831d35Sstevel * status will be set to ETIMEDOUT.
88103831d35Sstevel */
88203831d35Sstevel int
sbbc_mbox_request_response(sbbc_msg_t * request,sbbc_msg_t * response,time_t wait_time)88303831d35Sstevel sbbc_mbox_request_response(sbbc_msg_t *request,
88403831d35Sstevel sbbc_msg_t *response, time_t wait_time)
88503831d35Sstevel {
88603831d35Sstevel
88703831d35Sstevel struct sbbc_msg_waiter *waiter;
88803831d35Sstevel uint_t msg_id;
88903831d35Sstevel int rc = 0;
89003831d35Sstevel int flags;
89103831d35Sstevel uint16_t msg_type;
89203831d35Sstevel clock_t stop_time;
89303831d35Sstevel clock_t clockleft;
89403831d35Sstevel kmutex_t *mbox_wait_lock;
89503831d35Sstevel kmutex_t *mb_lock;
89603831d35Sstevel static fn_t f = "sbbc_mbox_request_response";
89703831d35Sstevel
89803831d35Sstevel if ((request == NULL) ||
89903831d35Sstevel (request->msg_type.type >= SBBC_MBOX_MSG_TYPES) ||
90003831d35Sstevel ((response != NULL) &&
90103831d35Sstevel (response->msg_type.type >= SBBC_MBOX_MSG_TYPES)))
90203831d35Sstevel return (EINVAL);
90303831d35Sstevel
90403831d35Sstevel msg_type = request->msg_type.type;
90503831d35Sstevel
90603831d35Sstevel /*
90703831d35Sstevel * Verify that we have already set up the master sbbc
90803831d35Sstevel */
90903831d35Sstevel if (master_mbox == NULL)
91003831d35Sstevel return (ENXIO);
91103831d35Sstevel mbox_wait_lock = &master_mbox->mbox_wait_lock[msg_type];
91203831d35Sstevel
91303831d35Sstevel flags = WAIT_FOR_REPLY|WAIT_FOR_SPACE;
91403831d35Sstevel
91503831d35Sstevel /*
91603831d35Sstevel * We want to place a lower limit on the shortest amount of time we
91703831d35Sstevel * will wait before timing out while communicating with the SC via
91803831d35Sstevel * the mailbox.
91903831d35Sstevel */
92003831d35Sstevel if (wait_time < sbbc_mbox_min_timeout)
92103831d35Sstevel wait_time = sbbc_mbox_default_timeout;
92203831d35Sstevel
92303831d35Sstevel stop_time = ddi_get_lbolt() + wait_time * drv_usectohz(MICROSEC);
92403831d35Sstevel
92503831d35Sstevel /*
92603831d35Sstevel * If there is a message being processed, sleep until it is our turn.
92703831d35Sstevel */
92803831d35Sstevel mutex_enter(&outbox_queue_lock);
92903831d35Sstevel
93003831d35Sstevel /*
93103831d35Sstevel * allocate an ID for this message, let it wrap
93203831d35Sstevel * around transparently.
93303831d35Sstevel * msg_id == 0 is unsolicited message
93403831d35Sstevel */
93503831d35Sstevel msg_id = ++(master_mbox->mbox_msg_id);
93603831d35Sstevel if (msg_id == 0)
93703831d35Sstevel msg_id = ++(master_mbox->mbox_msg_id);
93803831d35Sstevel
93903831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n",
94003831d35Sstevel f, msg_id, request->msg_len);
94103831d35Sstevel
94203831d35Sstevel /*
94303831d35Sstevel * A new message can actually grab the lock before the thread
94403831d35Sstevel * that has just been signaled. Therefore, we need to double
94503831d35Sstevel * check to make sure that outbox_busy is not already set
94603831d35Sstevel * after we wake up.
94703831d35Sstevel *
94803831d35Sstevel * Potentially this could mean starvation for certain unfortunate
94903831d35Sstevel * threads that keep getting woken up and putting back to sleep.
95003831d35Sstevel * But the window of such contention is very small to begin with.
95103831d35Sstevel */
95203831d35Sstevel while (outbox_busy) {
95303831d35Sstevel
95403831d35Sstevel clockleft = cv_timedwait(&outbox_queue, &outbox_queue_lock,
95503831d35Sstevel stop_time);
95603831d35Sstevel
95703831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up\n", f, msg_id);
95803831d35Sstevel
95903831d35Sstevel /*
96003831d35Sstevel * If we have timed out, set status to ETIMEOUT and return.
96103831d35Sstevel */
96203831d35Sstevel if (clockleft < 0) {
96303831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
96403831d35Sstevel f, msg_id);
96503831d35Sstevel cmn_err(CE_NOTE,
96603831d35Sstevel "Timed out obtaining SBBC outbox lock");
96703831d35Sstevel request->msg_status = ETIMEDOUT;
96803831d35Sstevel if (response != NULL)
96903831d35Sstevel response->msg_status = ETIMEDOUT;
97003831d35Sstevel mutex_exit(&outbox_queue_lock);
97103831d35Sstevel return (ETIMEDOUT);
97203831d35Sstevel }
97303831d35Sstevel }
97403831d35Sstevel
97503831d35Sstevel outbox_busy = 1;
97603831d35Sstevel mutex_exit(&outbox_queue_lock);
97703831d35Sstevel
97803831d35Sstevel /*
97903831d35Sstevel * We are only locking the OutBox from here, not the whole
98003831d35Sstevel * mailbox. This is based on the assumption of
98103831d35Sstevel * complete separation of mailboxes - outbox is
98203831d35Sstevel * read/write, inbox is read-only.
98303831d35Sstevel * We only ever update the producer for the
98403831d35Sstevel * outbox and the consumer for the inbox.
98503831d35Sstevel */
98603831d35Sstevel mb_lock = &master_mbox->mbox_out->mb_lock;
98703831d35Sstevel mutex_enter(mb_lock);
98803831d35Sstevel
98903831d35Sstevel /*
99003831d35Sstevel * No response expected ? Just send the message and return
99103831d35Sstevel */
99203831d35Sstevel if (response == NULL) {
99303831d35Sstevel rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time,
99403831d35Sstevel stop_time);
99503831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
99603831d35Sstevel f, msg_id, rc);
99703831d35Sstevel
99803831d35Sstevel wakeup_next();
99903831d35Sstevel
100003831d35Sstevel mutex_exit(mb_lock);
100103831d35Sstevel request->msg_status = rc;
100203831d35Sstevel return (rc);
100303831d35Sstevel }
100403831d35Sstevel
100503831d35Sstevel /*
100603831d35Sstevel * allocate/initialise a waiter
100703831d35Sstevel */
100803831d35Sstevel waiter = kmem_zalloc(sizeof (struct sbbc_msg_waiter), KM_NOSLEEP);
100903831d35Sstevel
101003831d35Sstevel if (waiter == (struct sbbc_msg_waiter *)NULL) {
101103831d35Sstevel cmn_err(CE_WARN, "SBBC Mailbox can't allocate waiter\n");
101203831d35Sstevel
101303831d35Sstevel wakeup_next();
101403831d35Sstevel
101503831d35Sstevel mutex_exit(mb_lock);
101603831d35Sstevel return (ENOMEM);
101703831d35Sstevel }
101803831d35Sstevel
101903831d35Sstevel waiter->w_id = 0; /* Until we get an ID from the send */
102003831d35Sstevel waiter->w_msg = response;
102103831d35Sstevel waiter->w_msg->msg_status = EINPROGRESS;
102203831d35Sstevel
102303831d35Sstevel cv_init(&waiter->w_cv, NULL, CV_DEFAULT, NULL);
102403831d35Sstevel
102503831d35Sstevel rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, stop_time);
102603831d35Sstevel
102703831d35Sstevel wakeup_next();
102803831d35Sstevel
102903831d35Sstevel if (rc != 0) {
103003831d35Sstevel
103103831d35Sstevel request->msg_status = response->msg_status = rc;
103203831d35Sstevel mutex_exit(mb_lock);
103303831d35Sstevel
103403831d35Sstevel /* Free the waiter */
103503831d35Sstevel cv_destroy(&waiter->w_cv);
103603831d35Sstevel kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
103703831d35Sstevel
103803831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
103903831d35Sstevel f, msg_id, rc);
104003831d35Sstevel
104103831d35Sstevel return (rc);
104203831d35Sstevel }
104303831d35Sstevel
104403831d35Sstevel waiter->w_id = msg_id;
104503831d35Sstevel
104603831d35Sstevel /*
104703831d35Sstevel * Lock this waiter list and add the waiter
104803831d35Sstevel */
104903831d35Sstevel mutex_enter(mbox_wait_lock);
105003831d35Sstevel
105103831d35Sstevel if (master_mbox->mbox_wait_list[msg_type] == NULL) {
105203831d35Sstevel master_mbox->mbox_wait_list[msg_type] = waiter;
105303831d35Sstevel waiter->w_next = NULL;
105403831d35Sstevel } else {
105503831d35Sstevel struct sbbc_msg_waiter *tmp;
105603831d35Sstevel tmp = master_mbox->mbox_wait_list[msg_type];
105703831d35Sstevel master_mbox->mbox_wait_list[msg_type] = waiter;
105803831d35Sstevel waiter->w_next = tmp;
105903831d35Sstevel }
106003831d35Sstevel
106103831d35Sstevel mutex_exit(mb_lock);
106203831d35Sstevel
106303831d35Sstevel /*
106403831d35Sstevel * wait here for a response to our message
106503831d35Sstevel * holding the mbox_wait_lock for the list ensures
106603831d35Sstevel * that the interrupt handler can't get in before
106703831d35Sstevel * we block.
106803831d35Sstevel * NOTE: We use the request msg_type for the
106903831d35Sstevel * the wait_list. This ensures that the
107003831d35Sstevel * msg_type won't change.
107103831d35Sstevel */
107203831d35Sstevel clockleft = cv_timedwait(&waiter->w_cv, mbox_wait_lock, stop_time);
107303831d35Sstevel
107403831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n",
107503831d35Sstevel f, msg_id);
107603831d35Sstevel
107703831d35Sstevel /*
107803831d35Sstevel * If we have timed out, set msg_status to ETIMEDOUT,
107903831d35Sstevel * and remove the waiter from the waiter list.
108003831d35Sstevel */
108103831d35Sstevel if (clockleft < 0) {
108203831d35Sstevel /*
108303831d35Sstevel * Remove the waiter from the waiter list.
108403831d35Sstevel * If we can't find the waiter in the list,
108503831d35Sstevel * 1. msg_status == EINPROGRESS
108603831d35Sstevel * It is being processed. We will give it
108703831d35Sstevel * a chance to finish.
108803831d35Sstevel * 2. msg_status != EINPROGRESS
108903831d35Sstevel * It is done processing. We can safely
109003831d35Sstevel * remove it.
109103831d35Sstevel * If we can find the waiter, it has timed out.
109203831d35Sstevel */
109303831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
109403831d35Sstevel f, msg_id);
109503831d35Sstevel if (mbox_find_waiter(msg_type, msg_id) == NULL) {
109603831d35Sstevel if (waiter->w_msg->msg_status == EINPROGRESS) {
109703831d35Sstevel SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x "
109803831d35Sstevel "complete.\n", f, msg_id);
109903831d35Sstevel cv_wait(&waiter->w_cv, mbox_wait_lock);
110003831d35Sstevel }
110103831d35Sstevel } else {
110203831d35Sstevel SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x "
110303831d35Sstevel "to ETIMEDOUT\n", f, msg_id);
110403831d35Sstevel cmn_err(CE_NOTE, "Timed out waiting for SC response");
110503831d35Sstevel rc = waiter->w_msg->msg_status = ETIMEDOUT;
110603831d35Sstevel }
110703831d35Sstevel }
110803831d35Sstevel
110903831d35Sstevel /*
111003831d35Sstevel * lose the waiter
111103831d35Sstevel */
111203831d35Sstevel cv_destroy(&waiter->w_cv);
111303831d35Sstevel kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
111403831d35Sstevel
111503831d35Sstevel mutex_exit(mbox_wait_lock);
111603831d35Sstevel
111703831d35Sstevel return (rc);
111803831d35Sstevel
111903831d35Sstevel }
112003831d35Sstevel
112103831d35Sstevel static void
wakeup_next()112203831d35Sstevel wakeup_next()
112303831d35Sstevel {
112403831d35Sstevel /*
112503831d35Sstevel * Done sending the current message or encounter an error.
112603831d35Sstevel * Wake up the one request in the outbox_queue.
112703831d35Sstevel */
112803831d35Sstevel mutex_enter(&outbox_queue_lock);
112903831d35Sstevel outbox_busy = 0;
113003831d35Sstevel cv_signal(&outbox_queue);
113103831d35Sstevel mutex_exit(&outbox_queue_lock);
113203831d35Sstevel }
113303831d35Sstevel
113403831d35Sstevel
113503831d35Sstevel /* ARGSUSED */
113603831d35Sstevel int
sbbc_mbox_send_msg(sbbc_msg_t * msg,int flags,uint_t msg_id,time_t wait_time,clock_t stop_time)113703831d35Sstevel sbbc_mbox_send_msg(sbbc_msg_t *msg, int flags, uint_t msg_id,
113803831d35Sstevel time_t wait_time, clock_t stop_time)
113903831d35Sstevel {
114003831d35Sstevel struct sbbc_mbox_header header;
114103831d35Sstevel struct sbbc_fragment frag;
114203831d35Sstevel int rc = 0;
114303831d35Sstevel int bytes_written;
114403831d35Sstevel uint32_t intr_enabled;
114503831d35Sstevel clock_t clockleft;
114603831d35Sstevel static fn_t f = "sbbc_mbox_send_msg";
114703831d35Sstevel
114803831d35Sstevel /*
114903831d35Sstevel * First check that the SC has enabled its mailbox
115003831d35Sstevel */
115103831d35Sstevel rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
115203831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled));
115303831d35Sstevel
115403831d35Sstevel if (rc)
115503831d35Sstevel return (rc);
115603831d35Sstevel
115703831d35Sstevel if (!(intr_enabled & SBBC_MAILBOX_OUT))
115803831d35Sstevel return (ENOTSUP);
115903831d35Sstevel
116003831d35Sstevel /*
116103831d35Sstevel * read the mailbox header
116203831d35Sstevel */
116303831d35Sstevel if (rc = mbox_read_header(SBBC_OUTBOX, &header))
116403831d35Sstevel return (rc);
116503831d35Sstevel
116603831d35Sstevel /*
116703831d35Sstevel * Allocate/initialise a fragment for this message
116803831d35Sstevel */
116903831d35Sstevel frag.f_id = msg_id;
117003831d35Sstevel frag.f_type = msg->msg_type;
117103831d35Sstevel frag.f_status = 0;
117203831d35Sstevel frag.f_total_len = msg->msg_len;
117303831d35Sstevel frag.f_frag_offset = 0;
117403831d35Sstevel /*
117503831d35Sstevel * Throw in the message data
117603831d35Sstevel */
117703831d35Sstevel bcopy(&msg->msg_data, &frag.f_data, sizeof (msg->msg_data));
117803831d35Sstevel
117903831d35Sstevel /*
118003831d35Sstevel * If not enough space is available
118103831d35Sstevel * write what we can and wait for
118203831d35Sstevel * an interrupt to tell us that more
118303831d35Sstevel * space is available
118403831d35Sstevel */
118503831d35Sstevel
118603831d35Sstevel bytes_written = 0;
118703831d35Sstevel do {
118803831d35Sstevel rc = mbox_write(&header, &frag, msg);
118903831d35Sstevel
119003831d35Sstevel if (rc != 0 && rc != ENOSPC) {
119103831d35Sstevel return (rc);
119203831d35Sstevel }
119303831d35Sstevel
119403831d35Sstevel if (rc == 0) {
119503831d35Sstevel /*
119603831d35Sstevel * Always tell the SC when there is a message.
119703831d35Sstevel * Ignore returned value as not being able to
119803831d35Sstevel * signal the SC about space available does
119903831d35Sstevel * not stop the SC from processing input.
120003831d35Sstevel */
120103831d35Sstevel (void) iosram_send_intr(SBBC_MAILBOX_OUT);
120203831d35Sstevel }
120303831d35Sstevel
120403831d35Sstevel bytes_written += frag.f_frag_len;
120503831d35Sstevel frag.f_frag_offset += frag.f_frag_len;
120603831d35Sstevel if ((bytes_written < msg->msg_len) || (rc == ENOSPC)) {
120703831d35Sstevel
120803831d35Sstevel if (mbox_has_free_space(&header) <=
120903831d35Sstevel sizeof (struct sbbc_fragment)) {
121003831d35Sstevel
121103831d35Sstevel int tmprc;
121203831d35Sstevel
121303831d35Sstevel clockleft = cv_timedwait(
121403831d35Sstevel &master_mbox->mbox_out->mb_full,
121503831d35Sstevel &master_mbox->mbox_out->mb_lock,
121603831d35Sstevel stop_time);
121703831d35Sstevel
121803831d35Sstevel /* Return ETIMEDOUT if we timed out */
121903831d35Sstevel if (clockleft < 0) {
122003831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x "
122103831d35Sstevel "has timed out\n", f, msg_id);
122203831d35Sstevel cmn_err(CE_NOTE,
122303831d35Sstevel "Timed out sending message "
122403831d35Sstevel "to SC");
122503831d35Sstevel return (ETIMEDOUT);
122603831d35Sstevel }
122703831d35Sstevel
122803831d35Sstevel /* Read updated header from IOSRAM */
122903831d35Sstevel if (tmprc = mbox_read_header(SBBC_OUTBOX,
123003831d35Sstevel &header)) {
123103831d35Sstevel
123203831d35Sstevel return (tmprc);
123303831d35Sstevel }
123403831d35Sstevel }
123503831d35Sstevel }
123603831d35Sstevel
123703831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, "
123803831d35Sstevel "msg_len = 0x%x\n", f,
123903831d35Sstevel msg_id, bytes_written, msg->msg_len);
124003831d35Sstevel } while ((bytes_written < msg->msg_len) || (rc == ENOSPC));
124103831d35Sstevel
124203831d35Sstevel /*
124303831d35Sstevel * this could be a spurious interrupt
124403831d35Sstevel * as the SC may be merrily readings its
124503831d35Sstevel * mail even as send, but what can you do ? No
124603831d35Sstevel * synchronization method between SC <-> OS
124703831d35Sstevel * SRAM data eaters means that this is inevitable.
124803831d35Sstevel * It would take a bigger brain to fix this.
124903831d35Sstevel *
125003831d35Sstevel */
125103831d35Sstevel (void) iosram_send_intr(SBBC_MAILBOX_OUT);
125203831d35Sstevel
125303831d35Sstevel return (rc);
125403831d35Sstevel }
125503831d35Sstevel
125603831d35Sstevel
125703831d35Sstevel /*
125803831d35Sstevel * get next message
125903831d35Sstevel * Read the next message from SRAM
126003831d35Sstevel * Check if theres an entry on the wait queue
126103831d35Sstevel * for this message
126203831d35Sstevel * If yes, read the message in and signal
126303831d35Sstevel * the waiter (if all the message has been received)
126403831d35Sstevel * No, its unsolicited, if theres a handler installed for
126503831d35Sstevel * this message type trigger it, otherwise toss
126603831d35Sstevel * the message
126703831d35Sstevel */
126803831d35Sstevel int
sbbc_mbox_recv_msg()126903831d35Sstevel sbbc_mbox_recv_msg()
127003831d35Sstevel {
127103831d35Sstevel struct sbbc_mbox_header header;
127203831d35Sstevel struct sbbc_fragment frag;
127303831d35Sstevel sbbc_msg_t tmpmsg; /* Temporary msg storage */
127403831d35Sstevel int rc = 0, i, first_hdlr, last_hdlr;
127503831d35Sstevel uint32_t intr_enabled;
127603831d35Sstevel sbbc_intrs_t *intr;
127703831d35Sstevel struct sbbc_msg_waiter *waiter;
127803831d35Sstevel uint16_t type; /* frag.f_type.type */
127903831d35Sstevel uint32_t f_id; /* frag.f_id */
128003831d35Sstevel uint32_t f_frag_offset, f_frag_len;
128103831d35Sstevel kmutex_t *mbox_wait_lock;
128203831d35Sstevel static fn_t f = "sbbc_mbox_recv_msg";
128303831d35Sstevel
128403831d35Sstevel /*
128503831d35Sstevel * First check that the OS has enabled its mailbox
128603831d35Sstevel */
128703831d35Sstevel rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
128803831d35Sstevel (caddr_t)&intr_enabled, sizeof (intr_enabled));
128903831d35Sstevel
129003831d35Sstevel if (rc) {
129103831d35Sstevel return (rc);
129203831d35Sstevel }
129303831d35Sstevel
129403831d35Sstevel if (!(intr_enabled & SBBC_MAILBOX_IN))
129503831d35Sstevel return (ENOTSUP);
129603831d35Sstevel
129703831d35Sstevel /*
129803831d35Sstevel * read the mailbox header
129903831d35Sstevel */
130003831d35Sstevel if (rc = mbox_read_header(SBBC_INBOX, &header))
130103831d35Sstevel return (rc);
130203831d35Sstevel
130303831d35Sstevel /*
130403831d35Sstevel * check if any messages available. If
130503831d35Sstevel * consumer == producer then no more
130603831d35Sstevel * messages
130703831d35Sstevel */
130803831d35Sstevel if ((header.mailboxes[SBBC_INBOX].mbox_consumer ==
130903831d35Sstevel header.mailboxes[SBBC_INBOX].mbox_producer)) {
131003831d35Sstevel
131103831d35Sstevel return (-1);
131203831d35Sstevel }
131303831d35Sstevel
131403831d35Sstevel /*
131503831d35Sstevel * read the fragment header for this message
131603831d35Sstevel */
131703831d35Sstevel if (rc = mbox_read_frag(&header, &frag)) {
131803831d35Sstevel
131903831d35Sstevel return (rc);
132003831d35Sstevel }
132103831d35Sstevel
132203831d35Sstevel /* Save to local variable for easy reading */
132303831d35Sstevel type = frag.f_type.type;
132403831d35Sstevel f_id = frag.f_id;
132503831d35Sstevel
132603831d35Sstevel SGSBBC_DBG_MBOX("%s: f_id = 0x%x\n", f, f_id);
132703831d35Sstevel
132803831d35Sstevel /*
132903831d35Sstevel * check the message type. If its invalid, we will
133003831d35Sstevel * just toss the message
133103831d35Sstevel */
133203831d35Sstevel if (type >= SBBC_MBOX_MSG_TYPES) {
133303831d35Sstevel goto done;
133403831d35Sstevel }
133503831d35Sstevel
133603831d35Sstevel /*
133703831d35Sstevel * if theres no waiters for this message type, and theres
133803831d35Sstevel * no message handler installed, toss it.
133903831d35Sstevel *
134003831d35Sstevel * Unsolicited messages (f_id == 0) are tricky because we won't know
134103831d35Sstevel * when the handler has finished so that we can
134203831d35Sstevel * remove the message, so, given the small brains in operation
134303831d35Sstevel * here, what we do is restrict junk mail to zero-length
134403831d35Sstevel * messages, then we allocate a fragment using kmem,
134503831d35Sstevel * make a copy of the fragment in this memory,
134603831d35Sstevel * pass this pointer to the fragment, then skip the message.
134703831d35Sstevel * So even if there is data associated with the junkmail,
134803831d35Sstevel * the message handler doesn't get to see it
134903831d35Sstevel * We expect the mesaage handler to free the memory.
135003831d35Sstevel */
135103831d35Sstevel if (type == SBBC_BROADCAST_MSG) {
135203831d35Sstevel /*
135303831d35Sstevel * Broadcast message, trigger all handlers
135403831d35Sstevel */
135503831d35Sstevel first_hdlr = 0;
135603831d35Sstevel last_hdlr = SBBC_MBOX_MSG_TYPES - 1;
135703831d35Sstevel } else if ((master_mbox->mbox_wait_list[type] == NULL) || (f_id == 0)) {
135803831d35Sstevel /*
135903831d35Sstevel * Theres no waiters, or its unsolicited anyway
136003831d35Sstevel */
136103831d35Sstevel first_hdlr = last_hdlr = type;
136203831d35Sstevel } else {
136303831d35Sstevel /*
136403831d35Sstevel * check the fragment message type, look at the wait list for
136503831d35Sstevel * that type to find its associated message
136603831d35Sstevel *
136703831d35Sstevel * First find the message. If we get it, take it off
136803831d35Sstevel * the waiter list and read the data. We will
136903831d35Sstevel * put it back on the list if necessary.
137003831d35Sstevel * This avoids the problem of a second message-in
137103831d35Sstevel * interrupt playing with this waiter.
137203831d35Sstevel * This will cut down on mutex spinning on the wait
137303831d35Sstevel * list locks, also, expect the next fragment to be
137403831d35Sstevel * for this messageso we might as well have it at the
137503831d35Sstevel * start of the list.
137603831d35Sstevel *
137703831d35Sstevel * its possible that a return message has a different type,
137803831d35Sstevel * (possible but not recommended!). So, if we don't find
137903831d35Sstevel * it on the list pointed to by the request type,
138003831d35Sstevel * go look at all the other lists
138103831d35Sstevel */
138203831d35Sstevel
138303831d35Sstevel mbox_wait_lock = &master_mbox->mbox_wait_lock[type];
138403831d35Sstevel
138503831d35Sstevel mutex_enter(mbox_wait_lock);
138603831d35Sstevel if ((waiter = mbox_find_waiter(type, f_id)) == NULL) {
138703831d35Sstevel for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
138803831d35Sstevel if (i == type)
138903831d35Sstevel continue;
139003831d35Sstevel if ((waiter = mbox_find_waiter(i, f_id))
139103831d35Sstevel != NULL)
139203831d35Sstevel break;
139303831d35Sstevel }
139403831d35Sstevel }
139503831d35Sstevel mutex_exit(mbox_wait_lock);
139603831d35Sstevel
139703831d35Sstevel if (waiter == NULL) {
139803831d35Sstevel rc = -1;
139903831d35Sstevel /*
140003831d35Sstevel * there's no waiter for this message, but that
140103831d35Sstevel * could mean that this message is the start of
140203831d35Sstevel * a send/receive to us, and every 'first' request
140303831d35Sstevel * must by definition be unsolicited,
140403831d35Sstevel * so trigger the handler
140503831d35Sstevel */
140603831d35Sstevel first_hdlr = last_hdlr = type;
140703831d35Sstevel } else {
140803831d35Sstevel SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, "
140903831d35Sstevel "msg_len = 0x%x\n",
141003831d35Sstevel f, f_id, waiter->w_id,
141103831d35Sstevel waiter->w_msg->msg_len);
141203831d35Sstevel
141303831d35Sstevel rc = mbox_read(&header, &frag, waiter->w_msg);
141403831d35Sstevel
141503831d35Sstevel SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, "
141603831d35Sstevel "len = 0x%x, total_len = 0x%x\n",
141703831d35Sstevel f, frag.f_id, frag.f_frag_offset,
141803831d35Sstevel frag.f_frag_len, frag.f_total_len);
141903831d35Sstevel
142003831d35Sstevel if (rc || ((frag.f_frag_offset + frag.f_frag_len) ==
142103831d35Sstevel frag.f_total_len)) {
142203831d35Sstevel /*
142303831d35Sstevel * failed or all the message has been read in
142403831d35Sstevel */
142503831d35Sstevel mutex_enter(mbox_wait_lock);
142603831d35Sstevel waiter->w_msg->msg_status = (rc == ENOMEM)?
142703831d35Sstevel rc : frag.f_status;
142803831d35Sstevel SGSBBC_DBG_MBOX("%s: msg_status = %d\n",
142903831d35Sstevel f, waiter->w_msg->msg_status);
143003831d35Sstevel cv_signal(&waiter->w_cv);
143103831d35Sstevel mutex_exit(mbox_wait_lock);
143203831d35Sstevel
143303831d35Sstevel } else {
143403831d35Sstevel /*
143503831d35Sstevel * back on the wait list
143603831d35Sstevel */
143703831d35Sstevel mutex_enter(mbox_wait_lock);
143803831d35Sstevel if (waiter->w_msg->msg_status == ETIMEDOUT) {
143903831d35Sstevel cv_signal(&waiter->w_cv);
144003831d35Sstevel mutex_exit(mbox_wait_lock);
144103831d35Sstevel goto done;
144203831d35Sstevel }
144303831d35Sstevel
144403831d35Sstevel if (master_mbox->mbox_wait_list[type] == NULL) {
144503831d35Sstevel master_mbox->mbox_wait_list[type] =
144603831d35Sstevel waiter;
144703831d35Sstevel waiter->w_next = NULL;
144803831d35Sstevel } else {
144903831d35Sstevel struct sbbc_msg_waiter *tmp;
145003831d35Sstevel tmp = master_mbox->mbox_wait_list[type];
145103831d35Sstevel master_mbox->mbox_wait_list[type] =
145203831d35Sstevel waiter;
145303831d35Sstevel waiter->w_next = tmp;
145403831d35Sstevel }
145503831d35Sstevel mutex_exit(mbox_wait_lock);
145603831d35Sstevel }
145703831d35Sstevel goto done;
145803831d35Sstevel }
145903831d35Sstevel }
146003831d35Sstevel
146103831d35Sstevel /*
146203831d35Sstevel * Set msg_len to f_frag_len so msg_buf will be large enough
146303831d35Sstevel * to contain what is in the fragment.
146403831d35Sstevel */
146503831d35Sstevel f_frag_len = tmpmsg.msg_len = frag.f_frag_len;
146603831d35Sstevel /*
146703831d35Sstevel * Save the f_frag_offset for copying into client's space.
146803831d35Sstevel * Set frag.f_frag_offset to 0 so we don't have to allocate
146903831d35Sstevel * too much space for reading in the message.
147003831d35Sstevel */
147103831d35Sstevel f_frag_offset = frag.f_frag_offset;
147203831d35Sstevel frag.f_frag_offset = 0;
147303831d35Sstevel
147403831d35Sstevel /* Allocate space for msg_buf */
147503831d35Sstevel if (f_frag_len != 0 && (tmpmsg.msg_buf =
147603831d35Sstevel kmem_alloc(f_frag_len, KM_NOSLEEP)) == NULL) {
147703831d35Sstevel
147803831d35Sstevel rc = ENOMEM;
147903831d35Sstevel cmn_err(CE_WARN, "Can't allocate memory"
148003831d35Sstevel " for unsolicited messages\n");
148103831d35Sstevel } else {
148203831d35Sstevel /* Save the incoming message in tmpmsg */
148303831d35Sstevel rc = mbox_read(&header, &frag, &tmpmsg);
148403831d35Sstevel
148503831d35Sstevel for (i = first_hdlr; rc == 0 && i <= last_hdlr; i++) {
148603831d35Sstevel
148703831d35Sstevel intr = master_mbox->intrs[i];
148803831d35Sstevel if ((intr == NULL) || (intr->sbbc_intr_id == 0)) {
148903831d35Sstevel continue;
149003831d35Sstevel }
149103831d35Sstevel
149203831d35Sstevel while (intr != NULL) {
149303831d35Sstevel /*
149403831d35Sstevel * If the client has allocated enough space
149503831d35Sstevel * for incoming message, copy into the
149603831d35Sstevel * client buffer.
149703831d35Sstevel */
149803831d35Sstevel sbbc_msg_t *arg = (sbbc_msg_t *)intr->sbbc_arg;
149903831d35Sstevel if (arg != (void *)NULL) {
150003831d35Sstevel if (arg->msg_len >= frag.f_total_len) {
150103831d35Sstevel if (f_frag_len > 0)
150203831d35Sstevel bcopy(tmpmsg.msg_buf,
150303831d35Sstevel arg->msg_buf +
150403831d35Sstevel f_frag_offset,
150503831d35Sstevel f_frag_len);
150603831d35Sstevel } else {
150703831d35Sstevel arg->msg_status = ENOMEM;
150803831d35Sstevel }
150903831d35Sstevel }
151003831d35Sstevel
151103831d35Sstevel /*
151203831d35Sstevel * Only trigger the interrupt when we
151303831d35Sstevel * have received the whole message.
151403831d35Sstevel */
151503831d35Sstevel if (f_frag_offset + f_frag_len ==
151603831d35Sstevel frag.f_total_len) {
151703831d35Sstevel
151803831d35Sstevel ddi_trigger_softintr(
151903831d35Sstevel intr->sbbc_intr_id);
152003831d35Sstevel }
152103831d35Sstevel intr = intr->sbbc_intr_next;
152203831d35Sstevel }
152303831d35Sstevel }
152403831d35Sstevel
152503831d35Sstevel if (f_frag_len != 0) {
152603831d35Sstevel /* Don't forget to free the buffer */
152703831d35Sstevel kmem_free(tmpmsg.msg_buf, f_frag_len);
152803831d35Sstevel }
152903831d35Sstevel }
153003831d35Sstevel done:
153103831d35Sstevel mbox_skip_next_msg(&header);
153203831d35Sstevel return (rc);
153303831d35Sstevel }
153403831d35Sstevel
153503831d35Sstevel /*
153603831d35Sstevel * available free space in the outbox
153703831d35Sstevel */
153803831d35Sstevel static int
mbox_has_free_space(struct sbbc_mbox_header * header)153903831d35Sstevel mbox_has_free_space(struct sbbc_mbox_header *header)
154003831d35Sstevel {
154103831d35Sstevel uint32_t space = 0;
154203831d35Sstevel
154303831d35Sstevel ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
154403831d35Sstevel
154503831d35Sstevel if (header->mailboxes[SBBC_OUTBOX].mbox_producer ==
154603831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
154703831d35Sstevel /*
154803831d35Sstevel * mailbox is empty
154903831d35Sstevel */
155003831d35Sstevel space += header->mailboxes[SBBC_OUTBOX].mbox_len -
155103831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer;
155203831d35Sstevel space +=
155303831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer;
155403831d35Sstevel } else if (header->mailboxes[SBBC_OUTBOX].mbox_producer >
155503831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
155603831d35Sstevel space += header->mailboxes[SBBC_OUTBOX].mbox_len -
155703831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer;
155803831d35Sstevel space += header->mailboxes[SBBC_OUTBOX].mbox_consumer;
155903831d35Sstevel } else {
156003831d35Sstevel /*
156103831d35Sstevel * mailbox wrapped around
156203831d35Sstevel */
156303831d35Sstevel space += header->mailboxes[SBBC_OUTBOX].mbox_consumer -
156403831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer;
156503831d35Sstevel }
156603831d35Sstevel
156703831d35Sstevel /*
156803831d35Sstevel * Need to make sure that the mailbox never
156903831d35Sstevel * gets completely full, as consumer == producer is
157003831d35Sstevel * our test for empty, so we drop MBOX_ALIGN_BYTES.
157103831d35Sstevel */
157203831d35Sstevel
157303831d35Sstevel if (space >= MBOX_ALIGN_BYTES)
157403831d35Sstevel space -= MBOX_ALIGN_BYTES;
157503831d35Sstevel else
157603831d35Sstevel space = 0;
157703831d35Sstevel
157803831d35Sstevel return (space);
157903831d35Sstevel
158003831d35Sstevel }
158103831d35Sstevel /*
158203831d35Sstevel * Write the data to IOSRAM
158303831d35Sstevel * Update the SRAM mailbox header
158403831d35Sstevel * Update the local mailbox pointers
158503831d35Sstevel * Only write a single fragment. If possible,
158603831d35Sstevel * put the whole message into a fragment.
158703831d35Sstevel *
158803831d35Sstevel * Note: We assume that there is no 'max' message
158903831d35Sstevel * size. We will just keep fragmenting.
159003831d35Sstevel * Note: We always write to SBBC_OUTBOX and
159103831d35Sstevel * read from SBBC_INBOX
159203831d35Sstevel *
159303831d35Sstevel * If we get an error at any time, return immediately
159403831d35Sstevel * without updating the mailbox header in SRAM
159503831d35Sstevel */
159603831d35Sstevel static int
mbox_write(struct sbbc_mbox_header * header,struct sbbc_fragment * frag,sbbc_msg_t * msg)159703831d35Sstevel mbox_write(struct sbbc_mbox_header *header,
159803831d35Sstevel struct sbbc_fragment *frag, sbbc_msg_t *msg)
159903831d35Sstevel {
160003831d35Sstevel int bytes_written, bytes_remaining, free_space;
160103831d35Sstevel int rc = 0;
160203831d35Sstevel caddr_t src;
160303831d35Sstevel uint32_t sram_dst;
160403831d35Sstevel int space_at_end, space_at_start;
160503831d35Sstevel uint32_t mbox_offset, mbox_len;
160603831d35Sstevel uint32_t mbox_producer, mbox_consumer;
160703831d35Sstevel uint32_t f_total_len, f_frag_offset;
160803831d35Sstevel uint32_t frag_header_size;
160903831d35Sstevel static fn_t f = "mbox_write";
161003831d35Sstevel
161103831d35Sstevel ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
161203831d35Sstevel
161303831d35Sstevel /*
161403831d35Sstevel * Save to local variables to make code more readable
161503831d35Sstevel */
161603831d35Sstevel mbox_offset = header->mailboxes[SBBC_OUTBOX].mbox_offset;
161703831d35Sstevel mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
161803831d35Sstevel mbox_producer = header->mailboxes[SBBC_OUTBOX].mbox_producer;
161903831d35Sstevel mbox_consumer = header->mailboxes[SBBC_OUTBOX].mbox_consumer;
162003831d35Sstevel f_total_len = frag->f_total_len;
162103831d35Sstevel f_frag_offset = frag->f_frag_offset;
162203831d35Sstevel frag_header_size = sizeof (struct sbbc_fragment);
162303831d35Sstevel
162403831d35Sstevel SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, "
162503831d35Sstevel "mbox_producer = 0x%x\n", f, mbox_consumer, mbox_producer);
162603831d35Sstevel
162703831d35Sstevel /*
162803831d35Sstevel * Write pointer in SRAM
162903831d35Sstevel */
163003831d35Sstevel sram_dst = mbox_offset + mbox_producer;
163103831d35Sstevel
163203831d35Sstevel /*
163303831d35Sstevel * NB We assume that the consumer stays constant
163403831d35Sstevel * during the write. It may not necessarily
163503831d35Sstevel * be the case but it won't cause us any problems, just means
163603831d35Sstevel * we fragment more than is absolutely necessary
163703831d35Sstevel *
163803831d35Sstevel * possible cases
163903831d35Sstevel * 1) consumer == producer, mailbox empty
164003831d35Sstevel * space_at_end == mailbox end - producer
164103831d35Sstevel * space_at_start == producer - MBOX_ALIGN_BYTES
164203831d35Sstevel * 2) producer < consumer
164303831d35Sstevel * space_at_end = (consumer - producer - MBOX_ALIGN_BYTES)
164403831d35Sstevel * space_at_start == 0
164503831d35Sstevel * 3) producer > consumer
164603831d35Sstevel * space_at_end = mailbox end - producer
164703831d35Sstevel * space_at_start = consumer - MBOX_ALIGN_BYTES
164803831d35Sstevel *
164903831d35Sstevel * (space - MBOX_ALIGN_BYTES) because we need to avoid the
165003831d35Sstevel * scenario where the producer wraps around completely and
165103831d35Sstevel * producer == consumer, as this is our test for 'empty'.
165203831d35Sstevel * Also we want it to be 8-byte aligned.
165303831d35Sstevel * Note: start is assumed = 0
165403831d35Sstevel */
165503831d35Sstevel if (mbox_producer < mbox_consumer) {
165603831d35Sstevel space_at_end = mbox_consumer - mbox_producer - MBOX_ALIGN_BYTES;
165703831d35Sstevel if (space_at_end < 0)
165803831d35Sstevel space_at_end = 0;
165903831d35Sstevel space_at_start = 0;
166003831d35Sstevel } else {
166103831d35Sstevel space_at_end = mbox_len - mbox_producer;
166203831d35Sstevel if (mbox_consumer == 0)
166303831d35Sstevel space_at_end -= MBOX_ALIGN_BYTES;
166403831d35Sstevel space_at_start = mbox_consumer - MBOX_ALIGN_BYTES;
166503831d35Sstevel if (space_at_start < 0)
166603831d35Sstevel space_at_start = 0;
166703831d35Sstevel }
166803831d35Sstevel
166903831d35Sstevel SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
167003831d35Sstevel f, space_at_end, space_at_start);
167103831d35Sstevel
167203831d35Sstevel free_space = space_at_end + space_at_start;
167303831d35Sstevel
167403831d35Sstevel if (free_space < frag_header_size) {
167503831d35Sstevel /*
167603831d35Sstevel * can't even write a fragment header, so just return
167703831d35Sstevel * the caller will block waiting for space
167803831d35Sstevel */
167903831d35Sstevel frag->f_frag_len = 0;
168003831d35Sstevel return (ENOSPC);
168103831d35Sstevel }
168203831d35Sstevel
168303831d35Sstevel /*
168403831d35Sstevel * How many bytes will be in the fragment ?
168503831d35Sstevel */
168603831d35Sstevel bytes_remaining = f_total_len - f_frag_offset;
168703831d35Sstevel frag->f_frag_len = min(bytes_remaining, free_space - frag_header_size);
168803831d35Sstevel
168903831d35Sstevel SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n",
169003831d35Sstevel f, sram_dst);
169103831d35Sstevel
169203831d35Sstevel /*
169303831d35Sstevel * we can write the fragment header and some data
169403831d35Sstevel * First, the fragment header
169503831d35Sstevel */
169603831d35Sstevel if (space_at_end >= frag_header_size) {
169703831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, (caddr_t)frag,
169803831d35Sstevel frag_header_size);
169903831d35Sstevel if (rc)
170003831d35Sstevel return (rc);
170103831d35Sstevel
170203831d35Sstevel sram_dst = (uint32_t)(sram_dst + frag_header_size);
170303831d35Sstevel /*
170403831d35Sstevel * Wrap around if we reach the end
170503831d35Sstevel */
170603831d35Sstevel if (sram_dst >= (mbox_len + mbox_offset)) {
170703831d35Sstevel sram_dst = mbox_offset;
170803831d35Sstevel }
170903831d35Sstevel space_at_end -= frag_header_size;
171003831d35Sstevel } else {
171103831d35Sstevel /* wraparound */
171203831d35Sstevel if (space_at_end) {
171303831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
171403831d35Sstevel (caddr_t)frag, space_at_end);
171503831d35Sstevel if (rc)
171603831d35Sstevel return (rc);
171703831d35Sstevel sram_dst = (uint32_t)mbox_offset;
171803831d35Sstevel }
171903831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
172003831d35Sstevel (caddr_t)((caddr_t)frag + space_at_end),
172103831d35Sstevel (frag_header_size - space_at_end));
172203831d35Sstevel if (rc)
172303831d35Sstevel return (rc);
172403831d35Sstevel sram_dst += frag_header_size - space_at_end;
172503831d35Sstevel space_at_start -= (frag_header_size - space_at_end);
172603831d35Sstevel space_at_end = 0;
172703831d35Sstevel }
172803831d35Sstevel
172903831d35Sstevel SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
173003831d35Sstevel f, space_at_end, space_at_start);
173103831d35Sstevel
173203831d35Sstevel /*
173303831d35Sstevel * Now the fragment data
173403831d35Sstevel */
173503831d35Sstevel free_space -= frag_header_size;
173603831d35Sstevel src = (caddr_t)(msg->msg_buf + f_frag_offset);
173703831d35Sstevel bytes_written = 0;
173803831d35Sstevel if (space_at_end) {
173903831d35Sstevel SGSBBC_DBG_MBOX("%s: writing data:sram_dst = 0x%x, "
174003831d35Sstevel "bytes_remaining = 0x%x\n",
174103831d35Sstevel f, sram_dst, bytes_remaining);
174203831d35Sstevel
174303831d35Sstevel if (space_at_end < bytes_remaining)
174403831d35Sstevel bytes_written = space_at_end;
174503831d35Sstevel else
174603831d35Sstevel bytes_written = bytes_remaining;
174703831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
174803831d35Sstevel bytes_written);
174903831d35Sstevel if (rc)
175003831d35Sstevel return (rc);
175103831d35Sstevel
175203831d35Sstevel sram_dst = (uint32_t)(sram_dst + bytes_written);
175303831d35Sstevel /*
175403831d35Sstevel * Wrap around if we reach the end
175503831d35Sstevel */
175603831d35Sstevel if (sram_dst >= (mbox_len + mbox_offset)) {
175703831d35Sstevel sram_dst = mbox_offset;
175803831d35Sstevel }
175903831d35Sstevel src = (caddr_t)(src + bytes_written);
176003831d35Sstevel bytes_remaining -= bytes_written;
176103831d35Sstevel }
176203831d35Sstevel
176303831d35Sstevel if ((bytes_remaining > 0) && space_at_start) {
176403831d35Sstevel SGSBBC_DBG_MBOX("%s: writing the rest:sram_dst = 0x%x, "
176503831d35Sstevel "bytes_remaining = 0x%x\n",
176603831d35Sstevel f, sram_dst, bytes_remaining);
176703831d35Sstevel if (space_at_start < bytes_remaining) {
176803831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
176903831d35Sstevel space_at_start);
177003831d35Sstevel bytes_written += space_at_start;
177103831d35Sstevel } else {
177203831d35Sstevel rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
177303831d35Sstevel bytes_remaining);
177403831d35Sstevel bytes_written += bytes_remaining;
177503831d35Sstevel }
177603831d35Sstevel if (rc)
177703831d35Sstevel return (rc);
177803831d35Sstevel }
177903831d35Sstevel
178003831d35Sstevel frag->f_frag_len = bytes_written;
178103831d35Sstevel
178203831d35Sstevel /*
178303831d35Sstevel * update header->mbox_producer (bytes_written + frag_size)
178403831d35Sstevel */
178503831d35Sstevel sram_dst = mbox_producer + bytes_written + frag_header_size;
178603831d35Sstevel if (sram_dst >= mbox_len) {
178703831d35Sstevel sram_dst = sram_dst % mbox_len;
178803831d35Sstevel }
178903831d35Sstevel
179003831d35Sstevel SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, "
179103831d35Sstevel "bytes_written = 0x%x\n", f, sram_dst, bytes_written);
179203831d35Sstevel
179303831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer = sram_dst;
179403831d35Sstevel
179503831d35Sstevel mbox_update_header(SBBC_OUTBOX, header);
179603831d35Sstevel
179703831d35Sstevel
179803831d35Sstevel return (rc);
179903831d35Sstevel }
180003831d35Sstevel
180103831d35Sstevel
180203831d35Sstevel /*
180303831d35Sstevel * Get the next frag from IOSRAM.
180403831d35Sstevel * Write it to the corresponding msg buf.
180503831d35Sstevel * The caller must update the SRAM pointers etc.
180603831d35Sstevel */
180703831d35Sstevel static int
mbox_read(struct sbbc_mbox_header * header,struct sbbc_fragment * frag,sbbc_msg_t * msg)180803831d35Sstevel mbox_read(struct sbbc_mbox_header *header,
180903831d35Sstevel struct sbbc_fragment *frag, sbbc_msg_t *msg)
181003831d35Sstevel {
181103831d35Sstevel int rc = 0;
181203831d35Sstevel uint32_t sram_src, sram_end;
181303831d35Sstevel caddr_t msg_buf;
181403831d35Sstevel int bytes_at_start, bytes_at_end;
181503831d35Sstevel int bytes_to_read;
181603831d35Sstevel uint32_t frag_header_size, frag_total_size;
181703831d35Sstevel uint32_t f_frag_offset, f_frag_len;
181803831d35Sstevel uint32_t mbox_producer, mbox_consumer;
181903831d35Sstevel uint32_t mbox_len, mbox_offset;
182003831d35Sstevel static fn_t f = "mbox_read";
182103831d35Sstevel
182203831d35Sstevel ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
182303831d35Sstevel
182403831d35Sstevel /*
182503831d35Sstevel * Save to local variables to make code more readable
182603831d35Sstevel */
182703831d35Sstevel mbox_producer = header->mailboxes[SBBC_INBOX].mbox_producer;
182803831d35Sstevel mbox_consumer = header->mailboxes[SBBC_INBOX].mbox_consumer;
182903831d35Sstevel mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
183003831d35Sstevel mbox_offset = header->mailboxes[SBBC_INBOX].mbox_offset;
183103831d35Sstevel frag_header_size = sizeof (struct sbbc_fragment);
183203831d35Sstevel f_frag_offset = frag->f_frag_offset;
183303831d35Sstevel f_frag_len = frag->f_frag_len;
183403831d35Sstevel frag_total_size = frag_header_size + f_frag_len;
183503831d35Sstevel
183603831d35Sstevel /*
183703831d35Sstevel * If the message buffer size is smaller than the fragment
183803831d35Sstevel * size, return an error.
183903831d35Sstevel */
184003831d35Sstevel if (msg->msg_len < f_frag_len) {
184103831d35Sstevel rc = ENOMEM;
184203831d35Sstevel goto done;
184303831d35Sstevel }
184403831d35Sstevel
184503831d35Sstevel msg_buf = (caddr_t)(msg->msg_buf + f_frag_offset);
184603831d35Sstevel
184703831d35Sstevel /*
184803831d35Sstevel * Throw in the message data
184903831d35Sstevel */
185003831d35Sstevel bcopy(&frag->f_data, &msg->msg_data, sizeof (msg->msg_data));
185103831d35Sstevel
185203831d35Sstevel /*
185303831d35Sstevel * We have it all, waiter, message, so lets
185403831d35Sstevel * go get that puppy!
185503831d35Sstevel * Message could be in one or two chunks -
185603831d35Sstevel * consumer < producer: 1 chunk, (producer - consumer)
185703831d35Sstevel * consumer > producer: 2 chunks, (end - consumer)
185803831d35Sstevel * (producer - start)
185903831d35Sstevel */
186003831d35Sstevel sram_end = (uint32_t)(mbox_offset + mbox_len);
186103831d35Sstevel sram_src = (uint32_t)(mbox_offset + mbox_consumer + frag_header_size);
186203831d35Sstevel
186303831d35Sstevel /*
186403831d35Sstevel * wraparound
186503831d35Sstevel */
186603831d35Sstevel if (sram_src >= sram_end)
186703831d35Sstevel sram_src -= mbox_len;
186803831d35Sstevel
186903831d35Sstevel /*
187003831d35Sstevel * find where the data is
187103831d35Sstevel * possible cases
187203831d35Sstevel * 1) consumer == producer, mailbox empty
187303831d35Sstevel * error
187403831d35Sstevel * 2) producer < consumer
187503831d35Sstevel * bytes_at_end = mailbox end - consumer
187603831d35Sstevel * bytes_at_start = producer
187703831d35Sstevel * 3) producer > consumer
187803831d35Sstevel * bytes_at_end = producer - consumer
187903831d35Sstevel * bytes_at_start = 0
188003831d35Sstevel */
188103831d35Sstevel
188203831d35Sstevel SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, "
188303831d35Sstevel "frag_len = 0x%x\n",
188403831d35Sstevel f, mbox_consumer, mbox_producer, f_frag_len);
188503831d35Sstevel
188603831d35Sstevel if (mbox_producer == mbox_consumer) {
188703831d35Sstevel bytes_at_end = bytes_at_start = 0;
188803831d35Sstevel } else if (mbox_producer < mbox_consumer) {
188903831d35Sstevel bytes_at_end = mbox_len - mbox_consumer;
189003831d35Sstevel bytes_at_start = mbox_producer;
189103831d35Sstevel } else {
189203831d35Sstevel bytes_at_end = mbox_producer - mbox_consumer;
189303831d35Sstevel bytes_at_start = 0;
189403831d35Sstevel }
189503831d35Sstevel
189603831d35Sstevel SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, "
189703831d35Sstevel "bytes_at_start = 0x%x\n", f, bytes_at_end, bytes_at_start);
189803831d35Sstevel
189903831d35Sstevel if ((bytes_at_end + bytes_at_start) < frag_total_size) {
190003831d35Sstevel
190103831d35Sstevel /*
190203831d35Sstevel * mailbox is corrupt
190303831d35Sstevel * but what to do ?
190403831d35Sstevel */
190503831d35Sstevel cmn_err(CE_PANIC, "Corrupt INBOX!\n"
190603831d35Sstevel "producer = %x, consumer = %x, bytes_at_start = %x, "
190703831d35Sstevel "bytes_at_end = %x\n", mbox_producer, mbox_consumer,
190803831d35Sstevel bytes_at_start, bytes_at_end);
190903831d35Sstevel }
191003831d35Sstevel
191103831d35Sstevel /*
191203831d35Sstevel * If bytes_at_end is greater than header size, read the
191303831d35Sstevel * part at the end of the mailbox, and then update the
191403831d35Sstevel * pointers and bytes_to_read.
191503831d35Sstevel */
191603831d35Sstevel if (bytes_at_end > frag_header_size) {
191703831d35Sstevel /*
191803831d35Sstevel * We are only interested in the data segment.
191903831d35Sstevel */
192003831d35Sstevel bytes_at_end -= frag_header_size;
192103831d35Sstevel bytes_to_read = (bytes_at_end >= f_frag_len)?
192203831d35Sstevel f_frag_len : bytes_at_end;
192303831d35Sstevel SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, "
192403831d35Sstevel "bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
192503831d35Sstevel rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
192603831d35Sstevel bytes_to_read);
192703831d35Sstevel if (rc) {
192803831d35Sstevel goto done;
192903831d35Sstevel }
193003831d35Sstevel
193103831d35Sstevel /*
193203831d35Sstevel * Update pointers in SRAM and message buffer.
193303831d35Sstevel */
193403831d35Sstevel sram_src = (uint32_t)mbox_offset;
193503831d35Sstevel msg_buf = (caddr_t)(msg_buf + bytes_to_read);
193603831d35Sstevel bytes_to_read = f_frag_len - bytes_to_read;
193703831d35Sstevel } else {
193803831d35Sstevel bytes_to_read = f_frag_len;
193903831d35Sstevel }
194003831d35Sstevel
194103831d35Sstevel /*
194203831d35Sstevel * wraparound to start of mailbox
194303831d35Sstevel */
194403831d35Sstevel if (bytes_to_read > 0) {
194503831d35Sstevel SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, "
194603831d35Sstevel "bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
194703831d35Sstevel rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
194803831d35Sstevel bytes_to_read);
194903831d35Sstevel }
195003831d35Sstevel
195103831d35Sstevel done:
195203831d35Sstevel msg->msg_bytes += f_frag_len;
195303831d35Sstevel
195403831d35Sstevel return (rc);
195503831d35Sstevel }
195603831d35Sstevel
195703831d35Sstevel /*
195803831d35Sstevel * move past the next message in the inbox
195903831d35Sstevel */
196003831d35Sstevel static void
mbox_skip_next_msg(struct sbbc_mbox_header * header)196103831d35Sstevel mbox_skip_next_msg(struct sbbc_mbox_header *header)
196203831d35Sstevel {
196303831d35Sstevel struct sbbc_fragment frag;
196403831d35Sstevel uint32_t next_msg;
196503831d35Sstevel
196603831d35Sstevel ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
196703831d35Sstevel
196803831d35Sstevel if (mbox_read_frag(header, &frag)) {
196903831d35Sstevel cmn_err(CE_PANIC, "INBOX is Corrupt !\n");
197003831d35Sstevel }
197103831d35Sstevel
197203831d35Sstevel /*
197303831d35Sstevel * Move on to the next message
197403831d35Sstevel */
197503831d35Sstevel next_msg = header->mailboxes[SBBC_INBOX].mbox_consumer;
197603831d35Sstevel next_msg += sizeof (struct sbbc_fragment);
197703831d35Sstevel next_msg += frag.f_frag_len;
197803831d35Sstevel if (next_msg >= header->mailboxes[SBBC_INBOX].mbox_len) {
197903831d35Sstevel next_msg = (next_msg +
198003831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_len) %
198103831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_len;
198203831d35Sstevel }
198303831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_consumer =
198403831d35Sstevel next_msg;
198503831d35Sstevel
198603831d35Sstevel mbox_update_header(SBBC_INBOX, header);
198703831d35Sstevel
198803831d35Sstevel return;
198903831d35Sstevel
199003831d35Sstevel }
199103831d35Sstevel
199203831d35Sstevel static struct sbbc_msg_waiter *
mbox_find_waiter(uint16_t msg_type,uint32_t msg_id)199303831d35Sstevel mbox_find_waiter(uint16_t msg_type, uint32_t msg_id)
199403831d35Sstevel {
199503831d35Sstevel struct sbbc_msg_waiter *waiter, *prev;
199603831d35Sstevel
199703831d35Sstevel prev = NULL;
199803831d35Sstevel for (waiter = master_mbox->mbox_wait_list[msg_type];
199903831d35Sstevel waiter != NULL; waiter = waiter->w_next) {
200003831d35Sstevel
200103831d35Sstevel if (waiter->w_id == msg_id) {
200203831d35Sstevel if (prev != NULL) {
200303831d35Sstevel prev->w_next = waiter->w_next;
200403831d35Sstevel } else {
200503831d35Sstevel master_mbox->mbox_wait_list[msg_type] =
200603831d35Sstevel waiter->w_next;
200703831d35Sstevel }
200803831d35Sstevel break;
200903831d35Sstevel }
201003831d35Sstevel prev = waiter;
201103831d35Sstevel }
201203831d35Sstevel
201303831d35Sstevel return (waiter);
201403831d35Sstevel }
201503831d35Sstevel
201603831d35Sstevel static int
mbox_read_header(uint32_t mailbox,struct sbbc_mbox_header * header)201703831d35Sstevel mbox_read_header(uint32_t mailbox, struct sbbc_mbox_header *header)
201803831d35Sstevel {
201903831d35Sstevel struct sbbc_mbox_header *hd;
202003831d35Sstevel uint32_t offset;
202103831d35Sstevel int rc;
202203831d35Sstevel
202303831d35Sstevel /*
202403831d35Sstevel * Initialize a sbbc_mbox_header pointer to 0 so that we
202503831d35Sstevel * can use it to calculate the offsets of fields inside
202603831d35Sstevel * the structure.
202703831d35Sstevel */
202803831d35Sstevel hd = (struct sbbc_mbox_header *)0;
202903831d35Sstevel
203003831d35Sstevel if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)header,
203103831d35Sstevel sizeof (struct sbbc_mbox_header)))
203203831d35Sstevel return (rc);
203303831d35Sstevel
203403831d35Sstevel /*
203503831d35Sstevel * Since the header is read in a byte-by-byte fashion
203603831d35Sstevel * using ddi_rep_get8, we need to re-read the producer
203703831d35Sstevel * or consumer pointer as integer in case it has changed
203803831d35Sstevel * after part of the previous value has been read.
203903831d35Sstevel */
204003831d35Sstevel switch (mailbox) {
204103831d35Sstevel
204203831d35Sstevel case SBBC_INBOX:
204303831d35Sstevel offset = (uint32_t)(uintptr_t)
204403831d35Sstevel (&hd->mailboxes[SBBC_INBOX].mbox_producer);
204503831d35Sstevel rc = iosram_read(SBBC_MAILBOX_KEY, offset,
204603831d35Sstevel (caddr_t)&header->mailboxes[SBBC_INBOX].mbox_producer,
204703831d35Sstevel sizeof (uint32_t));
204803831d35Sstevel break;
204903831d35Sstevel case SBBC_OUTBOX:
205003831d35Sstevel offset = (uint32_t)(uintptr_t)
205103831d35Sstevel (&hd->mailboxes[SBBC_OUTBOX].mbox_consumer);
205203831d35Sstevel rc = iosram_read(SBBC_MAILBOX_KEY, offset,
205303831d35Sstevel (caddr_t)&header->mailboxes[SBBC_OUTBOX].mbox_consumer,
205403831d35Sstevel sizeof (uint32_t));
205503831d35Sstevel break;
205603831d35Sstevel default:
205703831d35Sstevel cmn_err(CE_PANIC, "Invalid Mbox header type\n");
205803831d35Sstevel break;
205903831d35Sstevel
206003831d35Sstevel }
206103831d35Sstevel
206203831d35Sstevel return (rc);
206303831d35Sstevel }
206403831d35Sstevel
206503831d35Sstevel /*
206603831d35Sstevel * There are only two fields updated by the domain,
206703831d35Sstevel * the inbox consumer field and the outbox producer
206803831d35Sstevel * field. These fields are protected by the respective
206903831d35Sstevel * mbox_{in|out}->mb_lock so that accesses will
207003831d35Sstevel * be serialised. The only coherency issue is writing
207103831d35Sstevel * back the header, so we do it here after grabbing
207203831d35Sstevel * the global mailbox lock.
207303831d35Sstevel */
207403831d35Sstevel static void
mbox_update_header(uint32_t mailbox,struct sbbc_mbox_header * header)207503831d35Sstevel mbox_update_header(uint32_t mailbox, struct sbbc_mbox_header *header)
207603831d35Sstevel {
207703831d35Sstevel struct sbbc_mbox_header *hd;
207803831d35Sstevel uint32_t value, offset, mbox_len;
207903831d35Sstevel
208003831d35Sstevel /*
208103831d35Sstevel * Initialize a sbbc_mbox_header pointer to 0 so that we
208203831d35Sstevel * can use it to calculate the offsets of fields inside
208303831d35Sstevel * the structure.
208403831d35Sstevel */
208503831d35Sstevel hd = (struct sbbc_mbox_header *)0;
208603831d35Sstevel
208703831d35Sstevel switch (mailbox) {
208803831d35Sstevel
208903831d35Sstevel case SBBC_INBOX:
209003831d35Sstevel value = header->mailboxes[SBBC_INBOX].mbox_consumer;
209103831d35Sstevel offset = (uint32_t)(uintptr_t)
209203831d35Sstevel (&hd->mailboxes[SBBC_INBOX].mbox_consumer);
209303831d35Sstevel
209403831d35Sstevel mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
209503831d35Sstevel break;
209603831d35Sstevel case SBBC_OUTBOX:
209703831d35Sstevel value = header->mailboxes[SBBC_OUTBOX].mbox_producer;
209803831d35Sstevel offset = (uint32_t)(uintptr_t)
209903831d35Sstevel (&hd->mailboxes[SBBC_OUTBOX].mbox_producer);
210003831d35Sstevel mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
210103831d35Sstevel break;
210203831d35Sstevel default:
210303831d35Sstevel cmn_err(CE_PANIC, "Invalid Mbox header type\n");
210403831d35Sstevel break;
210503831d35Sstevel
210603831d35Sstevel }
210703831d35Sstevel
210803831d35Sstevel /*
210903831d35Sstevel * If the last read/write would cause the next read/write
211003831d35Sstevel * to be unaligned, we skip on modulo MBOX_ALIGN_BYTES.
211103831d35Sstevel * This is OK because all the mailbox handlers will
211203831d35Sstevel * conform to this.
211303831d35Sstevel */
211403831d35Sstevel if (value % MBOX_ALIGN_BYTES) {
211503831d35Sstevel value += (MBOX_ALIGN_BYTES - (value % MBOX_ALIGN_BYTES));
211603831d35Sstevel value %= mbox_len;
211703831d35Sstevel }
211803831d35Sstevel
211903831d35Sstevel if (iosram_write(SBBC_MAILBOX_KEY, offset, (caddr_t)&value,
212003831d35Sstevel sizeof (uint32_t))) {
212103831d35Sstevel cmn_err(CE_PANIC, "Mailbox Corrupt ! \n");
212203831d35Sstevel }
212303831d35Sstevel
212403831d35Sstevel /*
212503831d35Sstevel * Update internal pointers so they won't be out of sync with
212603831d35Sstevel * the values in IOSRAM.
212703831d35Sstevel */
212803831d35Sstevel switch (mailbox) {
212903831d35Sstevel
213003831d35Sstevel case SBBC_INBOX:
213103831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_consumer = value;
213203831d35Sstevel break;
213303831d35Sstevel case SBBC_OUTBOX:
213403831d35Sstevel header->mailboxes[SBBC_OUTBOX].mbox_producer = value;
213503831d35Sstevel break;
213603831d35Sstevel }
213703831d35Sstevel }
213803831d35Sstevel
213903831d35Sstevel static int
mbox_read_frag(struct sbbc_mbox_header * header,struct sbbc_fragment * frag)214003831d35Sstevel mbox_read_frag(struct sbbc_mbox_header *header,
214103831d35Sstevel struct sbbc_fragment *frag)
214203831d35Sstevel {
214303831d35Sstevel int rc = 0;
214403831d35Sstevel uint32_t sram_src, bytes;
214503831d35Sstevel caddr_t dst;
214603831d35Sstevel
214703831d35Sstevel ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
214803831d35Sstevel /*
214903831d35Sstevel * read the fragment header for this message
215003831d35Sstevel */
215103831d35Sstevel sram_src = (uint32_t)(header->mailboxes[SBBC_INBOX].mbox_offset +
215203831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_consumer);
215303831d35Sstevel
215403831d35Sstevel /*
215503831d35Sstevel * wraparound ?
215603831d35Sstevel */
215703831d35Sstevel if ((header->mailboxes[SBBC_INBOX].mbox_consumer +
215803831d35Sstevel sizeof (struct sbbc_fragment)) >=
215903831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_len) {
216003831d35Sstevel
216103831d35Sstevel dst = (caddr_t)frag;
216203831d35Sstevel bytes = header->mailboxes[SBBC_INBOX].mbox_len -
216303831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_consumer;
216403831d35Sstevel
216503831d35Sstevel if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, dst, bytes)) {
216603831d35Sstevel return (rc);
216703831d35Sstevel }
216803831d35Sstevel
216903831d35Sstevel dst += bytes;
217003831d35Sstevel sram_src = header->mailboxes[SBBC_INBOX].mbox_offset;
217103831d35Sstevel bytes = (header->mailboxes[SBBC_INBOX].mbox_consumer +
217203831d35Sstevel sizeof (struct sbbc_fragment)) %
217303831d35Sstevel header->mailboxes[SBBC_INBOX].mbox_len;
217403831d35Sstevel
217503831d35Sstevel if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src,
217603831d35Sstevel dst, bytes)) {
217703831d35Sstevel return (rc);
217803831d35Sstevel }
217903831d35Sstevel } else {
218003831d35Sstevel if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, (caddr_t)frag,
218103831d35Sstevel sizeof (struct sbbc_fragment))) {
218203831d35Sstevel return (rc);
218303831d35Sstevel }
218403831d35Sstevel }
218503831d35Sstevel
218603831d35Sstevel return (0);
218703831d35Sstevel }
218803831d35Sstevel
218903831d35Sstevel
219003831d35Sstevel /*
219103831d35Sstevel * This function is triggered by a soft interrupt and it's purpose is to call
219203831d35Sstevel * to kadmin() to shutdown the Domain.
219303831d35Sstevel */
219403831d35Sstevel /*ARGSUSED0*/
219503831d35Sstevel static uint_t
sbbc_do_fast_shutdown(char * arg)219603831d35Sstevel sbbc_do_fast_shutdown(char *arg)
219703831d35Sstevel {
219803831d35Sstevel (void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
219903831d35Sstevel
220003831d35Sstevel /*
220103831d35Sstevel * If kadmin fails for some reason then we bring the system down
220203831d35Sstevel * via power_down(), or failing that using halt().
220303831d35Sstevel */
220403831d35Sstevel power_down("kadmin() failed, trying power_down()");
220503831d35Sstevel
220603831d35Sstevel halt("power_down() failed, trying halt()");
220703831d35Sstevel
220803831d35Sstevel /*
220903831d35Sstevel * We should never make it this far, so something must have gone
221003831d35Sstevel * horribly, horribly wrong.
221103831d35Sstevel */
221203831d35Sstevel /*NOTREACHED*/
2213055d7c80Scarlsonj return (DDI_INTR_UNCLAIMED);
221403831d35Sstevel }
221503831d35Sstevel
221603831d35Sstevel
221703831d35Sstevel /*
221803831d35Sstevel * This function handles unsolicited PANIC_SHUTDOWN events
221903831d35Sstevel */
222003831d35Sstevel static uint_t
sbbc_panic_shutdown_handler(char * arg)222103831d35Sstevel sbbc_panic_shutdown_handler(char *arg)
222203831d35Sstevel {
222303831d35Sstevel static fn_t f = "sbbc_panic_shutdown_handler()";
222403831d35Sstevel
222503831d35Sstevel sg_panic_shutdown_t *payload = NULL;
222603831d35Sstevel sbbc_msg_t *msg = NULL;
222703831d35Sstevel
222803831d35Sstevel if (arg == NULL) {
222903831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
223003831d35Sstevel return (DDI_INTR_UNCLAIMED);
223103831d35Sstevel }
223203831d35Sstevel
223303831d35Sstevel msg = (sbbc_msg_t *)arg;
223403831d35Sstevel
223503831d35Sstevel if (msg->msg_buf == NULL) {
223603831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
223703831d35Sstevel return (DDI_INTR_UNCLAIMED);
223803831d35Sstevel }
223903831d35Sstevel
224003831d35Sstevel payload = (sg_panic_shutdown_t *)msg->msg_buf;
224103831d35Sstevel
224203831d35Sstevel switch (*payload) {
224303831d35Sstevel case SC_EVENT_PANIC_ENV:
224403831d35Sstevel
224503831d35Sstevel /*
224603831d35Sstevel * Let the user know why the domain is going down.
224703831d35Sstevel */
224803831d35Sstevel cmn_err(CE_WARN, "%s", PANIC_ENV_EVENT_MSG);
224903831d35Sstevel
225003831d35Sstevel /*
225103831d35Sstevel * trigger sbbc_do_fast_shutdown().
225203831d35Sstevel */
225303831d35Sstevel ddi_trigger_softintr(panic_softintr_id);
225403831d35Sstevel
225503831d35Sstevel /*NOTREACHED*/
225603831d35Sstevel break;
225703831d35Sstevel
225803831d35Sstevel case SC_EVENT_PANIC_KEYSWITCH:
225903831d35Sstevel /*
226003831d35Sstevel * The SC warns a user if they try a destructive keyswitch
226103831d35Sstevel * command on a Domain which is currently running Solaris.
226203831d35Sstevel * If the user chooses to continue despite our best advise
226303831d35Sstevel * then we bring down the Domain immediately without trying
226403831d35Sstevel * to shut the system down gracefully.
226503831d35Sstevel */
226603831d35Sstevel break;
226703831d35Sstevel
226803831d35Sstevel default:
226903831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: Unknown payload:%d", f,
227003831d35Sstevel *payload);
227103831d35Sstevel return (DDI_INTR_UNCLAIMED);
227203831d35Sstevel }
227303831d35Sstevel
227403831d35Sstevel return (DDI_INTR_CLAIMED);
227503831d35Sstevel }
227603831d35Sstevel
227703831d35Sstevel /*
227803831d35Sstevel * dp_get_cores()
227903831d35Sstevel *
228003831d35Sstevel * Checks cpu implementation for the input cpuid and returns
228103831d35Sstevel * the number of cores.
228203831d35Sstevel * If implementation cannot be determined, returns 1
228303831d35Sstevel */
228403831d35Sstevel static int
dp_get_cores(uint16_t cpuid)228503831d35Sstevel dp_get_cores(uint16_t cpuid)
228603831d35Sstevel {
228703831d35Sstevel int bd, ii, impl, nc;
228803831d35Sstevel
228903831d35Sstevel bd = cpuid / 4;
229003831d35Sstevel nc = SG_MAX_CPUS_PER_BD;
229103831d35Sstevel
229203831d35Sstevel /* find first with valid implementation */
229303831d35Sstevel for (ii = 0; ii < nc; ii++)
229403831d35Sstevel if (cpu[MAKE_CPUID(bd, ii)]) {
229503831d35Sstevel impl = cpunodes[MAKE_CPUID(bd, ii)].implementation;
229603831d35Sstevel break;
229703831d35Sstevel }
229803831d35Sstevel
229903831d35Sstevel if (IS_JAGUAR(impl) || IS_PANTHER(impl))
230003831d35Sstevel return (2);
230103831d35Sstevel else
230203831d35Sstevel return (1);
230303831d35Sstevel }
230403831d35Sstevel
230503831d35Sstevel /*
230603831d35Sstevel * dp_payload_add_cpus()
230703831d35Sstevel *
230803831d35Sstevel * From datapath mailbox message, determines the number of and safari IDs
230903831d35Sstevel * for affected cpus, then adds this info to the datapath ereport.
231003831d35Sstevel *
231103831d35Sstevel */
231203831d35Sstevel static int
dp_payload_add_cpus(plat_datapath_info_t * dpmsg,nvlist_t * erp)231303831d35Sstevel dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp)
231403831d35Sstevel {
231503831d35Sstevel int jj = 0, numcpus = 0;
231603831d35Sstevel int bd, procpos, ii, num, ncores, ret;
231703831d35Sstevel uint16_t *dparray, cpuid;
231803831d35Sstevel uint64_t *snarray;
231903831d35Sstevel
232003831d35Sstevel /* check for multiple core architectures */
232103831d35Sstevel ncores = dp_get_cores(dpmsg->cpuid);
232203831d35Sstevel
232303831d35Sstevel switch (dpmsg->type) {
232403831d35Sstevel case DP_CDS_TYPE:
232503831d35Sstevel numcpus = ncores;
232603831d35Sstevel break;
232703831d35Sstevel
232803831d35Sstevel case DP_DX_TYPE:
232903831d35Sstevel numcpus = 2 * ncores;
233003831d35Sstevel break;
233103831d35Sstevel
233203831d35Sstevel case DP_RP_TYPE:
233303831d35Sstevel numcpus = SG_MAX_CPUS_PER_BD;
233403831d35Sstevel break;
233503831d35Sstevel
233603831d35Sstevel default:
233703831d35Sstevel ASSERT(0);
233803831d35Sstevel return (-1);
233903831d35Sstevel }
234003831d35Sstevel
234103831d35Sstevel num = numcpus;
234203831d35Sstevel
234303831d35Sstevel /*
234403831d35Sstevel * populate dparray with impacted cores (only those present)
234503831d35Sstevel */
234603831d35Sstevel dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP);
234703831d35Sstevel bd = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
234803831d35Sstevel procpos = SG_CPUID_TO_PORTID(dpmsg->cpuid) & 0x3;
234903831d35Sstevel
235003831d35Sstevel mutex_enter(&cpu_lock);
235103831d35Sstevel
235203831d35Sstevel switch (dpmsg->type) {
235303831d35Sstevel
235403831d35Sstevel case DP_CDS_TYPE:
235503831d35Sstevel /*
235603831d35Sstevel * For a CDS error, it's the reporting cpuid
235703831d35Sstevel * and it's other core (if present)
235803831d35Sstevel */
235903831d35Sstevel cpuid = dpmsg->cpuid & 0x1FF; /* core 0 */
236003831d35Sstevel if (cpu[cpuid])
236103831d35Sstevel dparray[jj++] = cpuid;
236203831d35Sstevel
236303831d35Sstevel cpuid = dpmsg->cpuid | SG_CORE_ID_MASK; /* core 1 */
236403831d35Sstevel if (cpu[cpuid])
236503831d35Sstevel dparray[jj++] = cpuid;
236603831d35Sstevel break;
236703831d35Sstevel
236803831d35Sstevel case DP_DX_TYPE:
236903831d35Sstevel /*
237003831d35Sstevel * For a DX error, it's the reporting cpuid (all
237103831d35Sstevel * cores) and the other CPU sharing the same
237203831d35Sstevel * DX<-->DCDS interface (all cores)
237303831d35Sstevel */
237403831d35Sstevel
237503831d35Sstevel /* reporting cpuid */
237603831d35Sstevel cpuid = dpmsg->cpuid & 0x1FF; /* core 0 */
237703831d35Sstevel if (cpu[cpuid])
237803831d35Sstevel dparray[jj++] = cpuid;
237903831d35Sstevel
238003831d35Sstevel cpuid = dpmsg->cpuid | SG_CORE_ID_MASK; /* core 1 */
238103831d35Sstevel if (cpu[cpuid])
238203831d35Sstevel dparray[jj++] = cpuid;
238303831d35Sstevel
238403831d35Sstevel /* find partner cpuid */
238503831d35Sstevel if (procpos == 0 || procpos == 2)
238603831d35Sstevel cpuid = dpmsg->cpuid + 1;
238703831d35Sstevel else
238803831d35Sstevel cpuid = dpmsg->cpuid - 1;
238903831d35Sstevel
239003831d35Sstevel /* add partner cpuid */
239103831d35Sstevel cpuid &= 0x1FF; /* core 0 */
239203831d35Sstevel if (cpu[cpuid])
239303831d35Sstevel dparray[jj++] = cpuid;
239403831d35Sstevel
239503831d35Sstevel cpuid |= SG_CORE_ID_MASK; /* core 1 */
239603831d35Sstevel if (cpu[cpuid])
239703831d35Sstevel dparray[jj++] = cpuid;
239803831d35Sstevel break;
239903831d35Sstevel
240003831d35Sstevel case DP_RP_TYPE:
240103831d35Sstevel /*
240203831d35Sstevel * For a RP error, it's all cpuids (all cores) on
240303831d35Sstevel * the reporting board
240403831d35Sstevel */
240503831d35Sstevel for (ii = 0; ii < SG_MAX_CMPS_PER_BD; ii++) {
240603831d35Sstevel cpuid = MAKE_CPUID(bd, ii);
240703831d35Sstevel if (cpu[cpuid]) /* core 0 */
240803831d35Sstevel dparray[jj++] = cpuid;
240903831d35Sstevel cpuid |= SG_CORE_ID_MASK;
241003831d35Sstevel if (cpu[cpuid]) /* core 1 */
241103831d35Sstevel dparray[jj++] = cpuid;
241203831d35Sstevel }
241303831d35Sstevel break;
241403831d35Sstevel }
241503831d35Sstevel
241603831d35Sstevel mutex_exit(&cpu_lock);
241703831d35Sstevel
241803831d35Sstevel /*
241903831d35Sstevel * The datapath message could not be associated with any
242003831d35Sstevel * configured CPU.
242103831d35Sstevel */
242203831d35Sstevel if (!jj) {
242303831d35Sstevel kmem_free(dparray, num * sizeof (uint16_t *));
242403831d35Sstevel ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
242503831d35Sstevel ASSERT(ret == 0);
242603831d35Sstevel return (-1);
242703831d35Sstevel }
242803831d35Sstevel
242903831d35Sstevel snarray = kmem_zalloc(jj * sizeof (uint64_t), KM_SLEEP);
243003831d35Sstevel for (ii = 0; ii < jj; ii++)
243103831d35Sstevel snarray[ii] = cpunodes[dparray[ii]].device_id;
243203831d35Sstevel
243303831d35Sstevel ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
243403831d35Sstevel ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj);
243503831d35Sstevel ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj);
243603831d35Sstevel ASSERT(ret == 0);
243703831d35Sstevel
243803831d35Sstevel kmem_free(dparray, num * sizeof (uint16_t *));
243903831d35Sstevel kmem_free(snarray, jj * sizeof (uint64_t *));
244003831d35Sstevel
244103831d35Sstevel return (0);
244203831d35Sstevel }
244303831d35Sstevel
244403831d35Sstevel /*
244503831d35Sstevel * sbbc_dp_trans_event() - datapath message handler.
244603831d35Sstevel *
244703831d35Sstevel * Process datapath error and fault messages received from the SC. Checks
244803831d35Sstevel * for, and disregards, messages associated with I/O boards. Otherwise,
244903831d35Sstevel * extracts message info to produce a datapath ereport.
245003831d35Sstevel */
245103831d35Sstevel /*ARGSUSED*/
245203831d35Sstevel static uint_t
sbbc_dp_trans_event(char * arg)245303831d35Sstevel sbbc_dp_trans_event(char *arg)
245403831d35Sstevel {
245503831d35Sstevel const char *f = "sbbc_dp_trans_event()";
245603831d35Sstevel nvlist_t *erp, *detector, *hcelem;
245703831d35Sstevel char buf[FM_MAX_CLASS];
245803831d35Sstevel int board;
245903831d35Sstevel plat_datapath_info_t *dpmsg;
246003831d35Sstevel sbbc_msg_t *msg;
246103831d35Sstevel int msgtype;
246203831d35Sstevel
246303831d35Sstevel /* set i/f message and payload pointers */
246403831d35Sstevel msg = &dp_payload_msg;
246503831d35Sstevel dpmsg = &dp_payload;
246603831d35Sstevel msgtype = msg->msg_type.type;
246703831d35Sstevel
246803831d35Sstevel cmn_err(CE_NOTE, "%s: msgtype=0x%x\n", f, msgtype);
246903831d35Sstevel cmn_err(CE_NOTE, "type=0x%x cpuid=0x%x t_value=0x%x\n", dpmsg->type,
247003831d35Sstevel dpmsg->cpuid, dpmsg->t_value);
247103831d35Sstevel
247203831d35Sstevel /* check for valid type */
247303831d35Sstevel if (dpmsg->type > DP_RP_TYPE) {
247403831d35Sstevel cmn_err(CE_WARN, "%s: dpmsg type 0x%x invalid\n",
247503831d35Sstevel f, dpmsg->type);
247603831d35Sstevel return (DDI_INTR_CLAIMED);
247703831d35Sstevel }
247803831d35Sstevel
247903831d35Sstevel /* check for I/O board message - Schizo AIDs are 25 - 30 */
248003831d35Sstevel if (dpmsg->cpuid > 23) {
248103831d35Sstevel cmn_err(CE_NOTE, "%s: ignore I/O board msg\n", f);
248203831d35Sstevel return (DDI_INTR_CLAIMED);
248303831d35Sstevel }
248403831d35Sstevel
248503831d35Sstevel /* allocate space for ereport */
248603831d35Sstevel erp = fm_nvlist_create(NULL);
248703831d35Sstevel
248803831d35Sstevel /*
248903831d35Sstevel * Member Name Data Type Comments
249003831d35Sstevel * ----------- --------- -----------
249103831d35Sstevel * version uint8 0
249203831d35Sstevel * class string "asic"
249303831d35Sstevel * ENA uint64 ENA Format 1
249403831d35Sstevel * detector fmri aggregated ID data for SC-DE
249503831d35Sstevel *
249603831d35Sstevel * Datapath ereport subclasses and data payloads:
249703831d35Sstevel * There will be two types of ereports (error and fault) which will be
249803831d35Sstevel * identified by the "type" member.
249903831d35Sstevel *
250003831d35Sstevel * ereport.asic.serengeti.cds.cds-dp
250103831d35Sstevel * ereport.asic.serengeti.dx.dx-dp (board)
250203831d35Sstevel * ereport.asic.serengeti.rp.rp-dp (centerplane)
250303831d35Sstevel *
250403831d35Sstevel * Member Name Data Type Comments
250503831d35Sstevel * ----------- --------- -----------
250603831d35Sstevel * erptype uint16 derived from message type: error or
250703831d35Sstevel * fault
250803831d35Sstevel * t-value uint32 SC's datapath SERD timeout threshold
250903831d35Sstevel * dp-list-sz uint8 number of dp-list array elements
251003831d35Sstevel * dp-list array of uint16 Safari IDs of affected cpus
251103831d35Sstevel * sn-list array of uint64 Serial numbers of affected cpus
251203831d35Sstevel */
251303831d35Sstevel
251403831d35Sstevel /* compose common ereport elements */
251503831d35Sstevel detector = fm_nvlist_create(NULL);
251603831d35Sstevel
251703831d35Sstevel /*
251803831d35Sstevel * Create legacy FMRI for the detector
251903831d35Sstevel */
252003831d35Sstevel board = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
252103831d35Sstevel switch (dpmsg->type) {
252203831d35Sstevel case DP_CDS_TYPE:
252303831d35Sstevel case DP_DX_TYPE:
252403831d35Sstevel (void) snprintf(buf, FM_MAX_CLASS, "SB%d", board);
252503831d35Sstevel break;
252603831d35Sstevel case DP_RP_TYPE:
252703831d35Sstevel (void) snprintf(buf, FM_MAX_CLASS, "RP");
252803831d35Sstevel break;
252903831d35Sstevel default:
253003831d35Sstevel (void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN");
253103831d35Sstevel break;
253203831d35Sstevel }
253303831d35Sstevel
253403831d35Sstevel hcelem = fm_nvlist_create(NULL);
253503831d35Sstevel
253603831d35Sstevel (void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC);
253703831d35Sstevel (void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf);
253803831d35Sstevel
253903831d35Sstevel (void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION);
254003831d35Sstevel (void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
254103831d35Sstevel (void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, "");
254203831d35Sstevel (void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1);
254303831d35Sstevel (void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1);
254403831d35Sstevel
254503831d35Sstevel /* build ereport class name */
254603831d35Sstevel (void) snprintf(buf, FM_MAX_CLASS, "asic.serengeti.%s.%s-%s",
254703831d35Sstevel dperrtype[dpmsg->type], dperrtype[dpmsg->type],
254803831d35Sstevel FM_ERROR_DATAPATH);
254903831d35Sstevel
255003831d35Sstevel fm_ereport_set(erp, FM_EREPORT_VERSION, buf,
255103831d35Sstevel fm_ena_generate(0, FM_ENA_FMT1), detector, NULL);
255203831d35Sstevel
255303831d35Sstevel /* add payload elements */
255403831d35Sstevel if (msgtype == MBOX_EVENT_DP_ERROR)
255503831d35Sstevel fm_payload_set(erp,
255603831d35Sstevel DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL);
255703831d35Sstevel else
255803831d35Sstevel fm_payload_set(erp,
255903831d35Sstevel DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL);
256003831d35Sstevel
256103831d35Sstevel fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL);
256203831d35Sstevel
2563*07d06da5SSurya Prakki (void) dp_payload_add_cpus(dpmsg, erp);
256403831d35Sstevel
256503831d35Sstevel /* post ereport */
256603831d35Sstevel fm_ereport_post(erp, EVCH_SLEEP);
256703831d35Sstevel
256803831d35Sstevel /* free ereport memory */
256903831d35Sstevel fm_nvlist_destroy(erp, FM_NVA_FREE);
257003831d35Sstevel fm_nvlist_destroy(detector, FM_NVA_FREE);
257103831d35Sstevel
257203831d35Sstevel return (DDI_INTR_CLAIMED);
257303831d35Sstevel }
257403831d35Sstevel
257503831d35Sstevel static uint_t
sbbc_datapath_error_msg_handler(char * arg)257603831d35Sstevel sbbc_datapath_error_msg_handler(char *arg)
257703831d35Sstevel {
257803831d35Sstevel static fn_t f = "sbbc_datapath_error_msg_handler()";
257903831d35Sstevel sbbc_msg_t *msg = NULL;
258003831d35Sstevel
258103831d35Sstevel if (arg == NULL) {
258203831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
258303831d35Sstevel return (DDI_INTR_UNCLAIMED);
258403831d35Sstevel }
258503831d35Sstevel
258603831d35Sstevel msg = (sbbc_msg_t *)arg;
258703831d35Sstevel
258803831d35Sstevel if (msg->msg_buf == NULL) {
258903831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
259003831d35Sstevel return (DDI_INTR_UNCLAIMED);
259103831d35Sstevel }
259203831d35Sstevel
259303831d35Sstevel msg->msg_type.type = MBOX_EVENT_DP_ERROR;
259403831d35Sstevel
259503831d35Sstevel /* trigger sbbc_dp_trans_event() */
259603831d35Sstevel ddi_trigger_softintr(dp_softintr_id);
259703831d35Sstevel
259803831d35Sstevel return (DDI_INTR_CLAIMED);
259903831d35Sstevel }
260003831d35Sstevel
260103831d35Sstevel static uint_t
sbbc_datapath_fault_msg_handler(char * arg)260203831d35Sstevel sbbc_datapath_fault_msg_handler(char *arg)
260303831d35Sstevel {
260403831d35Sstevel
260503831d35Sstevel static fn_t f = "sbbc_datapath_fault_msg_handler()";
260603831d35Sstevel
260703831d35Sstevel sbbc_msg_t *msg = NULL;
260803831d35Sstevel
260903831d35Sstevel if (arg == NULL) {
261003831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
261103831d35Sstevel return (DDI_INTR_UNCLAIMED);
261203831d35Sstevel }
261303831d35Sstevel
261403831d35Sstevel msg = (sbbc_msg_t *)arg;
261503831d35Sstevel
261603831d35Sstevel if (msg->msg_buf == NULL) {
261703831d35Sstevel SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
261803831d35Sstevel return (DDI_INTR_UNCLAIMED);
261903831d35Sstevel }
262003831d35Sstevel
262103831d35Sstevel msg->msg_type.type = MBOX_EVENT_DP_FAULT;
262203831d35Sstevel
262303831d35Sstevel /* trigger sbbc_dp_trans_event() */
262403831d35Sstevel ddi_trigger_softintr(dp_softintr_id);
262503831d35Sstevel
262603831d35Sstevel return (DDI_INTR_CLAIMED);
262703831d35Sstevel }
262803831d35Sstevel
262903831d35Sstevel /*
263003831d35Sstevel * Log an ECC event message to the SC. This is called from the
263103831d35Sstevel * sbbc_ecc_mbox_taskq or directly from plat_send_ecc_mailbox_msg
263203831d35Sstevel * for indictment messages.
263303831d35Sstevel */
263403831d35Sstevel int
sbbc_mbox_ecc_output(sbbc_ecc_mbox_t * msgp)263503831d35Sstevel sbbc_mbox_ecc_output(sbbc_ecc_mbox_t *msgp)
263603831d35Sstevel {
263703831d35Sstevel int rv;
263803831d35Sstevel plat_capability_data_t *cap;
263903831d35Sstevel plat_dimm_sid_board_data_t *ddata;
264003831d35Sstevel plat_ecc_msg_hdr_t *hdr;
264103831d35Sstevel
264203831d35Sstevel rv = sbbc_mbox_request_response(&msgp->ecc_req, &msgp->ecc_resp,
264303831d35Sstevel sbbc_mbox_default_timeout);
264403831d35Sstevel
264503831d35Sstevel if (rv != 0) {
264603831d35Sstevel /*
264703831d35Sstevel * Indictment messages use the return value to indicate a
264803831d35Sstevel * problem in the mailbox. For Error mailbox messages, we'll
264903831d35Sstevel * have to use a syslog message.
265003831d35Sstevel */
265103831d35Sstevel if (msgp->ecc_log_error) {
265203831d35Sstevel if (sbbc_ecc_mbox_send_errs == 0) {
265303831d35Sstevel cmn_err(CE_NOTE, "!Solaris failed to send a "
265403831d35Sstevel "message (0x%x/0x%x) to the System "
265503831d35Sstevel "Controller. Error: %d, Message Status: %d",
265603831d35Sstevel msgp->ecc_resp.msg_type.type,
265703831d35Sstevel msgp->ecc_resp.msg_type.sub_type,
265803831d35Sstevel rv, msgp->ecc_resp.msg_status);
265903831d35Sstevel }
266003831d35Sstevel
266103831d35Sstevel if (++sbbc_ecc_mbox_send_errs >=
266203831d35Sstevel sbbc_ecc_mbox_err_throttle) {
266303831d35Sstevel sbbc_ecc_mbox_send_errs = 0;
266403831d35Sstevel }
266503831d35Sstevel }
266603831d35Sstevel
266703831d35Sstevel } else if (msgp->ecc_resp.msg_status != 0) {
266803831d35Sstevel if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
266903831d35Sstevel switch (msgp->ecc_resp.msg_type.sub_type) {
267003831d35Sstevel case INFO_MBOX_ECC:
267103831d35Sstevel hdr = (plat_ecc_msg_hdr_t *)
267203831d35Sstevel msgp->ecc_req.msg_buf;
267303831d35Sstevel if (hdr->emh_msg_type ==
267403831d35Sstevel PLAT_ECC_DIMM_SID_MESSAGE) {
267503831d35Sstevel rv = msgp->ecc_resp.msg_status;
267603831d35Sstevel break;
267703831d35Sstevel }
267803831d35Sstevel /*FALLTHROUGH*/
267903831d35Sstevel case INFO_MBOX_ECC_CAP:
268003831d35Sstevel /*
268103831d35Sstevel * The positive response comes only
268203831d35Sstevel * from the AVL FS1 updated SC.
268303831d35Sstevel * If the firmware is either downgraded
268403831d35Sstevel * or failover to an older version, then
268503831d35Sstevel * lets reset the SC capability to
268603831d35Sstevel * default.
268703831d35Sstevel */
268803831d35Sstevel plat_ecc_capability_sc_set
268903831d35Sstevel (PLAT_ECC_CAPABILITY_SC_DEFAULT);
269003831d35Sstevel break;
269103831d35Sstevel default:
269203831d35Sstevel break;
269303831d35Sstevel }
269403831d35Sstevel }
269503831d35Sstevel if (msgp->ecc_log_error) {
269603831d35Sstevel if (sbbc_ecc_mbox_inval_errs == 0) {
269703831d35Sstevel cmn_err(CE_NOTE, "!An internal error (%d) "
269803831d35Sstevel "occurred in the System Controller while "
269903831d35Sstevel "processing this message (0x%x/0x%x)",
270003831d35Sstevel msgp->ecc_resp.msg_status,
270103831d35Sstevel msgp->ecc_resp.msg_type.type,
270203831d35Sstevel msgp->ecc_resp.msg_type.sub_type);
270303831d35Sstevel }
270403831d35Sstevel if (msgp->ecc_resp.msg_status == EINVAL) {
270503831d35Sstevel if (++sbbc_ecc_mbox_inval_errs >=
270603831d35Sstevel sbbc_ecc_mbox_err_throttle) {
270703831d35Sstevel sbbc_ecc_mbox_inval_errs = 0;
270803831d35Sstevel }
270903831d35Sstevel rv = ENOMSG;
271003831d35Sstevel } else {
271103831d35Sstevel if (++sbbc_ecc_mbox_other_errs >=
271203831d35Sstevel sbbc_ecc_mbox_err_throttle) {
271303831d35Sstevel sbbc_ecc_mbox_other_errs = 0;
271403831d35Sstevel }
271503831d35Sstevel rv = msgp->ecc_resp.msg_status;
271603831d35Sstevel }
271703831d35Sstevel }
271803831d35Sstevel
271903831d35Sstevel } else {
272003831d35Sstevel if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
272103831d35Sstevel switch (msgp->ecc_resp.msg_type.sub_type) {
272203831d35Sstevel case INFO_MBOX_ECC_CAP:
272303831d35Sstevel /*
272403831d35Sstevel * Successfully received the response
272503831d35Sstevel * for the capability message, so updating
272603831d35Sstevel * the SC ECC messaging capability.
272703831d35Sstevel */
272803831d35Sstevel cap = (plat_capability_data_t *)
272903831d35Sstevel msgp->ecc_resp.msg_buf;
273003831d35Sstevel plat_ecc_capability_sc_set
273103831d35Sstevel (cap->capd_capability);
273203831d35Sstevel break;
273303831d35Sstevel
273403831d35Sstevel case INFO_MBOX_ECC:
273503831d35Sstevel hdr = (plat_ecc_msg_hdr_t *)
273603831d35Sstevel msgp->ecc_resp.msg_buf;
273703831d35Sstevel if (hdr && (hdr->emh_msg_type ==
273803831d35Sstevel PLAT_ECC_DIMM_SID_MESSAGE)) {
273903831d35Sstevel /*
274003831d35Sstevel * Successfully received a response
274103831d35Sstevel * to a request for DIMM serial ids.
274203831d35Sstevel */
274303831d35Sstevel ddata = (plat_dimm_sid_board_data_t *)
274403831d35Sstevel msgp->ecc_resp.msg_buf;
274503831d35Sstevel (void) plat_store_mem_sids(ddata);
274603831d35Sstevel }
274703831d35Sstevel break;
274803831d35Sstevel
274903831d35Sstevel default:
275003831d35Sstevel break;
275103831d35Sstevel }
275203831d35Sstevel }
275303831d35Sstevel }
275403831d35Sstevel
275503831d35Sstevel if (msgp->ecc_resp.msg_buf)
275603831d35Sstevel kmem_free((void *)msgp->ecc_resp.msg_buf,
275703831d35Sstevel (size_t)msgp->ecc_resp.msg_len);
275803831d35Sstevel
275903831d35Sstevel kmem_free((void *)msgp->ecc_req.msg_buf, (size_t)msgp->ecc_req.msg_len);
276003831d35Sstevel kmem_free(msgp, sizeof (sbbc_ecc_mbox_t));
276103831d35Sstevel return (rv);
276203831d35Sstevel }
276303831d35Sstevel
276403831d35Sstevel /*
276503831d35Sstevel * Enqueue ECC event message on taskq to SC. This is invoked from
276603831d35Sstevel * plat_send_ecc_mailbox_msg() for each ECC event generating a message.
276703831d35Sstevel */
276803831d35Sstevel void
sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t * sbbc_ecc_msgp)276903831d35Sstevel sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t *sbbc_ecc_msgp)
277003831d35Sstevel {
277103831d35Sstevel /*
277203831d35Sstevel * Create the ECC event mailbox taskq, if it does not yet exist.
277303831d35Sstevel * This must be done here rather than in sbbc_mbox_init(). The
277403831d35Sstevel * sgsbbc driver is loaded very early in the boot flow. Calling
277503831d35Sstevel * taskq_create() from sbbc_mbox_init could lead to a boot deadlock.
277603831d35Sstevel *
277703831d35Sstevel * There might be a tiny probability that two ECC handlers on
277803831d35Sstevel * different processors could arrive here simultaneously. If
277903831d35Sstevel * the taskq has not been created previously, then these two
278003831d35Sstevel * simultaneous events could cause the creation of an extra taskq.
278103831d35Sstevel * Given the extremely small likelihood (if not outright impossibility)
278203831d35Sstevel * of this occurrence, sbbc_ecc_mbox_taskq is not protected by a lock.
278303831d35Sstevel */
278403831d35Sstevel
278503831d35Sstevel if (sbbc_ecc_mbox_taskq == NULL) {
278603831d35Sstevel sbbc_ecc_mbox_taskq = taskq_create("ECC_event_mailbox", 1,
278703831d35Sstevel minclsyspri, ECC_MBOX_TASKQ_MIN, ECC_MBOX_TASKQ_MAX,
278803831d35Sstevel TASKQ_PREPOPULATE);
278903831d35Sstevel if (sbbc_ecc_mbox_taskq == NULL) {
279003831d35Sstevel if (sbbc_ecc_mbox_taskq_errs == 0) {
279103831d35Sstevel cmn_err(CE_NOTE, "Unable to create mailbox "
279203831d35Sstevel "task queue for ECC event logging to "
279303831d35Sstevel "System Controller");
279403831d35Sstevel }
279503831d35Sstevel if (++sbbc_ecc_mbox_taskq_errs >=
279603831d35Sstevel sbbc_ecc_mbox_err_throttle) {
279703831d35Sstevel sbbc_ecc_mbox_taskq_errs = 0;
279803831d35Sstevel }
279903831d35Sstevel
280003831d35Sstevel kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
280103831d35Sstevel (size_t)sbbc_ecc_msgp->ecc_req.msg_len);
280203831d35Sstevel kmem_free((void *)sbbc_ecc_msgp,
280303831d35Sstevel sizeof (sbbc_ecc_mbox_t));
280403831d35Sstevel return;
280503831d35Sstevel }
280603831d35Sstevel
280703831d35Sstevel /*
280803831d35Sstevel * Reset error counter so that first taskq_dispatch
280903831d35Sstevel * error will be output
281003831d35Sstevel */
281103831d35Sstevel sbbc_ecc_mbox_taskq_errs = 0;
281203831d35Sstevel }
281303831d35Sstevel
281403831d35Sstevel /*
281503831d35Sstevel * Enqueue the message
281603831d35Sstevel */
281703831d35Sstevel
281803831d35Sstevel if (taskq_dispatch(sbbc_ecc_mbox_taskq,
281903831d35Sstevel (task_func_t *)sbbc_mbox_ecc_output, sbbc_ecc_msgp,
282003831d35Sstevel TQ_NOSLEEP) == NULL) {
282103831d35Sstevel
282203831d35Sstevel if (sbbc_ecc_mbox_taskq_errs == 0) {
282303831d35Sstevel cmn_err(CE_NOTE, "Unable to send ECC event "
282403831d35Sstevel "message to System Controller");
282503831d35Sstevel }
282603831d35Sstevel if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) {
282703831d35Sstevel sbbc_ecc_mbox_taskq_errs = 0;
282803831d35Sstevel }
282903831d35Sstevel
283003831d35Sstevel kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
283103831d35Sstevel (size_t)sbbc_ecc_msgp->ecc_req.msg_len);
283203831d35Sstevel kmem_free((void *)sbbc_ecc_msgp, sizeof (sbbc_ecc_mbox_t));
283303831d35Sstevel }
283403831d35Sstevel }
283503831d35Sstevel
283603831d35Sstevel static uint_t
cap_ecc_msg_handler(char * addr)283703831d35Sstevel cap_ecc_msg_handler(char *addr)
283803831d35Sstevel {
283903831d35Sstevel sbbc_msg_t *msg = NULL;
284003831d35Sstevel plat_capability_data_t *cap = NULL;
284103831d35Sstevel static fn_t f = "cap_ecc_msg_handler";
284203831d35Sstevel
284303831d35Sstevel msg = (sbbc_msg_t *)addr;
284403831d35Sstevel
284503831d35Sstevel if (msg == NULL) {
284603831d35Sstevel SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
284703831d35Sstevel "null addr");
284803831d35Sstevel return (DDI_INTR_CLAIMED);
284903831d35Sstevel }
285003831d35Sstevel
285103831d35Sstevel if (msg->msg_buf == NULL) {
285203831d35Sstevel SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
285303831d35Sstevel "null data buffer");
285403831d35Sstevel return (DDI_INTR_CLAIMED);
285503831d35Sstevel }
285603831d35Sstevel
285703831d35Sstevel cap = (plat_capability_data_t *)msg->msg_buf;
285803831d35Sstevel switch (cap->capd_msg_type) {
285903831d35Sstevel case PLAT_ECC_CAPABILITY_MESSAGE:
286003831d35Sstevel SGSBBC_DBG_MBOX("%s: capability 0x%x\n", f,
286103831d35Sstevel cap->capd_capability);
286203831d35Sstevel plat_ecc_capability_sc_set(cap->capd_capability);
286303831d35Sstevel break;
286403831d35Sstevel default:
286503831d35Sstevel SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f,
286603831d35Sstevel cap->capd_msg_type);
286703831d35Sstevel break;
286803831d35Sstevel }
286903831d35Sstevel
287003831d35Sstevel return (DDI_INTR_CLAIMED);
287103831d35Sstevel }
2872