11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel /*
281708Sstevel * This file contains the implementation of the mboxsc module, a mailbox layer
291708Sstevel * built upon the Starcat IOSRAM driver.
301708Sstevel */
311708Sstevel
321708Sstevel #include <sys/types.h>
331708Sstevel #include <sys/systm.h>
341708Sstevel #include <sys/modctl.h>
351708Sstevel #include <sys/errno.h>
361708Sstevel #include <sys/ksynch.h>
371708Sstevel #include <sys/kmem.h>
381708Sstevel #include <sys/varargs.h>
391708Sstevel #include <sys/ddi.h>
401708Sstevel #include <sys/sunddi.h>
411708Sstevel #include <sys/cmn_err.h>
421708Sstevel #include <sys/debug.h>
431708Sstevel #include <sys/sysmacros.h>
441708Sstevel
451708Sstevel #include <sys/iosramreg.h>
461708Sstevel #include <sys/iosramio.h>
471708Sstevel #include <sys/mboxsc.h>
481708Sstevel #include <sys/mboxsc_impl.h>
491708Sstevel
501708Sstevel /*
511708Sstevel * Debugging facility
521708Sstevel */
531708Sstevel #define DBGACT_NONE (0x00000000)
541708Sstevel #define DBGACT_BREAK (0x00000001)
551708Sstevel #define DBGACT_SHOWPOS (0x00000002)
561708Sstevel #define DBGACT_DEFAULT DBGACT_NONE
571708Sstevel
581708Sstevel #define DBG_DEV (0x00000001)
591708Sstevel #define DBG_CALLS (0x00000002)
601708Sstevel #define DBG_RETS (0x00000004)
611708Sstevel #define DBG_ARGS (0x00000008)
621708Sstevel #define DBG_KMEM (0x00000010)
631708Sstevel #define DBG_ALL (0xFFFFFFFF)
641708Sstevel
651708Sstevel #ifdef DEBUG
661708Sstevel static uint32_t mboxsc_debug_mask = 0x00000000;
671708Sstevel #define DPRINTF0(class, action, fmt) \
681708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt))
691708Sstevel #define DPRINTF1(class, action, fmt, arg1) \
701708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
711708Sstevel (arg1))
721708Sstevel #define DPRINTF2(class, action, fmt, arg1, arg2) \
731708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
741708Sstevel (arg1), (arg2))
751708Sstevel #define DPRINTF3(class, action, fmt, arg1, arg2, arg3) \
761708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
771708Sstevel (arg1), (arg2), (arg3))
781708Sstevel #define DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4) \
791708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
801708Sstevel (arg1), (arg2), (arg3), (arg4))
811708Sstevel #define DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5) \
821708Sstevel mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
831708Sstevel (arg1), (arg2), (arg3), (arg4), (arg5))
841708Sstevel #else /* DEBUG */
851708Sstevel #define DPRINTF0(class, action, fmt)
861708Sstevel #define DPRINTF1(class, action, fmt, arg1)
871708Sstevel #define DPRINTF2(class, action, fmt, arg1, arg2)
881708Sstevel #define DPRINTF3(class, action, fmt, arg1, arg2, arg3)
891708Sstevel #define DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4)
901708Sstevel #define DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5)
911708Sstevel #endif /* DEBUG */
921708Sstevel
931708Sstevel /*
941708Sstevel * Basic constants
951708Sstevel */
961708Sstevel #ifndef TRUE
971708Sstevel #define TRUE (1)
981708Sstevel #endif /* TRUE */
991708Sstevel #ifndef FALSE
1001708Sstevel #define FALSE (0)
1011708Sstevel #endif /* FALSE */
1021708Sstevel
1031708Sstevel
1041708Sstevel /*
1051708Sstevel * Whenever mboxsc_init is called to create a new mailbox, an instance of
1061708Sstevel * mboxsc_mbox_t is created and inserted into a hash table to maintain
1071708Sstevel * various information about the mailbox. The mbox_state, mbox_refcount, and
1081708Sstevel * mbox_wait fields are all protected by the global mboxsc_lock mutex.
1091708Sstevel * If lock contention between mailboxes becomes an issue, each mailbox will
1101708Sstevel * need to be given its own mutex to protect the mbox_wait, mbox_state,
1111708Sstevel * and mbox_update_wait fields. The mbox_refcount field will probably need to
1121708Sstevel * remain under global protection, however, since it is used to keep track of
1131708Sstevel * the number of threads sleeping inside the mailbox's various synchronization
1141708Sstevel * mechanisms and would consequently be difficult to protect using those same
1151708Sstevel * mechanisms.
1161708Sstevel */
1171708Sstevel typedef struct mboxsc_mbox {
1181708Sstevel uint32_t mbox_key;
1191708Sstevel int mbox_direction;
1201708Sstevel void (*mbox_callback)(void);
1211708Sstevel uint32_t mbox_length;
1221708Sstevel uint16_t mbox_refcount;
1231708Sstevel uint16_t mbox_state;
1241708Sstevel kcondvar_t mbox_wait;
1251708Sstevel mboxsc_msghdr_t mbox_header;
1261708Sstevel struct mboxsc_mbox *mbox_hash_next;
1271708Sstevel } mboxsc_mbox_t;
1281708Sstevel
1291708Sstevel /*
1301708Sstevel * Various state flags that can be set on a mailbox. Multiple states may
1311708Sstevel * be active at the same time.
1321708Sstevel */
1331708Sstevel #define STATE_IDLE (0x0000)
1341708Sstevel #define STATE_WRITING (0x0001)
1351708Sstevel #define STATE_READING (0x0002)
1361708Sstevel #define STATE_HDRVALID (0x0004)
1371708Sstevel
1381708Sstevel /*
1391708Sstevel * Timeout periods for mboxsc_putmsg and mboxsc_getmsg, converted to ticks
1401708Sstevel * from the microsecond values found in mboxsc_impl.h.
1411708Sstevel */
1421708Sstevel #define EAGAIN_POLL (drv_usectohz(MBOXSC_EAGAIN_POLL_USECS))
1431708Sstevel #define PUTMSG_POLL (drv_usectohz(MBOXSC_PUTMSG_POLL_USECS))
1441708Sstevel #define HWLOCK_POLL (drv_usectohz(MBOXSC_HWLOCK_POLL_USECS))
1451708Sstevel #define LOOP_WARN_INTERVAL (drv_usectohz(MBOXSC_USECS_PER_SECOND * 15))
1461708Sstevel
1471708Sstevel /*
1481708Sstevel * Various tests that are performed on message header fields.
1491708Sstevel */
1501708Sstevel #define IS_UNSOLICITED_TYPE(type) ((type) != MBOXSC_MSG_REPLY)
1511708Sstevel #define MSG_TYPE_MATCHES(type, msgp) \
1521708Sstevel (((type) == 0) || ((type) & (msgp)->msg_type))
1531708Sstevel #define MSG_CMD_MATCHES(cmd, msgp) \
1541708Sstevel (((cmd) == 0) || ((cmd) == (msgp)->msg_cmd))
1551708Sstevel #define MSG_TRANSID_MATCHES(tid, msgp) \
1561708Sstevel (((tid) == 0) || ((tid) == (msgp)->msg_transid))
1571708Sstevel
1581708Sstevel /*
1591708Sstevel * These macros can be used to determine the offset or size of any field in the
1601708Sstevel * message header (or any other struct, for that matter).
1611708Sstevel */
1621708Sstevel #define FIELD_OFFSET(type, field) ((uint32_t)&(((type *)0)->field))
1631708Sstevel #define FIELD_SIZE(type, field) (sizeof (((type *)0)->field))
1641708Sstevel
1651708Sstevel /*
1661708Sstevel * Mask used when generating unique transaction ID values.
1671708Sstevel * This arbitrarily chosen value will be OR'd together with
1681708Sstevel * a counter for each successive internally-generated transaction ID.
1691708Sstevel */
1701708Sstevel #define TRANSID_GEN_MASK (0xFFC0000000000000)
1711708Sstevel
1721708Sstevel /*
1731708Sstevel * All existing mailboxes are stored in a hash table with HASHTBL_SIZE
1741708Sstevel * entries so they can be rapidly accessed by their key values.
1751708Sstevel */
1761708Sstevel #define HASHTBL_SIZE (32)
1771708Sstevel #define HASH_KEY(key) ((((key) >> 24) ^ ((key) >> 16) ^ ((key) >> 9) ^\
1781708Sstevel (key)) & (HASHTBL_SIZE - 1));
1791708Sstevel
1801708Sstevel /*
1811708Sstevel * Unfortunately, it is necessary to calculate checksums on data split up
1821708Sstevel * amongst different buffers in some cases. Consequently, mboxsc_checksum
1831708Sstevel * accepts a "seed" value as one of its parameters. When first starting a
1841708Sstevel * checksum calculation, the seed should be 0.
1851708Sstevel */
1861708Sstevel #define CHKSUM_INIT (0)
1871708Sstevel
1881708Sstevel /*
1891708Sstevel * local variables
1901708Sstevel */
1911708Sstevel static kmutex_t mboxsc_lock;
1921708Sstevel static mboxsc_mbox_t *mboxsc_hash_table[HASHTBL_SIZE];
1931708Sstevel static uint32_t mboxsc_flaglock_count;
1941708Sstevel static uint32_t mboxsc_active_version = MBOXSC_PROTOCOL_VERSION;
1951708Sstevel static kcondvar_t mboxsc_dereference_cv;
1961708Sstevel
1971708Sstevel /*
1981708Sstevel * Structures from modctl.h used for loadable module support.
1991708Sstevel * The mboxsc API is a "miscellaneous" module.
2001708Sstevel */
2011708Sstevel extern struct mod_ops mod_miscops;
2021708Sstevel
2031708Sstevel static struct modlmisc modlmisc = {
2041708Sstevel &mod_miscops,
2057799SRichard.Bean@Sun.COM "IOSRAM Mailbox API 'mboxsc'",
2061708Sstevel };
2071708Sstevel
2081708Sstevel static struct modlinkage modlinkage = {
2091708Sstevel MODREV_1,
2101708Sstevel (void *)&modlmisc,
2111708Sstevel NULL
2121708Sstevel };
2131708Sstevel
2141708Sstevel /*
2151708Sstevel * Prototypes for local functions
2161708Sstevel */
2171708Sstevel static void mboxsc_iosram_callback(void *arg);
2181708Sstevel static void mboxsc_hdrchange_callback(void);
2191708Sstevel static int mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp);
2201708Sstevel static void mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp);
2211708Sstevel static void mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp);
2221708Sstevel static mboxsc_mbox_t *mboxsc_hashfind_mailbox_by_key(uint32_t key);
2231708Sstevel static mboxsc_mbox_t *mboxsc_hashremove_mailbox_by_key(uint32_t key);
2241708Sstevel static mboxsc_chksum_t mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf,
2251708Sstevel uint32_t length);
2261708Sstevel static int mboxsc_lock_flags(uint8_t mandatory, clock_t deadline);
2271708Sstevel static int mboxsc_unlock_flags(uint8_t mandatory);
2281708Sstevel static int mboxsc_timed_read(clock_t deadline, uint32_t key,
2291708Sstevel uint32_t off, uint32_t len, caddr_t dptr);
2301708Sstevel static int mboxsc_timed_write(clock_t deadline, uint32_t key,
2311708Sstevel uint32_t off, uint32_t len, caddr_t dptr);
2321708Sstevel static int mboxsc_timed_get_flag(clock_t deadline, uint32_t key,
2331708Sstevel uint8_t *data_validp, uint8_t *int_pendingp);
2341708Sstevel static int mboxsc_timed_set_flag(clock_t deadline, uint32_t key,
2351708Sstevel uint8_t data_valid, uint8_t int_pending);
2361708Sstevel static int mboxsc_timed_send_intr(clock_t deadline);
2371708Sstevel static int mboxsc_expire_message(uint32_t key, int *resultp);
2381708Sstevel static uint64_t mboxsc_generate_transid(uint64_t prev_transid);
2391708Sstevel static void mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp);
2401708Sstevel static void mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp);
2411708Sstevel #ifdef DEBUG
2421708Sstevel /*PRINTFLIKE5*/
2431708Sstevel static void mboxsc_dprintf(const char *file, int line,
2441708Sstevel uint32_t class, uint32_t action, const char *fmt, ...);
2451708Sstevel int mboxsc_debug(int cmd, void *arg);
2461708Sstevel #endif /* DEBUG */
2471708Sstevel
2481708Sstevel
2491708Sstevel /*
2501708Sstevel * _init
2511708Sstevel *
2521708Sstevel * Loadable module support routine. Initializes global lock and hash table.
2531708Sstevel */
2541708Sstevel int
_init(void)2551708Sstevel _init(void)
2561708Sstevel {
2571708Sstevel int i;
2581708Sstevel uint32_t sms_version;
2591708Sstevel int error = 0;
2601708Sstevel
2611708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_init called\n");
2621708Sstevel
2631708Sstevel /*
2641708Sstevel * Initialize all module resources.
2651708Sstevel */
2661708Sstevel mutex_init(&mboxsc_lock, NULL, MUTEX_DRIVER, NULL);
2671708Sstevel cv_init(&mboxsc_dereference_cv, NULL, CV_DRIVER, NULL);
2681708Sstevel
2691708Sstevel for (i = 0; i < HASHTBL_SIZE; i++) {
2701708Sstevel mboxsc_hash_table[i] = NULL;
2711708Sstevel }
2721708Sstevel mboxsc_flaglock_count = 0;
2731708Sstevel
2741708Sstevel if (mod_install(&modlinkage) != 0) {
2751708Sstevel goto failed;
2761708Sstevel }
2771708Sstevel
2781708Sstevel /*
2791708Sstevel * Set the os_mbox_version field in the IOSRAM header to indicate the
2801708Sstevel * highest Mailbox Protocol version we support
2811708Sstevel */
2821708Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_SET_OS_MBOX_VER,
2831708Sstevel (void *)MBOXSC_PROTOCOL_VERSION);
2841708Sstevel if (error != 0) {
2851708Sstevel goto failed;
2861708Sstevel }
2871708Sstevel
2881708Sstevel /*
2891708Sstevel * Read the sms_mbox_version field in the IOSRAM header to determine
2901708Sstevel * what the greatest commonly supported version is.
2911708Sstevel */
2921708Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
2931708Sstevel (void *)&sms_version);
2941708Sstevel if (error != 0) {
2951708Sstevel goto failed;
2961708Sstevel }
2971708Sstevel mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION, sms_version);
2981708Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
2991708Sstevel "sms version: %d, active version: %d\n", sms_version,
3001708Sstevel mboxsc_active_version);
3011708Sstevel
3021708Sstevel /*
3031708Sstevel * Register a callback with the IOSRAM driver to receive notification of
3041708Sstevel * changes to the IOSRAM header, in case the sms_mbox_version field
3051708Sstevel * changes.
3061708Sstevel */
3071708Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK,
3081708Sstevel (void *)mboxsc_hdrchange_callback);
3091708Sstevel if (error != 0) {
3101708Sstevel goto failed;
3111708Sstevel }
3121708Sstevel
3131708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
3141708Sstevel return (0);
3151708Sstevel
3161708Sstevel /*
3171708Sstevel * If initialization fails, uninitialize resources.
3181708Sstevel */
3191708Sstevel failed:
3201708Sstevel mutex_destroy(&mboxsc_lock);
3211708Sstevel cv_destroy(&mboxsc_dereference_cv);
3221708Sstevel
3231708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
3241708Sstevel return (error);
3251708Sstevel }
3261708Sstevel
3271708Sstevel /*
3281708Sstevel * _fini
3291708Sstevel *
3301708Sstevel * Loadable module support routine. Closes all mailboxes and releases all
3311708Sstevel * resources.
3321708Sstevel */
3331708Sstevel int
_fini(void)3341708Sstevel _fini(void)
3351708Sstevel {
3361708Sstevel int i;
3371708Sstevel int error = 0;
3381708Sstevel mboxsc_mbox_t *mailboxp;
3391708Sstevel
3401708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_fini called\n");
3411708Sstevel
3421708Sstevel /*
3431708Sstevel * Attempt to remove the module. If successful, close all mailboxes
3441708Sstevel * and deallocate the global lock.
3451708Sstevel */
3461708Sstevel error = mod_remove(&modlinkage);
3471708Sstevel if (error == 0) {
3481708Sstevel mutex_enter(&mboxsc_lock);
3491708Sstevel
350*11311SSurya.Prakki@Sun.COM (void) iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK, NULL);
3511708Sstevel
3521708Sstevel for (i = 0; i < HASHTBL_SIZE; i++) {
3531708Sstevel while (mboxsc_hash_table[i] != NULL) {
3541708Sstevel mailboxp = mboxsc_hash_table[i];
3551708Sstevel mboxsc_close_mailbox(mailboxp);
3561708Sstevel }
3571708Sstevel }
3581708Sstevel mutex_exit(&mboxsc_lock);
3591708Sstevel mutex_destroy(&mboxsc_lock);
3601708Sstevel cv_destroy(&mboxsc_dereference_cv);
3611708Sstevel }
3621708Sstevel
3631708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_fini ret: 0x%08x\n", error);
3641708Sstevel return (error);
3651708Sstevel }
3661708Sstevel
3671708Sstevel /*
3681708Sstevel * _info
3691708Sstevel *
3701708Sstevel * Loadable module support routine.
3711708Sstevel */
3721708Sstevel int
_info(struct modinfo * modinfop)3731708Sstevel _info(struct modinfo *modinfop)
3741708Sstevel {
3751708Sstevel int error = 0;
3761708Sstevel
3771708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_info called\n");
3781708Sstevel
3791708Sstevel error = mod_info(&modlinkage, modinfop);
3801708Sstevel
3811708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_info ret: 0x%08x\n", error);
3821708Sstevel
3831708Sstevel return (error);
3841708Sstevel }
3851708Sstevel
3861708Sstevel /*
3871708Sstevel * mboxsc_init
3881708Sstevel *
3891708Sstevel * Attempts to create a new mailbox.
3901708Sstevel */
3911708Sstevel int
mboxsc_init(uint32_t key,int direction,void (* event_handler)(void))3921708Sstevel mboxsc_init(uint32_t key, int direction, void (*event_handler)(void))
3931708Sstevel {
3941708Sstevel int error = 0;
3951708Sstevel mboxsc_mbox_t *mailboxp;
3961708Sstevel
3971708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_init called\n");
3981708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
3991708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "direction = %d\n", direction);
4001708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "event_handlerp = %p\n",
401*11311SSurya.Prakki@Sun.COM (void *)event_handler);
4021708Sstevel
4031708Sstevel /*
4041708Sstevel * Check for valid direction and callback specification.
4051708Sstevel */
4061708Sstevel if (((direction != MBOXSC_MBOX_IN) && (direction != MBOXSC_MBOX_OUT)) ||
4071708Sstevel ((event_handler != NULL) && (direction != MBOXSC_MBOX_IN))) {
4081708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n",
4091708Sstevel EINVAL);
4101708Sstevel return (EINVAL);
4111708Sstevel }
4121708Sstevel
4131708Sstevel /*
4141708Sstevel * Allocate memory for the mailbox structure and initialize all
4151708Sstevel * caller-provided fields.
4161708Sstevel */
4171708Sstevel mailboxp = (mboxsc_mbox_t *)kmem_zalloc(sizeof (mboxsc_mbox_t),
4181708Sstevel KM_SLEEP);
419*11311SSurya.Prakki@Sun.COM DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_zalloc(%lu) = %p\n",
420*11311SSurya.Prakki@Sun.COM sizeof (mboxsc_mbox_t), (void *)mailboxp);
4211708Sstevel mailboxp->mbox_key = key;
4221708Sstevel mailboxp->mbox_direction = direction;
4231708Sstevel mailboxp->mbox_callback = event_handler;
4241708Sstevel
4251708Sstevel /*
4261708Sstevel * Attempt to add the mailbox. If unsuccessful, free the allocated
4271708Sstevel * memory.
4281708Sstevel */
4291708Sstevel mutex_enter(&mboxsc_lock);
4301708Sstevel error = mboxsc_add_mailbox(mailboxp);
4311708Sstevel mutex_exit(&mboxsc_lock);
4321708Sstevel
4331708Sstevel if (error != 0) {
434*11311SSurya.Prakki@Sun.COM DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n",
435*11311SSurya.Prakki@Sun.COM (void *)mailboxp, sizeof (mboxsc_mbox_t));
4361708Sstevel kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
4371708Sstevel }
4381708Sstevel
4391708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n", error);
4401708Sstevel return (error);
4411708Sstevel }
4421708Sstevel
4431708Sstevel /*
4441708Sstevel * mboxsc_fini
4451708Sstevel *
4461708Sstevel * Closes the mailbox with the indicated key, if it exists.
4471708Sstevel */
4481708Sstevel int
mboxsc_fini(uint32_t key)4491708Sstevel mboxsc_fini(uint32_t key)
4501708Sstevel {
4511708Sstevel int error = 0;
4521708Sstevel mboxsc_mbox_t *mailboxp;
4531708Sstevel
4541708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_fini called\n");
4551708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
4561708Sstevel
4571708Sstevel /*
4581708Sstevel * Attempt to close the mailbox.
4591708Sstevel */
4601708Sstevel mutex_enter(&mboxsc_lock);
4611708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
4621708Sstevel if (mailboxp == NULL) {
4631708Sstevel error = EBADF;
4641708Sstevel } else {
4651708Sstevel while (mailboxp->mbox_refcount != 0) {
4661708Sstevel cv_wait(&mboxsc_dereference_cv, &mboxsc_lock);
4671708Sstevel }
4681708Sstevel mboxsc_close_mailbox(mailboxp);
4691708Sstevel }
4701708Sstevel mutex_exit(&mboxsc_lock);
4711708Sstevel
4721708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_fini ret: 0x%08x\n", error);
4731708Sstevel return (error);
4741708Sstevel }
4751708Sstevel
4761708Sstevel /*
4771708Sstevel * mboxsc_putmsg
4781708Sstevel *
4791708Sstevel * Attempt to place a message into an outbound mailbox and signal the
4801708Sstevel * recipient. A successful return (0) indicates that the message was
4811708Sstevel * successfully delivered.
4821708Sstevel */
4831708Sstevel int
mboxsc_putmsg(uint32_t key,uint32_t type,uint32_t cmd,uint64_t * transidp,uint32_t length,void * datap,clock_t timeout)4841708Sstevel mboxsc_putmsg(uint32_t key, uint32_t type, uint32_t cmd, uint64_t *transidp,
4851708Sstevel uint32_t length, void *datap, clock_t timeout)
4861708Sstevel {
4871708Sstevel int i;
4881708Sstevel int error = 0;
4891708Sstevel int result;
4901708Sstevel int lock_held = 0;
4911708Sstevel int unlock_err;
4921708Sstevel uint8_t data_valid;
4931708Sstevel clock_t deadline;
4941708Sstevel clock_t remainder;
4951708Sstevel mboxsc_chksum_t checksum;
4961708Sstevel mboxsc_mbox_t *mailboxp;
4971708Sstevel mboxsc_msghdr_t header;
4981708Sstevel
4991708Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */
5001708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_putmsg called\n");
5011708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
5021708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "type = 0x%x\n", type);
5031708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
504*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp);
5051708Sstevel if (transidp != NULL) {
506*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%016lx\n",
5071708Sstevel *transidp);
5081708Sstevel }
5091708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
5101708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
511*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout);
5121708Sstevel #endif /* DEBUG */
5131708Sstevel
5141708Sstevel /*
5151708Sstevel * Perform some basic sanity checks on the message.
5161708Sstevel */
5171708Sstevel for (i = 0; i < MBOXSC_NUM_MSG_TYPES; i++) {
5181708Sstevel if (type == (1 << i)) {
5191708Sstevel break;
5201708Sstevel }
5211708Sstevel }
5221708Sstevel if ((i == MBOXSC_NUM_MSG_TYPES) || (cmd == 0) ||
5231708Sstevel ((datap == NULL) && (length != 0)) ||
5241708Sstevel (timeout < MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS) ||
5251708Sstevel (timeout > MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS)) {
5261708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
5271708Sstevel "mboxsc_putmsg ret: 0x%08x\n", EINVAL);
5281708Sstevel return (EINVAL);
5291708Sstevel }
5301708Sstevel
5311708Sstevel /*
5321708Sstevel * Initialize the header structure with values provided by the caller.
5331708Sstevel */
5341708Sstevel header.msg_version = mboxsc_active_version;
5351708Sstevel header.msg_type = type;
5361708Sstevel header.msg_cmd = cmd;
5371708Sstevel header.msg_length = MBOXSC_MSGHDR_SIZE + length;
5381708Sstevel if (transidp != NULL) {
5391708Sstevel header.msg_transid = *transidp;
5401708Sstevel } else {
5411708Sstevel header.msg_transid = 0;
5421708Sstevel }
5431708Sstevel
5441708Sstevel /*
5451708Sstevel * Perform additional sanity checks on the mailbox and message.
5461708Sstevel * Make sure that the specified mailbox really exists, that the
5471708Sstevel * given message will fit in it, and that the current message's
5481708Sstevel * transaction ID isn't the same as the last message's transaction
5491708Sstevel * ID unless both messages are replies (it's okay, necessary even,
5501708Sstevel * to reuse a transaction ID when resending a failed reply message,
5511708Sstevel * but that is the only case in which it is permissible).
5521708Sstevel */
5531708Sstevel mutex_enter(&mboxsc_lock);
5541708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
5551708Sstevel
5561708Sstevel if (mailboxp == NULL) {
5571708Sstevel error = EBADF;
5581708Sstevel } else if ((mailboxp->mbox_direction != MBOXSC_MBOX_OUT) ||
5591708Sstevel (length + MBOXSC_PROTOCOL_SIZE > mailboxp->mbox_length) ||
5601708Sstevel ((header.msg_transid == mailboxp->mbox_header.msg_transid) &&
5611708Sstevel ((type & mailboxp->mbox_header.msg_type) != MBOXSC_MSG_REPLY) &&
5621708Sstevel (header.msg_transid != 0))) {
5631708Sstevel error = EINVAL;
5641708Sstevel }
5651708Sstevel
5661708Sstevel if (error != 0) {
5671708Sstevel mutex_exit(&mboxsc_lock);
5681708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
5691708Sstevel "mboxsc_putmsg ret: 0x%08x\n", error);
5701708Sstevel return (error);
5711708Sstevel }
5721708Sstevel
5731708Sstevel /*
5741708Sstevel * If the message's transaction ID is set to 0, generate a unique
5751708Sstevel * transaction ID and copy it into the message header. If the message
5761708Sstevel * is successfully delivered and transidp != NULL, we'll copy this new
5771708Sstevel * transid into *transidp later.
5781708Sstevel */
5791708Sstevel if (header.msg_transid == 0) {
5801708Sstevel header.msg_transid =
5811708Sstevel mboxsc_generate_transid(mailboxp->mbox_header.msg_transid);
5821708Sstevel }
5831708Sstevel
5841708Sstevel /*
5851708Sstevel * Don't allow mboxsc_putmsg to attempt to place a message for
5861708Sstevel * longer than the caller's timeout.
5871708Sstevel */
5881708Sstevel deadline = ddi_get_lbolt() +
5891708Sstevel drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
5901708Sstevel
5911708Sstevel /*
5921708Sstevel * Increment the reference count on the mailbox to keep it from being
5931708Sstevel * closed, and wait for it to become available.
5941708Sstevel */
5951708Sstevel mboxsc_reference_mailbox(mailboxp);
5961708Sstevel remainder = 1;
5971708Sstevel while ((mailboxp->mbox_state & STATE_WRITING) &&
5981708Sstevel (remainder > 0)) {
5991708Sstevel remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
6001708Sstevel &mboxsc_lock, deadline);
6011708Sstevel }
6021708Sstevel
6031708Sstevel /*
6041708Sstevel * Check to see whether or not the mailbox became available. If it
6051708Sstevel * did not, decrement its reference count and return an error to the
6061708Sstevel * caller.
6071708Sstevel */
6081708Sstevel if (remainder == -1) {
6091708Sstevel error = ENOSPC;
6101708Sstevel } else if (remainder == 0) {
6111708Sstevel error = EINTR;
6121708Sstevel }
6131708Sstevel
6141708Sstevel if (error != 0) {
6151708Sstevel mboxsc_dereference_mailbox(mailboxp);
6161708Sstevel mutex_exit(&mboxsc_lock);
6171708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
6181708Sstevel "mboxsc_putmsg ret: 0x%08x\n", error);
6191708Sstevel return (error);
6201708Sstevel }
6211708Sstevel
6221708Sstevel /*
6231708Sstevel * Since the message is valid and we're going to try to write it to
6241708Sstevel * IOSRAM, record its header for future reference (e.g. to make sure the
6251708Sstevel * next message doesn't incorrectly use the same transID).
6261708Sstevel */
6271708Sstevel bcopy(&header, &(mailboxp->mbox_header), MBOXSC_MSGHDR_SIZE);
6281708Sstevel
6291708Sstevel /*
6301708Sstevel * Flag the mailbox as being in use and release the global lock.
6311708Sstevel */
6321708Sstevel mailboxp->mbox_state |= STATE_WRITING;
6331708Sstevel mutex_exit(&mboxsc_lock);
6341708Sstevel
6351708Sstevel /*
6361708Sstevel * Calculate the message checksum using the header and the data.
6371708Sstevel */
6381708Sstevel checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
6391708Sstevel MBOXSC_MSGHDR_SIZE);
6401708Sstevel checksum = mboxsc_checksum(checksum, (uint8_t *)datap, length);
6411708Sstevel
6421708Sstevel /*
6431708Sstevel * Attempt to write the message and checksum to IOSRAM until successful,
6441708Sstevel * or as long as time remains and no errors other than EAGAIN are
6451708Sstevel * returned from any call to the IOSRAM driver in case there is a tunnel
6461708Sstevel * switch in progress.
6471708Sstevel */
6481708Sstevel error = mboxsc_timed_write(deadline, key, MBOXSC_MSGHDR_OFFSET,
6491708Sstevel MBOXSC_MSGHDR_SIZE, (caddr_t)&header);
6501708Sstevel
6511708Sstevel if (error == 0) {
6521708Sstevel error = mboxsc_timed_write(deadline, key, MBOXSC_DATA_OFFSET,
6531708Sstevel length, (caddr_t)datap);
6541708Sstevel }
6551708Sstevel
6561708Sstevel if (error == 0) {
6571708Sstevel error = mboxsc_timed_write(deadline, key, header.msg_length,
6581708Sstevel MBOXSC_CHKSUM_SIZE, (caddr_t)&checksum);
6591708Sstevel }
6601708Sstevel
6611708Sstevel /*
6621708Sstevel * Lock the flags before setting data_valid. This isn't strictly
6631708Sstevel * necessary for correct protocol operation, but it gives us a chance to
6641708Sstevel * verify that the flags lock is functional before we commit to sending
6651708Sstevel * the message.
6661708Sstevel */
6671708Sstevel if (error == 0) {
6681708Sstevel error = mboxsc_lock_flags(FALSE, deadline);
6691708Sstevel if (error == 0) {
6701708Sstevel lock_held = 1;
6711708Sstevel } else if (error == EBUSY) {
6721708Sstevel error = EAGAIN;
6731708Sstevel }
6741708Sstevel }
6751708Sstevel
6761708Sstevel if (error == 0) {
6771708Sstevel error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_VALID,
6781708Sstevel IOSRAM_INT_TO_SSC);
6791708Sstevel }
6801708Sstevel
6811708Sstevel /*
6821708Sstevel * Unlock the flags. If an error is encountered, only return it if
6831708Sstevel * another error hasn't been encountered previously.
6841708Sstevel */
6851708Sstevel if (lock_held) {
6861708Sstevel unlock_err = mboxsc_unlock_flags(TRUE);
6871708Sstevel if ((unlock_err != 0) && ((error == 0) || (error == EAGAIN))) {
6881708Sstevel error = unlock_err;
6891708Sstevel }
6901708Sstevel }
6911708Sstevel
6921708Sstevel /*
6931708Sstevel * If time ran out or an IOSRAM call failed, notify other callers that
6941708Sstevel * the mailbox is available, decrement its reference count, and return
6951708Sstevel * an error.
6961708Sstevel */
6971708Sstevel if (error != 0) {
6981708Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE));
6991708Sstevel mutex_enter(&mboxsc_lock);
7001708Sstevel mailboxp->mbox_state &= ~STATE_WRITING;
7011708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
7021708Sstevel mboxsc_dereference_mailbox(mailboxp);
7031708Sstevel mutex_exit(&mboxsc_lock);
7041708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
7051708Sstevel "mboxsc_putmsg ret: 0x%08x\n", error);
7061708Sstevel return (error);
7071708Sstevel }
7081708Sstevel
7091708Sstevel /*
7101708Sstevel * Send an interrupt to the remote mailbox interface to announce the
7111708Sstevel * presence of a new, valid message.
7121708Sstevel */
7131708Sstevel error = mboxsc_timed_send_intr(deadline);
7141708Sstevel
7151708Sstevel /*
7161708Sstevel * Wait until either the data_valid flag is set INVALID by the
7171708Sstevel * remote client or time runs out. Since we're calling delay as
7181708Sstevel * a part of polling the flag anyway, we don't really need to do
7191708Sstevel * the usual continuous retry if iosram_get_flag returns EAGAIN.
7201708Sstevel */
7211708Sstevel data_valid = IOSRAM_DATA_VALID;
7221708Sstevel if (error == DDI_SUCCESS) {
7231708Sstevel do {
7241708Sstevel delay(MIN(PUTMSG_POLL, deadline - ddi_get_lbolt()));
7251708Sstevel error = iosram_get_flag(key, &data_valid, NULL);
7261708Sstevel } while ((data_valid == IOSRAM_DATA_VALID) &&
7271708Sstevel ((error == EAGAIN) || (error == 0)) &&
7281708Sstevel (deadline - ddi_get_lbolt() >= 0));
7291708Sstevel }
7301708Sstevel
7311708Sstevel /*
7321708Sstevel * If the data_valid flag was set to INVALID by the other side, the
7331708Sstevel * message was successfully transmitted. If it wasn't, but there
7341708Sstevel * weren't any IOSRAM errors, the operation timed out. If there was a
7351708Sstevel * problem with the IOSRAM, pass that info back to the caller.
7361708Sstevel */
7371708Sstevel if (data_valid == IOSRAM_DATA_INVALID) {
7381708Sstevel result = 0;
7391708Sstevel } else if ((error == 0) || (error == DDI_FAILURE)) {
7401708Sstevel result = ETIMEDOUT;
7411708Sstevel } else {
7421708Sstevel ASSERT(error != EINVAL);
7431708Sstevel result = error;
7441708Sstevel }
7451708Sstevel
7461708Sstevel /*
7471708Sstevel * If the message has not been picked up, expire it. Note that this may
7481708Sstevel * actually result in detecting successful message delivery if the SC
7491708Sstevel * picks it up at the last moment. If expiration fails due to an error,
7501708Sstevel * return an error to the user even if the message appears to have
7511708Sstevel * been successfully delivered.
7521708Sstevel */
7531708Sstevel if (data_valid == IOSRAM_DATA_VALID) {
7541708Sstevel error = mboxsc_expire_message(key, &result);
7551708Sstevel if ((error != 0) && ((result == 0) || (result == ETIMEDOUT))) {
7561708Sstevel result = error;
7571708Sstevel }
7581708Sstevel }
7591708Sstevel
7601708Sstevel /*
7611708Sstevel * If the message was successfully delivered, and we generated a
7621708Sstevel * transaction ID for the caller, and the caller wants to know what it
7631708Sstevel * was, give it to them.
7641708Sstevel */
7651708Sstevel if ((result == 0) && (transidp != NULL) && (*transidp == 0)) {
7661708Sstevel *transidp = header.msg_transid;
7671708Sstevel }
7681708Sstevel
7691708Sstevel /*
7701708Sstevel * Regardless of whether the message was successfully transmitted or
7711708Sstevel * not, notify other callers that the mailbox is available and decrement
7721708Sstevel * its reference count.
7731708Sstevel */
7741708Sstevel mutex_enter(&mboxsc_lock);
7751708Sstevel mailboxp->mbox_state &= ~STATE_WRITING;
7761708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
7771708Sstevel mboxsc_dereference_mailbox(mailboxp);
7781708Sstevel mutex_exit(&mboxsc_lock);
7791708Sstevel
7801708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_putmsg ret: 0x%08x\n",
7811708Sstevel result);
7821708Sstevel return (result);
7831708Sstevel }
7841708Sstevel
7851708Sstevel /*
7861708Sstevel * mboxsc_getmsg
7871708Sstevel *
7881708Sstevel * Attempt to retrieve a message from the mailbox with the given key that
7891708Sstevel * matches values provided in msgp. A successful return (0) indicates that
7901708Sstevel * a message matching the caller's request was successfully received within
7911708Sstevel * timeout milliseconds. If a message matching the caller's request is
7921708Sstevel * detected, but can't be successfully read, an error will be returned even
7931708Sstevel * if the caller's timeout hasn't expired.
7941708Sstevel */
7951708Sstevel int
mboxsc_getmsg(uint32_t key,uint32_t * typep,uint32_t * cmdp,uint64_t * transidp,uint32_t * lengthp,void * datap,clock_t timeout)7961708Sstevel mboxsc_getmsg(uint32_t key, uint32_t *typep, uint32_t *cmdp, uint64_t *transidp,
7971708Sstevel uint32_t *lengthp, void *datap, clock_t timeout)
7981708Sstevel {
7991708Sstevel int error = 0;
8001708Sstevel uint32_t datalen;
8011708Sstevel uint8_t data_valid;
8021708Sstevel uint8_t lock_held;
8031708Sstevel mboxsc_chksum_t read_checksum;
8041708Sstevel mboxsc_chksum_t calc_checksum;
8051708Sstevel uint64_t read_transid;
8061708Sstevel clock_t deadline;
8071708Sstevel clock_t remainder;
8081708Sstevel mboxsc_mbox_t *mailboxp;
8091708Sstevel mboxsc_msghdr_t header;
8101708Sstevel
8111708Sstevel #ifdef DEBUG /* because lint whines about if stmts without consequents */
8121708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_getmsg called\n");
8131708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
814*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "typep = %p\n", (void *)typep);
8151708Sstevel if (typep != NULL) {
8161708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*typep = 0x%x\n", *typep);
8171708Sstevel }
818*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmdp = %p\n", (void *)cmdp);
8191708Sstevel if (cmdp != NULL) {
8201708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*cmdp = 0x%x\n", *cmdp);
8211708Sstevel }
822*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", (void *)transidp);
8231708Sstevel if (transidp != NULL) {
824*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%lx\n",
8251708Sstevel *transidp);
8261708Sstevel }
827*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "lengthp = %p\n", (void *)lengthp);
8281708Sstevel if (lengthp != NULL) {
8291708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*lengthp = 0x%x\n",
8301708Sstevel *lengthp);
8311708Sstevel }
8321708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
8331708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout);
8341708Sstevel #endif /* DEBUG */
8351708Sstevel
8361708Sstevel /*
8371708Sstevel * Perform basic sanity checks on the caller's request.
8381708Sstevel */
8391708Sstevel if ((typep == NULL) || (*typep >= (1 << MBOXSC_NUM_MSG_TYPES)) ||
8401708Sstevel (cmdp == NULL) || (transidp == NULL) || (lengthp == NULL) ||
8411708Sstevel ((datap == NULL) && (*lengthp != 0)) ||
8421708Sstevel (timeout < MBOXSC_GETMSG_MIN_TIMEOUT_MSECS) ||
8431708Sstevel (timeout > MBOXSC_GETMSG_MAX_TIMEOUT_MSECS)) {
8441708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
8451708Sstevel "mboxsc_getmsg ret: 0x%08x\n", EINVAL);
8461708Sstevel return (EINVAL);
8471708Sstevel }
8481708Sstevel
8491708Sstevel /*
8501708Sstevel * Don't allow mboxsc_getmsg to attempt to receive a message for
8511708Sstevel * longer than the caller's timeout.
8521708Sstevel */
8531708Sstevel deadline = ddi_get_lbolt() +
8541708Sstevel drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
8551708Sstevel
8561708Sstevel /*
8571708Sstevel * Perform additional sanity checks on the client's request and the
8581708Sstevel * associated mailbox.
8591708Sstevel */
8601708Sstevel mutex_enter(&mboxsc_lock);
8611708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
8621708Sstevel if (mailboxp == NULL) {
8631708Sstevel error = EBADF;
8641708Sstevel } else if (mailboxp->mbox_direction != MBOXSC_MBOX_IN) {
8651708Sstevel error = EINVAL;
8661708Sstevel }
8671708Sstevel
8681708Sstevel if (error != 0) {
8691708Sstevel mutex_exit(&mboxsc_lock);
8701708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
8711708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
8721708Sstevel return (error);
8731708Sstevel }
8741708Sstevel
8751708Sstevel /*
8761708Sstevel * The request is okay, so reference the mailbox (to keep it from being
8771708Sstevel * closed), and proceed with the real work.
8781708Sstevel */
8791708Sstevel mboxsc_reference_mailbox(mailboxp);
8801708Sstevel
8811708Sstevel /*
8821708Sstevel * Certain failures that may occur late in the process of getting a
8831708Sstevel * message (e.g. checksum error, cancellation by the sender) are
8841708Sstevel * supposed to leave the recipient waiting for the next message to
8851708Sstevel * arrive rather than returning an error. To facilitate restarting
8861708Sstevel * the message acquisition process, the following label is provided
8871708Sstevel * as a target for a very few judiciously-placed "goto"s.
8881708Sstevel *
8891708Sstevel * The mboxsc_lock mutex MUST be held when jumping to this point.
8901708Sstevel */
8911708Sstevel mboxsc_getmsg_retry:
8921708Sstevel ;
8931708Sstevel
8941708Sstevel /*
8951708Sstevel * If there is a valid message in the mailbox right now, check to
8961708Sstevel * see if it matches the caller's request. If not, or if another
8971708Sstevel * caller is already reading it, wait for either the arrival of the
8981708Sstevel * next message or the expiration of the caller's specified timeout.
8991708Sstevel */
9001708Sstevel error = 0;
9011708Sstevel while (!(mailboxp->mbox_state & STATE_HDRVALID) ||
9021708Sstevel (mailboxp->mbox_state & STATE_READING) ||
9031708Sstevel !MSG_TYPE_MATCHES(*typep, &(mailboxp->mbox_header)) ||
9041708Sstevel !MSG_CMD_MATCHES(*cmdp, &(mailboxp->mbox_header)) ||
9051708Sstevel !MSG_TRANSID_MATCHES(*transidp, &(mailboxp->mbox_header))) {
9061708Sstevel remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
9071708Sstevel &mboxsc_lock, deadline);
9081708Sstevel if (remainder == -1) {
9091708Sstevel error = ETIMEDOUT;
9101708Sstevel } else if (remainder == 0) {
9111708Sstevel error = EINTR;
9121708Sstevel }
9131708Sstevel
9141708Sstevel if (error != 0) {
9151708Sstevel mboxsc_dereference_mailbox(mailboxp);
9161708Sstevel mutex_exit(&mboxsc_lock);
9171708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
9181708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
9191708Sstevel return (error);
9201708Sstevel }
9211708Sstevel }
9221708Sstevel
9231708Sstevel /*
9241708Sstevel * If somebody sends us a message using a Mailbox Protocol version
9251708Sstevel * greater than the highest one we understand, invalidate the message,
9261708Sstevel * because we can't safely interpret anything beyond the version field.
9271708Sstevel */
9281708Sstevel if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
9291708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
9301708Sstevel "incoming message with unsupported version %d\n",
9311708Sstevel mailboxp->mbox_header.msg_version);
9321708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
9331708Sstevel goto mboxsc_getmsg_retry;
9341708Sstevel }
9351708Sstevel
9361708Sstevel /*
9371708Sstevel * At this point, there is a stored message header that matches the
9381708Sstevel * caller's request, but the actual message may no longer be valid
9391708Sstevel * in IOSRAM. Check the data_valid flag to see whether or not
9401708Sstevel * this is the case. If the message has expired, go start over.
9411708Sstevel *
9421708Sstevel * The global mutex is held while reading flag data from IOSRAM to
9431708Sstevel * avoid certain race conditions. One race condition is still
9441708Sstevel * possible (i.e. SC-side has just set the data_valid flag for a
9451708Sstevel * new message, but the stored message header hasn't been updated
9461708Sstevel * yet), but it won't cause incorrect behavior (just some wasted work).
9471708Sstevel */
9481708Sstevel error = iosram_get_flag(key, &data_valid, NULL);
9491708Sstevel
9501708Sstevel ASSERT(error != EINVAL);
9511708Sstevel if (error == 0) {
9521708Sstevel if (data_valid != IOSRAM_DATA_VALID) {
9531708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
9541708Sstevel goto mboxsc_getmsg_retry;
9551708Sstevel }
9561708Sstevel } else if ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)) {
9571708Sstevel mutex_exit(&mboxsc_lock);
9581708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
9591708Sstevel mutex_enter(&mboxsc_lock);
9601708Sstevel goto mboxsc_getmsg_retry;
9611708Sstevel }
9621708Sstevel
9631708Sstevel /*
9641708Sstevel * If the message is larger than the caller's buffer, provide the caller
9651708Sstevel * with the length of the message and return an error.
9661708Sstevel */
9671708Sstevel datalen = mailboxp->mbox_header.msg_length - MBOXSC_MSGHDR_SIZE;
9681708Sstevel if ((error == 0) && (datalen > *lengthp)) {
9691708Sstevel *lengthp = datalen;
9701708Sstevel error = EMSGSIZE;
9711708Sstevel }
9721708Sstevel
9731708Sstevel /*
9741708Sstevel * Note that there's no need to check STATE_HDRVALID before broadcasting
9751708Sstevel * here because the header is guaranteed to be valid at this point.
9761708Sstevel */
9771708Sstevel if (error != 0) {
9781708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
9791708Sstevel mboxsc_dereference_mailbox(mailboxp);
9801708Sstevel mutex_exit(&mboxsc_lock);
9811708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
9821708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
9831708Sstevel return (error);
9841708Sstevel }
9851708Sstevel
9861708Sstevel /*
9871708Sstevel * Store a copy of the current message header, flag the mailbox to
9881708Sstevel * indicate that it is being read and attempt to read the message data
9891708Sstevel * and checksum.
9901708Sstevel */
9911708Sstevel bcopy(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE);
9921708Sstevel mailboxp->mbox_state |= STATE_READING;
9931708Sstevel mutex_exit(&mboxsc_lock);
9941708Sstevel
9951708Sstevel if (datalen > 0) {
9961708Sstevel error = mboxsc_timed_read(deadline, key, MBOXSC_DATA_OFFSET,
9971708Sstevel datalen, (caddr_t)datap);
9981708Sstevel }
9991708Sstevel
10001708Sstevel if (error == 0) {
10011708Sstevel error = mboxsc_timed_read(deadline, key, header.msg_length,
10021708Sstevel MBOXSC_CHKSUM_SIZE, (caddr_t)&read_checksum);
10031708Sstevel }
10041708Sstevel
10051708Sstevel /*
10061708Sstevel * Check for errors that may have occurred while accessing IOSRAM.
10071708Sstevel */
10081708Sstevel if (error != 0) {
10091708Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE));
10101708Sstevel mutex_enter(&mboxsc_lock);
10111708Sstevel mailboxp->mbox_state &= ~STATE_READING;
10121708Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) {
10131708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
10141708Sstevel }
10151708Sstevel mboxsc_dereference_mailbox(mailboxp);
10161708Sstevel mutex_exit(&mboxsc_lock);
10171708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
10181708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
10191708Sstevel return (error);
10201708Sstevel }
10211708Sstevel
10221708Sstevel /*
10231708Sstevel * Calculate the checksum for the header and data that was read from
10241708Sstevel * IOSRAM.
10251708Sstevel */
10261708Sstevel calc_checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
10271708Sstevel MBOXSC_MSGHDR_SIZE);
10281708Sstevel calc_checksum = mboxsc_checksum(calc_checksum, (uint8_t *)datap,
10291708Sstevel datalen);
10301708Sstevel
10311708Sstevel /*
10321708Sstevel * If the message header has been invalidated, note the change.
10331708Sstevel * If a the checksum verification fails, invalidate the message
10341708Sstevel * header. In either case, go back to the beginning and wait
10351708Sstevel * for a new message.
10361708Sstevel */
10371708Sstevel mutex_enter(&mboxsc_lock);
10381708Sstevel if (!(mailboxp->mbox_state & STATE_HDRVALID)) {
10391708Sstevel error = -1;
10401708Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
10411708Sstevel "mboxsc_getmsg - message invalidated while reading\n");
10421708Sstevel } else if (read_checksum != calc_checksum) {
10431708Sstevel error = -1;
10441708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
10451708Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
10461708Sstevel "mboxsc_getmsg - message failed checksum\n");
10471708Sstevel cmn_err(CE_NOTE,
10481708Sstevel "mboxsc_getmsg - message failed checksum\n");
10491708Sstevel }
10501708Sstevel
10511708Sstevel if (error == -1) {
10521708Sstevel mailboxp->mbox_state &= ~STATE_READING;
10531708Sstevel goto mboxsc_getmsg_retry;
10541708Sstevel }
10551708Sstevel
10561708Sstevel /*
10571708Sstevel * Acquire the hardware lock used for synchronization of data_valid flag
10581708Sstevel * access to avoid race conditions. If it is acquired, try to check the
10591708Sstevel * current data_valid flag and transaction ID to verify that the message
10601708Sstevel * is still valid.
10611708Sstevel */
10621708Sstevel mutex_exit(&mboxsc_lock);
10631708Sstevel
10641708Sstevel if ((error = mboxsc_lock_flags(FALSE, deadline)) != 0) {
10651708Sstevel lock_held = FALSE;
10661708Sstevel /*
10671708Sstevel * We don't "do" EBUSY here, so treat it as EAGAIN.
10681708Sstevel */
10691708Sstevel if (error == EBUSY) {
10701708Sstevel error = EAGAIN;
10711708Sstevel }
10721708Sstevel } else {
10731708Sstevel lock_held = TRUE;
10741708Sstevel }
10751708Sstevel
10761708Sstevel if (error == 0) {
10771708Sstevel error = mboxsc_timed_get_flag(deadline, key, &data_valid, NULL);
10781708Sstevel }
10791708Sstevel
10801708Sstevel if ((error == 0) && (data_valid == IOSRAM_DATA_VALID)) {
10811708Sstevel error = mboxsc_timed_read(deadline, key,
10821708Sstevel FIELD_OFFSET(mboxsc_msghdr_t, msg_transid),
10831708Sstevel FIELD_SIZE(mboxsc_msghdr_t, msg_transid),
10841708Sstevel (caddr_t)&read_transid);
10851708Sstevel }
10861708Sstevel
10871708Sstevel /*
10881708Sstevel * If something failed along the way, either the error is unrecoverable
10891708Sstevel * or we're just plain out of time, so unlock the flags if they were
10901708Sstevel * locked, release the mailbox, wake up other potential readers if
10911708Sstevel * there's still a message around, and return.
10921708Sstevel */
10931708Sstevel if (error != 0) {
10941708Sstevel ASSERT((error != EINVAL) && (error != EMSGSIZE));
10951708Sstevel if (lock_held) {
1096*11311SSurya.Prakki@Sun.COM (void) mboxsc_unlock_flags(TRUE);
10971708Sstevel }
10981708Sstevel mutex_enter(&mboxsc_lock);
10991708Sstevel mailboxp->mbox_state &= ~STATE_READING;
11001708Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) {
11011708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
11021708Sstevel }
11031708Sstevel mboxsc_dereference_mailbox(mailboxp);
11041708Sstevel mutex_exit(&mboxsc_lock);
11051708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
11061708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
11071708Sstevel return (error);
11081708Sstevel }
11091708Sstevel
11101708Sstevel /*
11111708Sstevel * If the data_valid flag isn't set to IOSRAM_DATA_VALID, or the
11121708Sstevel * message transaction ID in IOSRAM has changed, the message being
11131708Sstevel * read was timed out by its sender. Since the data_valid flag can't
11141708Sstevel * change as long as we have the flags locked, we can safely mark the
11151708Sstevel * stored message header invalid if either the data_valid flag isn't set
11161708Sstevel * or the stored transaction ID doesn't match the one we read. (If
11171708Sstevel * data_valid is set, the transaction ID shouldn't be changing
11181708Sstevel * underneath us.) On the other hand, if there may still be a valid
11191708Sstevel * message, wake up any pending readers.
11201708Sstevel */
11211708Sstevel if ((data_valid != IOSRAM_DATA_VALID) ||
11221708Sstevel (read_transid != header.msg_transid)) {
11231708Sstevel mutex_enter(&mboxsc_lock);
11241708Sstevel mailboxp->mbox_state &= ~STATE_READING;
11251708Sstevel if ((data_valid != IOSRAM_DATA_VALID) ||
11261708Sstevel (mailboxp->mbox_header.msg_transid != read_transid)) {
11271708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
11281708Sstevel } else if (mailboxp->mbox_state & STATE_HDRVALID) {
11291708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
11301708Sstevel }
11311708Sstevel
11321708Sstevel /*
11331708Sstevel * Unfortunately, we can't be holding mboxsc_lock when we unlock
11341708Sstevel * the flags. However, we have to hold the flags until here to
11351708Sstevel * make sure the SC doesn't change the message's state while
11361708Sstevel * we're checking to see if we should invalidate our stored
11371708Sstevel * header.
11381708Sstevel */
11391708Sstevel mutex_exit(&mboxsc_lock);
11401708Sstevel error = mboxsc_unlock_flags(TRUE);
11411708Sstevel mutex_enter(&mboxsc_lock);
11421708Sstevel
11431708Sstevel DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
11441708Sstevel "mboxsc_getmsg() - message invalidated by sender\n");
11451708Sstevel goto mboxsc_getmsg_retry;
11461708Sstevel }
11471708Sstevel
11481708Sstevel /*
11491708Sstevel * If everything has worked up to this point, all that remains is
11501708Sstevel * to set the data_valid flag to IOSRAM_DATA_INVALID, tidy up, and
11511708Sstevel * return the message. If the flag can't be set, the message can't
11521708Sstevel * be received, so keep trying as long as there is time.
11531708Sstevel */
11541708Sstevel error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_INVALID,
11551708Sstevel IOSRAM_INT_NONE);
11561708Sstevel
1157*11311SSurya.Prakki@Sun.COM (void) mboxsc_unlock_flags(TRUE);
11581708Sstevel mutex_enter(&mboxsc_lock);
11591708Sstevel
11601708Sstevel if (error != 0) {
11611708Sstevel ASSERT(error != EINVAL);
11621708Sstevel mboxsc_dereference_mailbox(mailboxp);
11631708Sstevel mailboxp->mbox_state &= ~STATE_READING;
11641708Sstevel if (mailboxp->mbox_state & STATE_HDRVALID) {
11651708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
11661708Sstevel }
11671708Sstevel mutex_exit(&mboxsc_lock);
11681708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
11691708Sstevel "mboxsc_getmsg ret: 0x%08x\n", error);
11701708Sstevel return (error);
11711708Sstevel }
11721708Sstevel
11731708Sstevel /*
11741708Sstevel * If the message was read 100% successfully and the stored message
11751708Sstevel * header for the mailbox still matches the message that was read,
11761708Sstevel * invalidate it to prevent other readers from trying to read it.
11771708Sstevel */
11781708Sstevel if (bcmp(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE) == 0) {
11791708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
11801708Sstevel } else if (mailboxp->mbox_state & STATE_HDRVALID) {
11811708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
11821708Sstevel }
11831708Sstevel
11841708Sstevel mboxsc_dereference_mailbox(mailboxp);
11851708Sstevel mailboxp->mbox_state &= ~STATE_READING;
11861708Sstevel mutex_exit(&mboxsc_lock);
11871708Sstevel
11881708Sstevel /*
11891708Sstevel * Since we're successfully returning a message, we need to provide the
11901708Sstevel * caller with all of the interesting header information.
11911708Sstevel */
11921708Sstevel *typep = header.msg_type;
11931708Sstevel *cmdp = header.msg_cmd;
11941708Sstevel *transidp = header.msg_transid;
11951708Sstevel *lengthp = datalen;
11961708Sstevel
11971708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_getmsg ret: 0x%08x\n", 0);
11981708Sstevel return (0);
11991708Sstevel }
12001708Sstevel
12011708Sstevel /*
12021708Sstevel * mboxsc_ctrl
12031708Sstevel *
12041708Sstevel * This routine provides access to a variety of services not available through
12051708Sstevel * the basic API.
12061708Sstevel */
12071708Sstevel int
mboxsc_ctrl(uint32_t key,uint32_t cmd,void * arg)12081708Sstevel mboxsc_ctrl(uint32_t key, uint32_t cmd, void *arg)
12091708Sstevel {
12101708Sstevel int error = 0;
12111708Sstevel mboxsc_mbox_t *mailboxp;
12121708Sstevel
12131708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_ctrl called\n");
12141708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
12151708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
12161708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = %p\n", arg);
12171708Sstevel
12181708Sstevel mutex_enter(&mboxsc_lock);
12191708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
12201708Sstevel if (mailboxp == NULL) {
12211708Sstevel mutex_exit(&mboxsc_lock);
12221708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n",
12231708Sstevel EBADF);
12241708Sstevel return (EBADF);
12251708Sstevel }
12261708Sstevel
12271708Sstevel switch (cmd) {
12281708Sstevel case MBOXSC_CMD_VERSION:
12291708Sstevel /*
12301708Sstevel * Return the Protocol version currently in use. Since
12311708Sstevel * there is only one version that exists right now, we
12321708Sstevel * can't be using anything else.
12331708Sstevel */
12341708Sstevel if (arg == NULL) {
12351708Sstevel error = EINVAL;
12361708Sstevel break;
12371708Sstevel }
12381708Sstevel
12391708Sstevel *(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
12401708Sstevel break;
12411708Sstevel
12421708Sstevel case MBOXSC_CMD_MAXVERSION:
12431708Sstevel /*
12441708Sstevel * Return the highest Protocol version that we support.
12451708Sstevel */
12461708Sstevel if (arg == NULL) {
12471708Sstevel error = EINVAL;
12481708Sstevel break;
12491708Sstevel }
12501708Sstevel
12511708Sstevel *(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
12521708Sstevel break;
12531708Sstevel
12541708Sstevel case MBOXSC_CMD_MAXDATALEN:
12551708Sstevel /*
12561708Sstevel * Return the amount of space available for client data
12571708Sstevel * in the indicated mailbox.
12581708Sstevel */
12591708Sstevel if (arg == NULL) {
12601708Sstevel error = EINVAL;
12611708Sstevel break;
12621708Sstevel }
12631708Sstevel
12641708Sstevel *(uint32_t *)arg = mailboxp->mbox_length -
12651708Sstevel MBOXSC_PROTOCOL_SIZE;
12661708Sstevel break;
12671708Sstevel
12681708Sstevel case MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE:
12691708Sstevel {
12701708Sstevel mboxsc_timeout_range_t *rangep;
12711708Sstevel
12721708Sstevel /*
12731708Sstevel * Return the range of acceptable timeout values for
12741708Sstevel * mboxsc_putmsg, expressed in milliseconds.
12751708Sstevel */
12761708Sstevel if (arg == NULL) {
12771708Sstevel error = EINVAL;
12781708Sstevel break;
12791708Sstevel }
12801708Sstevel
12811708Sstevel rangep = (mboxsc_timeout_range_t *)arg;
12821708Sstevel rangep->min_timeout = MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS;
12831708Sstevel rangep->max_timeout = MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS;
12841708Sstevel break;
12851708Sstevel }
12861708Sstevel
12871708Sstevel case MBOXSC_CMD_GETMSG_TIMEOUT_RANGE:
12881708Sstevel {
12891708Sstevel mboxsc_timeout_range_t *rangep;
12901708Sstevel
12911708Sstevel /*
12921708Sstevel * Return the range of acceptable timeout values for
12931708Sstevel * mboxsc_getmsg, expressed in milliseconds.
12941708Sstevel */
12951708Sstevel if (arg == NULL) {
12961708Sstevel error = EINVAL;
12971708Sstevel break;
12981708Sstevel }
12991708Sstevel
13001708Sstevel rangep = (mboxsc_timeout_range_t *)arg;
13011708Sstevel rangep->min_timeout = MBOXSC_GETMSG_MIN_TIMEOUT_MSECS;
13021708Sstevel rangep->max_timeout = MBOXSC_GETMSG_MAX_TIMEOUT_MSECS;
13031708Sstevel break;
13041708Sstevel }
13051708Sstevel
13061708Sstevel default:
13071708Sstevel error = ENOTSUP;
13081708Sstevel break;
13091708Sstevel }
13101708Sstevel
13111708Sstevel mutex_exit(&mboxsc_lock);
13121708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n", error);
13131708Sstevel return (error);
13141708Sstevel }
13151708Sstevel
13161708Sstevel /*
13171708Sstevel * mboxsc_putmsg_def_timeout
13181708Sstevel *
13191708Sstevel * This routine returns the default mboxsc_putmsg timeout provided for the
13201708Sstevel * convenience of clients.
13211708Sstevel */
13221708Sstevel clock_t
mboxsc_putmsg_def_timeout(void)13231708Sstevel mboxsc_putmsg_def_timeout(void)
13241708Sstevel {
13251708Sstevel return (MBOXSC_PUTMSG_DEF_TIMEOUT_MSECS);
13261708Sstevel }
13271708Sstevel
13281708Sstevel /*
13291708Sstevel * mboxsc_iosram_callback
13301708Sstevel *
13311708Sstevel * This routine is registered with the IOSRAM driver for all inbound mailboxes,
13321708Sstevel * and performs preliminary processing of all new messages.
13331708Sstevel */
13341708Sstevel static void
mboxsc_iosram_callback(void * arg)13351708Sstevel mboxsc_iosram_callback(void *arg)
13361708Sstevel {
13371708Sstevel int error = 0;
13381708Sstevel uint8_t data_valid;
13391708Sstevel uint32_t key = (uint32_t)(uintptr_t)arg;
13401708Sstevel mboxsc_mbox_t *mailboxp;
13411708Sstevel
13421708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_iosram_callback called\n");
13431708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = 0x%x\n", key);
13441708Sstevel
13451708Sstevel mutex_enter(&mboxsc_lock);
13461708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
13471708Sstevel
13481708Sstevel /*
13491708Sstevel * We shouldn't ever receive a callback for a mailbox that doesn't
13501708Sstevel * exist or for an output mailbox.
13511708Sstevel */
13521708Sstevel ASSERT(mailboxp != NULL);
13531708Sstevel ASSERT(mailboxp->mbox_direction == MBOXSC_MBOX_IN);
13541708Sstevel
13551708Sstevel /*
13561708Sstevel * Attempt to read the header of the mailbox. If the IOSRAM returns
13571708Sstevel * EAGAIN, indicating a tunnel switch is in progress, do not retry
13581708Sstevel * the operation.
13591708Sstevel */
13601708Sstevel mailboxp->mbox_state &= ~STATE_HDRVALID;
13611708Sstevel error = iosram_rd(key, MBOXSC_MSGHDR_OFFSET, MBOXSC_MSGHDR_SIZE,
13621708Sstevel (caddr_t)&(mailboxp->mbox_header));
13631708Sstevel
13641708Sstevel /*
13651708Sstevel * If somebody sends us a message using a Mailbox Protocol version
13661708Sstevel * greater than the highest one we understand, ignore the message,
13671708Sstevel * because we can't safely interpret anything beyond the version field.
13681708Sstevel */
13691708Sstevel if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
13701708Sstevel error = -1;
13711708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
13721708Sstevel "incoming message with unsupported version %d\n",
13731708Sstevel mailboxp->mbox_header.msg_version);
13741708Sstevel }
13751708Sstevel
13761708Sstevel /*
13771708Sstevel * If this message is a repeat of a previous message (which should
13781708Sstevel * only happen with reply messages), it is conceivable that a client
13791708Sstevel * already executing in mboxsc_getmsg for the previous message could
13801708Sstevel * end up receiving the new message before this callback gets a chance
13811708Sstevel * to execute. If that happens, the data_valid flag will already have
13821708Sstevel * been cleared. Call iosram_get_flag to see if that is the case, and
13831708Sstevel * do not process the message if it is.
13841708Sstevel */
13851708Sstevel if (error == 0) {
13861708Sstevel error = iosram_get_flag(key, &data_valid, NULL);
13871708Sstevel if ((error == 0) && (data_valid != IOSRAM_DATA_VALID)) {
13881708Sstevel error = -1;
13891708Sstevel }
13901708Sstevel }
13911708Sstevel
13921708Sstevel /*
13931708Sstevel * If the iosram_rd call failed, return.
13941708Sstevel */
13951708Sstevel if (error != 0) {
13961708Sstevel mutex_exit(&mboxsc_lock);
13971708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
13981708Sstevel "mboxsc_iosram_callback ret (0x%08x)\n", error);
13991708Sstevel return;
14001708Sstevel }
14011708Sstevel
14021708Sstevel /*
14031708Sstevel * If the message read from IOSRAM was unsolicited, invoke
14041708Sstevel * its callback. Otherwise, wake all threads that are waiting
14051708Sstevel * in mboxsc_getmsg.
14061708Sstevel */
14071708Sstevel mailboxp->mbox_state |= STATE_HDRVALID;
14081708Sstevel if (IS_UNSOLICITED_TYPE(mailboxp->mbox_header.msg_type) &&
14091708Sstevel (mailboxp->mbox_callback != NULL)) {
14101708Sstevel mboxsc_reference_mailbox(mailboxp);
14111708Sstevel mutex_exit(&mboxsc_lock);
14121708Sstevel (*(mailboxp->mbox_callback))();
14131708Sstevel mutex_enter(&mboxsc_lock);
14141708Sstevel mboxsc_dereference_mailbox(mailboxp);
14151708Sstevel } else {
14161708Sstevel cv_broadcast(&(mailboxp->mbox_wait));
14171708Sstevel }
14181708Sstevel
14191708Sstevel mutex_exit(&mboxsc_lock);
14201708Sstevel
14211708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_iosram_callback ret\n");
14221708Sstevel }
14231708Sstevel
14241708Sstevel /*
14251708Sstevel * mboxsc_hdrchange_callback
14261708Sstevel *
14271708Sstevel * This routine is registered with the IOSRAM driver to react to any changes SMS
14281708Sstevel * makes to the IOSRAM header.
14291708Sstevel */
14301708Sstevel static void
mboxsc_hdrchange_callback(void)14311708Sstevel mboxsc_hdrchange_callback(void)
14321708Sstevel {
14331708Sstevel int error;
14341708Sstevel uint32_t sms_version;
14351708Sstevel
14361708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
14371708Sstevel "mboxsc_hdrchange_callback called\n");
14381708Sstevel
14391708Sstevel error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
14401708Sstevel (void *)&sms_version);
14411708Sstevel if (error == 0) {
14421708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
14431708Sstevel "sms mailbox version = %d\n", sms_version);
14441708Sstevel mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION,
14451708Sstevel sms_version);
14461708Sstevel }
14471708Sstevel
14481708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_hdrchange_callback ret\n");
14491708Sstevel }
14501708Sstevel
14511708Sstevel
14521708Sstevel /*
14531708Sstevel * mboxsc_add_mailbox
14541708Sstevel *
14551708Sstevel * If no other mailbox exists with the same key as this mailbox, attempt to
14561708Sstevel * retrieve its length from the IOSRAM driver and register the mboxsc callback
14571708Sstevel * for the associated IOSRAM chunk. If successful, initialize the
14581708Sstevel * non-client-supplied mailbox fields and insert it into the hash table.
14591708Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
14601708Sstevel */
14611708Sstevel static int
mboxsc_add_mailbox(mboxsc_mbox_t * mailboxp)14621708Sstevel mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp)
14631708Sstevel {
14641708Sstevel int error = 0;
14651708Sstevel uint32_t key = mailboxp->mbox_key;
14661708Sstevel
14671708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_add_mailbox called\n");
1468*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
14691708Sstevel
14701708Sstevel /*
14711708Sstevel * The global lock must be held by the caller.
14721708Sstevel */
14731708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
14741708Sstevel
14751708Sstevel /*
14761708Sstevel * Don't create the mailbox if it already exists.
14771708Sstevel */
14781708Sstevel if (mboxsc_hashfind_mailbox_by_key(key) != NULL) {
14791708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
14801708Sstevel "mboxsc_add_mailbox ret: 0x%08x\n", EEXIST);
14811708Sstevel return (EEXIST);
14821708Sstevel }
14831708Sstevel
14841708Sstevel /*
14851708Sstevel * Obtain the mailbox length and register the mboxsc callback with the
14861708Sstevel * IOSRAM driver. If either call to the IOSRAM driver fails, or the
14871708Sstevel * chunk is too small to be used as a mailbox, return an error to the
14881708Sstevel * caller.
14891708Sstevel */
14901708Sstevel error = iosram_ctrl(key, IOSRAM_CMD_CHUNKLEN, &(mailboxp->mbox_length));
14911708Sstevel
14921708Sstevel if ((error == 0) && (mailboxp->mbox_length < MBOXSC_PROTOCOL_SIZE)) {
14931708Sstevel error = EFAULT;
14941708Sstevel }
14951708Sstevel
14961708Sstevel if ((error == 0) && (mailboxp->mbox_direction == MBOXSC_MBOX_IN)) {
14971708Sstevel error = iosram_register(key, mboxsc_iosram_callback,
14981708Sstevel (void *)(uintptr_t)(key));
14991708Sstevel if (error == EBUSY) {
15001708Sstevel error = EFAULT;
15011708Sstevel }
15021708Sstevel }
15031708Sstevel
15041708Sstevel if (error != 0) {
15051708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
15061708Sstevel "mboxsc_add_mailbox ret: 0x%08x\n", error);
15071708Sstevel return (error);
15081708Sstevel }
15091708Sstevel
15101708Sstevel /*
15111708Sstevel * Initialize remaining mailbox fields and insert mailbox into
15121708Sstevel * hash table.
15131708Sstevel */
15141708Sstevel mailboxp->mbox_state = STATE_IDLE;
15151708Sstevel mailboxp->mbox_refcount = 0;
15161708Sstevel cv_init(&(mailboxp->mbox_wait), NULL, CV_DRIVER, NULL);
15171708Sstevel mboxsc_hashinsert_mailbox(mailboxp);
15181708Sstevel
15191708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_add_mailbox ret: 0x%08x\n",
15201708Sstevel 0);
15211708Sstevel return (0);
15221708Sstevel }
15231708Sstevel
15241708Sstevel /*
15251708Sstevel * mboxsc_close_mailbox
15261708Sstevel *
15271708Sstevel * Remove a mailbox from the hash table, unregister its IOSRAM callback, and
15281708Sstevel * deallocate its resources.
15291708Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
15301708Sstevel */
15311708Sstevel static void
mboxsc_close_mailbox(mboxsc_mbox_t * mailboxp)15321708Sstevel mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp)
15331708Sstevel {
15341708Sstevel int error = 0;
15351708Sstevel uint32_t key = mailboxp->mbox_key;
15361708Sstevel
15371708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_close_mailbox called\n");
1538*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
15391708Sstevel
15401708Sstevel /*
15411708Sstevel * The global lock must be held by the caller.
15421708Sstevel */
15431708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
15441708Sstevel
15451708Sstevel /*
15461708Sstevel * Unregister the mboxsc callback for this particular mailbox.
15471708Sstevel */
15481708Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
15491708Sstevel error = iosram_unregister(key);
15501708Sstevel if (error == EINVAL) {
15511708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "invalid key (0x%08x)"
15521708Sstevel " reported in mboxsc_close_mailbox.\n", key);
15531708Sstevel error = 0;
15541708Sstevel }
15551708Sstevel }
15561708Sstevel
15571708Sstevel /*
15581708Sstevel * Remove the mailbox from the hash table and deallocate its resources.
15591708Sstevel */
1560*11311SSurya.Prakki@Sun.COM (void) mboxsc_hashremove_mailbox_by_key(key);
15611708Sstevel cv_destroy(&(mailboxp->mbox_wait));
1562*11311SSurya.Prakki@Sun.COM DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %lu)\n",
1563*11311SSurya.Prakki@Sun.COM (void *)mailboxp, sizeof (mboxsc_mbox_t));
15641708Sstevel kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
15651708Sstevel
15661708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_close_mailbox ret\n");
15671708Sstevel }
15681708Sstevel
15691708Sstevel /*
15701708Sstevel * mboxsc_hashinsert_mailbox
15711708Sstevel *
15721708Sstevel * Insert a fully initialized mailbox into the hash table. No duplicate
15731708Sstevel * checking is performed at this point, so the caller is responsible for
15741708Sstevel * duplicate prevention if it is desired.
15751708Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
15761708Sstevel */
15771708Sstevel static void
mboxsc_hashinsert_mailbox(mboxsc_mbox_t * mailboxp)15781708Sstevel mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp)
15791708Sstevel {
15801708Sstevel uint32_t hash;
15811708Sstevel
15821708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
15831708Sstevel "mboxsc_hashinsert_mailbox called\n");
1584*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", (void *)mailboxp);
15851708Sstevel
15861708Sstevel /*
15871708Sstevel * The global lock must be held by the caller.
15881708Sstevel */
15891708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
15901708Sstevel
15911708Sstevel hash = HASH_KEY(mailboxp->mbox_key);
15921708Sstevel mailboxp->mbox_hash_next = mboxsc_hash_table[hash];
15931708Sstevel mboxsc_hash_table[hash] = mailboxp;
15941708Sstevel
15951708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
15961708Sstevel "mboxsc_hashinsert_mailbox ret\n");
15971708Sstevel }
15981708Sstevel
15991708Sstevel /*
16001708Sstevel * mboxsc_hashfind_mailbox_by_key
16011708Sstevel *
16021708Sstevel * Locate a mailbox with the given key in the hash table. Return a pointer
16031708Sstevel * to the mailbox if it exists, or NULL if no matching mailbox is found.
16041708Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
16051708Sstevel */
16061708Sstevel static mboxsc_mbox_t *
mboxsc_hashfind_mailbox_by_key(uint32_t key)16071708Sstevel mboxsc_hashfind_mailbox_by_key(uint32_t key)
16081708Sstevel {
16091708Sstevel uint32_t hash;
16101708Sstevel mboxsc_mbox_t *mailboxp;
16111708Sstevel
16121708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
16131708Sstevel "mboxsc_hashfind_mailbox_by_key called\n");
16141708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
16151708Sstevel
16161708Sstevel /*
16171708Sstevel * The global lock must be held by the caller.
16181708Sstevel */
16191708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
16201708Sstevel
16211708Sstevel hash = HASH_KEY(key);
16221708Sstevel mailboxp = mboxsc_hash_table[hash];
16231708Sstevel while (mailboxp != NULL) {
16241708Sstevel if (mailboxp->mbox_key == key) {
16251708Sstevel break;
16261708Sstevel }
16271708Sstevel mailboxp = mailboxp->mbox_hash_next;
16281708Sstevel }
16291708Sstevel
16301708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1631*11311SSurya.Prakki@Sun.COM "mboxsc_hashfind_mailbox_by_key ret: %p\n", (void *)mailboxp);
16321708Sstevel return (mailboxp);
16331708Sstevel }
16341708Sstevel
16351708Sstevel /*
16361708Sstevel * mboxsc_hashremove_mailbox_by_key
16371708Sstevel *
16381708Sstevel * Locate a mailbox with the given key in the hash table. If it exists,
16391708Sstevel * remove it from the hash table and return a pointer to it. Otherwise,
16401708Sstevel * return NULL.
16411708Sstevel * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
16421708Sstevel */
16431708Sstevel static mboxsc_mbox_t *
mboxsc_hashremove_mailbox_by_key(uint32_t key)16441708Sstevel mboxsc_hashremove_mailbox_by_key(uint32_t key)
16451708Sstevel {
16461708Sstevel uint32_t hash;
16471708Sstevel mboxsc_mbox_t *mailboxp;
16481708Sstevel mboxsc_mbox_t *last;
16491708Sstevel
16501708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
16511708Sstevel "mboxsc_hashremove_mailbox_by_key called\n");
16521708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
16531708Sstevel
16541708Sstevel /*
16551708Sstevel * The global lock must be held by the caller.
16561708Sstevel */
16571708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
16581708Sstevel
16591708Sstevel hash = HASH_KEY(key);
16601708Sstevel mailboxp = mboxsc_hash_table[hash];
16611708Sstevel last = NULL;
16621708Sstevel while (mailboxp != NULL) {
16631708Sstevel if (mailboxp->mbox_key == key) {
16641708Sstevel break;
16651708Sstevel }
16661708Sstevel last = mailboxp;
16671708Sstevel mailboxp = mailboxp->mbox_hash_next;
16681708Sstevel }
16691708Sstevel
16701708Sstevel /*
16711708Sstevel * If a mailbox was found, remove it from the hash table.
16721708Sstevel */
16731708Sstevel if (mailboxp != NULL) {
16741708Sstevel if (last == NULL) {
16751708Sstevel mboxsc_hash_table[hash] = mailboxp->mbox_hash_next;
16761708Sstevel } else {
16771708Sstevel last->mbox_hash_next = mailboxp->mbox_hash_next;
16781708Sstevel }
16791708Sstevel
16801708Sstevel mailboxp->mbox_hash_next = NULL;
16811708Sstevel }
16821708Sstevel
16831708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1684*11311SSurya.Prakki@Sun.COM "mboxsc_hashremove_mailbox_by_key ret: %p\n", (void *)mailboxp);
16851708Sstevel return (mailboxp);
16861708Sstevel }
16871708Sstevel
16881708Sstevel /*
16891708Sstevel * mboxsc_checksum
16901708Sstevel *
16911708Sstevel * Given a pointer to a data buffer and its length, calculate the checksum of
16921708Sstevel * the data contained therein.
16931708Sstevel */
16941708Sstevel static mboxsc_chksum_t
mboxsc_checksum(mboxsc_chksum_t seed,uint8_t * buf,uint32_t length)16951708Sstevel mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf, uint32_t length)
16961708Sstevel {
16971708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_checksum called\n");
16981708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "seed = 0x%x\n", seed);
1699*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "buf = %p\n", (void *)buf);
17001708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
17011708Sstevel
17021708Sstevel while (length-- > 0) {
17031708Sstevel seed += *(buf++);
17041708Sstevel }
17051708Sstevel
17061708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_checksum ret: 0x%08x\n",
17071708Sstevel seed);
17081708Sstevel return (seed);
17091708Sstevel }
17101708Sstevel
17111708Sstevel /*
17121708Sstevel * mboxsc_lock_flags
17131708Sstevel *
17141708Sstevel * Acquire the hardware lock used for data_valid flag synchronization. If the
17151708Sstevel * lock is currently held by SMS and acquisition is mandatory, just keep on
17161708Sstevel * trying until it is acquired. If acquisition is not mandatory, keep trying
17171708Sstevel * until the given deadline has been reached. To avoid loading the system
17181708Sstevel * unreasonably on EBUSY or EAGAIN, sleep for an appropriate amount of time
17191708Sstevel * before retrying. If a hardware error is encountered return it to the caller.
17201708Sstevel *
17211708Sstevel * If the lock is held, but not by SMS, clear it and acquire it. Nobody
17221708Sstevel * else should be grabbing that lock.
17231708Sstevel */
17241708Sstevel static int
mboxsc_lock_flags(uint8_t mandatory,clock_t deadline)17251708Sstevel mboxsc_lock_flags(uint8_t mandatory, clock_t deadline)
17261708Sstevel {
17271708Sstevel int error;
17281708Sstevel int warned = 0;
17291708Sstevel uint32_t sema;
17301708Sstevel clock_t pause;
17311708Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
17321708Sstevel
17331708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_lock_flags called\n");
17341708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
1735*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
17361708Sstevel
17371708Sstevel /*
17381708Sstevel * Keep trying to acquire the lock until successful or (if acquisition
17391708Sstevel * is not mandatory) time runs out. If EBUSY (lock is already held) or
17401708Sstevel * EAGAIN (tunnel switch in progress) is encountered, sleep for an
17411708Sstevel * appropriate amount of time before retrying. Any other error is
17421708Sstevel * unrecoverable.
17431708Sstevel */
17441708Sstevel do {
17451708Sstevel pause = 0;
17461708Sstevel
17471708Sstevel /*
17481708Sstevel * Since multiple threads could conceivably want the flag lock
17491708Sstevel * at the same time, we place the lock under a mutex and keep a
17501708Sstevel * counter indicating how many threads have the flags locked at
17511708Sstevel * the moment.
17521708Sstevel */
17531708Sstevel mutex_enter(&mboxsc_lock);
17541708Sstevel if ((mboxsc_flaglock_count > 0) ||
17551708Sstevel ((error = iosram_sema_acquire(&sema)) == 0)) {
17561708Sstevel mboxsc_flaglock_count++;
17571708Sstevel mutex_exit(&mboxsc_lock);
17581708Sstevel
17591708Sstevel if (warned) {
17601708Sstevel cmn_err(CE_WARN, "Flags locked");
17611708Sstevel }
17621708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
17631708Sstevel "mboxsc_lock_flags ret: 0\n");
17641708Sstevel return (0);
17651708Sstevel }
17661708Sstevel
17671708Sstevel /*
17681708Sstevel * If iosram_sema_acquire returned EBUSY (lock already held),
17691708Sstevel * make sure the lock is held by SMS, since nobody else should
17701708Sstevel * ever be holding it. If EBUSY or EAGAIN (tunnel switch in
17711708Sstevel * progress) was returned, determine the appropriate amount of
17721708Sstevel * time to sleep before trying again.
17731708Sstevel */
17741708Sstevel if (error == EBUSY) {
17751708Sstevel if (IOSRAM_SEMA_GET_IDX(sema) != IOSRAM_SEMA_SMS_IDX) {
1776*11311SSurya.Prakki@Sun.COM (void) iosram_sema_release();
17771708Sstevel cmn_err(CE_WARN,
17781708Sstevel "Incorrect flag lock value read (0x%08x)",
17791708Sstevel sema);
17801708Sstevel } else {
17811708Sstevel pause = (mandatory ? HWLOCK_POLL :
17821708Sstevel MIN(HWLOCK_POLL, deadline -
17831708Sstevel ddi_get_lbolt()));
17841708Sstevel }
17851708Sstevel } else if (error == EAGAIN) {
17861708Sstevel pause = (mandatory ? EAGAIN_POLL : MIN(EAGAIN_POLL,
17871708Sstevel deadline - ddi_get_lbolt()));
17881708Sstevel }
17891708Sstevel
17901708Sstevel /*
17911708Sstevel * We had to hold the lock until now to protect the potential
17921708Sstevel * iosram_sema_release call above.
17931708Sstevel */
17941708Sstevel mutex_exit(&mboxsc_lock);
17951708Sstevel
17961708Sstevel /*
17971708Sstevel * If EAGAIN or EBUSY was encountered, we're looping.
17981708Sstevel */
17991708Sstevel if ((error == EAGAIN) || (error == EBUSY)) {
18001708Sstevel /*
18011708Sstevel * If we've been looping here for a while, something is
18021708Sstevel * probably wrong, so we should generated a warning.
18031708Sstevel */
18041708Sstevel if (warning_time - ddi_get_lbolt() <= 0) {
18051708Sstevel if (!warned) {
18061708Sstevel warned = 1;
18071708Sstevel cmn_err(CE_WARN,
18081708Sstevel "Unable to lock flags (0x%08x)",
18091708Sstevel error);
18101708Sstevel } else {
18111708Sstevel cmn_err(CE_WARN,
18121708Sstevel "Still unable to lock flags");
18131708Sstevel }
18141708Sstevel warning_time = ddi_get_lbolt() +
18151708Sstevel LOOP_WARN_INTERVAL;
18161708Sstevel }
18171708Sstevel
18181708Sstevel /*
18191708Sstevel * Sleep a while before trying again.
18201708Sstevel */
18211708Sstevel delay(pause);
18221708Sstevel }
18231708Sstevel } while (((error == EAGAIN) || (error == EBUSY)) &&
18241708Sstevel (mandatory || (deadline - ddi_get_lbolt() >= 0)));
18251708Sstevel
18261708Sstevel /*
18271708Sstevel * If something really bad has happened, generate a warning.
18281708Sstevel */
18291708Sstevel if ((error != EAGAIN) && (error != EBUSY)) {
18301708Sstevel cmn_err(CE_WARN, "Flag locking failed! (%d)", error);
18311708Sstevel }
18321708Sstevel
18331708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_lock_flags ret: 0x%08x\n",
18341708Sstevel error);
18351708Sstevel return (error);
18361708Sstevel }
18371708Sstevel
18381708Sstevel /*
18391708Sstevel * mboxsc_unlock_flags
18401708Sstevel *
18411708Sstevel * Release the hardware lock used for data_valid flag synchronization.
18421708Sstevel * If a hardware error is encountered, return it to the caller. If the
18431708Sstevel * mandatory flag is set, loop and retry if EAGAIN is encountered.
18441708Sstevel */
18451708Sstevel static int
mboxsc_unlock_flags(uint8_t mandatory)18461708Sstevel mboxsc_unlock_flags(uint8_t mandatory)
18471708Sstevel {
18481708Sstevel int error;
18491708Sstevel int warned = 0;
18501708Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
18511708Sstevel
18521708Sstevel ASSERT(mboxsc_flaglock_count != 0);
18531708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_unlock_flags called\n");
18541708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
18551708Sstevel
18561708Sstevel do {
18571708Sstevel /*
18581708Sstevel * Since multiple threads could conceivably want the flag lock
18591708Sstevel * at the same time, we place the lock under a mutex and keep a
18601708Sstevel * counter indicating how many threads have the flags locked at
18611708Sstevel * the moment.
18621708Sstevel */
18631708Sstevel mutex_enter(&mboxsc_lock);
18641708Sstevel if ((mboxsc_flaglock_count > 1) ||
18651708Sstevel ((error = iosram_sema_release()) == 0)) {
18661708Sstevel mboxsc_flaglock_count--;
18671708Sstevel mutex_exit(&mboxsc_lock);
18681708Sstevel
18691708Sstevel if (warned) {
18701708Sstevel cmn_err(CE_WARN, "Flags unlocked");
18711708Sstevel }
18721708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
18731708Sstevel "mboxsc_unlock_flags ret: 0\n");
18741708Sstevel return (0);
18751708Sstevel }
18761708Sstevel mutex_exit(&mboxsc_lock);
18771708Sstevel
18781708Sstevel /*
18791708Sstevel * If iosram_sema_release returned EAGAIN (tunnel switch in
18801708Sstevel * progress) and unlocking the flags is mandatory, sleep before
18811708Sstevel * trying again. If we've been trying for a while, display a
18821708Sstevel * warning message too.
18831708Sstevel */
18841708Sstevel if ((error == EAGAIN) && mandatory) {
18851708Sstevel if (warning_time - ddi_get_lbolt() <= 0) {
18861708Sstevel if (!warned) {
18871708Sstevel warned = 1;
18881708Sstevel cmn_err(CE_WARN, "Unable to unlock "
18891708Sstevel "flags (iosram EAGAIN)");
18901708Sstevel } else {
18911708Sstevel cmn_err(CE_WARN,
18921708Sstevel "Still unable to unlock flags");
18931708Sstevel }
18941708Sstevel warning_time = ddi_get_lbolt() +
18951708Sstevel LOOP_WARN_INTERVAL;
18961708Sstevel }
18971708Sstevel
18981708Sstevel delay(EAGAIN_POLL);
18991708Sstevel }
19001708Sstevel } while ((error == EAGAIN) && mandatory);
19011708Sstevel
19021708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_unlock_flags ret: 0x%08x\n",
19031708Sstevel error);
19041708Sstevel return (error);
19051708Sstevel }
19061708Sstevel
19071708Sstevel /*
19081708Sstevel * mboxsc_timed_read
19091708Sstevel *
19101708Sstevel * This function is just a wrapper around iosram_rd that will keep sleeping
19111708Sstevel * and retrying, up to a given deadline, if iosram_rd returns EAGAIN
19121708Sstevel * (presumably due to a tunnel switch).
19131708Sstevel */
19141708Sstevel static int
mboxsc_timed_read(clock_t deadline,uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)19151708Sstevel mboxsc_timed_read(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
19161708Sstevel caddr_t dptr)
19171708Sstevel {
19181708Sstevel int error;
19191708Sstevel
19201708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_read called\n");
1921*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
19221708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
19231708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
19241708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
1925*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr);
19261708Sstevel
19271708Sstevel do {
19281708Sstevel error = iosram_rd(key, off, len, dptr);
19291708Sstevel if (error == EAGAIN) {
19301708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
19311708Sstevel }
19321708Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
19331708Sstevel
19341708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
19351708Sstevel "mboxsc_timed_read ret: 0x%08x\n", error);
19361708Sstevel return (error);
19371708Sstevel }
19381708Sstevel
19391708Sstevel /*
19401708Sstevel * mboxsc_timed_write
19411708Sstevel *
19421708Sstevel * This function is just a wrapper around iosram_wr that will keep sleeping
19431708Sstevel * and retrying, up to a given deadline, if iosram_wr returns EAGAIN
19441708Sstevel * (presumably due to a tunnel switch).
19451708Sstevel */
19461708Sstevel static int
mboxsc_timed_write(clock_t deadline,uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)19471708Sstevel mboxsc_timed_write(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
19481708Sstevel caddr_t dptr)
19491708Sstevel {
19501708Sstevel int error;
19511708Sstevel
19521708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_write called\n");
1953*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
19541708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
19551708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
19561708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
1957*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", (void *)dptr);
19581708Sstevel
19591708Sstevel do {
19601708Sstevel error = iosram_wr(key, off, len, dptr);
19611708Sstevel if (error == EAGAIN) {
19621708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
19631708Sstevel }
19641708Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
19651708Sstevel
19661708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
19671708Sstevel "mboxsc_timed_write ret: 0x%08x\n", error);
19681708Sstevel return (error);
19691708Sstevel }
19701708Sstevel
19711708Sstevel /*
19721708Sstevel * mboxsc_timed_get_flag
19731708Sstevel *
19741708Sstevel * This function is just a wrapper around iosram_get_flag that will keep
19751708Sstevel * sleeping and retrying, up to a given deadline, if iosram_get_flag returns
19761708Sstevel * EAGAIN (presumably due to a tunnel switch).
19771708Sstevel */
19781708Sstevel static int
mboxsc_timed_get_flag(clock_t deadline,uint32_t key,uint8_t * data_validp,uint8_t * int_pendingp)19791708Sstevel mboxsc_timed_get_flag(clock_t deadline, uint32_t key, uint8_t *data_validp,
19801708Sstevel uint8_t *int_pendingp)
19811708Sstevel {
19821708Sstevel int error;
19831708Sstevel
19841708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_get_flag called\n");
1985*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
19861708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1987*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_validp = %p\n",
1988*11311SSurya.Prakki@Sun.COM (void *)data_validp);
1989*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pendingp = %p\n",
1990*11311SSurya.Prakki@Sun.COM (void *)int_pendingp);
19911708Sstevel
19921708Sstevel do {
19931708Sstevel error = iosram_get_flag(key, data_validp, int_pendingp);
19941708Sstevel if (error == EAGAIN) {
19951708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
19961708Sstevel }
19971708Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
19981708Sstevel
19991708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
20001708Sstevel "mboxsc_timed_get_flag ret: 0x%08x\n", error);
20011708Sstevel return (error);
20021708Sstevel }
20031708Sstevel
20041708Sstevel /*
20051708Sstevel * mboxsc_timed_set_flag
20061708Sstevel *
20071708Sstevel * This function is just a wrapper around iosram_set_flag that will keep
20081708Sstevel * sleeping and retrying, up to a given deadline, if iosram_set_flag returns
20091708Sstevel * EAGAIN (presumably due to a tunnel switch).
20101708Sstevel */
20111708Sstevel static int
mboxsc_timed_set_flag(clock_t deadline,uint32_t key,uint8_t data_valid,uint8_t int_pending)20121708Sstevel mboxsc_timed_set_flag(clock_t deadline, uint32_t key, uint8_t data_valid,
20131708Sstevel uint8_t int_pending)
20141708Sstevel {
20151708Sstevel int error;
20161708Sstevel
20171708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_set_flag called\n");
2018*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
20191708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
20201708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_valid = %d\n", data_valid);
20211708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pending = %d\n", int_pending);
20221708Sstevel
20231708Sstevel do {
20241708Sstevel error = iosram_set_flag(key, data_valid, int_pending);
20251708Sstevel if (error == EAGAIN) {
20261708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
20271708Sstevel }
20281708Sstevel } while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
20291708Sstevel
20301708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
20311708Sstevel "mboxsc_timed_set_flag ret: 0x%08x\n", error);
20321708Sstevel return (error);
20331708Sstevel }
20341708Sstevel
20351708Sstevel /*
20361708Sstevel * mboxsc_timed_send_intr
20371708Sstevel *
20381708Sstevel * This function is just a wrapper around iosram_send_intr that will keep
20391708Sstevel * sleeping and retrying, up to a given deadline, if iosram_send_intr returns
20401708Sstevel * EAGAIN (presumably due to a tunnel switch).
20411708Sstevel */
20421708Sstevel static int
mboxsc_timed_send_intr(clock_t deadline)20431708Sstevel mboxsc_timed_send_intr(clock_t deadline)
20441708Sstevel {
20451708Sstevel int error;
20461708Sstevel
20471708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_send_intr called\n");
2048*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%lx\n", deadline);
20491708Sstevel
20501708Sstevel do {
20511708Sstevel error = iosram_send_intr();
20521708Sstevel if (error == DDI_FAILURE) {
20531708Sstevel delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
20541708Sstevel }
20551708Sstevel } while ((error == DDI_FAILURE) && (deadline - ddi_get_lbolt() >= 0));
20561708Sstevel
20571708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
20581708Sstevel "mboxsc_timed_send_intr ret: 0x%08x\n", error);
20591708Sstevel return (error);
20601708Sstevel }
20611708Sstevel
20621708Sstevel /*
20631708Sstevel * mboxsc_expire_message
20641708Sstevel *
20651708Sstevel * This function is called by mboxsc_putmsg to handle expiration of messages
20661708Sstevel * that weren't picked up before they timed out. It will not return until the
20671708Sstevel * message has been picked up (which isn't expected), the message has been
20681708Sstevel * successfully expired, or a serious error has been encountered. If the
20691708Sstevel * message is finally picked up, it will set the value pointed to by "resultp"
20701708Sstevel * to 0. Unlike other sections of code, this function will never time out on
20711708Sstevel * EAGAIN from the iosram driver, since it is important that both sides of the
20721708Sstevel * IOSRAM agree on whether or not a message was delivered successfully.
20731708Sstevel */
20741708Sstevel static int
mboxsc_expire_message(uint32_t key,int * resultp)20751708Sstevel mboxsc_expire_message(uint32_t key, int *resultp)
20761708Sstevel {
20771708Sstevel int error = 0;
20781708Sstevel int lock_held = 0;
20791708Sstevel int warned = 0;
20801708Sstevel uint8_t data_valid;
20811708Sstevel clock_t warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
20821708Sstevel
20831708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_expire_message called\n");
20841708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
2085*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "resultp = %p\n", (void *)resultp);
20861708Sstevel
20871708Sstevel do {
20881708Sstevel error = 0;
20891708Sstevel
20901708Sstevel /*
20911708Sstevel * Lock the flags if they aren't locked already.
20921708Sstevel */
20931708Sstevel if (!lock_held) {
20941708Sstevel error = mboxsc_lock_flags(TRUE, 0);
20951708Sstevel if (error == 0) {
20961708Sstevel lock_held = 1;
20971708Sstevel }
20981708Sstevel }
20991708Sstevel
21001708Sstevel /*
21011708Sstevel * If the flags were locked successfully, reread the data-valid
21021708Sstevel * flag.
21031708Sstevel */
21041708Sstevel if (error == 0) {
21051708Sstevel error = iosram_get_flag(key, &data_valid, NULL);
21061708Sstevel }
21071708Sstevel
21081708Sstevel /*
21091708Sstevel * If the data-valid flag was read successfully, see if it has
21101708Sstevel * been cleared or not, as the other side may have finally read
21111708Sstevel * the message.
21121708Sstevel */
21131708Sstevel if (error == 0) {
21141708Sstevel if (data_valid == IOSRAM_DATA_INVALID) {
21151708Sstevel /*
21161708Sstevel * Surprise! The SC finally picked up the
21171708Sstevel * message, so delivery succeeded after all.
21181708Sstevel */
21191708Sstevel if (*resultp == ETIMEDOUT) {
21201708Sstevel *resultp = 0;
21211708Sstevel }
21221708Sstevel } else {
21231708Sstevel /*
21241708Sstevel * The message still hasn't been read, so try to
21251708Sstevel * clear the data-valid flag.
21261708Sstevel */
21271708Sstevel error = iosram_set_flag(key,
21281708Sstevel IOSRAM_DATA_INVALID, IOSRAM_INT_NONE);
21291708Sstevel }
21301708Sstevel }
21311708Sstevel
21321708Sstevel /*
21331708Sstevel * If the flags were locked, unlock them, no matter what else
21341708Sstevel * has or has not succeeded. Don't overwrite the existing value
21351708Sstevel * of "error" unless no errors other than EAGAIN have been
21361708Sstevel * encountered previously. If we hit EAGAIN at some point,
21371708Sstevel * unlocking the flags here is optional. In all other cases, it
21381708Sstevel * is mandatory.
21391708Sstevel */
21401708Sstevel if (lock_held) {
21411708Sstevel int unlock_err;
21421708Sstevel
21431708Sstevel if (error == EAGAIN) {
21441708Sstevel unlock_err = mboxsc_unlock_flags(FALSE);
21451708Sstevel } else {
21461708Sstevel unlock_err = mboxsc_unlock_flags(TRUE);
21471708Sstevel }
21481708Sstevel
21491708Sstevel if (unlock_err == 0) {
21501708Sstevel lock_held = 0;
21511708Sstevel } else if ((error == 0) || (error == EAGAIN)) {
21521708Sstevel error = unlock_err;
21531708Sstevel }
21541708Sstevel }
21551708Sstevel
21561708Sstevel /*
21571708Sstevel * Did we hit a tunnel switch? (iosram driver returns EAGAIN)
21581708Sstevel * If so, sleep for a while before trying the whole process
21591708Sstevel * again.
21601708Sstevel */
21611708Sstevel if (error == EAGAIN) {
21621708Sstevel /*
21631708Sstevel * If we've been stuck in this loop for a while,
21641708Sstevel * something is probably wrong, and we should display a
21651708Sstevel * warning.
21661708Sstevel */
21671708Sstevel if (warning_time - ddi_get_lbolt() <= 0) {
21681708Sstevel if (!warned) {
21691708Sstevel warned = 1;
21701708Sstevel cmn_err(CE_WARN, "Unable to clear flag "
21711708Sstevel "(iosram EAGAIN)");
21721708Sstevel } else {
21731708Sstevel cmn_err(CE_WARN,
21741708Sstevel "Still unable to clear flag");
21751708Sstevel }
21761708Sstevel warning_time = ddi_get_lbolt() +
21771708Sstevel LOOP_WARN_INTERVAL;
21781708Sstevel }
21791708Sstevel
21801708Sstevel delay(EAGAIN_POLL);
21811708Sstevel }
21821708Sstevel } while (error == EAGAIN);
21831708Sstevel
21841708Sstevel /*
21851708Sstevel * If the data-valid flag was not successfully cleared due to some sort
21861708Sstevel * of problem, report it. Otherwise, if we looped for a while on EAGAIN
21871708Sstevel * and generated a warning about it, indicate that everything is okay
21881708Sstevel * now.
21891708Sstevel */
21901708Sstevel if (error != 0) {
21911708Sstevel cmn_err(CE_WARN, "Message expiration failure! (%d)", error);
21921708Sstevel } else if (warned) {
21931708Sstevel cmn_err(CE_WARN, "Flag cleared");
21941708Sstevel }
21951708Sstevel
21961708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
21971708Sstevel "mboxsc_expire_message ret: 0x%08x\n", error);
21981708Sstevel return (error);
21991708Sstevel }
22001708Sstevel
22011708Sstevel
22021708Sstevel /*
22031708Sstevel * mboxsc_generate_transid
22041708Sstevel *
22051708Sstevel * This function generates unique transaction IDs using an incrementing counter.
22061708Sstevel * The value generated is guaranteed not to be the same as the prev_transid
22071708Sstevel * value passed in by the caller.
22081708Sstevel */
22091708Sstevel static uint64_t
mboxsc_generate_transid(uint64_t prev_transid)22101708Sstevel mboxsc_generate_transid(uint64_t prev_transid)
22111708Sstevel {
22121708Sstevel uint64_t new_transid;
22131708Sstevel static uint64_t transid_counter = 0;
22141708Sstevel
22151708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_generate_transid called");
2216*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "prev_transid = 0x%016lx\n",
22171708Sstevel prev_transid);
22181708Sstevel
22191708Sstevel do {
22201708Sstevel new_transid = TRANSID_GEN_MASK | transid_counter++;
22211708Sstevel if (transid_counter & TRANSID_GEN_MASK) {
22221708Sstevel transid_counter = 0;
22231708Sstevel }
22241708Sstevel } while (new_transid == prev_transid);
22251708Sstevel
22261708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2227*11311SSurya.Prakki@Sun.COM "mboxsc_generate_transid ret: 0x%016lx", new_transid);
22281708Sstevel return (new_transid);
22291708Sstevel }
22301708Sstevel
22311708Sstevel
22321708Sstevel /*
22331708Sstevel * mboxsc_reference_mailbox
22341708Sstevel *
22351708Sstevel * Increment the mailbox's reference count to prevent it from being closed.
22361708Sstevel * This really doesn't deserve to be a function, but since a dereference
22371708Sstevel * function is needed, having a corresponding reference function makes the code
22381708Sstevel * clearer.
22391708Sstevel */
22401708Sstevel static void
mboxsc_reference_mailbox(mboxsc_mbox_t * mailboxp)22411708Sstevel mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp)
22421708Sstevel {
22431708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_reference_mailbox called");
2244*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n",
2245*11311SSurya.Prakki@Sun.COM (void *)mailboxp);
22461708Sstevel
22471708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
22481708Sstevel
22491708Sstevel mailboxp->mbox_refcount++;
22501708Sstevel
22511708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_reference_mailbox ret");
22521708Sstevel }
22531708Sstevel
22541708Sstevel
22551708Sstevel /*
22561708Sstevel * mboxsc_dereference_mailbox
22571708Sstevel *
22581708Sstevel * Decrement the mailbox's reference count, and if the count has gone to zero,
22591708Sstevel * signal any threads waiting for mailboxes to be completely dereferenced.
22601708Sstevel */
22611708Sstevel static void
mboxsc_dereference_mailbox(mboxsc_mbox_t * mailboxp)22621708Sstevel mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp)
22631708Sstevel {
22641708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
22651708Sstevel "mboxsc_dereference_mailbox called");
2266*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n",
2267*11311SSurya.Prakki@Sun.COM (void *)mailboxp);
22681708Sstevel
22691708Sstevel ASSERT(mutex_owned(&mboxsc_lock));
22701708Sstevel
22711708Sstevel mailboxp->mbox_refcount--;
22721708Sstevel if (mailboxp->mbox_refcount == 0) {
22731708Sstevel cv_broadcast(&mboxsc_dereference_cv);
22741708Sstevel }
22751708Sstevel
22761708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_dereference_mailbox ret");
22771708Sstevel }
22781708Sstevel
22791708Sstevel
22801708Sstevel #ifndef DEBUG
22811708Sstevel /* ARGSUSED */
22821708Sstevel int
mboxsc_debug(int cmd,void * arg)22831708Sstevel mboxsc_debug(int cmd, void *arg)
22841708Sstevel {
22851708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called");
22861708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret");
22871708Sstevel return (ENOTSUP);
22881708Sstevel }
22891708Sstevel #else /* DEBUG */
22901708Sstevel
22911708Sstevel static void print_hash_table(void);
22921708Sstevel static int print_mailbox_by_key(uint32_t key);
22931708Sstevel static void print_mailbox(mboxsc_mbox_t *mailboxp);
22941708Sstevel
22951708Sstevel int
mboxsc_debug(int cmd,void * arg)22961708Sstevel mboxsc_debug(int cmd, void *arg)
22971708Sstevel {
22981708Sstevel int error = 0;
22991708Sstevel
23001708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called\n");
23011708Sstevel
23021708Sstevel switch (cmd) {
23031708Sstevel case MBOXSC_PRNMBOX:
23041708Sstevel error = print_mailbox_by_key((uint32_t)(uintptr_t)arg);
23051708Sstevel break;
23061708Sstevel
23071708Sstevel case MBOXSC_PRNHASHTBL:
23081708Sstevel print_hash_table();
23091708Sstevel break;
23101708Sstevel
23111708Sstevel case MBOXSC_SETDBGMASK:
23121708Sstevel mboxsc_debug_mask = (uint32_t)(uintptr_t)arg;
23131708Sstevel break;
23141708Sstevel
23151708Sstevel default:
23161708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
23171708Sstevel "Error: unknown mboxsc debug cmd (%d)\n", cmd);
23181708Sstevel error = ENOTTY;
23191708Sstevel break;
23201708Sstevel }
23211708Sstevel
23221708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret: 0x%08x\n", error);
23231708Sstevel
23241708Sstevel return (error);
23251708Sstevel }
23261708Sstevel
23271708Sstevel /*PRINTFLIKE5*/
23281708Sstevel static void
mboxsc_dprintf(const char * file,int line,uint32_t class,uint32_t action,const char * fmt,...)23291708Sstevel mboxsc_dprintf(
23301708Sstevel const char *file,
23311708Sstevel int line,
23321708Sstevel uint32_t class,
23331708Sstevel uint32_t action,
23341708Sstevel const char *fmt,
23351708Sstevel ...)
23361708Sstevel {
23371708Sstevel int i;
23381708Sstevel char indent_buf[64];
23391708Sstevel char msg_buf[256];
23401708Sstevel va_list adx;
23411708Sstevel static uint32_t indent = 0;
23421708Sstevel
23431708Sstevel if (action & DBGACT_SHOWPOS) {
23441708Sstevel cmn_err(CE_CONT, "%s at line %d:\n", file, line);
23451708Sstevel }
23461708Sstevel
23471708Sstevel if (class & DBG_RETS) {
23481708Sstevel indent--;
23491708Sstevel }
23501708Sstevel
23511708Sstevel if (class & mboxsc_debug_mask) {
23521708Sstevel indent_buf[0] = '\0';
23531708Sstevel for (i = 0; i < indent; i++) {
2354*11311SSurya.Prakki@Sun.COM (void) strcat(indent_buf, " ");
23551708Sstevel }
23561708Sstevel
23571708Sstevel va_start(adx, fmt);
2358*11311SSurya.Prakki@Sun.COM (void) vsprintf(msg_buf, fmt, adx);
23591708Sstevel va_end(adx);
23601708Sstevel
23611708Sstevel cmn_err(CE_CONT, "%s%s", indent_buf, msg_buf);
23621708Sstevel }
23631708Sstevel
23641708Sstevel if (class & DBG_CALLS) {
23651708Sstevel indent++;
23661708Sstevel }
23671708Sstevel
23681708Sstevel if (action & DBGACT_BREAK) {
23691708Sstevel debug_enter("");
23701708Sstevel }
23711708Sstevel }
23721708Sstevel
23731708Sstevel static void
print_hash_table(void)23741708Sstevel print_hash_table(void)
23751708Sstevel {
23761708Sstevel int i;
23771708Sstevel mboxsc_mbox_t *mailboxp;
23781708Sstevel
23791708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_hash_table called\n");
23801708Sstevel
23811708Sstevel mutex_enter(&mboxsc_lock);
23821708Sstevel
23831708Sstevel for (i = 0; i < HASHTBL_SIZE; i++) {
23841708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hash[%02d]:\n", i);
23851708Sstevel
23861708Sstevel for (mailboxp = mboxsc_hash_table[i]; mailboxp != NULL;
23871708Sstevel mailboxp = mailboxp->mbox_hash_next) {
23881708Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
23891708Sstevel " key: 0x%08x, dir: %d\n", mailboxp->mbox_key,
23901708Sstevel mailboxp->mbox_direction);
23911708Sstevel }
23921708Sstevel }
23931708Sstevel
23941708Sstevel mutex_exit(&mboxsc_lock);
23951708Sstevel
23961708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_hash_table ret\n");
23971708Sstevel }
23981708Sstevel
23991708Sstevel static int
print_mailbox_by_key(uint32_t key)24001708Sstevel print_mailbox_by_key(uint32_t key)
24011708Sstevel {
24021708Sstevel int error = 0;
24031708Sstevel mboxsc_mbox_t *mailboxp;
24041708Sstevel
24051708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox_by_key called\n");
24061708Sstevel DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%08x\n", key);
24071708Sstevel
24081708Sstevel mutex_enter(&mboxsc_lock);
24091708Sstevel
24101708Sstevel mailboxp = mboxsc_hashfind_mailbox_by_key(key);
24111708Sstevel if (mailboxp != NULL) {
24121708Sstevel print_mailbox(mailboxp);
24131708Sstevel error = 0;
24141708Sstevel } else {
24151708Sstevel DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
24161708Sstevel "print_mailbox_by_key: no such mbox 0x%08x\n", key);
24171708Sstevel error = EBADF;
24181708Sstevel }
24191708Sstevel
24201708Sstevel mutex_exit(&mboxsc_lock);
24211708Sstevel DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
24221708Sstevel "print_mailbox_by_key ret: 0x%08x\n", error);
24231708Sstevel
24241708Sstevel return (error);
24251708Sstevel }
24261708Sstevel
24271708Sstevel /* ARGSUSED */
24281708Sstevel static void
print_mailbox(mboxsc_mbox_t * mailboxp)24291708Sstevel print_mailbox(mboxsc_mbox_t *mailboxp)
24301708Sstevel {
24311708Sstevel DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox called\n");
2432*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n",
2433*11311SSurya.Prakki@Sun.COM (void *)mailboxp);
24341708Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
24351708Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
24361708Sstevel "key = 0x%08x, dir = %d, callback = %p\n",
24371708Sstevel mailboxp->mbox_key, mailboxp->mbox_direction,
2438*11311SSurya.Prakki@Sun.COM (void *)mailboxp->mbox_callback);
24391708Sstevel } else {
24401708Sstevel DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "key = 0x%08x, dir = %d\n",
2441*11311SSurya.Prakki@Sun.COM (int)mailboxp->mbox_key, mailboxp->mbox_direction);
24421708Sstevel }
24431708Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
24441708Sstevel "length = %d, refcount = %d, state = %d\n",
24451708Sstevel mailboxp->mbox_length, mailboxp->mbox_refcount,
24461708Sstevel mailboxp->mbox_state);
2447*11311SSurya.Prakki@Sun.COM /* LINTED E_BAD_FORMAT_ARG_TYPE2 */
2448*11311SSurya.Prakki@Sun.COM DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "waitcv = %p, hashnext = %p\n",
2449*11311SSurya.Prakki@Sun.COM (void *)&mailboxp->mbox_wait, (void *)mailboxp->mbox_hash_next);
24501708Sstevel if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
24511708Sstevel DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
24521708Sstevel "hdr.type = 0x%x, hdr.cmd = 0x%x, hdr.len = 0x%x\n",
24531708Sstevel mailboxp->mbox_header.msg_type,
24541708Sstevel mailboxp->mbox_header.msg_cmd,
24551708Sstevel mailboxp->mbox_header.msg_length);
2456*11311SSurya.Prakki@Sun.COM DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hdr.tid = 0x%016lx\n",
24571708Sstevel mailboxp->mbox_header.msg_transid);
24581708Sstevel }
24591708Sstevel DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_mailbox ret\n");
24601708Sstevel }
24611708Sstevel #endif /* DEBUG */
2462