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 /* 2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel /* 2803831d35Sstevel * This file contains the implementation of the mboxsc module, a mailbox layer 2903831d35Sstevel * built upon the Starcat IOSRAM driver. 3003831d35Sstevel */ 3103831d35Sstevel 3203831d35Sstevel #include <sys/types.h> 3303831d35Sstevel #include <sys/systm.h> 3403831d35Sstevel #include <sys/modctl.h> 3503831d35Sstevel #include <sys/errno.h> 3603831d35Sstevel #include <sys/ksynch.h> 3703831d35Sstevel #include <sys/kmem.h> 3803831d35Sstevel #include <sys/varargs.h> 3903831d35Sstevel #include <sys/ddi.h> 4003831d35Sstevel #include <sys/sunddi.h> 4103831d35Sstevel #include <sys/cmn_err.h> 4203831d35Sstevel #include <sys/debug.h> 4303831d35Sstevel #include <sys/sysmacros.h> 4403831d35Sstevel 4503831d35Sstevel #include <sys/iosramreg.h> 4603831d35Sstevel #include <sys/iosramio.h> 4703831d35Sstevel #include <sys/mboxsc.h> 4803831d35Sstevel #include <sys/mboxsc_impl.h> 4903831d35Sstevel 5003831d35Sstevel /* 5103831d35Sstevel * Debugging facility 5203831d35Sstevel */ 5303831d35Sstevel #define DBGACT_NONE (0x00000000) 5403831d35Sstevel #define DBGACT_BREAK (0x00000001) 5503831d35Sstevel #define DBGACT_SHOWPOS (0x00000002) 5603831d35Sstevel #define DBGACT_DEFAULT DBGACT_NONE 5703831d35Sstevel 5803831d35Sstevel #define DBG_DEV (0x00000001) 5903831d35Sstevel #define DBG_CALLS (0x00000002) 6003831d35Sstevel #define DBG_RETS (0x00000004) 6103831d35Sstevel #define DBG_ARGS (0x00000008) 6203831d35Sstevel #define DBG_KMEM (0x00000010) 6303831d35Sstevel #define DBG_ALL (0xFFFFFFFF) 6403831d35Sstevel 6503831d35Sstevel #ifdef DEBUG 6603831d35Sstevel static uint32_t mboxsc_debug_mask = 0x00000000; 6703831d35Sstevel #define DPRINTF0(class, action, fmt) \ 6803831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt)) 6903831d35Sstevel #define DPRINTF1(class, action, fmt, arg1) \ 7003831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\ 7103831d35Sstevel (arg1)) 7203831d35Sstevel #define DPRINTF2(class, action, fmt, arg1, arg2) \ 7303831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\ 7403831d35Sstevel (arg1), (arg2)) 7503831d35Sstevel #define DPRINTF3(class, action, fmt, arg1, arg2, arg3) \ 7603831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\ 7703831d35Sstevel (arg1), (arg2), (arg3)) 7803831d35Sstevel #define DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4) \ 7903831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\ 8003831d35Sstevel (arg1), (arg2), (arg3), (arg4)) 8103831d35Sstevel #define DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5) \ 8203831d35Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\ 8303831d35Sstevel (arg1), (arg2), (arg3), (arg4), (arg5)) 8403831d35Sstevel #else /* DEBUG */ 8503831d35Sstevel #define DPRINTF0(class, action, fmt) 8603831d35Sstevel #define DPRINTF1(class, action, fmt, arg1) 8703831d35Sstevel #define DPRINTF2(class, action, fmt, arg1, arg2) 8803831d35Sstevel #define DPRINTF3(class, action, fmt, arg1, arg2, arg3) 8903831d35Sstevel #define DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4) 9003831d35Sstevel #define DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5) 9103831d35Sstevel #endif /* DEBUG */ 9203831d35Sstevel 9303831d35Sstevel /* 9403831d35Sstevel * Basic constants 9503831d35Sstevel */ 9603831d35Sstevel #ifndef TRUE 9703831d35Sstevel #define TRUE (1) 9803831d35Sstevel #endif /* TRUE */ 9903831d35Sstevel #ifndef FALSE 10003831d35Sstevel #define FALSE (0) 10103831d35Sstevel #endif /* FALSE */ 10203831d35Sstevel 10303831d35Sstevel 10403831d35Sstevel /* 10503831d35Sstevel * Whenever mboxsc_init is called to create a new mailbox, an instance of 10603831d35Sstevel * mboxsc_mbox_t is created and inserted into a hash table to maintain 10703831d35Sstevel * various information about the mailbox. The mbox_state, mbox_refcount, and 10803831d35Sstevel * mbox_wait fields are all protected by the global mboxsc_lock mutex. 10903831d35Sstevel * If lock contention between mailboxes becomes an issue, each mailbox will 11003831d35Sstevel * need to be given its own mutex to protect the mbox_wait, mbox_state, 11103831d35Sstevel * and mbox_update_wait fields. The mbox_refcount field will probably need to 11203831d35Sstevel * remain under global protection, however, since it is used to keep track of 11303831d35Sstevel * the number of threads sleeping inside the mailbox's various synchronization 11403831d35Sstevel * mechanisms and would consequently be difficult to protect using those same 11503831d35Sstevel * mechanisms. 11603831d35Sstevel */ 11703831d35Sstevel typedef struct mboxsc_mbox { 11803831d35Sstevel uint32_t mbox_key; 11903831d35Sstevel int mbox_direction; 12003831d35Sstevel void (*mbox_callback)(void); 12103831d35Sstevel uint32_t mbox_length; 12203831d35Sstevel uint16_t mbox_refcount; 12303831d35Sstevel uint16_t mbox_state; 12403831d35Sstevel kcondvar_t mbox_wait; 12503831d35Sstevel mboxsc_msghdr_t mbox_header; 12603831d35Sstevel struct mboxsc_mbox *mbox_hash_next; 12703831d35Sstevel } mboxsc_mbox_t; 12803831d35Sstevel 12903831d35Sstevel /* 13003831d35Sstevel * Various state flags that can be set on a mailbox. Multiple states may 13103831d35Sstevel * be active at the same time. 13203831d35Sstevel */ 13303831d35Sstevel #define STATE_IDLE (0x0000) 13403831d35Sstevel #define STATE_WRITING (0x0001) 13503831d35Sstevel #define STATE_READING (0x0002) 13603831d35Sstevel #define STATE_HDRVALID (0x0004) 13703831d35Sstevel 13803831d35Sstevel /* 13903831d35Sstevel * Timeout periods for mboxsc_putmsg and mboxsc_getmsg, converted to ticks 14003831d35Sstevel * from the microsecond values found in mboxsc_impl.h. 14103831d35Sstevel */ 14203831d35Sstevel #define EAGAIN_POLL (drv_usectohz(MBOXSC_EAGAIN_POLL_USECS)) 14303831d35Sstevel #define PUTMSG_POLL (drv_usectohz(MBOXSC_PUTMSG_POLL_USECS)) 14403831d35Sstevel #define HWLOCK_POLL (drv_usectohz(MBOXSC_HWLOCK_POLL_USECS)) 14503831d35Sstevel #define LOOP_WARN_INTERVAL (drv_usectohz(MBOXSC_USECS_PER_SECOND * 15)) 14603831d35Sstevel 14703831d35Sstevel /* 14803831d35Sstevel * Various tests that are performed on message header fields. 14903831d35Sstevel */ 15003831d35Sstevel #define IS_UNSOLICITED_TYPE(type) ((type) != MBOXSC_MSG_REPLY) 15103831d35Sstevel #define MSG_TYPE_MATCHES(type, msgp) \ 15203831d35Sstevel (((type) == 0) || ((type) & (msgp)->msg_type)) 15303831d35Sstevel #define MSG_CMD_MATCHES(cmd, msgp) \ 15403831d35Sstevel (((cmd) == 0) || ((cmd) == (msgp)->msg_cmd)) 15503831d35Sstevel #define MSG_TRANSID_MATCHES(tid, msgp) \ 15603831d35Sstevel (((tid) == 0) || ((tid) == (msgp)->msg_transid)) 15703831d35Sstevel 15803831d35Sstevel /* 159*ad9a7bd3SRichard Lowe * This macro can be used to determine the size of any field in the message 160*ad9a7bd3SRichard Lowe * header (or any other struct, for that matter). 16103831d35Sstevel */ 16203831d35Sstevel #define FIELD_SIZE(type, field) (sizeof (((type *)0)->field)) 16303831d35Sstevel 16403831d35Sstevel /* 16503831d35Sstevel * Mask used when generating unique transaction ID values. 16603831d35Sstevel * This arbitrarily chosen value will be OR'd together with 16703831d35Sstevel * a counter for each successive internally-generated transaction ID. 16803831d35Sstevel */ 16903831d35Sstevel #define TRANSID_GEN_MASK (0xFFC0000000000000) 17003831d35Sstevel 17103831d35Sstevel /* 17203831d35Sstevel * All existing mailboxes are stored in a hash table with HASHTBL_SIZE 17303831d35Sstevel * entries so they can be rapidly accessed by their key values. 17403831d35Sstevel */ 17503831d35Sstevel #define HASHTBL_SIZE (32) 17603831d35Sstevel #define HASH_KEY(key) ((((key) >> 24) ^ ((key) >> 16) ^ ((key) >> 9) ^\ 17703831d35Sstevel (key)) & (HASHTBL_SIZE - 1)); 17803831d35Sstevel 17903831d35Sstevel /* 18003831d35Sstevel * Unfortunately, it is necessary to calculate checksums on data split up 18103831d35Sstevel * amongst different buffers in some cases. Consequently, mboxsc_checksum 18203831d35Sstevel * accepts a "seed" value as one of its parameters. When first starting a 18303831d35Sstevel * checksum calculation, the seed should be 0. 18403831d35Sstevel */ 18503831d35Sstevel #define CHKSUM_INIT (0) 18603831d35Sstevel 18703831d35Sstevel /* 18803831d35Sstevel * local variables 18903831d35Sstevel */ 19003831d35Sstevel static kmutex_t mboxsc_lock; 19103831d35Sstevel static mboxsc_mbox_t *mboxsc_hash_table[HASHTBL_SIZE]; 19203831d35Sstevel static uint32_t mboxsc_flaglock_count; 19303831d35Sstevel static uint32_t mboxsc_active_version = MBOXSC_PROTOCOL_VERSION; 19403831d35Sstevel static kcondvar_t mboxsc_dereference_cv; 19503831d35Sstevel 19603831d35Sstevel /* 19703831d35Sstevel * Structures from modctl.h used for loadable module support. 19803831d35Sstevel * The mboxsc API is a "miscellaneous" module. 19903831d35Sstevel */ 20003831d35Sstevel extern struct mod_ops mod_miscops; 20103831d35Sstevel 20203831d35Sstevel static struct modlmisc modlmisc = { 20303831d35Sstevel &mod_miscops, 204f500b196SRichard Bean "IOSRAM Mailbox API 'mboxsc'", 20503831d35Sstevel }; 20603831d35Sstevel 20703831d35Sstevel static struct modlinkage modlinkage = { 20803831d35Sstevel MODREV_1, 20903831d35Sstevel (void *)&modlmisc, 21003831d35Sstevel NULL 21103831d35Sstevel }; 21203831d35Sstevel 21303831d35Sstevel /* 21403831d35Sstevel * Prototypes for local functions 21503831d35Sstevel */ 21603831d35Sstevel static void mboxsc_iosram_callback(void *arg); 21703831d35Sstevel static void mboxsc_hdrchange_callback(void); 21803831d35Sstevel static int mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp); 21903831d35Sstevel static void mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp); 22003831d35Sstevel static void mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp); 22103831d35Sstevel static mboxsc_mbox_t *mboxsc_hashfind_mailbox_by_key(uint32_t key); 22203831d35Sstevel static mboxsc_mbox_t *mboxsc_hashremove_mailbox_by_key(uint32_t key); 22303831d35Sstevel static mboxsc_chksum_t mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf, 22403831d35Sstevel uint32_t length); 22503831d35Sstevel static int mboxsc_lock_flags(uint8_t mandatory, clock_t deadline); 22603831d35Sstevel static int mboxsc_unlock_flags(uint8_t mandatory); 22703831d35Sstevel static int mboxsc_timed_read(clock_t deadline, uint32_t key, 22803831d35Sstevel uint32_t off, uint32_t len, caddr_t dptr); 22903831d35Sstevel static int mboxsc_timed_write(clock_t deadline, uint32_t key, 23003831d35Sstevel uint32_t off, uint32_t len, caddr_t dptr); 23103831d35Sstevel static int mboxsc_timed_get_flag(clock_t deadline, uint32_t key, 23203831d35Sstevel uint8_t *data_validp, uint8_t *int_pendingp); 23303831d35Sstevel static int mboxsc_timed_set_flag(clock_t deadline, uint32_t key, 23403831d35Sstevel uint8_t data_valid, uint8_t int_pending); 23503831d35Sstevel static int mboxsc_timed_send_intr(clock_t deadline); 23603831d35Sstevel static int mboxsc_expire_message(uint32_t key, int *resultp); 23703831d35Sstevel static uint64_t mboxsc_generate_transid(uint64_t prev_transid); 23803831d35Sstevel static void mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp); 23903831d35Sstevel static void mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp); 24003831d35Sstevel #ifdef DEBUG 24103831d35Sstevel /*PRINTFLIKE5*/ 24203831d35Sstevel static void mboxsc_dprintf(const char *file, int line, 24303831d35Sstevel uint32_t class, uint32_t action, const char *fmt, ...); 24403831d35Sstevel int mboxsc_debug(int cmd, void *arg); 24503831d35Sstevel #endif /* DEBUG */ 24603831d35Sstevel 24703831d35Sstevel 24803831d35Sstevel /* 24903831d35Sstevel * _init 25003831d35Sstevel * 25103831d35Sstevel * Loadable module support routine. Initializes global lock and hash table. 25203831d35Sstevel */ 25303831d35Sstevel int 25403831d35Sstevel _init(void) 25503831d35Sstevel { 25603831d35Sstevel int i; 25703831d35Sstevel uint32_t sms_version; 25803831d35Sstevel int error = 0; 25903831d35Sstevel 26003831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_init called\n"); 26103831d35Sstevel 26203831d35Sstevel /* 26303831d35Sstevel * Initialize all module resources. 26403831d35Sstevel */ 26503831d35Sstevel mutex_init(&mboxsc_lock, NULL, MUTEX_DRIVER, NULL); 26603831d35Sstevel cv_init(&mboxsc_dereference_cv, NULL, CV_DRIVER, NULL); 26703831d35Sstevel 26803831d35Sstevel for (i = 0; i < HASHTBL_SIZE; i++) { 26903831d35Sstevel mboxsc_hash_table[i] = NULL; 27003831d35Sstevel } 27103831d35Sstevel mboxsc_flaglock_count = 0; 27203831d35Sstevel 27303831d35Sstevel if (mod_install(&modlinkage) != 0) { 27403831d35Sstevel goto failed; 27503831d35Sstevel } 27603831d35Sstevel 27703831d35Sstevel /* 27803831d35Sstevel * Set the os_mbox_version field in the IOSRAM header to indicate the 27903831d35Sstevel * highest Mailbox Protocol version we support 28003831d35Sstevel */ 28103831d35Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_SET_OS_MBOX_VER, 28203831d35Sstevel (void *)MBOXSC_PROTOCOL_VERSION); 28303831d35Sstevel if (error != 0) { 28403831d35Sstevel goto failed; 28503831d35Sstevel } 28603831d35Sstevel 28703831d35Sstevel /* 28803831d35Sstevel * Read the sms_mbox_version field in the IOSRAM header to determine 28903831d35Sstevel * what the greatest commonly supported version is. 29003831d35Sstevel */ 29103831d35Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER, 29203831d35Sstevel (void *)&sms_version); 29303831d35Sstevel if (error != 0) { 29403831d35Sstevel goto failed; 29503831d35Sstevel } 29603831d35Sstevel mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION, sms_version); 29703831d35Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT, 29803831d35Sstevel "sms version: %d, active version: %d\n", sms_version, 29903831d35Sstevel mboxsc_active_version); 30003831d35Sstevel 30103831d35Sstevel /* 30203831d35Sstevel * Register a callback with the IOSRAM driver to receive notification of 30303831d35Sstevel * changes to the IOSRAM header, in case the sms_mbox_version field 30403831d35Sstevel * changes. 30503831d35Sstevel */ 30603831d35Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK, 30703831d35Sstevel (void *)mboxsc_hdrchange_callback); 30803831d35Sstevel if (error != 0) { 30903831d35Sstevel goto failed; 31003831d35Sstevel } 31103831d35Sstevel 31203831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error); 31303831d35Sstevel return (0); 31403831d35Sstevel 31503831d35Sstevel /* 31603831d35Sstevel * If initialization fails, uninitialize resources. 31703831d35Sstevel */ 31803831d35Sstevel failed: 31903831d35Sstevel mutex_destroy(&mboxsc_lock); 32003831d35Sstevel cv_destroy(&mboxsc_dereference_cv); 32103831d35Sstevel 32203831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error); 32303831d35Sstevel return (error); 32403831d35Sstevel } 32503831d35Sstevel 32603831d35Sstevel /* 32703831d35Sstevel * _fini 32803831d35Sstevel * 32903831d35Sstevel * Loadable module support routine. Closes all mailboxes and releases all 33003831d35Sstevel * resources. 33103831d35Sstevel */ 33203831d35Sstevel int 33303831d35Sstevel _fini(void) 33403831d35Sstevel { 33503831d35Sstevel int i; 33603831d35Sstevel int error = 0; 33703831d35Sstevel mboxsc_mbox_t *mailboxp; 33803831d35Sstevel 33903831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_fini called\n"); 34003831d35Sstevel 34103831d35Sstevel /* 34203831d35Sstevel * Attempt to remove the module. If successful, close all mailboxes 34303831d35Sstevel * and deallocate the global lock. 34403831d35Sstevel */ 34503831d35Sstevel error = mod_remove(&modlinkage); 34603831d35Sstevel if (error == 0) { 34703831d35Sstevel mutex_enter(&mboxsc_lock); 34803831d35Sstevel 34907d06da5SSurya Prakki (void) iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK, NULL); 35003831d35Sstevel 35103831d35Sstevel for (i = 0; i < HASHTBL_SIZE; i++) { 35203831d35Sstevel while (mboxsc_hash_table[i] != NULL) { 35303831d35Sstevel mailboxp = mboxsc_hash_table[i]; 35403831d35Sstevel mboxsc_close_mailbox(mailboxp); 35503831d35Sstevel } 35603831d35Sstevel } 35703831d35Sstevel mutex_exit(&mboxsc_lock); 35803831d35Sstevel mutex_destroy(&mboxsc_lock); 35903831d35Sstevel cv_destroy(&mboxsc_dereference_cv); 36003831d35Sstevel } 36103831d35Sstevel 36203831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_fini ret: 0x%08x\n", error); 36303831d35Sstevel return (error); 36403831d35Sstevel } 36503831d35Sstevel 36603831d35Sstevel /* 36703831d35Sstevel * _info 36803831d35Sstevel * 36903831d35Sstevel * Loadable module support routine. 37003831d35Sstevel */ 37103831d35Sstevel int 37203831d35Sstevel _info(struct modinfo *modinfop) 37303831d35Sstevel { 37403831d35Sstevel int error = 0; 37503831d35Sstevel 37603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_info called\n"); 37703831d35Sstevel 37803831d35Sstevel error = mod_info(&modlinkage, modinfop); 37903831d35Sstevel 38003831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_info ret: 0x%08x\n", error); 38103831d35Sstevel 38203831d35Sstevel return (error); 38303831d35Sstevel } 38403831d35Sstevel 38503831d35Sstevel /* 38603831d35Sstevel * mboxsc_init 38703831d35Sstevel * 38803831d35Sstevel * Attempts to create a new mailbox. 38903831d35Sstevel */ 39003831d35Sstevel int 39103831d35Sstevel mboxsc_init(uint32_t key, int direction, void (*event_handler)(void)) 39203831d35Sstevel { 39303831d35Sstevel int error = 0; 39403831d35Sstevel mboxsc_mbox_t *mailboxp; 39503831d35Sstevel 39603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_init called\n"); 39703831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 39803831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "direction = %d\n", direction); 39903831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "event_handlerp = %p\n", 40007d06da5SSurya Prakki (void *)event_handler); 40103831d35Sstevel 40203831d35Sstevel /* 40303831d35Sstevel * Check for valid direction and callback specification. 40403831d35Sstevel */ 40503831d35Sstevel if (((direction != MBOXSC_MBOX_IN) && (direction != MBOXSC_MBOX_OUT)) || 40603831d35Sstevel ((event_handler != NULL) && (direction != MBOXSC_MBOX_IN))) { 40703831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n", 40803831d35Sstevel EINVAL); 40903831d35Sstevel return (EINVAL); 41003831d35Sstevel } 41103831d35Sstevel 41203831d35Sstevel /* 41303831d35Sstevel * Allocate memory for the mailbox structure and initialize all 41403831d35Sstevel * caller-provided fields. 41503831d35Sstevel */ 41603831d35Sstevel mailboxp = (mboxsc_mbox_t *)kmem_zalloc(sizeof (mboxsc_mbox_t), 41703831d35Sstevel KM_SLEEP); 41807d06da5SSurya Prakki DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_zalloc(%lu) = %p\n", 41907d06da5SSurya Prakki sizeof (mboxsc_mbox_t), (void *)mailboxp); 42003831d35Sstevel mailboxp->mbox_key = key; 42103831d35Sstevel mailboxp->mbox_direction = direction; 42203831d35Sstevel mailboxp->mbox_callback = event_handler; 42303831d35Sstevel 42403831d35Sstevel /* 42503831d35Sstevel * Attempt to add the mailbox. If unsuccessful, free the allocated 42603831d35Sstevel * memory. 42703831d35Sstevel */ 42803831d35Sstevel mutex_enter(&mboxsc_lock); 42903831d35Sstevel error = mboxsc_add_mailbox(mailboxp); 43003831d35Sstevel mutex_exit(&mboxsc_lock); 43103831d35Sstevel 43203831d35Sstevel if (error != 0) { 43307d06da5SSurya Prakki DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n", 43407d06da5SSurya Prakki (void *)mailboxp, sizeof (mboxsc_mbox_t)); 43503831d35Sstevel kmem_free(mailboxp, sizeof (mboxsc_mbox_t)); 43603831d35Sstevel } 43703831d35Sstevel 43803831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n", error); 43903831d35Sstevel return (error); 44003831d35Sstevel } 44103831d35Sstevel 44203831d35Sstevel /* 44303831d35Sstevel * mboxsc_fini 44403831d35Sstevel * 44503831d35Sstevel * Closes the mailbox with the indicated key, if it exists. 44603831d35Sstevel */ 44703831d35Sstevel int 44803831d35Sstevel mboxsc_fini(uint32_t key) 44903831d35Sstevel { 45003831d35Sstevel int error = 0; 45103831d35Sstevel mboxsc_mbox_t *mailboxp; 45203831d35Sstevel 45303831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_fini called\n"); 45403831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 45503831d35Sstevel 45603831d35Sstevel /* 45703831d35Sstevel * Attempt to close the mailbox. 45803831d35Sstevel */ 45903831d35Sstevel mutex_enter(&mboxsc_lock); 46003831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 46103831d35Sstevel if (mailboxp == NULL) { 46203831d35Sstevel error = EBADF; 46303831d35Sstevel } else { 46403831d35Sstevel while (mailboxp->mbox_refcount != 0) { 46503831d35Sstevel cv_wait(&mboxsc_dereference_cv, &mboxsc_lock); 46603831d35Sstevel } 46703831d35Sstevel mboxsc_close_mailbox(mailboxp); 46803831d35Sstevel } 46903831d35Sstevel mutex_exit(&mboxsc_lock); 47003831d35Sstevel 47103831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_fini ret: 0x%08x\n", error); 47203831d35Sstevel return (error); 47303831d35Sstevel } 47403831d35Sstevel 47503831d35Sstevel /* 47603831d35Sstevel * mboxsc_putmsg 47703831d35Sstevel * 47803831d35Sstevel * Attempt to place a message into an outbound mailbox and signal the 47903831d35Sstevel * recipient. A successful return (0) indicates that the message was 48003831d35Sstevel * successfully delivered. 48103831d35Sstevel */ 48203831d35Sstevel int 48303831d35Sstevel mboxsc_putmsg(uint32_t key, uint32_t type, uint32_t cmd, uint64_t *transidp, 48403831d35Sstevel uint32_t length, void *datap, clock_t timeout) 48503831d35Sstevel { 48603831d35Sstevel int i; 48703831d35Sstevel int error = 0; 48803831d35Sstevel int result; 48903831d35Sstevel int lock_held = 0; 49003831d35Sstevel int unlock_err; 49103831d35Sstevel uint8_t data_valid; 49203831d35Sstevel clock_t deadline; 49303831d35Sstevel clock_t remainder; 49403831d35Sstevel mboxsc_chksum_t checksum; 49503831d35Sstevel mboxsc_mbox_t *mailboxp; 49603831d35Sstevel mboxsc_msghdr_t header; 49703831d35Sstevel 49803831d35Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */ 49903831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_putmsg called\n"); 50003831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 50103831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "type = 0x%x\n", type); 50203831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd); 50307d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp); 50403831d35Sstevel if (transidp != NULL) { 50507d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%016lx\n", 50603831d35Sstevel *transidp); 50703831d35Sstevel } 50803831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length); 50903831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap); 51007d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout); 51103831d35Sstevel #endif /* DEBUG */ 51203831d35Sstevel 51303831d35Sstevel /* 51403831d35Sstevel * Perform some basic sanity checks on the message. 51503831d35Sstevel */ 51603831d35Sstevel for (i = 0; i < MBOXSC_NUM_MSG_TYPES; i++) { 51703831d35Sstevel if (type == (1 << i)) { 51803831d35Sstevel break; 51903831d35Sstevel } 52003831d35Sstevel } 52103831d35Sstevel if ((i == MBOXSC_NUM_MSG_TYPES) || (cmd == 0) || 52203831d35Sstevel ((datap == NULL) && (length != 0)) || 52303831d35Sstevel (timeout < MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS) || 52403831d35Sstevel (timeout > MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS)) { 52503831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 52603831d35Sstevel "mboxsc_putmsg ret: 0x%08x\n", EINVAL); 52703831d35Sstevel return (EINVAL); 52803831d35Sstevel } 52903831d35Sstevel 53003831d35Sstevel /* 53103831d35Sstevel * Initialize the header structure with values provided by the caller. 53203831d35Sstevel */ 53303831d35Sstevel header.msg_version = mboxsc_active_version; 53403831d35Sstevel header.msg_type = type; 53503831d35Sstevel header.msg_cmd = cmd; 53603831d35Sstevel header.msg_length = MBOXSC_MSGHDR_SIZE + length; 53703831d35Sstevel if (transidp != NULL) { 53803831d35Sstevel header.msg_transid = *transidp; 53903831d35Sstevel } else { 54003831d35Sstevel header.msg_transid = 0; 54103831d35Sstevel } 54203831d35Sstevel 54303831d35Sstevel /* 54403831d35Sstevel * Perform additional sanity checks on the mailbox and message. 54503831d35Sstevel * Make sure that the specified mailbox really exists, that the 54603831d35Sstevel * given message will fit in it, and that the current message's 54703831d35Sstevel * transaction ID isn't the same as the last message's transaction 54803831d35Sstevel * ID unless both messages are replies (it's okay, necessary even, 54903831d35Sstevel * to reuse a transaction ID when resending a failed reply message, 55003831d35Sstevel * but that is the only case in which it is permissible). 55103831d35Sstevel */ 55203831d35Sstevel mutex_enter(&mboxsc_lock); 55303831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 55403831d35Sstevel 55503831d35Sstevel if (mailboxp == NULL) { 55603831d35Sstevel error = EBADF; 55703831d35Sstevel } else if ((mailboxp->mbox_direction != MBOXSC_MBOX_OUT) || 55803831d35Sstevel (length + MBOXSC_PROTOCOL_SIZE > mailboxp->mbox_length) || 55903831d35Sstevel ((header.msg_transid == mailboxp->mbox_header.msg_transid) && 56003831d35Sstevel ((type & mailboxp->mbox_header.msg_type) != MBOXSC_MSG_REPLY) && 56103831d35Sstevel (header.msg_transid != 0))) { 56203831d35Sstevel error = EINVAL; 56303831d35Sstevel } 56403831d35Sstevel 56503831d35Sstevel if (error != 0) { 56603831d35Sstevel mutex_exit(&mboxsc_lock); 56703831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 56803831d35Sstevel "mboxsc_putmsg ret: 0x%08x\n", error); 56903831d35Sstevel return (error); 57003831d35Sstevel } 57103831d35Sstevel 57203831d35Sstevel /* 57303831d35Sstevel * If the message's transaction ID is set to 0, generate a unique 57403831d35Sstevel * transaction ID and copy it into the message header. If the message 57503831d35Sstevel * is successfully delivered and transidp != NULL, we'll copy this new 57603831d35Sstevel * transid into *transidp later. 57703831d35Sstevel */ 57803831d35Sstevel if (header.msg_transid == 0) { 57903831d35Sstevel header.msg_transid = 58003831d35Sstevel mboxsc_generate_transid(mailboxp->mbox_header.msg_transid); 58103831d35Sstevel } 58203831d35Sstevel 58303831d35Sstevel /* 58403831d35Sstevel * Don't allow mboxsc_putmsg to attempt to place a message for 58503831d35Sstevel * longer than the caller's timeout. 58603831d35Sstevel */ 58703831d35Sstevel deadline = ddi_get_lbolt() + 58803831d35Sstevel drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC); 58903831d35Sstevel 59003831d35Sstevel /* 59103831d35Sstevel * Increment the reference count on the mailbox to keep it from being 59203831d35Sstevel * closed, and wait for it to become available. 59303831d35Sstevel */ 59403831d35Sstevel mboxsc_reference_mailbox(mailboxp); 59503831d35Sstevel remainder = 1; 59603831d35Sstevel while ((mailboxp->mbox_state & STATE_WRITING) && 59703831d35Sstevel (remainder > 0)) { 59803831d35Sstevel remainder = cv_timedwait_sig(&(mailboxp->mbox_wait), 59903831d35Sstevel &mboxsc_lock, deadline); 60003831d35Sstevel } 60103831d35Sstevel 60203831d35Sstevel /* 60303831d35Sstevel * Check to see whether or not the mailbox became available. If it 60403831d35Sstevel * did not, decrement its reference count and return an error to the 60503831d35Sstevel * caller. 60603831d35Sstevel */ 60703831d35Sstevel if (remainder == -1) { 60803831d35Sstevel error = ENOSPC; 60903831d35Sstevel } else if (remainder == 0) { 61003831d35Sstevel error = EINTR; 61103831d35Sstevel } 61203831d35Sstevel 61303831d35Sstevel if (error != 0) { 61403831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 61503831d35Sstevel mutex_exit(&mboxsc_lock); 61603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 61703831d35Sstevel "mboxsc_putmsg ret: 0x%08x\n", error); 61803831d35Sstevel return (error); 61903831d35Sstevel } 62003831d35Sstevel 62103831d35Sstevel /* 62203831d35Sstevel * Since the message is valid and we're going to try to write it to 62303831d35Sstevel * IOSRAM, record its header for future reference (e.g. to make sure the 62403831d35Sstevel * next message doesn't incorrectly use the same transID). 62503831d35Sstevel */ 62603831d35Sstevel bcopy(&header, &(mailboxp->mbox_header), MBOXSC_MSGHDR_SIZE); 62703831d35Sstevel 62803831d35Sstevel /* 62903831d35Sstevel * Flag the mailbox as being in use and release the global lock. 63003831d35Sstevel */ 63103831d35Sstevel mailboxp->mbox_state |= STATE_WRITING; 63203831d35Sstevel mutex_exit(&mboxsc_lock); 63303831d35Sstevel 63403831d35Sstevel /* 63503831d35Sstevel * Calculate the message checksum using the header and the data. 63603831d35Sstevel */ 63703831d35Sstevel checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header, 63803831d35Sstevel MBOXSC_MSGHDR_SIZE); 63903831d35Sstevel checksum = mboxsc_checksum(checksum, (uint8_t *)datap, length); 64003831d35Sstevel 64103831d35Sstevel /* 64203831d35Sstevel * Attempt to write the message and checksum to IOSRAM until successful, 64303831d35Sstevel * or as long as time remains and no errors other than EAGAIN are 64403831d35Sstevel * returned from any call to the IOSRAM driver in case there is a tunnel 64503831d35Sstevel * switch in progress. 64603831d35Sstevel */ 64703831d35Sstevel error = mboxsc_timed_write(deadline, key, MBOXSC_MSGHDR_OFFSET, 64803831d35Sstevel MBOXSC_MSGHDR_SIZE, (caddr_t)&header); 64903831d35Sstevel 65003831d35Sstevel if (error == 0) { 65103831d35Sstevel error = mboxsc_timed_write(deadline, key, MBOXSC_DATA_OFFSET, 65203831d35Sstevel length, (caddr_t)datap); 65303831d35Sstevel } 65403831d35Sstevel 65503831d35Sstevel if (error == 0) { 65603831d35Sstevel error = mboxsc_timed_write(deadline, key, header.msg_length, 65703831d35Sstevel MBOXSC_CHKSUM_SIZE, (caddr_t)&checksum); 65803831d35Sstevel } 65903831d35Sstevel 66003831d35Sstevel /* 66103831d35Sstevel * Lock the flags before setting data_valid. This isn't strictly 66203831d35Sstevel * necessary for correct protocol operation, but it gives us a chance to 66303831d35Sstevel * verify that the flags lock is functional before we commit to sending 66403831d35Sstevel * the message. 66503831d35Sstevel */ 66603831d35Sstevel if (error == 0) { 66703831d35Sstevel error = mboxsc_lock_flags(FALSE, deadline); 66803831d35Sstevel if (error == 0) { 66903831d35Sstevel lock_held = 1; 67003831d35Sstevel } else if (error == EBUSY) { 67103831d35Sstevel error = EAGAIN; 67203831d35Sstevel } 67303831d35Sstevel } 67403831d35Sstevel 67503831d35Sstevel if (error == 0) { 67603831d35Sstevel error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_VALID, 67703831d35Sstevel IOSRAM_INT_TO_SSC); 67803831d35Sstevel } 67903831d35Sstevel 68003831d35Sstevel /* 68103831d35Sstevel * Unlock the flags. If an error is encountered, only return it if 68203831d35Sstevel * another error hasn't been encountered previously. 68303831d35Sstevel */ 68403831d35Sstevel if (lock_held) { 68503831d35Sstevel unlock_err = mboxsc_unlock_flags(TRUE); 68603831d35Sstevel if ((unlock_err != 0) && ((error == 0) || (error == EAGAIN))) { 68703831d35Sstevel error = unlock_err; 68803831d35Sstevel } 68903831d35Sstevel } 69003831d35Sstevel 69103831d35Sstevel /* 69203831d35Sstevel * If time ran out or an IOSRAM call failed, notify other callers that 69303831d35Sstevel * the mailbox is available, decrement its reference count, and return 69403831d35Sstevel * an error. 69503831d35Sstevel */ 69603831d35Sstevel if (error != 0) { 69703831d35Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE)); 69803831d35Sstevel mutex_enter(&mboxsc_lock); 69903831d35Sstevel mailboxp->mbox_state &= ~STATE_WRITING; 70003831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 70103831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 70203831d35Sstevel mutex_exit(&mboxsc_lock); 70303831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 70403831d35Sstevel "mboxsc_putmsg ret: 0x%08x\n", error); 70503831d35Sstevel return (error); 70603831d35Sstevel } 70703831d35Sstevel 70803831d35Sstevel /* 70903831d35Sstevel * Send an interrupt to the remote mailbox interface to announce the 71003831d35Sstevel * presence of a new, valid message. 71103831d35Sstevel */ 71203831d35Sstevel error = mboxsc_timed_send_intr(deadline); 71303831d35Sstevel 71403831d35Sstevel /* 71503831d35Sstevel * Wait until either the data_valid flag is set INVALID by the 71603831d35Sstevel * remote client or time runs out. Since we're calling delay as 71703831d35Sstevel * a part of polling the flag anyway, we don't really need to do 71803831d35Sstevel * the usual continuous retry if iosram_get_flag returns EAGAIN. 71903831d35Sstevel */ 72003831d35Sstevel data_valid = IOSRAM_DATA_VALID; 72103831d35Sstevel if (error == DDI_SUCCESS) { 72203831d35Sstevel do { 72303831d35Sstevel delay(MIN(PUTMSG_POLL, deadline - ddi_get_lbolt())); 72403831d35Sstevel error = iosram_get_flag(key, &data_valid, NULL); 72503831d35Sstevel } while ((data_valid == IOSRAM_DATA_VALID) && 72603831d35Sstevel ((error == EAGAIN) || (error == 0)) && 72703831d35Sstevel (deadline - ddi_get_lbolt() >= 0)); 72803831d35Sstevel } 72903831d35Sstevel 73003831d35Sstevel /* 73103831d35Sstevel * If the data_valid flag was set to INVALID by the other side, the 73203831d35Sstevel * message was successfully transmitted. If it wasn't, but there 73303831d35Sstevel * weren't any IOSRAM errors, the operation timed out. If there was a 73403831d35Sstevel * problem with the IOSRAM, pass that info back to the caller. 73503831d35Sstevel */ 73603831d35Sstevel if (data_valid == IOSRAM_DATA_INVALID) { 73703831d35Sstevel result = 0; 73803831d35Sstevel } else if ((error == 0) || (error == DDI_FAILURE)) { 73903831d35Sstevel result = ETIMEDOUT; 74003831d35Sstevel } else { 74103831d35Sstevel ASSERT(error != EINVAL); 74203831d35Sstevel result = error; 74303831d35Sstevel } 74403831d35Sstevel 74503831d35Sstevel /* 74603831d35Sstevel * If the message has not been picked up, expire it. Note that this may 74703831d35Sstevel * actually result in detecting successful message delivery if the SC 74803831d35Sstevel * picks it up at the last moment. If expiration fails due to an error, 74903831d35Sstevel * return an error to the user even if the message appears to have 75003831d35Sstevel * been successfully delivered. 75103831d35Sstevel */ 75203831d35Sstevel if (data_valid == IOSRAM_DATA_VALID) { 75303831d35Sstevel error = mboxsc_expire_message(key, &result); 75403831d35Sstevel if ((error != 0) && ((result == 0) || (result == ETIMEDOUT))) { 75503831d35Sstevel result = error; 75603831d35Sstevel } 75703831d35Sstevel } 75803831d35Sstevel 75903831d35Sstevel /* 76003831d35Sstevel * If the message was successfully delivered, and we generated a 76103831d35Sstevel * transaction ID for the caller, and the caller wants to know what it 76203831d35Sstevel * was, give it to them. 76303831d35Sstevel */ 76403831d35Sstevel if ((result == 0) && (transidp != NULL) && (*transidp == 0)) { 76503831d35Sstevel *transidp = header.msg_transid; 76603831d35Sstevel } 76703831d35Sstevel 76803831d35Sstevel /* 76903831d35Sstevel * Regardless of whether the message was successfully transmitted or 77003831d35Sstevel * not, notify other callers that the mailbox is available and decrement 77103831d35Sstevel * its reference count. 77203831d35Sstevel */ 77303831d35Sstevel mutex_enter(&mboxsc_lock); 77403831d35Sstevel mailboxp->mbox_state &= ~STATE_WRITING; 77503831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 77603831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 77703831d35Sstevel mutex_exit(&mboxsc_lock); 77803831d35Sstevel 77903831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_putmsg ret: 0x%08x\n", 78003831d35Sstevel result); 78103831d35Sstevel return (result); 78203831d35Sstevel } 78303831d35Sstevel 78403831d35Sstevel /* 78503831d35Sstevel * mboxsc_getmsg 78603831d35Sstevel * 78703831d35Sstevel * Attempt to retrieve a message from the mailbox with the given key that 78803831d35Sstevel * matches values provided in msgp. A successful return (0) indicates that 78903831d35Sstevel * a message matching the caller's request was successfully received within 79003831d35Sstevel * timeout milliseconds. If a message matching the caller's request is 79103831d35Sstevel * detected, but can't be successfully read, an error will be returned even 79203831d35Sstevel * if the caller's timeout hasn't expired. 79303831d35Sstevel */ 79403831d35Sstevel int 79503831d35Sstevel mboxsc_getmsg(uint32_t key, uint32_t *typep, uint32_t *cmdp, uint64_t *transidp, 79603831d35Sstevel uint32_t *lengthp, void *datap, clock_t timeout) 79703831d35Sstevel { 79803831d35Sstevel int error = 0; 79903831d35Sstevel uint32_t datalen; 80003831d35Sstevel uint8_t data_valid; 80103831d35Sstevel uint8_t lock_held; 80203831d35Sstevel mboxsc_chksum_t read_checksum; 80303831d35Sstevel mboxsc_chksum_t calc_checksum; 80403831d35Sstevel uint64_t read_transid; 80503831d35Sstevel clock_t deadline; 80603831d35Sstevel clock_t remainder; 80703831d35Sstevel mboxsc_mbox_t *mailboxp; 80803831d35Sstevel mboxsc_msghdr_t header; 80903831d35Sstevel 81003831d35Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */ 81103831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_getmsg called\n"); 81203831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 81307d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "typep = %p\n", (void *)typep); 81403831d35Sstevel if (typep != NULL) { 81503831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*typep = 0x%x\n", *typep); 81603831d35Sstevel } 81707d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmdp = %p\n", (void *)cmdp); 81803831d35Sstevel if (cmdp != NULL) { 81903831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*cmdp = 0x%x\n", *cmdp); 82003831d35Sstevel } 82107d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp); 82203831d35Sstevel if (transidp != NULL) { 82307d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%lx\n", 82403831d35Sstevel *transidp); 82503831d35Sstevel } 82607d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "lengthp = %p\n", (void *)lengthp); 82703831d35Sstevel if (lengthp != NULL) { 82803831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*lengthp = 0x%x\n", 82903831d35Sstevel *lengthp); 83003831d35Sstevel } 83103831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap); 83203831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout); 83303831d35Sstevel #endif /* DEBUG */ 83403831d35Sstevel 83503831d35Sstevel /* 83603831d35Sstevel * Perform basic sanity checks on the caller's request. 83703831d35Sstevel */ 83803831d35Sstevel if ((typep == NULL) || (*typep >= (1 << MBOXSC_NUM_MSG_TYPES)) || 83903831d35Sstevel (cmdp == NULL) || (transidp == NULL) || (lengthp == NULL) || 84003831d35Sstevel ((datap == NULL) && (*lengthp != 0)) || 84103831d35Sstevel (timeout < MBOXSC_GETMSG_MIN_TIMEOUT_MSECS) || 84203831d35Sstevel (timeout > MBOXSC_GETMSG_MAX_TIMEOUT_MSECS)) { 84303831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 84403831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", EINVAL); 84503831d35Sstevel return (EINVAL); 84603831d35Sstevel } 84703831d35Sstevel 84803831d35Sstevel /* 84903831d35Sstevel * Don't allow mboxsc_getmsg to attempt to receive a message for 85003831d35Sstevel * longer than the caller's timeout. 85103831d35Sstevel */ 85203831d35Sstevel deadline = ddi_get_lbolt() + 85303831d35Sstevel drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC); 85403831d35Sstevel 85503831d35Sstevel /* 85603831d35Sstevel * Perform additional sanity checks on the client's request and the 85703831d35Sstevel * associated mailbox. 85803831d35Sstevel */ 85903831d35Sstevel mutex_enter(&mboxsc_lock); 86003831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 86103831d35Sstevel if (mailboxp == NULL) { 86203831d35Sstevel error = EBADF; 86303831d35Sstevel } else if (mailboxp->mbox_direction != MBOXSC_MBOX_IN) { 86403831d35Sstevel error = EINVAL; 86503831d35Sstevel } 86603831d35Sstevel 86703831d35Sstevel if (error != 0) { 86803831d35Sstevel mutex_exit(&mboxsc_lock); 86903831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 87003831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 87103831d35Sstevel return (error); 87203831d35Sstevel } 87303831d35Sstevel 87403831d35Sstevel /* 87503831d35Sstevel * The request is okay, so reference the mailbox (to keep it from being 87603831d35Sstevel * closed), and proceed with the real work. 87703831d35Sstevel */ 87803831d35Sstevel mboxsc_reference_mailbox(mailboxp); 87903831d35Sstevel 88003831d35Sstevel /* 88103831d35Sstevel * Certain failures that may occur late in the process of getting a 88203831d35Sstevel * message (e.g. checksum error, cancellation by the sender) are 88303831d35Sstevel * supposed to leave the recipient waiting for the next message to 88403831d35Sstevel * arrive rather than returning an error. To facilitate restarting 88503831d35Sstevel * the message acquisition process, the following label is provided 88603831d35Sstevel * as a target for a very few judiciously-placed "goto"s. 88703831d35Sstevel * 88803831d35Sstevel * The mboxsc_lock mutex MUST be held when jumping to this point. 88903831d35Sstevel */ 89003831d35Sstevel mboxsc_getmsg_retry: 89103831d35Sstevel ; 89203831d35Sstevel 89303831d35Sstevel /* 89403831d35Sstevel * If there is a valid message in the mailbox right now, check to 89503831d35Sstevel * see if it matches the caller's request. If not, or if another 89603831d35Sstevel * caller is already reading it, wait for either the arrival of the 89703831d35Sstevel * next message or the expiration of the caller's specified timeout. 89803831d35Sstevel */ 89903831d35Sstevel error = 0; 90003831d35Sstevel while (!(mailboxp->mbox_state & STATE_HDRVALID) || 90103831d35Sstevel (mailboxp->mbox_state & STATE_READING) || 90203831d35Sstevel !MSG_TYPE_MATCHES(*typep, &(mailboxp->mbox_header)) || 90303831d35Sstevel !MSG_CMD_MATCHES(*cmdp, &(mailboxp->mbox_header)) || 90403831d35Sstevel !MSG_TRANSID_MATCHES(*transidp, &(mailboxp->mbox_header))) { 90503831d35Sstevel remainder = cv_timedwait_sig(&(mailboxp->mbox_wait), 90603831d35Sstevel &mboxsc_lock, deadline); 90703831d35Sstevel if (remainder == -1) { 90803831d35Sstevel error = ETIMEDOUT; 90903831d35Sstevel } else if (remainder == 0) { 91003831d35Sstevel error = EINTR; 91103831d35Sstevel } 91203831d35Sstevel 91303831d35Sstevel if (error != 0) { 91403831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 91503831d35Sstevel mutex_exit(&mboxsc_lock); 91603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 91703831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 91803831d35Sstevel return (error); 91903831d35Sstevel } 92003831d35Sstevel } 92103831d35Sstevel 92203831d35Sstevel /* 92303831d35Sstevel * If somebody sends us a message using a Mailbox Protocol version 92403831d35Sstevel * greater than the highest one we understand, invalidate the message, 92503831d35Sstevel * because we can't safely interpret anything beyond the version field. 92603831d35Sstevel */ 92703831d35Sstevel if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) { 92803831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, 92903831d35Sstevel "incoming message with unsupported version %d\n", 93003831d35Sstevel mailboxp->mbox_header.msg_version); 93103831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 93203831d35Sstevel goto mboxsc_getmsg_retry; 93303831d35Sstevel } 93403831d35Sstevel 93503831d35Sstevel /* 93603831d35Sstevel * At this point, there is a stored message header that matches the 93703831d35Sstevel * caller's request, but the actual message may no longer be valid 93803831d35Sstevel * in IOSRAM. Check the data_valid flag to see whether or not 93903831d35Sstevel * this is the case. If the message has expired, go start over. 94003831d35Sstevel * 94103831d35Sstevel * The global mutex is held while reading flag data from IOSRAM to 94203831d35Sstevel * avoid certain race conditions. One race condition is still 94303831d35Sstevel * possible (i.e. SC-side has just set the data_valid flag for a 94403831d35Sstevel * new message, but the stored message header hasn't been updated 94503831d35Sstevel * yet), but it won't cause incorrect behavior (just some wasted work). 94603831d35Sstevel */ 94703831d35Sstevel error = iosram_get_flag(key, &data_valid, NULL); 94803831d35Sstevel 94903831d35Sstevel ASSERT(error != EINVAL); 95003831d35Sstevel if (error == 0) { 95103831d35Sstevel if (data_valid != IOSRAM_DATA_VALID) { 95203831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 95303831d35Sstevel goto mboxsc_getmsg_retry; 95403831d35Sstevel } 95503831d35Sstevel } else if ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)) { 95603831d35Sstevel mutex_exit(&mboxsc_lock); 95703831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 95803831d35Sstevel mutex_enter(&mboxsc_lock); 95903831d35Sstevel goto mboxsc_getmsg_retry; 96003831d35Sstevel } 96103831d35Sstevel 96203831d35Sstevel /* 96303831d35Sstevel * If the message is larger than the caller's buffer, provide the caller 96403831d35Sstevel * with the length of the message and return an error. 96503831d35Sstevel */ 96603831d35Sstevel datalen = mailboxp->mbox_header.msg_length - MBOXSC_MSGHDR_SIZE; 96703831d35Sstevel if ((error == 0) && (datalen > *lengthp)) { 96803831d35Sstevel *lengthp = datalen; 96903831d35Sstevel error = EMSGSIZE; 97003831d35Sstevel } 97103831d35Sstevel 97203831d35Sstevel /* 97303831d35Sstevel * Note that there's no need to check STATE_HDRVALID before broadcasting 97403831d35Sstevel * here because the header is guaranteed to be valid at this point. 97503831d35Sstevel */ 97603831d35Sstevel if (error != 0) { 97703831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 97803831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 97903831d35Sstevel mutex_exit(&mboxsc_lock); 98003831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 98103831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 98203831d35Sstevel return (error); 98303831d35Sstevel } 98403831d35Sstevel 98503831d35Sstevel /* 98603831d35Sstevel * Store a copy of the current message header, flag the mailbox to 98703831d35Sstevel * indicate that it is being read and attempt to read the message data 98803831d35Sstevel * and checksum. 98903831d35Sstevel */ 99003831d35Sstevel bcopy(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE); 99103831d35Sstevel mailboxp->mbox_state |= STATE_READING; 99203831d35Sstevel mutex_exit(&mboxsc_lock); 99303831d35Sstevel 99403831d35Sstevel if (datalen > 0) { 99503831d35Sstevel error = mboxsc_timed_read(deadline, key, MBOXSC_DATA_OFFSET, 99603831d35Sstevel datalen, (caddr_t)datap); 99703831d35Sstevel } 99803831d35Sstevel 99903831d35Sstevel if (error == 0) { 100003831d35Sstevel error = mboxsc_timed_read(deadline, key, header.msg_length, 100103831d35Sstevel MBOXSC_CHKSUM_SIZE, (caddr_t)&read_checksum); 100203831d35Sstevel } 100303831d35Sstevel 100403831d35Sstevel /* 100503831d35Sstevel * Check for errors that may have occurred while accessing IOSRAM. 100603831d35Sstevel */ 100703831d35Sstevel if (error != 0) { 100803831d35Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE)); 100903831d35Sstevel mutex_enter(&mboxsc_lock); 101003831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 101103831d35Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) { 101203831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 101303831d35Sstevel } 101403831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 101503831d35Sstevel mutex_exit(&mboxsc_lock); 101603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 101703831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 101803831d35Sstevel return (error); 101903831d35Sstevel } 102003831d35Sstevel 102103831d35Sstevel /* 102203831d35Sstevel * Calculate the checksum for the header and data that was read from 102303831d35Sstevel * IOSRAM. 102403831d35Sstevel */ 102503831d35Sstevel calc_checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header, 102603831d35Sstevel MBOXSC_MSGHDR_SIZE); 102703831d35Sstevel calc_checksum = mboxsc_checksum(calc_checksum, (uint8_t *)datap, 102803831d35Sstevel datalen); 102903831d35Sstevel 103003831d35Sstevel /* 103103831d35Sstevel * If the message header has been invalidated, note the change. 103203831d35Sstevel * If a the checksum verification fails, invalidate the message 103303831d35Sstevel * header. In either case, go back to the beginning and wait 103403831d35Sstevel * for a new message. 103503831d35Sstevel */ 103603831d35Sstevel mutex_enter(&mboxsc_lock); 103703831d35Sstevel if (!(mailboxp->mbox_state & STATE_HDRVALID)) { 103803831d35Sstevel error = -1; 103903831d35Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT, 104003831d35Sstevel "mboxsc_getmsg - message invalidated while reading\n"); 104103831d35Sstevel } else if (read_checksum != calc_checksum) { 104203831d35Sstevel error = -1; 104303831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 104403831d35Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT, 104503831d35Sstevel "mboxsc_getmsg - message failed checksum\n"); 104603831d35Sstevel cmn_err(CE_NOTE, 104703831d35Sstevel "mboxsc_getmsg - message failed checksum\n"); 104803831d35Sstevel } 104903831d35Sstevel 105003831d35Sstevel if (error == -1) { 105103831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 105203831d35Sstevel goto mboxsc_getmsg_retry; 105303831d35Sstevel } 105403831d35Sstevel 105503831d35Sstevel /* 105603831d35Sstevel * Acquire the hardware lock used for synchronization of data_valid flag 105703831d35Sstevel * access to avoid race conditions. If it is acquired, try to check the 105803831d35Sstevel * current data_valid flag and transaction ID to verify that the message 105903831d35Sstevel * is still valid. 106003831d35Sstevel */ 106103831d35Sstevel mutex_exit(&mboxsc_lock); 106203831d35Sstevel 106303831d35Sstevel if ((error = mboxsc_lock_flags(FALSE, deadline)) != 0) { 106403831d35Sstevel lock_held = FALSE; 106503831d35Sstevel /* 106603831d35Sstevel * We don't "do" EBUSY here, so treat it as EAGAIN. 106703831d35Sstevel */ 106803831d35Sstevel if (error == EBUSY) { 106903831d35Sstevel error = EAGAIN; 107003831d35Sstevel } 107103831d35Sstevel } else { 107203831d35Sstevel lock_held = TRUE; 107303831d35Sstevel } 107403831d35Sstevel 107503831d35Sstevel if (error == 0) { 107603831d35Sstevel error = mboxsc_timed_get_flag(deadline, key, &data_valid, NULL); 107703831d35Sstevel } 107803831d35Sstevel 107903831d35Sstevel if ((error == 0) && (data_valid == IOSRAM_DATA_VALID)) { 108003831d35Sstevel error = mboxsc_timed_read(deadline, key, 1081*ad9a7bd3SRichard Lowe offsetof(mboxsc_msghdr_t, msg_transid), 108203831d35Sstevel FIELD_SIZE(mboxsc_msghdr_t, msg_transid), 108303831d35Sstevel (caddr_t)&read_transid); 108403831d35Sstevel } 108503831d35Sstevel 108603831d35Sstevel /* 108703831d35Sstevel * If something failed along the way, either the error is unrecoverable 108803831d35Sstevel * or we're just plain out of time, so unlock the flags if they were 108903831d35Sstevel * locked, release the mailbox, wake up other potential readers if 109003831d35Sstevel * there's still a message around, and return. 109103831d35Sstevel */ 109203831d35Sstevel if (error != 0) { 109303831d35Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE)); 109403831d35Sstevel if (lock_held) { 109507d06da5SSurya Prakki (void) mboxsc_unlock_flags(TRUE); 109603831d35Sstevel } 109703831d35Sstevel mutex_enter(&mboxsc_lock); 109803831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 109903831d35Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) { 110003831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 110103831d35Sstevel } 110203831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 110303831d35Sstevel mutex_exit(&mboxsc_lock); 110403831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 110503831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 110603831d35Sstevel return (error); 110703831d35Sstevel } 110803831d35Sstevel 110903831d35Sstevel /* 111003831d35Sstevel * If the data_valid flag isn't set to IOSRAM_DATA_VALID, or the 111103831d35Sstevel * message transaction ID in IOSRAM has changed, the message being 111203831d35Sstevel * read was timed out by its sender. Since the data_valid flag can't 111303831d35Sstevel * change as long as we have the flags locked, we can safely mark the 111403831d35Sstevel * stored message header invalid if either the data_valid flag isn't set 111503831d35Sstevel * or the stored transaction ID doesn't match the one we read. (If 111603831d35Sstevel * data_valid is set, the transaction ID shouldn't be changing 111703831d35Sstevel * underneath us.) On the other hand, if there may still be a valid 111803831d35Sstevel * message, wake up any pending readers. 111903831d35Sstevel */ 112003831d35Sstevel if ((data_valid != IOSRAM_DATA_VALID) || 112103831d35Sstevel (read_transid != header.msg_transid)) { 112203831d35Sstevel mutex_enter(&mboxsc_lock); 112303831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 112403831d35Sstevel if ((data_valid != IOSRAM_DATA_VALID) || 112503831d35Sstevel (mailboxp->mbox_header.msg_transid != read_transid)) { 112603831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 112703831d35Sstevel } else if (mailboxp->mbox_state & STATE_HDRVALID) { 112803831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 112903831d35Sstevel } 113003831d35Sstevel 113103831d35Sstevel /* 113203831d35Sstevel * Unfortunately, we can't be holding mboxsc_lock when we unlock 113303831d35Sstevel * the flags. However, we have to hold the flags until here to 113403831d35Sstevel * make sure the SC doesn't change the message's state while 113503831d35Sstevel * we're checking to see if we should invalidate our stored 113603831d35Sstevel * header. 113703831d35Sstevel */ 113803831d35Sstevel mutex_exit(&mboxsc_lock); 113903831d35Sstevel error = mboxsc_unlock_flags(TRUE); 114003831d35Sstevel mutex_enter(&mboxsc_lock); 114103831d35Sstevel 114203831d35Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT, 114303831d35Sstevel "mboxsc_getmsg() - message invalidated by sender\n"); 114403831d35Sstevel goto mboxsc_getmsg_retry; 114503831d35Sstevel } 114603831d35Sstevel 114703831d35Sstevel /* 114803831d35Sstevel * If everything has worked up to this point, all that remains is 114903831d35Sstevel * to set the data_valid flag to IOSRAM_DATA_INVALID, tidy up, and 115003831d35Sstevel * return the message. If the flag can't be set, the message can't 115103831d35Sstevel * be received, so keep trying as long as there is time. 115203831d35Sstevel */ 115303831d35Sstevel error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_INVALID, 115403831d35Sstevel IOSRAM_INT_NONE); 115503831d35Sstevel 115607d06da5SSurya Prakki (void) mboxsc_unlock_flags(TRUE); 115703831d35Sstevel mutex_enter(&mboxsc_lock); 115803831d35Sstevel 115903831d35Sstevel if (error != 0) { 116003831d35Sstevel ASSERT(error != EINVAL); 116103831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 116203831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 116303831d35Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) { 116403831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 116503831d35Sstevel } 116603831d35Sstevel mutex_exit(&mboxsc_lock); 116703831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 116803831d35Sstevel "mboxsc_getmsg ret: 0x%08x\n", error); 116903831d35Sstevel return (error); 117003831d35Sstevel } 117103831d35Sstevel 117203831d35Sstevel /* 117303831d35Sstevel * If the message was read 100% successfully and the stored message 117403831d35Sstevel * header for the mailbox still matches the message that was read, 117503831d35Sstevel * invalidate it to prevent other readers from trying to read it. 117603831d35Sstevel */ 117703831d35Sstevel if (bcmp(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE) == 0) { 117803831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 117903831d35Sstevel } else if (mailboxp->mbox_state & STATE_HDRVALID) { 118003831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 118103831d35Sstevel } 118203831d35Sstevel 118303831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 118403831d35Sstevel mailboxp->mbox_state &= ~STATE_READING; 118503831d35Sstevel mutex_exit(&mboxsc_lock); 118603831d35Sstevel 118703831d35Sstevel /* 118803831d35Sstevel * Since we're successfully returning a message, we need to provide the 118903831d35Sstevel * caller with all of the interesting header information. 119003831d35Sstevel */ 119103831d35Sstevel *typep = header.msg_type; 119203831d35Sstevel *cmdp = header.msg_cmd; 119303831d35Sstevel *transidp = header.msg_transid; 119403831d35Sstevel *lengthp = datalen; 119503831d35Sstevel 119603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_getmsg ret: 0x%08x\n", 0); 119703831d35Sstevel return (0); 119803831d35Sstevel } 119903831d35Sstevel 120003831d35Sstevel /* 120103831d35Sstevel * mboxsc_ctrl 120203831d35Sstevel * 120303831d35Sstevel * This routine provides access to a variety of services not available through 120403831d35Sstevel * the basic API. 120503831d35Sstevel */ 120603831d35Sstevel int 120703831d35Sstevel mboxsc_ctrl(uint32_t key, uint32_t cmd, void *arg) 120803831d35Sstevel { 120903831d35Sstevel int error = 0; 121003831d35Sstevel mboxsc_mbox_t *mailboxp; 121103831d35Sstevel 121203831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_ctrl called\n"); 121303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 121403831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd); 121503831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = %p\n", arg); 121603831d35Sstevel 121703831d35Sstevel mutex_enter(&mboxsc_lock); 121803831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 121903831d35Sstevel if (mailboxp == NULL) { 122003831d35Sstevel mutex_exit(&mboxsc_lock); 122103831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n", 122203831d35Sstevel EBADF); 122303831d35Sstevel return (EBADF); 122403831d35Sstevel } 122503831d35Sstevel 122603831d35Sstevel switch (cmd) { 122703831d35Sstevel case MBOXSC_CMD_VERSION: 122803831d35Sstevel /* 122903831d35Sstevel * Return the Protocol version currently in use. Since 123003831d35Sstevel * there is only one version that exists right now, we 123103831d35Sstevel * can't be using anything else. 123203831d35Sstevel */ 123303831d35Sstevel if (arg == NULL) { 123403831d35Sstevel error = EINVAL; 123503831d35Sstevel break; 123603831d35Sstevel } 123703831d35Sstevel 123803831d35Sstevel *(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION; 123903831d35Sstevel break; 124003831d35Sstevel 124103831d35Sstevel case MBOXSC_CMD_MAXVERSION: 124203831d35Sstevel /* 124303831d35Sstevel * Return the highest Protocol version that we support. 124403831d35Sstevel */ 124503831d35Sstevel if (arg == NULL) { 124603831d35Sstevel error = EINVAL; 124703831d35Sstevel break; 124803831d35Sstevel } 124903831d35Sstevel 125003831d35Sstevel *(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION; 125103831d35Sstevel break; 125203831d35Sstevel 125303831d35Sstevel case MBOXSC_CMD_MAXDATALEN: 125403831d35Sstevel /* 125503831d35Sstevel * Return the amount of space available for client data 125603831d35Sstevel * in the indicated mailbox. 125703831d35Sstevel */ 125803831d35Sstevel if (arg == NULL) { 125903831d35Sstevel error = EINVAL; 126003831d35Sstevel break; 126103831d35Sstevel } 126203831d35Sstevel 126303831d35Sstevel *(uint32_t *)arg = mailboxp->mbox_length - 126403831d35Sstevel MBOXSC_PROTOCOL_SIZE; 126503831d35Sstevel break; 126603831d35Sstevel 126703831d35Sstevel case MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE: 126803831d35Sstevel { 126903831d35Sstevel mboxsc_timeout_range_t *rangep; 127003831d35Sstevel 127103831d35Sstevel /* 127203831d35Sstevel * Return the range of acceptable timeout values for 127303831d35Sstevel * mboxsc_putmsg, expressed in milliseconds. 127403831d35Sstevel */ 127503831d35Sstevel if (arg == NULL) { 127603831d35Sstevel error = EINVAL; 127703831d35Sstevel break; 127803831d35Sstevel } 127903831d35Sstevel 128003831d35Sstevel rangep = (mboxsc_timeout_range_t *)arg; 128103831d35Sstevel rangep->min_timeout = MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS; 128203831d35Sstevel rangep->max_timeout = MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS; 128303831d35Sstevel break; 128403831d35Sstevel } 128503831d35Sstevel 128603831d35Sstevel case MBOXSC_CMD_GETMSG_TIMEOUT_RANGE: 128703831d35Sstevel { 128803831d35Sstevel mboxsc_timeout_range_t *rangep; 128903831d35Sstevel 129003831d35Sstevel /* 129103831d35Sstevel * Return the range of acceptable timeout values for 129203831d35Sstevel * mboxsc_getmsg, expressed in milliseconds. 129303831d35Sstevel */ 129403831d35Sstevel if (arg == NULL) { 129503831d35Sstevel error = EINVAL; 129603831d35Sstevel break; 129703831d35Sstevel } 129803831d35Sstevel 129903831d35Sstevel rangep = (mboxsc_timeout_range_t *)arg; 130003831d35Sstevel rangep->min_timeout = MBOXSC_GETMSG_MIN_TIMEOUT_MSECS; 130103831d35Sstevel rangep->max_timeout = MBOXSC_GETMSG_MAX_TIMEOUT_MSECS; 130203831d35Sstevel break; 130303831d35Sstevel } 130403831d35Sstevel 130503831d35Sstevel default: 130603831d35Sstevel error = ENOTSUP; 130703831d35Sstevel break; 130803831d35Sstevel } 130903831d35Sstevel 131003831d35Sstevel mutex_exit(&mboxsc_lock); 131103831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n", error); 131203831d35Sstevel return (error); 131303831d35Sstevel } 131403831d35Sstevel 131503831d35Sstevel /* 131603831d35Sstevel * mboxsc_putmsg_def_timeout 131703831d35Sstevel * 131803831d35Sstevel * This routine returns the default mboxsc_putmsg timeout provided for the 131903831d35Sstevel * convenience of clients. 132003831d35Sstevel */ 132103831d35Sstevel clock_t 132203831d35Sstevel mboxsc_putmsg_def_timeout(void) 132303831d35Sstevel { 132403831d35Sstevel return (MBOXSC_PUTMSG_DEF_TIMEOUT_MSECS); 132503831d35Sstevel } 132603831d35Sstevel 132703831d35Sstevel /* 132803831d35Sstevel * mboxsc_iosram_callback 132903831d35Sstevel * 133003831d35Sstevel * This routine is registered with the IOSRAM driver for all inbound mailboxes, 133103831d35Sstevel * and performs preliminary processing of all new messages. 133203831d35Sstevel */ 133303831d35Sstevel static void 133403831d35Sstevel mboxsc_iosram_callback(void *arg) 133503831d35Sstevel { 133603831d35Sstevel int error = 0; 133703831d35Sstevel uint8_t data_valid; 133803831d35Sstevel uint32_t key = (uint32_t)(uintptr_t)arg; 133903831d35Sstevel mboxsc_mbox_t *mailboxp; 134003831d35Sstevel 134103831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_iosram_callback called\n"); 134203831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = 0x%x\n", key); 134303831d35Sstevel 134403831d35Sstevel mutex_enter(&mboxsc_lock); 134503831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 134603831d35Sstevel 134703831d35Sstevel /* 134803831d35Sstevel * We shouldn't ever receive a callback for a mailbox that doesn't 134903831d35Sstevel * exist or for an output mailbox. 135003831d35Sstevel */ 135103831d35Sstevel ASSERT(mailboxp != NULL); 135203831d35Sstevel ASSERT(mailboxp->mbox_direction == MBOXSC_MBOX_IN); 135303831d35Sstevel 135403831d35Sstevel /* 135503831d35Sstevel * Attempt to read the header of the mailbox. If the IOSRAM returns 135603831d35Sstevel * EAGAIN, indicating a tunnel switch is in progress, do not retry 135703831d35Sstevel * the operation. 135803831d35Sstevel */ 135903831d35Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID; 136003831d35Sstevel error = iosram_rd(key, MBOXSC_MSGHDR_OFFSET, MBOXSC_MSGHDR_SIZE, 136103831d35Sstevel (caddr_t)&(mailboxp->mbox_header)); 136203831d35Sstevel 136303831d35Sstevel /* 136403831d35Sstevel * If somebody sends us a message using a Mailbox Protocol version 136503831d35Sstevel * greater than the highest one we understand, ignore the message, 136603831d35Sstevel * because we can't safely interpret anything beyond the version field. 136703831d35Sstevel */ 136803831d35Sstevel if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) { 136903831d35Sstevel error = -1; 137003831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, 137103831d35Sstevel "incoming message with unsupported version %d\n", 137203831d35Sstevel mailboxp->mbox_header.msg_version); 137303831d35Sstevel } 137403831d35Sstevel 137503831d35Sstevel /* 137603831d35Sstevel * If this message is a repeat of a previous message (which should 137703831d35Sstevel * only happen with reply messages), it is conceivable that a client 137803831d35Sstevel * already executing in mboxsc_getmsg for the previous message could 137903831d35Sstevel * end up receiving the new message before this callback gets a chance 138003831d35Sstevel * to execute. If that happens, the data_valid flag will already have 138103831d35Sstevel * been cleared. Call iosram_get_flag to see if that is the case, and 138203831d35Sstevel * do not process the message if it is. 138303831d35Sstevel */ 138403831d35Sstevel if (error == 0) { 138503831d35Sstevel error = iosram_get_flag(key, &data_valid, NULL); 138603831d35Sstevel if ((error == 0) && (data_valid != IOSRAM_DATA_VALID)) { 138703831d35Sstevel error = -1; 138803831d35Sstevel } 138903831d35Sstevel } 139003831d35Sstevel 139103831d35Sstevel /* 139203831d35Sstevel * If the iosram_rd call failed, return. 139303831d35Sstevel */ 139403831d35Sstevel if (error != 0) { 139503831d35Sstevel mutex_exit(&mboxsc_lock); 139603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 139703831d35Sstevel "mboxsc_iosram_callback ret (0x%08x)\n", error); 139803831d35Sstevel return; 139903831d35Sstevel } 140003831d35Sstevel 140103831d35Sstevel /* 140203831d35Sstevel * If the message read from IOSRAM was unsolicited, invoke 140303831d35Sstevel * its callback. Otherwise, wake all threads that are waiting 140403831d35Sstevel * in mboxsc_getmsg. 140503831d35Sstevel */ 140603831d35Sstevel mailboxp->mbox_state |= STATE_HDRVALID; 140703831d35Sstevel if (IS_UNSOLICITED_TYPE(mailboxp->mbox_header.msg_type) && 140803831d35Sstevel (mailboxp->mbox_callback != NULL)) { 140903831d35Sstevel mboxsc_reference_mailbox(mailboxp); 141003831d35Sstevel mutex_exit(&mboxsc_lock); 141103831d35Sstevel (*(mailboxp->mbox_callback))(); 141203831d35Sstevel mutex_enter(&mboxsc_lock); 141303831d35Sstevel mboxsc_dereference_mailbox(mailboxp); 141403831d35Sstevel } else { 141503831d35Sstevel cv_broadcast(&(mailboxp->mbox_wait)); 141603831d35Sstevel } 141703831d35Sstevel 141803831d35Sstevel mutex_exit(&mboxsc_lock); 141903831d35Sstevel 142003831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_iosram_callback ret\n"); 142103831d35Sstevel } 142203831d35Sstevel 142303831d35Sstevel /* 142403831d35Sstevel * mboxsc_hdrchange_callback 142503831d35Sstevel * 142603831d35Sstevel * This routine is registered with the IOSRAM driver to react to any changes SMS 142703831d35Sstevel * makes to the IOSRAM header. 142803831d35Sstevel */ 142903831d35Sstevel static void 143003831d35Sstevel mboxsc_hdrchange_callback(void) 143103831d35Sstevel { 143203831d35Sstevel int error; 143303831d35Sstevel uint32_t sms_version; 143403831d35Sstevel 143503831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, 143603831d35Sstevel "mboxsc_hdrchange_callback called\n"); 143703831d35Sstevel 143803831d35Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER, 143903831d35Sstevel (void *)&sms_version); 144003831d35Sstevel if (error == 0) { 144103831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, 144203831d35Sstevel "sms mailbox version = %d\n", sms_version); 144303831d35Sstevel mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION, 144403831d35Sstevel sms_version); 144503831d35Sstevel } 144603831d35Sstevel 144703831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_hdrchange_callback ret\n"); 144803831d35Sstevel } 144903831d35Sstevel 145003831d35Sstevel 145103831d35Sstevel /* 145203831d35Sstevel * mboxsc_add_mailbox 145303831d35Sstevel * 145403831d35Sstevel * If no other mailbox exists with the same key as this mailbox, attempt to 145503831d35Sstevel * retrieve its length from the IOSRAM driver and register the mboxsc callback 145603831d35Sstevel * for the associated IOSRAM chunk. If successful, initialize the 145703831d35Sstevel * non-client-supplied mailbox fields and insert it into the hash table. 145803831d35Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table. 145903831d35Sstevel */ 146003831d35Sstevel static int 146103831d35Sstevel mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp) 146203831d35Sstevel { 146303831d35Sstevel int error = 0; 146403831d35Sstevel uint32_t key = mailboxp->mbox_key; 146503831d35Sstevel 146603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_add_mailbox called\n"); 146707d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp); 146803831d35Sstevel 146903831d35Sstevel /* 147003831d35Sstevel * The global lock must be held by the caller. 147103831d35Sstevel */ 147203831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 147303831d35Sstevel 147403831d35Sstevel /* 147503831d35Sstevel * Don't create the mailbox if it already exists. 147603831d35Sstevel */ 147703831d35Sstevel if (mboxsc_hashfind_mailbox_by_key(key) != NULL) { 147803831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 147903831d35Sstevel "mboxsc_add_mailbox ret: 0x%08x\n", EEXIST); 148003831d35Sstevel return (EEXIST); 148103831d35Sstevel } 148203831d35Sstevel 148303831d35Sstevel /* 148403831d35Sstevel * Obtain the mailbox length and register the mboxsc callback with the 148503831d35Sstevel * IOSRAM driver. If either call to the IOSRAM driver fails, or the 148603831d35Sstevel * chunk is too small to be used as a mailbox, return an error to the 148703831d35Sstevel * caller. 148803831d35Sstevel */ 148903831d35Sstevel error = iosram_ctrl(key, IOSRAM_CMD_CHUNKLEN, &(mailboxp->mbox_length)); 149003831d35Sstevel 149103831d35Sstevel if ((error == 0) && (mailboxp->mbox_length < MBOXSC_PROTOCOL_SIZE)) { 149203831d35Sstevel error = EFAULT; 149303831d35Sstevel } 149403831d35Sstevel 149503831d35Sstevel if ((error == 0) && (mailboxp->mbox_direction == MBOXSC_MBOX_IN)) { 149603831d35Sstevel error = iosram_register(key, mboxsc_iosram_callback, 149703831d35Sstevel (void *)(uintptr_t)(key)); 149803831d35Sstevel if (error == EBUSY) { 149903831d35Sstevel error = EFAULT; 150003831d35Sstevel } 150103831d35Sstevel } 150203831d35Sstevel 150303831d35Sstevel if (error != 0) { 150403831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 150503831d35Sstevel "mboxsc_add_mailbox ret: 0x%08x\n", error); 150603831d35Sstevel return (error); 150703831d35Sstevel } 150803831d35Sstevel 150903831d35Sstevel /* 151003831d35Sstevel * Initialize remaining mailbox fields and insert mailbox into 151103831d35Sstevel * hash table. 151203831d35Sstevel */ 151303831d35Sstevel mailboxp->mbox_state = STATE_IDLE; 151403831d35Sstevel mailboxp->mbox_refcount = 0; 151503831d35Sstevel cv_init(&(mailboxp->mbox_wait), NULL, CV_DRIVER, NULL); 151603831d35Sstevel mboxsc_hashinsert_mailbox(mailboxp); 151703831d35Sstevel 151803831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_add_mailbox ret: 0x%08x\n", 151903831d35Sstevel 0); 152003831d35Sstevel return (0); 152103831d35Sstevel } 152203831d35Sstevel 152303831d35Sstevel /* 152403831d35Sstevel * mboxsc_close_mailbox 152503831d35Sstevel * 152603831d35Sstevel * Remove a mailbox from the hash table, unregister its IOSRAM callback, and 152703831d35Sstevel * deallocate its resources. 152803831d35Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table. 152903831d35Sstevel */ 153003831d35Sstevel static void 153103831d35Sstevel mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp) 153203831d35Sstevel { 153303831d35Sstevel int error = 0; 153403831d35Sstevel uint32_t key = mailboxp->mbox_key; 153503831d35Sstevel 153603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_close_mailbox called\n"); 153707d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp); 153803831d35Sstevel 153903831d35Sstevel /* 154003831d35Sstevel * The global lock must be held by the caller. 154103831d35Sstevel */ 154203831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 154303831d35Sstevel 154403831d35Sstevel /* 154503831d35Sstevel * Unregister the mboxsc callback for this particular mailbox. 154603831d35Sstevel */ 154703831d35Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) { 154803831d35Sstevel error = iosram_unregister(key); 154903831d35Sstevel if (error == EINVAL) { 155003831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "invalid key (0x%08x)" 155103831d35Sstevel " reported in mboxsc_close_mailbox.\n", key); 155203831d35Sstevel error = 0; 155303831d35Sstevel } 155403831d35Sstevel } 155503831d35Sstevel 155603831d35Sstevel /* 155703831d35Sstevel * Remove the mailbox from the hash table and deallocate its resources. 155803831d35Sstevel */ 155907d06da5SSurya Prakki (void) mboxsc_hashremove_mailbox_by_key(key); 156003831d35Sstevel cv_destroy(&(mailboxp->mbox_wait)); 156107d06da5SSurya Prakki DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n", 156207d06da5SSurya Prakki (void *)mailboxp, sizeof (mboxsc_mbox_t)); 156303831d35Sstevel kmem_free(mailboxp, sizeof (mboxsc_mbox_t)); 156403831d35Sstevel 156503831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_close_mailbox ret\n"); 156603831d35Sstevel } 156703831d35Sstevel 156803831d35Sstevel /* 156903831d35Sstevel * mboxsc_hashinsert_mailbox 157003831d35Sstevel * 157103831d35Sstevel * Insert a fully initialized mailbox into the hash table. No duplicate 157203831d35Sstevel * checking is performed at this point, so the caller is responsible for 157303831d35Sstevel * duplicate prevention if it is desired. 157403831d35Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table. 157503831d35Sstevel */ 157603831d35Sstevel static void 157703831d35Sstevel mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp) 157803831d35Sstevel { 157903831d35Sstevel uint32_t hash; 158003831d35Sstevel 158103831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, 158203831d35Sstevel "mboxsc_hashinsert_mailbox called\n"); 158307d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp); 158403831d35Sstevel 158503831d35Sstevel /* 158603831d35Sstevel * The global lock must be held by the caller. 158703831d35Sstevel */ 158803831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 158903831d35Sstevel 159003831d35Sstevel hash = HASH_KEY(mailboxp->mbox_key); 159103831d35Sstevel mailboxp->mbox_hash_next = mboxsc_hash_table[hash]; 159203831d35Sstevel mboxsc_hash_table[hash] = mailboxp; 159303831d35Sstevel 159403831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, 159503831d35Sstevel "mboxsc_hashinsert_mailbox ret\n"); 159603831d35Sstevel } 159703831d35Sstevel 159803831d35Sstevel /* 159903831d35Sstevel * mboxsc_hashfind_mailbox_by_key 160003831d35Sstevel * 160103831d35Sstevel * Locate a mailbox with the given key in the hash table. Return a pointer 160203831d35Sstevel * to the mailbox if it exists, or NULL if no matching mailbox is found. 160303831d35Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table. 160403831d35Sstevel */ 160503831d35Sstevel static mboxsc_mbox_t * 160603831d35Sstevel mboxsc_hashfind_mailbox_by_key(uint32_t key) 160703831d35Sstevel { 160803831d35Sstevel uint32_t hash; 160903831d35Sstevel mboxsc_mbox_t *mailboxp; 161003831d35Sstevel 161103831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, 161203831d35Sstevel "mboxsc_hashfind_mailbox_by_key called\n"); 161303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 161403831d35Sstevel 161503831d35Sstevel /* 161603831d35Sstevel * The global lock must be held by the caller. 161703831d35Sstevel */ 161803831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 161903831d35Sstevel 162003831d35Sstevel hash = HASH_KEY(key); 162103831d35Sstevel mailboxp = mboxsc_hash_table[hash]; 162203831d35Sstevel while (mailboxp != NULL) { 162303831d35Sstevel if (mailboxp->mbox_key == key) { 162403831d35Sstevel break; 162503831d35Sstevel } 162603831d35Sstevel mailboxp = mailboxp->mbox_hash_next; 162703831d35Sstevel } 162803831d35Sstevel 162903831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 163007d06da5SSurya Prakki "mboxsc_hashfind_mailbox_by_key ret: %p\n", (void *)mailboxp); 163103831d35Sstevel return (mailboxp); 163203831d35Sstevel } 163303831d35Sstevel 163403831d35Sstevel /* 163503831d35Sstevel * mboxsc_hashremove_mailbox_by_key 163603831d35Sstevel * 163703831d35Sstevel * Locate a mailbox with the given key in the hash table. If it exists, 163803831d35Sstevel * remove it from the hash table and return a pointer to it. Otherwise, 163903831d35Sstevel * return NULL. 164003831d35Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table. 164103831d35Sstevel */ 164203831d35Sstevel static mboxsc_mbox_t * 164303831d35Sstevel mboxsc_hashremove_mailbox_by_key(uint32_t key) 164403831d35Sstevel { 164503831d35Sstevel uint32_t hash; 164603831d35Sstevel mboxsc_mbox_t *mailboxp; 164703831d35Sstevel mboxsc_mbox_t *last; 164803831d35Sstevel 164903831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, 165003831d35Sstevel "mboxsc_hashremove_mailbox_by_key called\n"); 165103831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 165203831d35Sstevel 165303831d35Sstevel /* 165403831d35Sstevel * The global lock must be held by the caller. 165503831d35Sstevel */ 165603831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 165703831d35Sstevel 165803831d35Sstevel hash = HASH_KEY(key); 165903831d35Sstevel mailboxp = mboxsc_hash_table[hash]; 166003831d35Sstevel last = NULL; 166103831d35Sstevel while (mailboxp != NULL) { 166203831d35Sstevel if (mailboxp->mbox_key == key) { 166303831d35Sstevel break; 166403831d35Sstevel } 166503831d35Sstevel last = mailboxp; 166603831d35Sstevel mailboxp = mailboxp->mbox_hash_next; 166703831d35Sstevel } 166803831d35Sstevel 166903831d35Sstevel /* 167003831d35Sstevel * If a mailbox was found, remove it from the hash table. 167103831d35Sstevel */ 167203831d35Sstevel if (mailboxp != NULL) { 167303831d35Sstevel if (last == NULL) { 167403831d35Sstevel mboxsc_hash_table[hash] = mailboxp->mbox_hash_next; 167503831d35Sstevel } else { 167603831d35Sstevel last->mbox_hash_next = mailboxp->mbox_hash_next; 167703831d35Sstevel } 167803831d35Sstevel 167903831d35Sstevel mailboxp->mbox_hash_next = NULL; 168003831d35Sstevel } 168103831d35Sstevel 168203831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 168307d06da5SSurya Prakki "mboxsc_hashremove_mailbox_by_key ret: %p\n", (void *)mailboxp); 168403831d35Sstevel return (mailboxp); 168503831d35Sstevel } 168603831d35Sstevel 168703831d35Sstevel /* 168803831d35Sstevel * mboxsc_checksum 168903831d35Sstevel * 169003831d35Sstevel * Given a pointer to a data buffer and its length, calculate the checksum of 169103831d35Sstevel * the data contained therein. 169203831d35Sstevel */ 169303831d35Sstevel static mboxsc_chksum_t 169403831d35Sstevel mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf, uint32_t length) 169503831d35Sstevel { 169603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_checksum called\n"); 169703831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "seed = 0x%x\n", seed); 169807d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "buf = %p\n", (void *)buf); 169903831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length); 170003831d35Sstevel 170103831d35Sstevel while (length-- > 0) { 170203831d35Sstevel seed += *(buf++); 170303831d35Sstevel } 170403831d35Sstevel 170503831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_checksum ret: 0x%08x\n", 170603831d35Sstevel seed); 170703831d35Sstevel return (seed); 170803831d35Sstevel } 170903831d35Sstevel 171003831d35Sstevel /* 171103831d35Sstevel * mboxsc_lock_flags 171203831d35Sstevel * 171303831d35Sstevel * Acquire the hardware lock used for data_valid flag synchronization. If the 171403831d35Sstevel * lock is currently held by SMS and acquisition is mandatory, just keep on 171503831d35Sstevel * trying until it is acquired. If acquisition is not mandatory, keep trying 171603831d35Sstevel * until the given deadline has been reached. To avoid loading the system 171703831d35Sstevel * unreasonably on EBUSY or EAGAIN, sleep for an appropriate amount of time 171803831d35Sstevel * before retrying. If a hardware error is encountered return it to the caller. 171903831d35Sstevel * 172003831d35Sstevel * If the lock is held, but not by SMS, clear it and acquire it. Nobody 172103831d35Sstevel * else should be grabbing that lock. 172203831d35Sstevel */ 172303831d35Sstevel static int 172403831d35Sstevel mboxsc_lock_flags(uint8_t mandatory, clock_t deadline) 172503831d35Sstevel { 172603831d35Sstevel int error; 172703831d35Sstevel int warned = 0; 172803831d35Sstevel uint32_t sema; 172903831d35Sstevel clock_t pause; 173003831d35Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL; 173103831d35Sstevel 173203831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_lock_flags called\n"); 173303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory); 173407d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 173503831d35Sstevel 173603831d35Sstevel /* 173703831d35Sstevel * Keep trying to acquire the lock until successful or (if acquisition 173803831d35Sstevel * is not mandatory) time runs out. If EBUSY (lock is already held) or 173903831d35Sstevel * EAGAIN (tunnel switch in progress) is encountered, sleep for an 174003831d35Sstevel * appropriate amount of time before retrying. Any other error is 174103831d35Sstevel * unrecoverable. 174203831d35Sstevel */ 174303831d35Sstevel do { 174403831d35Sstevel pause = 0; 174503831d35Sstevel 174603831d35Sstevel /* 174703831d35Sstevel * Since multiple threads could conceivably want the flag lock 174803831d35Sstevel * at the same time, we place the lock under a mutex and keep a 174903831d35Sstevel * counter indicating how many threads have the flags locked at 175003831d35Sstevel * the moment. 175103831d35Sstevel */ 175203831d35Sstevel mutex_enter(&mboxsc_lock); 175303831d35Sstevel if ((mboxsc_flaglock_count > 0) || 175403831d35Sstevel ((error = iosram_sema_acquire(&sema)) == 0)) { 175503831d35Sstevel mboxsc_flaglock_count++; 175603831d35Sstevel mutex_exit(&mboxsc_lock); 175703831d35Sstevel 175803831d35Sstevel if (warned) { 175903831d35Sstevel cmn_err(CE_WARN, "Flags locked"); 176003831d35Sstevel } 176103831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, 176203831d35Sstevel "mboxsc_lock_flags ret: 0\n"); 176303831d35Sstevel return (0); 176403831d35Sstevel } 176503831d35Sstevel 176603831d35Sstevel /* 176703831d35Sstevel * If iosram_sema_acquire returned EBUSY (lock already held), 176803831d35Sstevel * make sure the lock is held by SMS, since nobody else should 176903831d35Sstevel * ever be holding it. If EBUSY or EAGAIN (tunnel switch in 177003831d35Sstevel * progress) was returned, determine the appropriate amount of 177103831d35Sstevel * time to sleep before trying again. 177203831d35Sstevel */ 177303831d35Sstevel if (error == EBUSY) { 177403831d35Sstevel if (IOSRAM_SEMA_GET_IDX(sema) != IOSRAM_SEMA_SMS_IDX) { 177507d06da5SSurya Prakki (void) iosram_sema_release(); 177603831d35Sstevel cmn_err(CE_WARN, 177703831d35Sstevel "Incorrect flag lock value read (0x%08x)", 177803831d35Sstevel sema); 177903831d35Sstevel } else { 178003831d35Sstevel pause = (mandatory ? HWLOCK_POLL : 178103831d35Sstevel MIN(HWLOCK_POLL, deadline - 178203831d35Sstevel ddi_get_lbolt())); 178303831d35Sstevel } 178403831d35Sstevel } else if (error == EAGAIN) { 178503831d35Sstevel pause = (mandatory ? EAGAIN_POLL : MIN(EAGAIN_POLL, 178603831d35Sstevel deadline - ddi_get_lbolt())); 178703831d35Sstevel } 178803831d35Sstevel 178903831d35Sstevel /* 179003831d35Sstevel * We had to hold the lock until now to protect the potential 179103831d35Sstevel * iosram_sema_release call above. 179203831d35Sstevel */ 179303831d35Sstevel mutex_exit(&mboxsc_lock); 179403831d35Sstevel 179503831d35Sstevel /* 179603831d35Sstevel * If EAGAIN or EBUSY was encountered, we're looping. 179703831d35Sstevel */ 179803831d35Sstevel if ((error == EAGAIN) || (error == EBUSY)) { 179903831d35Sstevel /* 180003831d35Sstevel * If we've been looping here for a while, something is 180103831d35Sstevel * probably wrong, so we should generated a warning. 180203831d35Sstevel */ 180303831d35Sstevel if (warning_time - ddi_get_lbolt() <= 0) { 180403831d35Sstevel if (!warned) { 180503831d35Sstevel warned = 1; 180603831d35Sstevel cmn_err(CE_WARN, 180703831d35Sstevel "Unable to lock flags (0x%08x)", 180803831d35Sstevel error); 180903831d35Sstevel } else { 181003831d35Sstevel cmn_err(CE_WARN, 181103831d35Sstevel "Still unable to lock flags"); 181203831d35Sstevel } 181303831d35Sstevel warning_time = ddi_get_lbolt() + 181403831d35Sstevel LOOP_WARN_INTERVAL; 181503831d35Sstevel } 181603831d35Sstevel 181703831d35Sstevel /* 181803831d35Sstevel * Sleep a while before trying again. 181903831d35Sstevel */ 182003831d35Sstevel delay(pause); 182103831d35Sstevel } 182203831d35Sstevel } while (((error == EAGAIN) || (error == EBUSY)) && 182303831d35Sstevel (mandatory || (deadline - ddi_get_lbolt() >= 0))); 182403831d35Sstevel 182503831d35Sstevel /* 182603831d35Sstevel * If something really bad has happened, generate a warning. 182703831d35Sstevel */ 182803831d35Sstevel if ((error != EAGAIN) && (error != EBUSY)) { 182903831d35Sstevel cmn_err(CE_WARN, "Flag locking failed! (%d)", error); 183003831d35Sstevel } 183103831d35Sstevel 183203831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_lock_flags ret: 0x%08x\n", 183303831d35Sstevel error); 183403831d35Sstevel return (error); 183503831d35Sstevel } 183603831d35Sstevel 183703831d35Sstevel /* 183803831d35Sstevel * mboxsc_unlock_flags 183903831d35Sstevel * 184003831d35Sstevel * Release the hardware lock used for data_valid flag synchronization. 184103831d35Sstevel * If a hardware error is encountered, return it to the caller. If the 184203831d35Sstevel * mandatory flag is set, loop and retry if EAGAIN is encountered. 184303831d35Sstevel */ 184403831d35Sstevel static int 184503831d35Sstevel mboxsc_unlock_flags(uint8_t mandatory) 184603831d35Sstevel { 184703831d35Sstevel int error; 184803831d35Sstevel int warned = 0; 184903831d35Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL; 185003831d35Sstevel 185103831d35Sstevel ASSERT(mboxsc_flaglock_count != 0); 185203831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_unlock_flags called\n"); 185303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory); 185403831d35Sstevel 185503831d35Sstevel do { 185603831d35Sstevel /* 185703831d35Sstevel * Since multiple threads could conceivably want the flag lock 185803831d35Sstevel * at the same time, we place the lock under a mutex and keep a 185903831d35Sstevel * counter indicating how many threads have the flags locked at 186003831d35Sstevel * the moment. 186103831d35Sstevel */ 186203831d35Sstevel mutex_enter(&mboxsc_lock); 186303831d35Sstevel if ((mboxsc_flaglock_count > 1) || 186403831d35Sstevel ((error = iosram_sema_release()) == 0)) { 186503831d35Sstevel mboxsc_flaglock_count--; 186603831d35Sstevel mutex_exit(&mboxsc_lock); 186703831d35Sstevel 186803831d35Sstevel if (warned) { 186903831d35Sstevel cmn_err(CE_WARN, "Flags unlocked"); 187003831d35Sstevel } 187103831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, 187203831d35Sstevel "mboxsc_unlock_flags ret: 0\n"); 187303831d35Sstevel return (0); 187403831d35Sstevel } 187503831d35Sstevel mutex_exit(&mboxsc_lock); 187603831d35Sstevel 187703831d35Sstevel /* 187803831d35Sstevel * If iosram_sema_release returned EAGAIN (tunnel switch in 187903831d35Sstevel * progress) and unlocking the flags is mandatory, sleep before 188003831d35Sstevel * trying again. If we've been trying for a while, display a 188103831d35Sstevel * warning message too. 188203831d35Sstevel */ 188303831d35Sstevel if ((error == EAGAIN) && mandatory) { 188403831d35Sstevel if (warning_time - ddi_get_lbolt() <= 0) { 188503831d35Sstevel if (!warned) { 188603831d35Sstevel warned = 1; 188703831d35Sstevel cmn_err(CE_WARN, "Unable to unlock " 188803831d35Sstevel "flags (iosram EAGAIN)"); 188903831d35Sstevel } else { 189003831d35Sstevel cmn_err(CE_WARN, 189103831d35Sstevel "Still unable to unlock flags"); 189203831d35Sstevel } 189303831d35Sstevel warning_time = ddi_get_lbolt() + 189403831d35Sstevel LOOP_WARN_INTERVAL; 189503831d35Sstevel } 189603831d35Sstevel 189703831d35Sstevel delay(EAGAIN_POLL); 189803831d35Sstevel } 189903831d35Sstevel } while ((error == EAGAIN) && mandatory); 190003831d35Sstevel 190103831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_unlock_flags ret: 0x%08x\n", 190203831d35Sstevel error); 190303831d35Sstevel return (error); 190403831d35Sstevel } 190503831d35Sstevel 190603831d35Sstevel /* 190703831d35Sstevel * mboxsc_timed_read 190803831d35Sstevel * 190903831d35Sstevel * This function is just a wrapper around iosram_rd that will keep sleeping 191003831d35Sstevel * and retrying, up to a given deadline, if iosram_rd returns EAGAIN 191103831d35Sstevel * (presumably due to a tunnel switch). 191203831d35Sstevel */ 191303831d35Sstevel static int 191403831d35Sstevel mboxsc_timed_read(clock_t deadline, uint32_t key, uint32_t off, uint32_t len, 191503831d35Sstevel caddr_t dptr) 191603831d35Sstevel { 191703831d35Sstevel int error; 191803831d35Sstevel 191903831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_read called\n"); 192007d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 192103831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 192203831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off); 192303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len); 192407d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr); 192503831d35Sstevel 192603831d35Sstevel do { 192703831d35Sstevel error = iosram_rd(key, off, len, dptr); 192803831d35Sstevel if (error == EAGAIN) { 192903831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 193003831d35Sstevel } 193103831d35Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)); 193203831d35Sstevel 193303831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 193403831d35Sstevel "mboxsc_timed_read ret: 0x%08x\n", error); 193503831d35Sstevel return (error); 193603831d35Sstevel } 193703831d35Sstevel 193803831d35Sstevel /* 193903831d35Sstevel * mboxsc_timed_write 194003831d35Sstevel * 194103831d35Sstevel * This function is just a wrapper around iosram_wr that will keep sleeping 194203831d35Sstevel * and retrying, up to a given deadline, if iosram_wr returns EAGAIN 194303831d35Sstevel * (presumably due to a tunnel switch). 194403831d35Sstevel */ 194503831d35Sstevel static int 194603831d35Sstevel mboxsc_timed_write(clock_t deadline, uint32_t key, uint32_t off, uint32_t len, 194703831d35Sstevel caddr_t dptr) 194803831d35Sstevel { 194903831d35Sstevel int error; 195003831d35Sstevel 195103831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_write called\n"); 195207d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 195303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 195403831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off); 195503831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len); 195607d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr); 195703831d35Sstevel 195803831d35Sstevel do { 195903831d35Sstevel error = iosram_wr(key, off, len, dptr); 196003831d35Sstevel if (error == EAGAIN) { 196103831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 196203831d35Sstevel } 196303831d35Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)); 196403831d35Sstevel 196503831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 196603831d35Sstevel "mboxsc_timed_write ret: 0x%08x\n", error); 196703831d35Sstevel return (error); 196803831d35Sstevel } 196903831d35Sstevel 197003831d35Sstevel /* 197103831d35Sstevel * mboxsc_timed_get_flag 197203831d35Sstevel * 197303831d35Sstevel * This function is just a wrapper around iosram_get_flag that will keep 197403831d35Sstevel * sleeping and retrying, up to a given deadline, if iosram_get_flag returns 197503831d35Sstevel * EAGAIN (presumably due to a tunnel switch). 197603831d35Sstevel */ 197703831d35Sstevel static int 197803831d35Sstevel mboxsc_timed_get_flag(clock_t deadline, uint32_t key, uint8_t *data_validp, 197903831d35Sstevel uint8_t *int_pendingp) 198003831d35Sstevel { 198103831d35Sstevel int error; 198203831d35Sstevel 198303831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_get_flag called\n"); 198407d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 198503831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 198607d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_validp = %p\n", 198707d06da5SSurya Prakki (void *)data_validp); 198807d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pendingp = %p\n", 198907d06da5SSurya Prakki (void *)int_pendingp); 199003831d35Sstevel 199103831d35Sstevel do { 199203831d35Sstevel error = iosram_get_flag(key, data_validp, int_pendingp); 199303831d35Sstevel if (error == EAGAIN) { 199403831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 199503831d35Sstevel } 199603831d35Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)); 199703831d35Sstevel 199803831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 199903831d35Sstevel "mboxsc_timed_get_flag ret: 0x%08x\n", error); 200003831d35Sstevel return (error); 200103831d35Sstevel } 200203831d35Sstevel 200303831d35Sstevel /* 200403831d35Sstevel * mboxsc_timed_set_flag 200503831d35Sstevel * 200603831d35Sstevel * This function is just a wrapper around iosram_set_flag that will keep 200703831d35Sstevel * sleeping and retrying, up to a given deadline, if iosram_set_flag returns 200803831d35Sstevel * EAGAIN (presumably due to a tunnel switch). 200903831d35Sstevel */ 201003831d35Sstevel static int 201103831d35Sstevel mboxsc_timed_set_flag(clock_t deadline, uint32_t key, uint8_t data_valid, 201203831d35Sstevel uint8_t int_pending) 201303831d35Sstevel { 201403831d35Sstevel int error; 201503831d35Sstevel 201603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_set_flag called\n"); 201707d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 201803831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 201903831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_valid = %d\n", data_valid); 202003831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pending = %d\n", int_pending); 202103831d35Sstevel 202203831d35Sstevel do { 202303831d35Sstevel error = iosram_set_flag(key, data_valid, int_pending); 202403831d35Sstevel if (error == EAGAIN) { 202503831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 202603831d35Sstevel } 202703831d35Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)); 202803831d35Sstevel 202903831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 203003831d35Sstevel "mboxsc_timed_set_flag ret: 0x%08x\n", error); 203103831d35Sstevel return (error); 203203831d35Sstevel } 203303831d35Sstevel 203403831d35Sstevel /* 203503831d35Sstevel * mboxsc_timed_send_intr 203603831d35Sstevel * 203703831d35Sstevel * This function is just a wrapper around iosram_send_intr that will keep 203803831d35Sstevel * sleeping and retrying, up to a given deadline, if iosram_send_intr returns 203903831d35Sstevel * EAGAIN (presumably due to a tunnel switch). 204003831d35Sstevel */ 204103831d35Sstevel static int 204203831d35Sstevel mboxsc_timed_send_intr(clock_t deadline) 204303831d35Sstevel { 204403831d35Sstevel int error; 204503831d35Sstevel 204603831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_send_intr called\n"); 204707d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline); 204803831d35Sstevel 204903831d35Sstevel do { 205003831d35Sstevel error = iosram_send_intr(); 205103831d35Sstevel if (error == DDI_FAILURE) { 205203831d35Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt())); 205303831d35Sstevel } 205403831d35Sstevel } while ((error == DDI_FAILURE) && (deadline - ddi_get_lbolt() >= 0)); 205503831d35Sstevel 205603831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 205703831d35Sstevel "mboxsc_timed_send_intr ret: 0x%08x\n", error); 205803831d35Sstevel return (error); 205903831d35Sstevel } 206003831d35Sstevel 206103831d35Sstevel /* 206203831d35Sstevel * mboxsc_expire_message 206303831d35Sstevel * 206403831d35Sstevel * This function is called by mboxsc_putmsg to handle expiration of messages 206503831d35Sstevel * that weren't picked up before they timed out. It will not return until the 206603831d35Sstevel * message has been picked up (which isn't expected), the message has been 206703831d35Sstevel * successfully expired, or a serious error has been encountered. If the 206803831d35Sstevel * message is finally picked up, it will set the value pointed to by "resultp" 206903831d35Sstevel * to 0. Unlike other sections of code, this function will never time out on 207003831d35Sstevel * EAGAIN from the iosram driver, since it is important that both sides of the 207103831d35Sstevel * IOSRAM agree on whether or not a message was delivered successfully. 207203831d35Sstevel */ 207303831d35Sstevel static int 207403831d35Sstevel mboxsc_expire_message(uint32_t key, int *resultp) 207503831d35Sstevel { 207603831d35Sstevel int error = 0; 207703831d35Sstevel int lock_held = 0; 207803831d35Sstevel int warned = 0; 207903831d35Sstevel uint8_t data_valid; 208003831d35Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL; 208103831d35Sstevel 208203831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_expire_message called\n"); 208303831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key); 208407d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "resultp = %p\n", (void *)resultp); 208503831d35Sstevel 208603831d35Sstevel do { 208703831d35Sstevel error = 0; 208803831d35Sstevel 208903831d35Sstevel /* 209003831d35Sstevel * Lock the flags if they aren't locked already. 209103831d35Sstevel */ 209203831d35Sstevel if (!lock_held) { 209303831d35Sstevel error = mboxsc_lock_flags(TRUE, 0); 209403831d35Sstevel if (error == 0) { 209503831d35Sstevel lock_held = 1; 209603831d35Sstevel } 209703831d35Sstevel } 209803831d35Sstevel 209903831d35Sstevel /* 210003831d35Sstevel * If the flags were locked successfully, reread the data-valid 210103831d35Sstevel * flag. 210203831d35Sstevel */ 210303831d35Sstevel if (error == 0) { 210403831d35Sstevel error = iosram_get_flag(key, &data_valid, NULL); 210503831d35Sstevel } 210603831d35Sstevel 210703831d35Sstevel /* 210803831d35Sstevel * If the data-valid flag was read successfully, see if it has 210903831d35Sstevel * been cleared or not, as the other side may have finally read 211003831d35Sstevel * the message. 211103831d35Sstevel */ 211203831d35Sstevel if (error == 0) { 211303831d35Sstevel if (data_valid == IOSRAM_DATA_INVALID) { 211403831d35Sstevel /* 211503831d35Sstevel * Surprise! The SC finally picked up the 211603831d35Sstevel * message, so delivery succeeded after all. 211703831d35Sstevel */ 211803831d35Sstevel if (*resultp == ETIMEDOUT) { 211903831d35Sstevel *resultp = 0; 212003831d35Sstevel } 212103831d35Sstevel } else { 212203831d35Sstevel /* 212303831d35Sstevel * The message still hasn't been read, so try to 212403831d35Sstevel * clear the data-valid flag. 212503831d35Sstevel */ 212603831d35Sstevel error = iosram_set_flag(key, 212703831d35Sstevel IOSRAM_DATA_INVALID, IOSRAM_INT_NONE); 212803831d35Sstevel } 212903831d35Sstevel } 213003831d35Sstevel 213103831d35Sstevel /* 213203831d35Sstevel * If the flags were locked, unlock them, no matter what else 213303831d35Sstevel * has or has not succeeded. Don't overwrite the existing value 213403831d35Sstevel * of "error" unless no errors other than EAGAIN have been 213503831d35Sstevel * encountered previously. If we hit EAGAIN at some point, 213603831d35Sstevel * unlocking the flags here is optional. In all other cases, it 213703831d35Sstevel * is mandatory. 213803831d35Sstevel */ 213903831d35Sstevel if (lock_held) { 214003831d35Sstevel int unlock_err; 214103831d35Sstevel 214203831d35Sstevel if (error == EAGAIN) { 214303831d35Sstevel unlock_err = mboxsc_unlock_flags(FALSE); 214403831d35Sstevel } else { 214503831d35Sstevel unlock_err = mboxsc_unlock_flags(TRUE); 214603831d35Sstevel } 214703831d35Sstevel 214803831d35Sstevel if (unlock_err == 0) { 214903831d35Sstevel lock_held = 0; 215003831d35Sstevel } else if ((error == 0) || (error == EAGAIN)) { 215103831d35Sstevel error = unlock_err; 215203831d35Sstevel } 215303831d35Sstevel } 215403831d35Sstevel 215503831d35Sstevel /* 215603831d35Sstevel * Did we hit a tunnel switch? (iosram driver returns EAGAIN) 215703831d35Sstevel * If so, sleep for a while before trying the whole process 215803831d35Sstevel * again. 215903831d35Sstevel */ 216003831d35Sstevel if (error == EAGAIN) { 216103831d35Sstevel /* 216203831d35Sstevel * If we've been stuck in this loop for a while, 216303831d35Sstevel * something is probably wrong, and we should display a 216403831d35Sstevel * warning. 216503831d35Sstevel */ 216603831d35Sstevel if (warning_time - ddi_get_lbolt() <= 0) { 216703831d35Sstevel if (!warned) { 216803831d35Sstevel warned = 1; 216903831d35Sstevel cmn_err(CE_WARN, "Unable to clear flag " 217003831d35Sstevel "(iosram EAGAIN)"); 217103831d35Sstevel } else { 217203831d35Sstevel cmn_err(CE_WARN, 217303831d35Sstevel "Still unable to clear flag"); 217403831d35Sstevel } 217503831d35Sstevel warning_time = ddi_get_lbolt() + 217603831d35Sstevel LOOP_WARN_INTERVAL; 217703831d35Sstevel } 217803831d35Sstevel 217903831d35Sstevel delay(EAGAIN_POLL); 218003831d35Sstevel } 218103831d35Sstevel } while (error == EAGAIN); 218203831d35Sstevel 218303831d35Sstevel /* 218403831d35Sstevel * If the data-valid flag was not successfully cleared due to some sort 218503831d35Sstevel * of problem, report it. Otherwise, if we looped for a while on EAGAIN 218603831d35Sstevel * and generated a warning about it, indicate that everything is okay 218703831d35Sstevel * now. 218803831d35Sstevel */ 218903831d35Sstevel if (error != 0) { 219003831d35Sstevel cmn_err(CE_WARN, "Message expiration failure! (%d)", error); 219103831d35Sstevel } else if (warned) { 219203831d35Sstevel cmn_err(CE_WARN, "Flag cleared"); 219303831d35Sstevel } 219403831d35Sstevel 219503831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 219603831d35Sstevel "mboxsc_expire_message ret: 0x%08x\n", error); 219703831d35Sstevel return (error); 219803831d35Sstevel } 219903831d35Sstevel 220003831d35Sstevel 220103831d35Sstevel /* 220203831d35Sstevel * mboxsc_generate_transid 220303831d35Sstevel * 220403831d35Sstevel * This function generates unique transaction IDs using an incrementing counter. 220503831d35Sstevel * The value generated is guaranteed not to be the same as the prev_transid 220603831d35Sstevel * value passed in by the caller. 220703831d35Sstevel */ 220803831d35Sstevel static uint64_t 220903831d35Sstevel mboxsc_generate_transid(uint64_t prev_transid) 221003831d35Sstevel { 221103831d35Sstevel uint64_t new_transid; 221203831d35Sstevel static uint64_t transid_counter = 0; 221303831d35Sstevel 221403831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_generate_transid called"); 221507d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "prev_transid = 0x%016lx\n", 221603831d35Sstevel prev_transid); 221703831d35Sstevel 221803831d35Sstevel do { 221903831d35Sstevel new_transid = TRANSID_GEN_MASK | transid_counter++; 222003831d35Sstevel if (transid_counter & TRANSID_GEN_MASK) { 222103831d35Sstevel transid_counter = 0; 222203831d35Sstevel } 222303831d35Sstevel } while (new_transid == prev_transid); 222403831d35Sstevel 222503831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 222607d06da5SSurya Prakki "mboxsc_generate_transid ret: 0x%016lx", new_transid); 222703831d35Sstevel return (new_transid); 222803831d35Sstevel } 222903831d35Sstevel 223003831d35Sstevel 223103831d35Sstevel /* 223203831d35Sstevel * mboxsc_reference_mailbox 223303831d35Sstevel * 223403831d35Sstevel * Increment the mailbox's reference count to prevent it from being closed. 223503831d35Sstevel * This really doesn't deserve to be a function, but since a dereference 223603831d35Sstevel * function is needed, having a corresponding reference function makes the code 223703831d35Sstevel * clearer. 223803831d35Sstevel */ 223903831d35Sstevel static void 224003831d35Sstevel mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp) 224103831d35Sstevel { 224203831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_reference_mailbox called"); 224307d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n", 224407d06da5SSurya Prakki (void *)mailboxp); 224503831d35Sstevel 224603831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 224703831d35Sstevel 224803831d35Sstevel mailboxp->mbox_refcount++; 224903831d35Sstevel 225003831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_reference_mailbox ret"); 225103831d35Sstevel } 225203831d35Sstevel 225303831d35Sstevel 225403831d35Sstevel /* 225503831d35Sstevel * mboxsc_dereference_mailbox 225603831d35Sstevel * 225703831d35Sstevel * Decrement the mailbox's reference count, and if the count has gone to zero, 225803831d35Sstevel * signal any threads waiting for mailboxes to be completely dereferenced. 225903831d35Sstevel */ 226003831d35Sstevel static void 226103831d35Sstevel mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp) 226203831d35Sstevel { 226303831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, 226403831d35Sstevel "mboxsc_dereference_mailbox called"); 226507d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n", 226607d06da5SSurya Prakki (void *)mailboxp); 226703831d35Sstevel 226803831d35Sstevel ASSERT(mutex_owned(&mboxsc_lock)); 226903831d35Sstevel 227003831d35Sstevel mailboxp->mbox_refcount--; 227103831d35Sstevel if (mailboxp->mbox_refcount == 0) { 227203831d35Sstevel cv_broadcast(&mboxsc_dereference_cv); 227303831d35Sstevel } 227403831d35Sstevel 227503831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_dereference_mailbox ret"); 227603831d35Sstevel } 227703831d35Sstevel 227803831d35Sstevel 227903831d35Sstevel #ifndef DEBUG 228003831d35Sstevel /* ARGSUSED */ 228103831d35Sstevel int 228203831d35Sstevel mboxsc_debug(int cmd, void *arg) 228303831d35Sstevel { 228403831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called"); 228503831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret"); 228603831d35Sstevel return (ENOTSUP); 228703831d35Sstevel } 228803831d35Sstevel #else /* DEBUG */ 228903831d35Sstevel 229003831d35Sstevel static void print_hash_table(void); 229103831d35Sstevel static int print_mailbox_by_key(uint32_t key); 229203831d35Sstevel static void print_mailbox(mboxsc_mbox_t *mailboxp); 229303831d35Sstevel 229403831d35Sstevel int 229503831d35Sstevel mboxsc_debug(int cmd, void *arg) 229603831d35Sstevel { 229703831d35Sstevel int error = 0; 229803831d35Sstevel 229903831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called\n"); 230003831d35Sstevel 230103831d35Sstevel switch (cmd) { 230203831d35Sstevel case MBOXSC_PRNMBOX: 230303831d35Sstevel error = print_mailbox_by_key((uint32_t)(uintptr_t)arg); 230403831d35Sstevel break; 230503831d35Sstevel 230603831d35Sstevel case MBOXSC_PRNHASHTBL: 230703831d35Sstevel print_hash_table(); 230803831d35Sstevel break; 230903831d35Sstevel 231003831d35Sstevel case MBOXSC_SETDBGMASK: 231103831d35Sstevel mboxsc_debug_mask = (uint32_t)(uintptr_t)arg; 231203831d35Sstevel break; 231303831d35Sstevel 231403831d35Sstevel default: 231503831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, 231603831d35Sstevel "Error: unknown mboxsc debug cmd (%d)\n", cmd); 231703831d35Sstevel error = ENOTTY; 231803831d35Sstevel break; 231903831d35Sstevel } 232003831d35Sstevel 232103831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret: 0x%08x\n", error); 232203831d35Sstevel 232303831d35Sstevel return (error); 232403831d35Sstevel } 232503831d35Sstevel 232603831d35Sstevel /*PRINTFLIKE5*/ 232703831d35Sstevel static void 232803831d35Sstevel mboxsc_dprintf( 232903831d35Sstevel const char *file, 233003831d35Sstevel int line, 233103831d35Sstevel uint32_t class, 233203831d35Sstevel uint32_t action, 233303831d35Sstevel const char *fmt, 233403831d35Sstevel ...) 233503831d35Sstevel { 233603831d35Sstevel int i; 233703831d35Sstevel char indent_buf[64]; 233803831d35Sstevel char msg_buf[256]; 233903831d35Sstevel va_list adx; 234003831d35Sstevel static uint32_t indent = 0; 234103831d35Sstevel 234203831d35Sstevel if (action & DBGACT_SHOWPOS) { 234303831d35Sstevel cmn_err(CE_CONT, "%s at line %d:\n", file, line); 234403831d35Sstevel } 234503831d35Sstevel 234603831d35Sstevel if (class & DBG_RETS) { 234703831d35Sstevel indent--; 234803831d35Sstevel } 234903831d35Sstevel 235003831d35Sstevel if (class & mboxsc_debug_mask) { 235103831d35Sstevel indent_buf[0] = '\0'; 235203831d35Sstevel for (i = 0; i < indent; i++) { 235307d06da5SSurya Prakki (void) strcat(indent_buf, " "); 235403831d35Sstevel } 235503831d35Sstevel 235603831d35Sstevel va_start(adx, fmt); 235707d06da5SSurya Prakki (void) vsprintf(msg_buf, fmt, adx); 235803831d35Sstevel va_end(adx); 235903831d35Sstevel 236003831d35Sstevel cmn_err(CE_CONT, "%s%s", indent_buf, msg_buf); 236103831d35Sstevel } 236203831d35Sstevel 236303831d35Sstevel if (class & DBG_CALLS) { 236403831d35Sstevel indent++; 236503831d35Sstevel } 236603831d35Sstevel 236703831d35Sstevel if (action & DBGACT_BREAK) { 236803831d35Sstevel debug_enter(""); 236903831d35Sstevel } 237003831d35Sstevel } 237103831d35Sstevel 237203831d35Sstevel static void 237303831d35Sstevel print_hash_table(void) 237403831d35Sstevel { 237503831d35Sstevel int i; 237603831d35Sstevel mboxsc_mbox_t *mailboxp; 237703831d35Sstevel 237803831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_hash_table called\n"); 237903831d35Sstevel 238003831d35Sstevel mutex_enter(&mboxsc_lock); 238103831d35Sstevel 238203831d35Sstevel for (i = 0; i < HASHTBL_SIZE; i++) { 238303831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hash[%02d]:\n", i); 238403831d35Sstevel 238503831d35Sstevel for (mailboxp = mboxsc_hash_table[i]; mailboxp != NULL; 238603831d35Sstevel mailboxp = mailboxp->mbox_hash_next) { 238703831d35Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT, 238803831d35Sstevel " key: 0x%08x, dir: %d\n", mailboxp->mbox_key, 238903831d35Sstevel mailboxp->mbox_direction); 239003831d35Sstevel } 239103831d35Sstevel } 239203831d35Sstevel 239303831d35Sstevel mutex_exit(&mboxsc_lock); 239403831d35Sstevel 239503831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_hash_table ret\n"); 239603831d35Sstevel } 239703831d35Sstevel 239803831d35Sstevel static int 239903831d35Sstevel print_mailbox_by_key(uint32_t key) 240003831d35Sstevel { 240103831d35Sstevel int error = 0; 240203831d35Sstevel mboxsc_mbox_t *mailboxp; 240303831d35Sstevel 240403831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox_by_key called\n"); 240503831d35Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%08x\n", key); 240603831d35Sstevel 240703831d35Sstevel mutex_enter(&mboxsc_lock); 240803831d35Sstevel 240903831d35Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key); 241003831d35Sstevel if (mailboxp != NULL) { 241103831d35Sstevel print_mailbox(mailboxp); 241203831d35Sstevel error = 0; 241303831d35Sstevel } else { 241403831d35Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, 241503831d35Sstevel "print_mailbox_by_key: no such mbox 0x%08x\n", key); 241603831d35Sstevel error = EBADF; 241703831d35Sstevel } 241803831d35Sstevel 241903831d35Sstevel mutex_exit(&mboxsc_lock); 242003831d35Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, 242103831d35Sstevel "print_mailbox_by_key ret: 0x%08x\n", error); 242203831d35Sstevel 242303831d35Sstevel return (error); 242403831d35Sstevel } 242503831d35Sstevel 242603831d35Sstevel /* ARGSUSED */ 242703831d35Sstevel static void 242803831d35Sstevel print_mailbox(mboxsc_mbox_t *mailboxp) 242903831d35Sstevel { 243003831d35Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox called\n"); 243107d06da5SSurya Prakki DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", 243207d06da5SSurya Prakki (void *)mailboxp); 243303831d35Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) { 243403831d35Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT, 243503831d35Sstevel "key = 0x%08x, dir = %d, callback = %p\n", 243603831d35Sstevel mailboxp->mbox_key, mailboxp->mbox_direction, 243707d06da5SSurya Prakki (void *)mailboxp->mbox_callback); 243803831d35Sstevel } else { 243903831d35Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "key = 0x%08x, dir = %d\n", 244007d06da5SSurya Prakki (int)mailboxp->mbox_key, mailboxp->mbox_direction); 244103831d35Sstevel } 244203831d35Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT, 244303831d35Sstevel "length = %d, refcount = %d, state = %d\n", 244403831d35Sstevel mailboxp->mbox_length, mailboxp->mbox_refcount, 244503831d35Sstevel mailboxp->mbox_state); 244607d06da5SSurya Prakki /* LINTED E_BAD_FORMAT_ARG_TYPE2 */ 244707d06da5SSurya Prakki DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "waitcv = %p, hashnext = %p\n", 244807d06da5SSurya Prakki (void *)&mailboxp->mbox_wait, (void *)mailboxp->mbox_hash_next); 244903831d35Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) { 245003831d35Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT, 245103831d35Sstevel "hdr.type = 0x%x, hdr.cmd = 0x%x, hdr.len = 0x%x\n", 245203831d35Sstevel mailboxp->mbox_header.msg_type, 245303831d35Sstevel mailboxp->mbox_header.msg_cmd, 245403831d35Sstevel mailboxp->mbox_header.msg_length); 245507d06da5SSurya Prakki DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hdr.tid = 0x%016lx\n", 245603831d35Sstevel mailboxp->mbox_header.msg_transid); 245703831d35Sstevel } 245803831d35Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_mailbox ret\n"); 245903831d35Sstevel } 246003831d35Sstevel #endif /* DEBUG */ 2461