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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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