xref: /titanic_50/usr/src/uts/sun4u/starcat/io/mboxsc.c (revision 7eea693d6b672899726e75993fddc4e95b52647f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains the implementation of the mboxsc module, a mailbox layer
31  * built upon the Starcat IOSRAM driver.
32  */
33 
34 #include <sys/types.h>
35 #include <sys/systm.h>
36 #include <sys/modctl.h>
37 #include <sys/errno.h>
38 #include <sys/ksynch.h>
39 #include <sys/kmem.h>
40 #include <sys/varargs.h>
41 #include <sys/ddi.h>
42 #include <sys/sunddi.h>
43 #include <sys/cmn_err.h>
44 #include <sys/debug.h>
45 #include <sys/sysmacros.h>
46 
47 #include <sys/iosramreg.h>
48 #include <sys/iosramio.h>
49 #include <sys/mboxsc.h>
50 #include <sys/mboxsc_impl.h>
51 
52 /*
53  * Debugging facility
54  */
55 #define	DBGACT_NONE	(0x00000000)
56 #define	DBGACT_BREAK	(0x00000001)
57 #define	DBGACT_SHOWPOS	(0x00000002)
58 #define	DBGACT_DEFAULT	DBGACT_NONE
59 
60 #define	DBG_DEV		(0x00000001)
61 #define	DBG_CALLS	(0x00000002)
62 #define	DBG_RETS	(0x00000004)
63 #define	DBG_ARGS	(0x00000008)
64 #define	DBG_KMEM	(0x00000010)
65 #define	DBG_ALL		(0xFFFFFFFF)
66 
67 #ifdef DEBUG
68 static uint32_t	mboxsc_debug_mask = 0x00000000;
69 #define	DPRINTF0(class, action, fmt) \
70 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt))
71 #define	DPRINTF1(class, action, fmt, arg1) \
72 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
73 	    (arg1))
74 #define	DPRINTF2(class, action, fmt, arg1, arg2) \
75 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
76 	    (arg1), (arg2))
77 #define	DPRINTF3(class, action, fmt, arg1, arg2, arg3) \
78 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
79 	    (arg1), (arg2), (arg3))
80 #define	DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4) \
81 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
82 	    (arg1), (arg2), (arg3), (arg4))
83 #define	DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5) \
84 	mboxsc_dprintf(__FILE__, __LINE__, (class), (action), (fmt),\
85 	    (arg1), (arg2), (arg3), (arg4), (arg5))
86 #else	/* DEBUG */
87 #define	DPRINTF0(class, action, fmt)
88 #define	DPRINTF1(class, action, fmt, arg1)
89 #define	DPRINTF2(class, action, fmt, arg1, arg2)
90 #define	DPRINTF3(class, action, fmt, arg1, arg2, arg3)
91 #define	DPRINTF4(class, action, fmt, arg1, arg2, arg3, arg4)
92 #define	DPRINTF5(class, action, fmt, arg1, arg2, arg3, arg4, arg5)
93 #endif	/* DEBUG */
94 
95 /*
96  * Basic constants
97  */
98 #ifndef TRUE
99 #define	TRUE	(1)
100 #endif	/* TRUE */
101 #ifndef FALSE
102 #define	FALSE	(0)
103 #endif	/* FALSE */
104 
105 
106 /*
107  * Whenever mboxsc_init is called to create a new mailbox, an instance of
108  * mboxsc_mbox_t is created and inserted into a hash table to maintain
109  * various information about the mailbox.  The mbox_state, mbox_refcount, and
110  * mbox_wait fields are all protected by the global mboxsc_lock mutex.
111  * If lock contention between mailboxes becomes an issue, each mailbox will
112  * need to be given its own mutex to protect the mbox_wait, mbox_state,
113  * and mbox_update_wait fields.  The mbox_refcount field will probably need to
114  * remain under global protection, however, since it is used to keep track of
115  * the number of threads sleeping inside the mailbox's various synchronization
116  * mechanisms and would consequently be difficult to protect using those same
117  * mechanisms.
118  */
119 typedef struct mboxsc_mbox {
120 	uint32_t		mbox_key;
121 	int			mbox_direction;
122 	void			(*mbox_callback)(void);
123 	uint32_t		mbox_length;
124 	uint16_t		mbox_refcount;
125 	uint16_t		mbox_state;
126 	kcondvar_t		mbox_wait;
127 	mboxsc_msghdr_t		mbox_header;
128 	struct mboxsc_mbox	*mbox_hash_next;
129 } mboxsc_mbox_t;
130 
131 /*
132  * Various state flags that can be set on a mailbox.  Multiple states may
133  * be active at the same time.
134  */
135 #define	STATE_IDLE	(0x0000)
136 #define	STATE_WRITING	(0x0001)
137 #define	STATE_READING	(0x0002)
138 #define	STATE_HDRVALID	(0x0004)
139 
140 /*
141  * Timeout periods for mboxsc_putmsg and mboxsc_getmsg, converted to ticks
142  * from the microsecond values found in mboxsc_impl.h.
143  */
144 #define	EAGAIN_POLL		(drv_usectohz(MBOXSC_EAGAIN_POLL_USECS))
145 #define	PUTMSG_POLL		(drv_usectohz(MBOXSC_PUTMSG_POLL_USECS))
146 #define	HWLOCK_POLL		(drv_usectohz(MBOXSC_HWLOCK_POLL_USECS))
147 #define	LOOP_WARN_INTERVAL	(drv_usectohz(MBOXSC_USECS_PER_SECOND * 15))
148 
149 /*
150  * Various tests that are performed on message header fields.
151  */
152 #define	IS_UNSOLICITED_TYPE(type)	((type) != MBOXSC_MSG_REPLY)
153 #define	MSG_TYPE_MATCHES(type, msgp)	\
154 	(((type) == 0) || ((type) & (msgp)->msg_type))
155 #define	MSG_CMD_MATCHES(cmd, msgp)	\
156 	(((cmd) == 0) || ((cmd) == (msgp)->msg_cmd))
157 #define	MSG_TRANSID_MATCHES(tid, msgp)	\
158 	(((tid) == 0) || ((tid) == (msgp)->msg_transid))
159 
160 /*
161  * These macros can be used to determine the offset or size of any field in the
162  * message header (or any other struct, for that matter).
163  */
164 #define	FIELD_OFFSET(type, field)	((uint32_t)&(((type *)0)->field))
165 #define	FIELD_SIZE(type, field)		(sizeof (((type *)0)->field))
166 
167 /*
168  * Mask used when generating unique transaction ID values.
169  * This arbitrarily chosen value will be OR'd together with
170  * a counter for each successive internally-generated transaction ID.
171  */
172 #define	TRANSID_GEN_MASK	(0xFFC0000000000000)
173 
174 /*
175  * All existing mailboxes are stored in a hash table with HASHTBL_SIZE
176  * entries so they can be rapidly accessed by their key values.
177  */
178 #define	HASHTBL_SIZE	(32)
179 #define	HASH_KEY(key)	((((key) >> 24) ^ ((key) >> 16) ^ ((key) >> 9) ^\
180 			    (key)) & (HASHTBL_SIZE - 1));
181 
182 /*
183  * Unfortunately, it is necessary to calculate checksums on data split up
184  * amongst different buffers in some cases.  Consequently, mboxsc_checksum
185  * accepts a "seed" value as one of its parameters.  When first starting a
186  * checksum calculation, the seed should be 0.
187  */
188 #define	CHKSUM_INIT	(0)
189 
190 /*
191  * local variables
192  */
193 static kmutex_t		mboxsc_lock;
194 static mboxsc_mbox_t	*mboxsc_hash_table[HASHTBL_SIZE];
195 static uint32_t		mboxsc_flaglock_count;
196 static uint32_t		mboxsc_active_version = MBOXSC_PROTOCOL_VERSION;
197 static kcondvar_t	mboxsc_dereference_cv;
198 
199 /*
200  * Structures from modctl.h used for loadable module support.
201  * The mboxsc API is a "miscellaneous" module.
202  */
203 extern struct mod_ops mod_miscops;
204 
205 static struct modlmisc modlmisc = {
206 	&mod_miscops,
207 	"IOSRAM Mailbox API 'mboxsc' v%I%",
208 };
209 
210 static struct modlinkage modlinkage = {
211 	MODREV_1,
212 	(void *)&modlmisc,
213 	NULL
214 };
215 
216 /*
217  * Prototypes for local functions
218  */
219 static void		mboxsc_iosram_callback(void *arg);
220 static void		mboxsc_hdrchange_callback(void);
221 static int		mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp);
222 static void		mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp);
223 static void		mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp);
224 static mboxsc_mbox_t	*mboxsc_hashfind_mailbox_by_key(uint32_t key);
225 static mboxsc_mbox_t	*mboxsc_hashremove_mailbox_by_key(uint32_t key);
226 static mboxsc_chksum_t	mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf,
227 	uint32_t length);
228 static int		mboxsc_lock_flags(uint8_t mandatory, clock_t deadline);
229 static int		mboxsc_unlock_flags(uint8_t mandatory);
230 static int		mboxsc_timed_read(clock_t deadline, uint32_t key,
231 	uint32_t off, uint32_t len, caddr_t dptr);
232 static int		mboxsc_timed_write(clock_t deadline, uint32_t key,
233 	uint32_t off, uint32_t len, caddr_t dptr);
234 static int		mboxsc_timed_get_flag(clock_t deadline, uint32_t key,
235 	uint8_t *data_validp, uint8_t *int_pendingp);
236 static int		mboxsc_timed_set_flag(clock_t deadline, uint32_t key,
237 	uint8_t data_valid, uint8_t int_pending);
238 static int		mboxsc_timed_send_intr(clock_t deadline);
239 static int		mboxsc_expire_message(uint32_t key, int *resultp);
240 static uint64_t		mboxsc_generate_transid(uint64_t prev_transid);
241 static void		mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp);
242 static void		mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp);
243 #ifdef DEBUG
244 /*PRINTFLIKE5*/
245 static void		mboxsc_dprintf(const char *file, int line,
246 	uint32_t class, uint32_t action, const char *fmt, ...);
247 int			mboxsc_debug(int cmd, void *arg);
248 #endif /* DEBUG */
249 
250 
251 /*
252  * _init
253  *
254  * Loadable module support routine.  Initializes global lock and hash table.
255  */
256 int
257 _init(void)
258 {
259 	int		i;
260 	uint32_t	sms_version;
261 	int		error = 0;
262 
263 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_init called\n");
264 
265 	/*
266 	 * Initialize all module resources.
267 	 */
268 	mutex_init(&mboxsc_lock, NULL, MUTEX_DRIVER, NULL);
269 	cv_init(&mboxsc_dereference_cv, NULL, CV_DRIVER, NULL);
270 
271 	for (i = 0; i < HASHTBL_SIZE; i++) {
272 		mboxsc_hash_table[i] = NULL;
273 	}
274 	mboxsc_flaglock_count = 0;
275 
276 	if (mod_install(&modlinkage) != 0) {
277 		goto failed;
278 	}
279 
280 	/*
281 	 * Set the os_mbox_version field in the IOSRAM header to indicate the
282 	 * highest Mailbox Protocol version we support
283 	 */
284 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_SET_OS_MBOX_VER,
285 	    (void *)MBOXSC_PROTOCOL_VERSION);
286 	if (error != 0) {
287 		goto failed;
288 	}
289 
290 	/*
291 	 * Read the sms_mbox_version field in the IOSRAM header to determine
292 	 * what the greatest commonly supported version is.
293 	 */
294 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
295 	    (void *)&sms_version);
296 	if (error != 0) {
297 		goto failed;
298 	}
299 	mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION, sms_version);
300 	DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
301 	    "sms version: %d, active version: %d\n", sms_version,
302 	    mboxsc_active_version);
303 
304 	/*
305 	 * Register a callback with the IOSRAM driver to receive notification of
306 	 * changes to the IOSRAM header, in case the sms_mbox_version field
307 	 * changes.
308 	 */
309 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK,
310 	    (void *)mboxsc_hdrchange_callback);
311 	if (error != 0) {
312 		goto failed;
313 	}
314 
315 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
316 	return (0);
317 
318 	/*
319 	 * If initialization fails, uninitialize resources.
320 	 */
321 failed:
322 	mutex_destroy(&mboxsc_lock);
323 	cv_destroy(&mboxsc_dereference_cv);
324 
325 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_init ret: 0x%08x\n", error);
326 	return (error);
327 }
328 
329 /*
330  * _fini
331  *
332  * Loadable module support routine. Closes all mailboxes and releases all
333  * resources.
334  */
335 int
336 _fini(void)
337 {
338 	int		i;
339 	int		error = 0;
340 	mboxsc_mbox_t	*mailboxp;
341 
342 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_fini called\n");
343 
344 	/*
345 	 * Attempt to remove the module.  If successful, close all mailboxes
346 	 * and deallocate the global lock.
347 	 */
348 	error = mod_remove(&modlinkage);
349 	if (error == 0) {
350 		mutex_enter(&mboxsc_lock);
351 
352 		iosram_hdr_ctrl(IOSRAM_HDRCMD_REG_CALLBACK, NULL);
353 
354 		for (i = 0; i < HASHTBL_SIZE; i++) {
355 			while (mboxsc_hash_table[i] != NULL) {
356 				mailboxp = mboxsc_hash_table[i];
357 				mboxsc_close_mailbox(mailboxp);
358 			}
359 		}
360 		mutex_exit(&mboxsc_lock);
361 		mutex_destroy(&mboxsc_lock);
362 		cv_destroy(&mboxsc_dereference_cv);
363 	}
364 
365 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_fini ret: 0x%08x\n", error);
366 	return (error);
367 }
368 
369 /*
370  * _info
371  *
372  * Loadable module support routine.
373  */
374 int
375 _info(struct modinfo *modinfop)
376 {
377 	int		error = 0;
378 
379 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "_info called\n");
380 
381 	error = mod_info(&modlinkage, modinfop);
382 
383 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "_info ret: 0x%08x\n", error);
384 
385 	return (error);
386 }
387 
388 /*
389  * mboxsc_init
390  *
391  * Attempts to create a new mailbox.
392  */
393 int
394 mboxsc_init(uint32_t key, int direction, void (*event_handler)(void))
395 {
396 	int		error = 0;
397 	mboxsc_mbox_t	*mailboxp;
398 
399 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_init called\n");
400 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
401 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "direction = %d\n", direction);
402 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "event_handlerp = %p\n",
403 	    event_handler);
404 
405 	/*
406 	 * Check for valid direction and callback specification.
407 	 */
408 	if (((direction != MBOXSC_MBOX_IN) && (direction != MBOXSC_MBOX_OUT)) ||
409 	    ((event_handler != NULL) && (direction != MBOXSC_MBOX_IN))) {
410 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n",
411 		    EINVAL);
412 		return (EINVAL);
413 	}
414 
415 	/*
416 	 * Allocate memory for the mailbox structure and initialize all
417 	 * caller-provided fields.
418 	 */
419 	mailboxp = (mboxsc_mbox_t *)kmem_zalloc(sizeof (mboxsc_mbox_t),
420 	    KM_SLEEP);
421 	DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_zalloc(%d) = %p\n",
422 	    sizeof (mboxsc_mbox_t), mailboxp);
423 	mailboxp->mbox_key = key;
424 	mailboxp->mbox_direction = direction;
425 	mailboxp->mbox_callback = event_handler;
426 
427 	/*
428 	 * Attempt to add the mailbox.  If unsuccessful, free the allocated
429 	 * memory.
430 	 */
431 	mutex_enter(&mboxsc_lock);
432 	error = mboxsc_add_mailbox(mailboxp);
433 	mutex_exit(&mboxsc_lock);
434 
435 	if (error != 0) {
436 		DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %d)\n",
437 		    mailboxp, sizeof (mboxsc_mbox_t));
438 		kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
439 	}
440 
441 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_init ret: 0x%08x\n", error);
442 	return (error);
443 }
444 
445 /*
446  * mboxsc_fini
447  *
448  * Closes the mailbox with the indicated key, if it exists.
449  */
450 int
451 mboxsc_fini(uint32_t key)
452 {
453 	int		error = 0;
454 	mboxsc_mbox_t	*mailboxp;
455 
456 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_fini called\n");
457 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
458 
459 	/*
460 	 * Attempt to close the mailbox.
461 	 */
462 	mutex_enter(&mboxsc_lock);
463 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
464 	if (mailboxp == NULL) {
465 		error = EBADF;
466 	} else {
467 		while (mailboxp->mbox_refcount != 0) {
468 			cv_wait(&mboxsc_dereference_cv, &mboxsc_lock);
469 		}
470 		mboxsc_close_mailbox(mailboxp);
471 	}
472 	mutex_exit(&mboxsc_lock);
473 
474 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_fini ret: 0x%08x\n", error);
475 	return (error);
476 }
477 
478 /*
479  * mboxsc_putmsg
480  *
481  * Attempt to place a message into an outbound mailbox and signal the
482  * recipient.  A successful return (0) indicates that the message was
483  * successfully delivered.
484  */
485 int
486 mboxsc_putmsg(uint32_t key, uint32_t type, uint32_t cmd, uint64_t *transidp,
487 		uint32_t length, void *datap, clock_t timeout)
488 {
489 	int		i;
490 	int		error = 0;
491 	int		result;
492 	int		lock_held = 0;
493 	int		unlock_err;
494 	uint8_t		data_valid;
495 	clock_t		deadline;
496 	clock_t		remainder;
497 	mboxsc_chksum_t	checksum;
498 	mboxsc_mbox_t	*mailboxp;
499 	mboxsc_msghdr_t	header;
500 
501 #ifdef DEBUG /* because lint whines about if stmts without consequents */
502 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_putmsg called\n");
503 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
504 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "type = 0x%x\n", type);
505 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
506 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", transidp);
507 	if (transidp != NULL) {
508 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%016llx\n",
509 		    *transidp);
510 	}
511 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
512 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
513 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %d\n", timeout);
514 #endif /* DEBUG */
515 
516 	/*
517 	 * Perform some basic sanity checks on the message.
518 	 */
519 	for (i = 0; i < MBOXSC_NUM_MSG_TYPES; i++) {
520 		if (type == (1 << i)) {
521 			break;
522 		}
523 	}
524 	if ((i == MBOXSC_NUM_MSG_TYPES) || (cmd == 0) ||
525 	    ((datap == NULL) && (length != 0)) ||
526 	    (timeout < MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS) ||
527 	    (timeout > MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS)) {
528 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
529 		    "mboxsc_putmsg ret: 0x%08x\n", EINVAL);
530 		return (EINVAL);
531 	}
532 
533 	/*
534 	 * Initialize the header structure with values provided by the caller.
535 	 */
536 	header.msg_version = mboxsc_active_version;
537 	header.msg_type = type;
538 	header.msg_cmd = cmd;
539 	header.msg_length = MBOXSC_MSGHDR_SIZE + length;
540 	if (transidp != NULL) {
541 		header.msg_transid = *transidp;
542 	} else {
543 		header.msg_transid = 0;
544 	}
545 
546 	/*
547 	 * Perform additional sanity checks on the mailbox and message.
548 	 * Make sure that the specified mailbox really exists, that the
549 	 * given message will fit in it, and that the current message's
550 	 * transaction ID isn't the same as the last message's transaction
551 	 * ID unless both messages are replies (it's okay, necessary even,
552 	 * to reuse a transaction ID when resending a failed reply message,
553 	 * but that is the only case in which it is permissible).
554 	 */
555 	mutex_enter(&mboxsc_lock);
556 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
557 
558 	if (mailboxp == NULL) {
559 		error = EBADF;
560 	} else if ((mailboxp->mbox_direction != MBOXSC_MBOX_OUT) ||
561 	    (length + MBOXSC_PROTOCOL_SIZE > mailboxp->mbox_length) ||
562 	    ((header.msg_transid == mailboxp->mbox_header.msg_transid) &&
563 	    ((type & mailboxp->mbox_header.msg_type) != MBOXSC_MSG_REPLY) &&
564 	    (header.msg_transid != 0))) {
565 		error = EINVAL;
566 	}
567 
568 	if (error != 0) {
569 		mutex_exit(&mboxsc_lock);
570 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
571 		    "mboxsc_putmsg ret: 0x%08x\n", error);
572 		return (error);
573 	}
574 
575 	/*
576 	 * If the message's transaction ID is set to 0, generate a unique
577 	 * transaction ID and copy it into the message header.  If the message
578 	 * is successfully delivered and transidp != NULL, we'll copy this new
579 	 * transid into *transidp later.
580 	 */
581 	if (header.msg_transid == 0) {
582 		header.msg_transid =
583 		    mboxsc_generate_transid(mailboxp->mbox_header.msg_transid);
584 	}
585 
586 	/*
587 	 * Don't allow mboxsc_putmsg to attempt to place a message for
588 	 * longer than the caller's timeout.
589 	 */
590 	deadline = ddi_get_lbolt() +
591 	    drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
592 
593 	/*
594 	 * Increment the reference count on the mailbox to keep it from being
595 	 * closed, and wait for it to become available.
596 	 */
597 	mboxsc_reference_mailbox(mailboxp);
598 	remainder = 1;
599 	while ((mailboxp->mbox_state & STATE_WRITING) &&
600 	    (remainder > 0)) {
601 		remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
602 		    &mboxsc_lock, deadline);
603 	}
604 
605 	/*
606 	 * Check to see whether or not the mailbox became available.  If it
607 	 * did not, decrement its reference count and return an error to the
608 	 * caller.
609 	 */
610 	if (remainder == -1) {
611 		error = ENOSPC;
612 	} else if (remainder == 0) {
613 		error = EINTR;
614 	}
615 
616 	if (error != 0) {
617 		mboxsc_dereference_mailbox(mailboxp);
618 		mutex_exit(&mboxsc_lock);
619 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
620 		    "mboxsc_putmsg ret: 0x%08x\n", error);
621 		return (error);
622 	}
623 
624 	/*
625 	 * Since the message is valid and we're going to try to write it to
626 	 * IOSRAM, record its header for future reference (e.g. to make sure the
627 	 * next message doesn't incorrectly use the same transID).
628 	 */
629 	bcopy(&header, &(mailboxp->mbox_header), MBOXSC_MSGHDR_SIZE);
630 
631 	/*
632 	 * Flag the mailbox as being in use and release the global lock.
633 	 */
634 	mailboxp->mbox_state |= STATE_WRITING;
635 	mutex_exit(&mboxsc_lock);
636 
637 	/*
638 	 * Calculate the message checksum using the header and the data.
639 	 */
640 	checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
641 	    MBOXSC_MSGHDR_SIZE);
642 	checksum = mboxsc_checksum(checksum, (uint8_t *)datap, length);
643 
644 	/*
645 	 * Attempt to write the message and checksum to IOSRAM until successful,
646 	 * or as long as time remains and no errors other than EAGAIN are
647 	 * returned from any call to the IOSRAM driver in case there is a tunnel
648 	 * switch in progress.
649 	 */
650 	error = mboxsc_timed_write(deadline, key, MBOXSC_MSGHDR_OFFSET,
651 	    MBOXSC_MSGHDR_SIZE, (caddr_t)&header);
652 
653 	if (error == 0) {
654 		error = mboxsc_timed_write(deadline, key, MBOXSC_DATA_OFFSET,
655 		    length, (caddr_t)datap);
656 	}
657 
658 	if (error == 0) {
659 		error = mboxsc_timed_write(deadline, key, header.msg_length,
660 		    MBOXSC_CHKSUM_SIZE, (caddr_t)&checksum);
661 	}
662 
663 	/*
664 	 * Lock the flags before setting data_valid.  This isn't strictly
665 	 * necessary for correct protocol operation, but it gives us a chance to
666 	 * verify that the flags lock is functional before we commit to sending
667 	 * the message.
668 	 */
669 	if (error == 0) {
670 		error = mboxsc_lock_flags(FALSE, deadline);
671 		if (error == 0) {
672 			lock_held = 1;
673 		} else if (error == EBUSY) {
674 			error = EAGAIN;
675 		}
676 	}
677 
678 	if (error == 0) {
679 		error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_VALID,
680 		    IOSRAM_INT_TO_SSC);
681 	}
682 
683 	/*
684 	 * Unlock the flags.  If an error is encountered, only return it if
685 	 * another error hasn't been encountered previously.
686 	 */
687 	if (lock_held) {
688 		unlock_err = mboxsc_unlock_flags(TRUE);
689 		if ((unlock_err != 0) && ((error == 0) || (error == EAGAIN))) {
690 			error = unlock_err;
691 		}
692 	}
693 
694 	/*
695 	 * If time ran out or an IOSRAM call failed, notify other callers that
696 	 * the mailbox is available, decrement its reference count, and return
697 	 * an error.
698 	 */
699 	if (error != 0) {
700 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
701 		mutex_enter(&mboxsc_lock);
702 		mailboxp->mbox_state &= ~STATE_WRITING;
703 		cv_broadcast(&(mailboxp->mbox_wait));
704 		mboxsc_dereference_mailbox(mailboxp);
705 		mutex_exit(&mboxsc_lock);
706 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
707 		    "mboxsc_putmsg ret: 0x%08x\n", error);
708 		return (error);
709 	}
710 
711 	/*
712 	 * Send an interrupt to the remote mailbox interface to announce the
713 	 * presence of a new, valid message.
714 	 */
715 	error = mboxsc_timed_send_intr(deadline);
716 
717 	/*
718 	 * Wait until either the data_valid flag is set INVALID by the
719 	 * remote client or time runs out.  Since we're calling delay as
720 	 * a part of polling the flag anyway, we don't really need to do
721 	 * the usual continuous retry if iosram_get_flag returns EAGAIN.
722 	 */
723 	data_valid = IOSRAM_DATA_VALID;
724 	if (error == DDI_SUCCESS) {
725 		do {
726 			delay(MIN(PUTMSG_POLL, deadline - ddi_get_lbolt()));
727 			error = iosram_get_flag(key, &data_valid, NULL);
728 		} while ((data_valid == IOSRAM_DATA_VALID) &&
729 		    ((error == EAGAIN) || (error == 0)) &&
730 		    (deadline - ddi_get_lbolt() >= 0));
731 	}
732 
733 	/*
734 	 * If the data_valid flag was set to INVALID by the other side, the
735 	 * message was successfully transmitted.  If it wasn't, but there
736 	 * weren't any IOSRAM errors, the operation timed out.  If there was a
737 	 * problem with the IOSRAM, pass that info back to the caller.
738 	 */
739 	if (data_valid == IOSRAM_DATA_INVALID) {
740 		result = 0;
741 	} else if ((error == 0) || (error == DDI_FAILURE)) {
742 		result = ETIMEDOUT;
743 	} else {
744 		ASSERT(error != EINVAL);
745 		result = error;
746 	}
747 
748 	/*
749 	 * If the message has not been picked up, expire it. Note that this may
750 	 * actually result in detecting successful message delivery if the SC
751 	 * picks it up at the last moment.  If expiration fails due to an error,
752 	 * return an error to the user even if the message appears to have
753 	 * been successfully delivered.
754 	 */
755 	if (data_valid == IOSRAM_DATA_VALID) {
756 		error = mboxsc_expire_message(key, &result);
757 		if ((error != 0) && ((result == 0) || (result == ETIMEDOUT))) {
758 			result = error;
759 		}
760 	}
761 
762 	/*
763 	 * If the message was successfully delivered, and we generated a
764 	 * transaction ID for the caller, and the caller wants to know what it
765 	 * was, give it to them.
766 	 */
767 	if ((result == 0) && (transidp != NULL) && (*transidp == 0)) {
768 		*transidp = header.msg_transid;
769 	}
770 
771 	/*
772 	 * Regardless of whether the message was successfully transmitted or
773 	 * not, notify other callers that the mailbox is available and decrement
774 	 * its reference count.
775 	 */
776 	mutex_enter(&mboxsc_lock);
777 	mailboxp->mbox_state &= ~STATE_WRITING;
778 	cv_broadcast(&(mailboxp->mbox_wait));
779 	mboxsc_dereference_mailbox(mailboxp);
780 	mutex_exit(&mboxsc_lock);
781 
782 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_putmsg ret: 0x%08x\n",
783 	    result);
784 	return (result);
785 }
786 
787 /*
788  * mboxsc_getmsg
789  *
790  * Attempt to retrieve a message from the mailbox with the given key that
791  * matches values provided in msgp.  A successful return (0) indicates that
792  * a message matching the caller's request was successfully received within
793  * timeout milliseconds.  If a message matching the caller's request is
794  * detected, but can't be successfully read, an error will be returned even
795  * if the caller's timeout hasn't expired.
796  */
797 int
798 mboxsc_getmsg(uint32_t key, uint32_t *typep, uint32_t *cmdp, uint64_t *transidp,
799 		uint32_t *lengthp, void *datap, clock_t timeout)
800 {
801 	int		error = 0;
802 	uint32_t	datalen;
803 	uint8_t		data_valid;
804 	uint8_t		lock_held;
805 	mboxsc_chksum_t	read_checksum;
806 	mboxsc_chksum_t	calc_checksum;
807 	uint64_t	read_transid;
808 	clock_t		deadline;
809 	clock_t		remainder;
810 	mboxsc_mbox_t	*mailboxp;
811 	mboxsc_msghdr_t	header;
812 
813 #ifdef DEBUG /* because lint whines about if stmts without consequents */
814 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_getmsg called\n");
815 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
816 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "typep = %p\n", typep);
817 	if (typep != NULL) {
818 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*typep = 0x%x\n", *typep);
819 	}
820 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmdp = %p\n", cmdp);
821 	if (cmdp != NULL) {
822 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*cmdp = 0x%x\n", *cmdp);
823 	}
824 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "transidp = %p\n", transidp);
825 	if (transidp != NULL) {
826 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*transidp = 0x%llx\n",
827 		    *transidp);
828 	}
829 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "lengthp = %p\n", lengthp);
830 	if (lengthp != NULL) {
831 		DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "*lengthp = 0x%x\n",
832 		    *lengthp);
833 	}
834 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "datap = %p\n", datap);
835 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "timeout = %ld\n", timeout);
836 #endif /* DEBUG */
837 
838 	/*
839 	 * Perform basic sanity checks on the caller's request.
840 	 */
841 	if ((typep == NULL) || (*typep >= (1 << MBOXSC_NUM_MSG_TYPES)) ||
842 	    (cmdp == NULL) || (transidp == NULL) || (lengthp == NULL) ||
843 	    ((datap == NULL) && (*lengthp != 0)) ||
844 	    (timeout < MBOXSC_GETMSG_MIN_TIMEOUT_MSECS) ||
845 	    (timeout > MBOXSC_GETMSG_MAX_TIMEOUT_MSECS)) {
846 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
847 		    "mboxsc_getmsg ret: 0x%08x\n", EINVAL);
848 		return (EINVAL);
849 	}
850 
851 	/*
852 	 * Don't allow mboxsc_getmsg to attempt to receive a message for
853 	 * longer than the caller's timeout.
854 	 */
855 	deadline = ddi_get_lbolt() +
856 	    drv_usectohz(timeout * MBOXSC_USECS_PER_MSEC);
857 
858 	/*
859 	 * Perform additional sanity checks on the client's request and the
860 	 * associated mailbox.
861 	 */
862 	mutex_enter(&mboxsc_lock);
863 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
864 	if (mailboxp == NULL) {
865 		error = EBADF;
866 	} else if (mailboxp->mbox_direction != MBOXSC_MBOX_IN) {
867 		error = EINVAL;
868 	}
869 
870 	if (error != 0) {
871 		mutex_exit(&mboxsc_lock);
872 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
873 		    "mboxsc_getmsg ret: 0x%08x\n", error);
874 		return (error);
875 	}
876 
877 	/*
878 	 * The request is okay, so reference the mailbox (to keep it from being
879 	 * closed), and proceed with the real work.
880 	 */
881 	mboxsc_reference_mailbox(mailboxp);
882 
883 	/*
884 	 * Certain failures that may occur late in the process of getting a
885 	 * message (e.g. checksum error, cancellation by the sender) are
886 	 * supposed to leave the recipient waiting for the next message to
887 	 * arrive rather than returning an error.  To facilitate restarting
888 	 * the message acquisition process, the following label is provided
889 	 * as a target for a very few judiciously-placed "goto"s.
890 	 *
891 	 * The mboxsc_lock mutex MUST be held when jumping to this point.
892 	 */
893 mboxsc_getmsg_retry:
894 	;
895 
896 	/*
897 	 * If there is a valid message in the mailbox right now, check to
898 	 * see if it matches the caller's request.  If not, or if another
899 	 * caller is already reading it, wait for either the arrival of the
900 	 * next message or the expiration of the caller's specified timeout.
901 	 */
902 	error = 0;
903 	while (!(mailboxp->mbox_state & STATE_HDRVALID) ||
904 	    (mailboxp->mbox_state & STATE_READING) ||
905 	    !MSG_TYPE_MATCHES(*typep, &(mailboxp->mbox_header)) ||
906 	    !MSG_CMD_MATCHES(*cmdp, &(mailboxp->mbox_header)) ||
907 	    !MSG_TRANSID_MATCHES(*transidp, &(mailboxp->mbox_header))) {
908 		remainder = cv_timedwait_sig(&(mailboxp->mbox_wait),
909 		    &mboxsc_lock, deadline);
910 		if (remainder == -1) {
911 			error = ETIMEDOUT;
912 		} else if (remainder == 0) {
913 			error = EINTR;
914 		}
915 
916 		if (error != 0) {
917 			mboxsc_dereference_mailbox(mailboxp);
918 			mutex_exit(&mboxsc_lock);
919 			DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
920 			    "mboxsc_getmsg ret: 0x%08x\n", error);
921 			return (error);
922 		}
923 	}
924 
925 	/*
926 	 * If somebody sends us a message using a Mailbox Protocol version
927 	 * greater than the highest one we understand, invalidate the message,
928 	 * because we can't safely interpret anything beyond the version field.
929 	 */
930 	if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
931 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
932 		    "incoming message with unsupported version %d\n",
933 		    mailboxp->mbox_header.msg_version);
934 		mailboxp->mbox_state &= ~STATE_HDRVALID;
935 		goto mboxsc_getmsg_retry;
936 	}
937 
938 	/*
939 	 * At this point, there is a stored message header that matches the
940 	 * caller's request, but the actual message may no longer be valid
941 	 * in IOSRAM.  Check the data_valid flag to see whether or not
942 	 * this is the case.  If the message has expired, go start over.
943 	 *
944 	 * The global mutex is held while reading flag data from IOSRAM to
945 	 * avoid certain race conditions.  One race condition is still
946 	 * possible (i.e. SC-side has just set the data_valid flag for a
947 	 * new message, but the stored message header hasn't been updated
948 	 * yet), but it won't cause incorrect behavior (just some wasted work).
949 	 */
950 	error = iosram_get_flag(key, &data_valid, NULL);
951 
952 	ASSERT(error != EINVAL);
953 	if (error == 0) {
954 		if (data_valid != IOSRAM_DATA_VALID) {
955 			mailboxp->mbox_state &= ~STATE_HDRVALID;
956 			goto mboxsc_getmsg_retry;
957 		}
958 	} else if ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0)) {
959 		mutex_exit(&mboxsc_lock);
960 		delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
961 		mutex_enter(&mboxsc_lock);
962 		goto mboxsc_getmsg_retry;
963 	}
964 
965 	/*
966 	 * If the message is larger than the caller's buffer, provide the caller
967 	 * with the length of the message and return an error.
968 	 */
969 	datalen = mailboxp->mbox_header.msg_length - MBOXSC_MSGHDR_SIZE;
970 	if ((error == 0) && (datalen > *lengthp)) {
971 		*lengthp = datalen;
972 		error = EMSGSIZE;
973 	}
974 
975 	/*
976 	 * Note that there's no need to check STATE_HDRVALID before broadcasting
977 	 * here because the header is guaranteed to be valid at this point.
978 	 */
979 	if (error != 0) {
980 		cv_broadcast(&(mailboxp->mbox_wait));
981 		mboxsc_dereference_mailbox(mailboxp);
982 		mutex_exit(&mboxsc_lock);
983 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
984 		    "mboxsc_getmsg ret: 0x%08x\n", error);
985 		return (error);
986 	}
987 
988 	/*
989 	 * Store a copy of the current message header, flag the mailbox to
990 	 * indicate that it is being read and attempt to read the message data
991 	 * and checksum.
992 	 */
993 	bcopy(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE);
994 	mailboxp->mbox_state |= STATE_READING;
995 	mutex_exit(&mboxsc_lock);
996 
997 	if (datalen > 0) {
998 		error = mboxsc_timed_read(deadline, key, MBOXSC_DATA_OFFSET,
999 		    datalen, (caddr_t)datap);
1000 	}
1001 
1002 	if (error == 0) {
1003 		error = mboxsc_timed_read(deadline, key, header.msg_length,
1004 		    MBOXSC_CHKSUM_SIZE, (caddr_t)&read_checksum);
1005 	}
1006 
1007 	/*
1008 	 * Check for errors that may have occurred while accessing IOSRAM.
1009 	 */
1010 	if (error != 0) {
1011 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
1012 		mutex_enter(&mboxsc_lock);
1013 		mailboxp->mbox_state &= ~STATE_READING;
1014 		if (mailboxp->mbox_state & STATE_HDRVALID) {
1015 			cv_broadcast(&(mailboxp->mbox_wait));
1016 		}
1017 		mboxsc_dereference_mailbox(mailboxp);
1018 		mutex_exit(&mboxsc_lock);
1019 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1020 		    "mboxsc_getmsg ret: 0x%08x\n", error);
1021 		return (error);
1022 	}
1023 
1024 	/*
1025 	 * Calculate the checksum for the header and data that was read from
1026 	 * IOSRAM.
1027 	 */
1028 	calc_checksum = mboxsc_checksum(CHKSUM_INIT, (uint8_t *)&header,
1029 	    MBOXSC_MSGHDR_SIZE);
1030 	calc_checksum = mboxsc_checksum(calc_checksum, (uint8_t *)datap,
1031 	    datalen);
1032 
1033 	/*
1034 	 * If the message header has been invalidated, note the change.
1035 	 * If a the checksum verification fails, invalidate the message
1036 	 * header.  In either case, go back to the beginning and wait
1037 	 * for a new message.
1038 	 */
1039 	mutex_enter(&mboxsc_lock);
1040 	if (!(mailboxp->mbox_state & STATE_HDRVALID)) {
1041 		error = -1;
1042 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
1043 		    "mboxsc_getmsg - message invalidated while reading\n");
1044 	} else if (read_checksum != calc_checksum) {
1045 		error = -1;
1046 		mailboxp->mbox_state &= ~STATE_HDRVALID;
1047 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
1048 		    "mboxsc_getmsg - message failed checksum\n");
1049 		cmn_err(CE_NOTE,
1050 		    "mboxsc_getmsg - message failed checksum\n");
1051 	}
1052 
1053 	if (error == -1) {
1054 		mailboxp->mbox_state &= ~STATE_READING;
1055 		goto mboxsc_getmsg_retry;
1056 	}
1057 
1058 	/*
1059 	 * Acquire the hardware lock used for synchronization of data_valid flag
1060 	 * access to avoid race conditions.  If it is acquired, try to check the
1061 	 * current data_valid flag and transaction ID to verify that the message
1062 	 * is still valid.
1063 	 */
1064 	mutex_exit(&mboxsc_lock);
1065 
1066 	if ((error = mboxsc_lock_flags(FALSE, deadline)) != 0) {
1067 		lock_held = FALSE;
1068 		/*
1069 		 * We don't "do" EBUSY here, so treat it as EAGAIN.
1070 		 */
1071 		if (error == EBUSY) {
1072 			error = EAGAIN;
1073 		}
1074 	} else {
1075 		lock_held = TRUE;
1076 	}
1077 
1078 	if (error == 0) {
1079 		error = mboxsc_timed_get_flag(deadline, key, &data_valid, NULL);
1080 	}
1081 
1082 	if ((error == 0) && (data_valid == IOSRAM_DATA_VALID)) {
1083 		error = mboxsc_timed_read(deadline, key,
1084 		    FIELD_OFFSET(mboxsc_msghdr_t, msg_transid),
1085 		    FIELD_SIZE(mboxsc_msghdr_t, msg_transid),
1086 		    (caddr_t)&read_transid);
1087 	}
1088 
1089 	/*
1090 	 * If something failed along the way, either the error is unrecoverable
1091 	 * or we're just plain out of time, so unlock the flags if they were
1092 	 * locked, release the mailbox, wake up other potential readers if
1093 	 * there's still a message around, and return.
1094 	 */
1095 	if (error != 0) {
1096 		ASSERT((error != EINVAL) && (error != EMSGSIZE));
1097 		if (lock_held) {
1098 			mboxsc_unlock_flags(TRUE);
1099 		}
1100 		mutex_enter(&mboxsc_lock);
1101 		mailboxp->mbox_state &= ~STATE_READING;
1102 		if (mailboxp->mbox_state & STATE_HDRVALID) {
1103 			cv_broadcast(&(mailboxp->mbox_wait));
1104 		}
1105 		mboxsc_dereference_mailbox(mailboxp);
1106 		mutex_exit(&mboxsc_lock);
1107 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1108 		    "mboxsc_getmsg ret: 0x%08x\n", error);
1109 		return (error);
1110 	}
1111 
1112 	/*
1113 	 * If the data_valid flag isn't set to IOSRAM_DATA_VALID, or the
1114 	 * message transaction ID in IOSRAM has changed, the message being
1115 	 * read was timed out by its sender.  Since the data_valid flag can't
1116 	 * change as long as we have the flags locked, we can safely mark the
1117 	 * stored message header invalid if either the data_valid flag isn't set
1118 	 * or the stored transaction ID doesn't match the one we read.  (If
1119 	 * data_valid is set, the transaction ID shouldn't be changing
1120 	 * underneath us.)  On the other hand, if there may still be a valid
1121 	 * message, wake up any pending readers.
1122 	 */
1123 	if ((data_valid != IOSRAM_DATA_VALID) ||
1124 	    (read_transid != header.msg_transid)) {
1125 		mutex_enter(&mboxsc_lock);
1126 		mailboxp->mbox_state &= ~STATE_READING;
1127 		if ((data_valid != IOSRAM_DATA_VALID) ||
1128 		    (mailboxp->mbox_header.msg_transid != read_transid)) {
1129 			mailboxp->mbox_state &= ~STATE_HDRVALID;
1130 		} else if (mailboxp->mbox_state & STATE_HDRVALID) {
1131 			cv_broadcast(&(mailboxp->mbox_wait));
1132 		}
1133 
1134 		/*
1135 		 * Unfortunately, we can't be holding mboxsc_lock when we unlock
1136 		 * the flags.  However, we have to hold the flags until here to
1137 		 * make sure the SC doesn't change the message's state while
1138 		 * we're checking to see if we should invalidate our stored
1139 		 * header.
1140 		 */
1141 		mutex_exit(&mboxsc_lock);
1142 		error = mboxsc_unlock_flags(TRUE);
1143 		mutex_enter(&mboxsc_lock);
1144 
1145 		DPRINTF0(DBG_DEV, DBGACT_DEFAULT,
1146 		    "mboxsc_getmsg() - message invalidated by sender\n");
1147 		goto mboxsc_getmsg_retry;
1148 	}
1149 
1150 	/*
1151 	 * If everything has worked up to this point, all that remains is
1152 	 * to set the data_valid flag to IOSRAM_DATA_INVALID, tidy up, and
1153 	 * return the message.  If the flag can't be set, the message can't
1154 	 * be received, so keep trying as long as there is time.
1155 	 */
1156 	error = mboxsc_timed_set_flag(deadline, key, IOSRAM_DATA_INVALID,
1157 	    IOSRAM_INT_NONE);
1158 
1159 	mboxsc_unlock_flags(TRUE);
1160 	mutex_enter(&mboxsc_lock);
1161 
1162 	if (error != 0) {
1163 		ASSERT(error != EINVAL);
1164 		mboxsc_dereference_mailbox(mailboxp);
1165 		mailboxp->mbox_state &= ~STATE_READING;
1166 		if (mailboxp->mbox_state & STATE_HDRVALID) {
1167 			cv_broadcast(&(mailboxp->mbox_wait));
1168 		}
1169 		mutex_exit(&mboxsc_lock);
1170 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1171 		    "mboxsc_getmsg ret: 0x%08x\n", error);
1172 		return (error);
1173 	}
1174 
1175 	/*
1176 	 * If the message was read 100% successfully and the stored message
1177 	 * header for the mailbox still matches the message that was read,
1178 	 * invalidate it to prevent other readers from trying to read it.
1179 	 */
1180 	if (bcmp(&(mailboxp->mbox_header), &header, MBOXSC_MSGHDR_SIZE) == 0) {
1181 		mailboxp->mbox_state &= ~STATE_HDRVALID;
1182 	} else if (mailboxp->mbox_state & STATE_HDRVALID) {
1183 		cv_broadcast(&(mailboxp->mbox_wait));
1184 	}
1185 
1186 	mboxsc_dereference_mailbox(mailboxp);
1187 	mailboxp->mbox_state &= ~STATE_READING;
1188 	mutex_exit(&mboxsc_lock);
1189 
1190 	/*
1191 	 * Since we're successfully returning a message, we need to provide the
1192 	 * caller with all of the interesting header information.
1193 	 */
1194 	*typep = header.msg_type;
1195 	*cmdp = header.msg_cmd;
1196 	*transidp = header.msg_transid;
1197 	*lengthp = datalen;
1198 
1199 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_getmsg ret: 0x%08x\n", 0);
1200 	return (0);
1201 }
1202 
1203 /*
1204  * mboxsc_ctrl
1205  *
1206  * This routine provides access to a variety of services not available through
1207  * the basic API.
1208  */
1209 int
1210 mboxsc_ctrl(uint32_t key, uint32_t cmd, void *arg)
1211 {
1212 	int		error = 0;
1213 	mboxsc_mbox_t	*mailboxp;
1214 
1215 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_ctrl called\n");
1216 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1217 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "cmd = 0x%x\n", cmd);
1218 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = %p\n", arg);
1219 
1220 	mutex_enter(&mboxsc_lock);
1221 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
1222 	if (mailboxp == NULL) {
1223 		mutex_exit(&mboxsc_lock);
1224 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n",
1225 		    EBADF);
1226 		return (EBADF);
1227 	}
1228 
1229 	switch (cmd) {
1230 		case MBOXSC_CMD_VERSION:
1231 			/*
1232 			 * Return the Protocol version currently in use.  Since
1233 			 * there is only one version that exists right now, we
1234 			 * can't be using anything else.
1235 			 */
1236 			if (arg == NULL) {
1237 				error = EINVAL;
1238 				break;
1239 			}
1240 
1241 			*(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
1242 			break;
1243 
1244 		case MBOXSC_CMD_MAXVERSION:
1245 			/*
1246 			 * Return the highest Protocol version that we support.
1247 			 */
1248 			if (arg == NULL) {
1249 				error = EINVAL;
1250 				break;
1251 			}
1252 
1253 			*(uint32_t *)arg = MBOXSC_PROTOCOL_VERSION;
1254 			break;
1255 
1256 		case MBOXSC_CMD_MAXDATALEN:
1257 			/*
1258 			 * Return the amount of space available for client data
1259 			 * in the indicated mailbox.
1260 			 */
1261 			if (arg == NULL) {
1262 				error = EINVAL;
1263 				break;
1264 			}
1265 
1266 			*(uint32_t *)arg = mailboxp->mbox_length -
1267 			    MBOXSC_PROTOCOL_SIZE;
1268 			break;
1269 
1270 		case MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE:
1271 		{
1272 			mboxsc_timeout_range_t *rangep;
1273 
1274 			/*
1275 			 * Return the range of acceptable timeout values for
1276 			 * mboxsc_putmsg, expressed in milliseconds.
1277 			 */
1278 			if (arg == NULL) {
1279 				error = EINVAL;
1280 				break;
1281 			}
1282 
1283 			rangep = (mboxsc_timeout_range_t *)arg;
1284 			rangep->min_timeout = MBOXSC_PUTMSG_MIN_TIMEOUT_MSECS;
1285 			rangep->max_timeout = MBOXSC_PUTMSG_MAX_TIMEOUT_MSECS;
1286 			break;
1287 		}
1288 
1289 		case MBOXSC_CMD_GETMSG_TIMEOUT_RANGE:
1290 		{
1291 			mboxsc_timeout_range_t *rangep;
1292 
1293 			/*
1294 			 * Return the range of acceptable timeout values for
1295 			 * mboxsc_getmsg, expressed in milliseconds.
1296 			 */
1297 			if (arg == NULL) {
1298 				error = EINVAL;
1299 				break;
1300 			}
1301 
1302 			rangep = (mboxsc_timeout_range_t *)arg;
1303 			rangep->min_timeout = MBOXSC_GETMSG_MIN_TIMEOUT_MSECS;
1304 			rangep->max_timeout = MBOXSC_GETMSG_MAX_TIMEOUT_MSECS;
1305 			break;
1306 		}
1307 
1308 		default:
1309 			error = ENOTSUP;
1310 			break;
1311 	}
1312 
1313 	mutex_exit(&mboxsc_lock);
1314 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_ctrl ret: 0x%08x\n", error);
1315 	return (error);
1316 }
1317 
1318 /*
1319  * mboxsc_putmsg_def_timeout
1320  *
1321  * This routine returns the default mboxsc_putmsg timeout provided for the
1322  * convenience of clients.
1323  */
1324 clock_t
1325 mboxsc_putmsg_def_timeout(void)
1326 {
1327 	return (MBOXSC_PUTMSG_DEF_TIMEOUT_MSECS);
1328 }
1329 
1330 /*
1331  * mboxsc_iosram_callback
1332  *
1333  * This routine is registered with the IOSRAM driver for all inbound mailboxes,
1334  * and performs preliminary processing of all new messages.
1335  */
1336 static void
1337 mboxsc_iosram_callback(void *arg)
1338 {
1339 	int		error = 0;
1340 	uint8_t		data_valid;
1341 	uint32_t	key = (uint32_t)(uintptr_t)arg;
1342 	mboxsc_mbox_t	*mailboxp;
1343 
1344 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_iosram_callback called\n");
1345 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "arg = 0x%x\n", key);
1346 
1347 	mutex_enter(&mboxsc_lock);
1348 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
1349 
1350 	/*
1351 	 * We shouldn't ever receive a callback for a mailbox that doesn't
1352 	 * exist or for an output mailbox.
1353 	 */
1354 	ASSERT(mailboxp != NULL);
1355 	ASSERT(mailboxp->mbox_direction == MBOXSC_MBOX_IN);
1356 
1357 	/*
1358 	 * Attempt to read the header of the mailbox.  If the IOSRAM returns
1359 	 * EAGAIN, indicating a tunnel switch is in progress, do not retry
1360 	 * the operation.
1361 	 */
1362 	mailboxp->mbox_state &= ~STATE_HDRVALID;
1363 	error = iosram_rd(key, MBOXSC_MSGHDR_OFFSET, MBOXSC_MSGHDR_SIZE,
1364 	    (caddr_t)&(mailboxp->mbox_header));
1365 
1366 	/*
1367 	 * If somebody sends us a message using a Mailbox Protocol version
1368 	 * greater than the highest one we understand, ignore the message,
1369 	 * because we can't safely interpret anything beyond the version field.
1370 	 */
1371 	if (mailboxp->mbox_header.msg_version > MBOXSC_PROTOCOL_VERSION) {
1372 		error = -1;
1373 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
1374 		    "incoming message with unsupported version %d\n",
1375 		    mailboxp->mbox_header.msg_version);
1376 	}
1377 
1378 	/*
1379 	 * If this message is a repeat of a previous message (which should
1380 	 * only happen with reply messages), it is conceivable that a client
1381 	 * already executing in mboxsc_getmsg for the previous message could
1382 	 * end up receiving the new message before this callback gets a chance
1383 	 * to execute.  If that happens, the data_valid flag will already have
1384 	 * been cleared.  Call iosram_get_flag to see if that is the case, and
1385 	 * do not process the message if it is.
1386 	 */
1387 	if (error == 0) {
1388 		error = iosram_get_flag(key, &data_valid, NULL);
1389 		if ((error == 0) && (data_valid != IOSRAM_DATA_VALID)) {
1390 			error = -1;
1391 		}
1392 	}
1393 
1394 	/*
1395 	 * If the iosram_rd call failed, return.
1396 	 */
1397 	if (error != 0) {
1398 		mutex_exit(&mboxsc_lock);
1399 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1400 		    "mboxsc_iosram_callback ret (0x%08x)\n", error);
1401 		return;
1402 	}
1403 
1404 	/*
1405 	 * If the message read from IOSRAM was unsolicited, invoke
1406 	 * its callback.  Otherwise, wake all threads that are waiting
1407 	 * in mboxsc_getmsg.
1408 	 */
1409 	mailboxp->mbox_state |= STATE_HDRVALID;
1410 	if (IS_UNSOLICITED_TYPE(mailboxp->mbox_header.msg_type) &&
1411 	    (mailboxp->mbox_callback != NULL)) {
1412 		mboxsc_reference_mailbox(mailboxp);
1413 		mutex_exit(&mboxsc_lock);
1414 		(*(mailboxp->mbox_callback))();
1415 		mutex_enter(&mboxsc_lock);
1416 		mboxsc_dereference_mailbox(mailboxp);
1417 	} else {
1418 		cv_broadcast(&(mailboxp->mbox_wait));
1419 	}
1420 
1421 	mutex_exit(&mboxsc_lock);
1422 
1423 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_iosram_callback ret\n");
1424 }
1425 
1426 /*
1427  * mboxsc_hdrchange_callback
1428  *
1429  * This routine is registered with the IOSRAM driver to react to any changes SMS
1430  * makes to the IOSRAM header.
1431  */
1432 static void
1433 mboxsc_hdrchange_callback(void)
1434 {
1435 	int		error;
1436 	uint32_t	sms_version;
1437 
1438 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
1439 	    "mboxsc_hdrchange_callback called\n");
1440 
1441 	error = iosram_hdr_ctrl(IOSRAM_HDRCMD_GET_SMS_MBOX_VER,
1442 	    (void *)&sms_version);
1443 	if (error == 0) {
1444 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
1445 		    "sms mailbox version = %d\n", sms_version);
1446 		mboxsc_active_version = MIN(MBOXSC_PROTOCOL_VERSION,
1447 		    sms_version);
1448 	}
1449 
1450 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_hdrchange_callback ret\n");
1451 }
1452 
1453 
1454 /*
1455  * mboxsc_add_mailbox
1456  *
1457  * If no other mailbox exists with the same key as this mailbox, attempt to
1458  * retrieve its length from the IOSRAM driver and register the mboxsc callback
1459  * for the associated IOSRAM chunk.  If successful, initialize the
1460  * non-client-supplied mailbox fields and insert it into the hash table.
1461  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
1462  */
1463 static int
1464 mboxsc_add_mailbox(mboxsc_mbox_t *mailboxp)
1465 {
1466 	int		error = 0;
1467 	uint32_t	key = mailboxp->mbox_key;
1468 
1469 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_add_mailbox called\n");
1470 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", mailboxp);
1471 
1472 	/*
1473 	 * The global lock must be held by the caller.
1474 	 */
1475 	ASSERT(mutex_owned(&mboxsc_lock));
1476 
1477 	/*
1478 	 * Don't create the mailbox if it already exists.
1479 	 */
1480 	if (mboxsc_hashfind_mailbox_by_key(key) != NULL) {
1481 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1482 		    "mboxsc_add_mailbox ret: 0x%08x\n", EEXIST);
1483 		return (EEXIST);
1484 	}
1485 
1486 	/*
1487 	 * Obtain the mailbox length and register the mboxsc callback with the
1488 	 * IOSRAM driver.  If either call to the IOSRAM driver fails, or the
1489 	 * chunk is too small to be used as a mailbox, return an error to the
1490 	 * caller.
1491 	 */
1492 	error = iosram_ctrl(key, IOSRAM_CMD_CHUNKLEN, &(mailboxp->mbox_length));
1493 
1494 	if ((error == 0) && (mailboxp->mbox_length < MBOXSC_PROTOCOL_SIZE)) {
1495 		error = EFAULT;
1496 	}
1497 
1498 	if ((error == 0) && (mailboxp->mbox_direction == MBOXSC_MBOX_IN)) {
1499 		error = iosram_register(key, mboxsc_iosram_callback,
1500 		    (void *)(uintptr_t)(key));
1501 		if (error == EBUSY) {
1502 			error = EFAULT;
1503 		}
1504 	}
1505 
1506 	if (error != 0) {
1507 		DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1508 		    "mboxsc_add_mailbox ret: 0x%08x\n", error);
1509 		return (error);
1510 	}
1511 
1512 	/*
1513 	 * Initialize remaining mailbox fields and insert mailbox into
1514 	 * hash table.
1515 	 */
1516 	mailboxp->mbox_state = STATE_IDLE;
1517 	mailboxp->mbox_refcount = 0;
1518 	cv_init(&(mailboxp->mbox_wait), NULL, CV_DRIVER, NULL);
1519 	mboxsc_hashinsert_mailbox(mailboxp);
1520 
1521 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_add_mailbox ret: 0x%08x\n",
1522 	    0);
1523 	return (0);
1524 }
1525 
1526 /*
1527  * mboxsc_close_mailbox
1528  *
1529  * Remove a mailbox from the hash table, unregister its IOSRAM callback, and
1530  * deallocate its resources.
1531  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
1532  */
1533 static void
1534 mboxsc_close_mailbox(mboxsc_mbox_t *mailboxp)
1535 {
1536 	int		error = 0;
1537 	uint32_t	key = mailboxp->mbox_key;
1538 
1539 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_close_mailbox called\n");
1540 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", mailboxp);
1541 
1542 	/*
1543 	 * The global lock must be held by the caller.
1544 	 */
1545 	ASSERT(mutex_owned(&mboxsc_lock));
1546 
1547 	/*
1548 	 * Unregister the mboxsc callback for this particular mailbox.
1549 	 */
1550 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
1551 		error = iosram_unregister(key);
1552 		if (error == EINVAL) {
1553 			DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "invalid key (0x%08x)"
1554 			    " reported in mboxsc_close_mailbox.\n", key);
1555 			error = 0;
1556 		}
1557 	}
1558 
1559 	/*
1560 	 * Remove the mailbox from the hash table and deallocate its resources.
1561 	 */
1562 	mboxsc_hashremove_mailbox_by_key(key);
1563 	cv_destroy(&(mailboxp->mbox_wait));
1564 	DPRINTF2(DBG_KMEM, DBGACT_DEFAULT, "kmem_free(%p, %d)\n", mailboxp,
1565 	    sizeof (mboxsc_mbox_t));
1566 	kmem_free(mailboxp, sizeof (mboxsc_mbox_t));
1567 
1568 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_close_mailbox ret\n");
1569 }
1570 
1571 /*
1572  * mboxsc_hashinsert_mailbox
1573  *
1574  * Insert a fully initialized mailbox into the hash table.  No duplicate
1575  * checking is performed at this point, so the caller is responsible for
1576  * duplicate prevention if it is desired.
1577  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
1578  */
1579 static void
1580 mboxsc_hashinsert_mailbox(mboxsc_mbox_t *mailboxp)
1581 {
1582 	uint32_t	hash;
1583 
1584 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
1585 	    "mboxsc_hashinsert_mailbox called\n");
1586 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", mailboxp);
1587 
1588 	/*
1589 	 * The global lock must be held by the caller.
1590 	 */
1591 	ASSERT(mutex_owned(&mboxsc_lock));
1592 
1593 	hash = HASH_KEY(mailboxp->mbox_key);
1594 	mailboxp->mbox_hash_next = mboxsc_hash_table[hash];
1595 	mboxsc_hash_table[hash] = mailboxp;
1596 
1597 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
1598 	    "mboxsc_hashinsert_mailbox ret\n");
1599 }
1600 
1601 /*
1602  * mboxsc_hashfind_mailbox_by_key
1603  *
1604  * Locate a mailbox with the given key in the hash table.  Return a pointer
1605  * to the mailbox if it exists, or NULL if no matching mailbox is found.
1606  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
1607  */
1608 static mboxsc_mbox_t *
1609 mboxsc_hashfind_mailbox_by_key(uint32_t key)
1610 {
1611 	uint32_t	hash;
1612 	mboxsc_mbox_t	*mailboxp;
1613 
1614 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
1615 	    "mboxsc_hashfind_mailbox_by_key called\n");
1616 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1617 
1618 	/*
1619 	 * The global lock must be held by the caller.
1620 	 */
1621 	ASSERT(mutex_owned(&mboxsc_lock));
1622 
1623 	hash = HASH_KEY(key);
1624 	mailboxp = mboxsc_hash_table[hash];
1625 	while (mailboxp != NULL) {
1626 		if (mailboxp->mbox_key == key) {
1627 			break;
1628 		}
1629 		mailboxp = mailboxp->mbox_hash_next;
1630 	}
1631 
1632 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1633 	    "mboxsc_hashfind_mailbox_by_key ret: %p\n", mailboxp);
1634 	return (mailboxp);
1635 }
1636 
1637 /*
1638  * mboxsc_hashremove_mailbox_by_key
1639  *
1640  * Locate a mailbox with the given key in the hash table.  If it exists,
1641  * remove it from the hash table and return a pointer to it.  Otherwise,
1642  * return NULL.
1643  * NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
1644  */
1645 static mboxsc_mbox_t *
1646 mboxsc_hashremove_mailbox_by_key(uint32_t key)
1647 {
1648 	uint32_t	hash;
1649 	mboxsc_mbox_t	*mailboxp;
1650 	mboxsc_mbox_t	*last;
1651 
1652 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
1653 	    "mboxsc_hashremove_mailbox_by_key called\n");
1654 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1655 
1656 	/*
1657 	 * The global lock must be held by the caller.
1658 	 */
1659 	ASSERT(mutex_owned(&mboxsc_lock));
1660 
1661 	hash = HASH_KEY(key);
1662 	mailboxp = mboxsc_hash_table[hash];
1663 	last = NULL;
1664 	while (mailboxp != NULL) {
1665 		if (mailboxp->mbox_key == key) {
1666 			break;
1667 		}
1668 		last = mailboxp;
1669 		mailboxp = mailboxp->mbox_hash_next;
1670 	}
1671 
1672 	/*
1673 	 * If a mailbox was found, remove it from the hash table.
1674 	 */
1675 	if (mailboxp != NULL) {
1676 		if (last == NULL) {
1677 			mboxsc_hash_table[hash] = mailboxp->mbox_hash_next;
1678 		} else {
1679 			last->mbox_hash_next = mailboxp->mbox_hash_next;
1680 		}
1681 
1682 		mailboxp->mbox_hash_next = NULL;
1683 	}
1684 
1685 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1686 	    "mboxsc_hashremove_mailbox_by_key ret: %p\n", mailboxp);
1687 	return (mailboxp);
1688 }
1689 
1690 /*
1691  * mboxsc_checksum
1692  *
1693  * Given a pointer to a data buffer and its length, calculate the checksum of
1694  * the data contained therein.
1695  */
1696 static mboxsc_chksum_t
1697 mboxsc_checksum(mboxsc_chksum_t seed, uint8_t *buf, uint32_t length)
1698 {
1699 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_checksum called\n");
1700 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "seed = 0x%x\n", seed);
1701 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "buf = %p\n", buf);
1702 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "length = 0x%x\n", length);
1703 
1704 	while (length-- > 0) {
1705 		seed += *(buf++);
1706 	}
1707 
1708 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_checksum ret: 0x%08x\n",
1709 	    seed);
1710 	return (seed);
1711 }
1712 
1713 /*
1714  * mboxsc_lock_flags
1715  *
1716  * Acquire the hardware lock used for data_valid flag synchronization.  If the
1717  * lock is currently held by SMS and acquisition is mandatory, just keep on
1718  * trying until it is acquired.  If acquisition is not mandatory, keep trying
1719  * until the given deadline has been reached.  To avoid loading the system
1720  * unreasonably on EBUSY or EAGAIN, sleep for an appropriate amount of time
1721  * before retrying.  If a hardware error is encountered return it to the caller.
1722  *
1723  * If the lock is held, but not by SMS, clear it and acquire it.  Nobody
1724  * else should be grabbing that lock.
1725  */
1726 static int
1727 mboxsc_lock_flags(uint8_t mandatory, clock_t deadline)
1728 {
1729 	int		error;
1730 	int		warned = 0;
1731 	uint32_t	sema;
1732 	clock_t		pause;
1733 	clock_t		warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
1734 
1735 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_lock_flags called\n");
1736 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
1737 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
1738 
1739 	/*
1740 	 * Keep trying to acquire the lock until successful or (if acquisition
1741 	 * is not mandatory) time runs out.  If EBUSY (lock is already held) or
1742 	 * EAGAIN (tunnel switch in progress) is encountered, sleep for an
1743 	 * appropriate amount of time before retrying.  Any other error is
1744 	 * unrecoverable.
1745 	 */
1746 	do {
1747 		pause = 0;
1748 
1749 		/*
1750 		 * Since multiple threads could conceivably want the flag lock
1751 		 * at the same time, we place the lock under a mutex and keep a
1752 		 * counter indicating how many threads have the flags locked at
1753 		 * the moment.
1754 		 */
1755 		mutex_enter(&mboxsc_lock);
1756 		if ((mboxsc_flaglock_count > 0) ||
1757 		    ((error = iosram_sema_acquire(&sema)) == 0)) {
1758 			mboxsc_flaglock_count++;
1759 			mutex_exit(&mboxsc_lock);
1760 
1761 			if (warned) {
1762 				cmn_err(CE_WARN, "Flags locked");
1763 			}
1764 			DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
1765 			    "mboxsc_lock_flags ret: 0\n");
1766 			return (0);
1767 		}
1768 
1769 		/*
1770 		 * If iosram_sema_acquire returned EBUSY (lock already held),
1771 		 * make sure the lock is held by SMS, since nobody else should
1772 		 * ever be holding it.  If EBUSY or EAGAIN (tunnel switch in
1773 		 * progress) was returned, determine the appropriate amount of
1774 		 * time to sleep before trying again.
1775 		 */
1776 		if (error == EBUSY) {
1777 			if (IOSRAM_SEMA_GET_IDX(sema) != IOSRAM_SEMA_SMS_IDX) {
1778 				iosram_sema_release();
1779 				cmn_err(CE_WARN,
1780 				    "Incorrect flag lock value read (0x%08x)",
1781 				    sema);
1782 			} else {
1783 				pause = (mandatory ? HWLOCK_POLL :
1784 				    MIN(HWLOCK_POLL, deadline -
1785 				    ddi_get_lbolt()));
1786 			}
1787 		} else if (error == EAGAIN) {
1788 			pause = (mandatory ? EAGAIN_POLL : MIN(EAGAIN_POLL,
1789 			    deadline - ddi_get_lbolt()));
1790 		}
1791 
1792 		/*
1793 		 * We had to hold the lock until now to protect the potential
1794 		 * iosram_sema_release call above.
1795 		 */
1796 		mutex_exit(&mboxsc_lock);
1797 
1798 		/*
1799 		 * If EAGAIN or EBUSY was encountered, we're looping.
1800 		 */
1801 		if ((error == EAGAIN) || (error == EBUSY)) {
1802 			/*
1803 			 * If we've been looping here for a while, something is
1804 			 * probably wrong, so we should generated a warning.
1805 			 */
1806 			if (warning_time - ddi_get_lbolt() <= 0) {
1807 				if (!warned) {
1808 					warned = 1;
1809 					cmn_err(CE_WARN,
1810 					    "Unable to lock flags (0x%08x)",
1811 					    error);
1812 				} else {
1813 					cmn_err(CE_WARN,
1814 					    "Still unable to lock flags");
1815 				}
1816 				warning_time = ddi_get_lbolt() +
1817 				    LOOP_WARN_INTERVAL;
1818 			}
1819 
1820 			/*
1821 			 * Sleep a while before trying again.
1822 			 */
1823 			delay(pause);
1824 		}
1825 	} while (((error == EAGAIN) || (error == EBUSY)) &&
1826 	    (mandatory || (deadline - ddi_get_lbolt() >= 0)));
1827 
1828 	/*
1829 	 * If something really bad has happened, generate a warning.
1830 	 */
1831 	if ((error != EAGAIN) && (error != EBUSY)) {
1832 		cmn_err(CE_WARN, "Flag locking failed! (%d)", error);
1833 	}
1834 
1835 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_lock_flags ret: 0x%08x\n",
1836 	    error);
1837 	return (error);
1838 }
1839 
1840 /*
1841  * mboxsc_unlock_flags
1842  *
1843  * Release the hardware lock used for data_valid flag synchronization.
1844  * If a hardware error is encountered, return it to the caller.  If the
1845  * mandatory flag is set, loop and retry if EAGAIN is encountered.
1846  */
1847 static int
1848 mboxsc_unlock_flags(uint8_t mandatory)
1849 {
1850 	int	error;
1851 	int	warned = 0;
1852 	clock_t	warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
1853 
1854 	ASSERT(mboxsc_flaglock_count != 0);
1855 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_unlock_flags called\n");
1856 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mandatory = 0x%x\n", mandatory);
1857 
1858 	do {
1859 		/*
1860 		 * Since multiple threads could conceivably want the flag lock
1861 		 * at the same time, we place the lock under a mutex and keep a
1862 		 * counter indicating how many threads have the flags locked at
1863 		 * the moment.
1864 		 */
1865 		mutex_enter(&mboxsc_lock);
1866 		if ((mboxsc_flaglock_count > 1) ||
1867 		    ((error = iosram_sema_release()) == 0)) {
1868 			mboxsc_flaglock_count--;
1869 			mutex_exit(&mboxsc_lock);
1870 
1871 			if (warned) {
1872 				cmn_err(CE_WARN, "Flags unlocked");
1873 			}
1874 			DPRINTF0(DBG_RETS, DBGACT_DEFAULT,
1875 			    "mboxsc_unlock_flags ret: 0\n");
1876 			return (0);
1877 		}
1878 		mutex_exit(&mboxsc_lock);
1879 
1880 		/*
1881 		 * If iosram_sema_release returned EAGAIN (tunnel switch in
1882 		 * progress) and unlocking the flags is mandatory, sleep before
1883 		 * trying again.  If we've been trying for a while, display a
1884 		 * warning message too.
1885 		 */
1886 		if ((error == EAGAIN) && mandatory) {
1887 			if (warning_time - ddi_get_lbolt() <= 0) {
1888 				if (!warned) {
1889 					warned = 1;
1890 					cmn_err(CE_WARN, "Unable to unlock "
1891 					    "flags (iosram EAGAIN)");
1892 				} else {
1893 					cmn_err(CE_WARN,
1894 					    "Still unable to unlock flags");
1895 				}
1896 				warning_time = ddi_get_lbolt() +
1897 				    LOOP_WARN_INTERVAL;
1898 			}
1899 
1900 			delay(EAGAIN_POLL);
1901 		}
1902 	} while ((error == EAGAIN) && mandatory);
1903 
1904 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_unlock_flags ret: 0x%08x\n",
1905 	    error);
1906 	return (error);
1907 }
1908 
1909 /*
1910  * mboxsc_timed_read
1911  *
1912  * This function is just a wrapper around iosram_rd that will keep sleeping
1913  * and retrying, up to a given deadline, if iosram_rd returns EAGAIN
1914  * (presumably due to a tunnel switch).
1915  */
1916 static int
1917 mboxsc_timed_read(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
1918 	caddr_t dptr)
1919 {
1920 	int error;
1921 
1922 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_read called\n");
1923 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
1924 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1925 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
1926 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
1927 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", dptr);
1928 
1929 	do {
1930 		error = iosram_rd(key, off, len, dptr);
1931 		if (error == EAGAIN) {
1932 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
1933 		}
1934 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
1935 
1936 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1937 	    "mboxsc_timed_read ret: 0x%08x\n", error);
1938 	return (error);
1939 }
1940 
1941 /*
1942  * mboxsc_timed_write
1943  *
1944  * This function is just a wrapper around iosram_wr that will keep sleeping
1945  * and retrying, up to a given deadline, if iosram_wr returns EAGAIN
1946  * (presumably due to a tunnel switch).
1947  */
1948 static int
1949 mboxsc_timed_write(clock_t deadline, uint32_t key, uint32_t off, uint32_t len,
1950 	caddr_t dptr)
1951 {
1952 	int error;
1953 
1954 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_write called\n");
1955 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
1956 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1957 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "off = 0x%x\n", off);
1958 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "len = 0x%x\n", len);
1959 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "dptr = %p\n", dptr);
1960 
1961 	do {
1962 		error = iosram_wr(key, off, len, dptr);
1963 		if (error == EAGAIN) {
1964 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
1965 		}
1966 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
1967 
1968 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
1969 	    "mboxsc_timed_write ret: 0x%08x\n", error);
1970 	return (error);
1971 }
1972 
1973 /*
1974  * mboxsc_timed_get_flag
1975  *
1976  * This function is just a wrapper around iosram_get_flag that will keep
1977  * sleeping and retrying, up to a given deadline, if iosram_get_flag returns
1978  * EAGAIN (presumably due to a tunnel switch).
1979  */
1980 static int
1981 mboxsc_timed_get_flag(clock_t deadline, uint32_t key, uint8_t *data_validp,
1982 	uint8_t *int_pendingp)
1983 {
1984 	int error;
1985 
1986 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_get_flag called\n");
1987 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
1988 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
1989 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_validp = %p\n", data_validp);
1990 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pendingp = %p\n", int_pendingp);
1991 
1992 	do {
1993 		error = iosram_get_flag(key, data_validp, int_pendingp);
1994 		if (error == EAGAIN) {
1995 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
1996 		}
1997 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
1998 
1999 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2000 	    "mboxsc_timed_get_flag ret: 0x%08x\n", error);
2001 	return (error);
2002 }
2003 
2004 /*
2005  * mboxsc_timed_set_flag
2006  *
2007  * This function is just a wrapper around iosram_set_flag that will keep
2008  * sleeping and retrying, up to a given deadline, if iosram_set_flag returns
2009  * EAGAIN (presumably due to a tunnel switch).
2010  */
2011 static int
2012 mboxsc_timed_set_flag(clock_t deadline, uint32_t key, uint8_t data_valid,
2013 	uint8_t int_pending)
2014 {
2015 	int error;
2016 
2017 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_set_flag called\n");
2018 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
2019 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
2020 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "data_valid = %d\n", data_valid);
2021 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "int_pending = %d\n", int_pending);
2022 
2023 	do {
2024 		error = iosram_set_flag(key, data_valid, int_pending);
2025 		if (error == EAGAIN) {
2026 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
2027 		}
2028 	} while ((error == EAGAIN) && (deadline - ddi_get_lbolt() >= 0));
2029 
2030 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2031 	    "mboxsc_timed_set_flag ret: 0x%08x\n", error);
2032 	return (error);
2033 }
2034 
2035 /*
2036  * mboxsc_timed_send_intr
2037  *
2038  * This function is just a wrapper around iosram_send_intr that will keep
2039  * sleeping and retrying, up to a given deadline, if iosram_send_intr returns
2040  * EAGAIN (presumably due to a tunnel switch).
2041  */
2042 static int
2043 mboxsc_timed_send_intr(clock_t deadline)
2044 {
2045 	int error;
2046 
2047 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_timed_send_intr called\n");
2048 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "deadline = 0x%x\n", deadline);
2049 
2050 	do {
2051 		error = iosram_send_intr();
2052 		if (error == DDI_FAILURE) {
2053 			delay(MIN(EAGAIN_POLL, deadline - ddi_get_lbolt()));
2054 		}
2055 	} while ((error == DDI_FAILURE) && (deadline - ddi_get_lbolt() >= 0));
2056 
2057 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2058 	    "mboxsc_timed_send_intr ret: 0x%08x\n", error);
2059 	return (error);
2060 }
2061 
2062 /*
2063  * mboxsc_expire_message
2064  *
2065  * This function is called by mboxsc_putmsg to handle expiration of messages
2066  * that weren't picked up before they timed out.  It will not return until the
2067  * message has been picked up (which isn't expected), the message has been
2068  * successfully expired, or a serious error has been encountered.  If the
2069  * message is finally picked up, it will set the value pointed to by "resultp"
2070  * to 0.  Unlike other sections of code, this function will never time out on
2071  * EAGAIN from the iosram driver, since it is important that both sides of the
2072  * IOSRAM agree on whether or not a message was delivered successfully.
2073  */
2074 static int
2075 mboxsc_expire_message(uint32_t key, int *resultp)
2076 {
2077 	int	error = 0;
2078 	int	lock_held = 0;
2079 	int	warned = 0;
2080 	uint8_t	data_valid;
2081 	clock_t	warning_time = ddi_get_lbolt() + LOOP_WARN_INTERVAL;
2082 
2083 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_expire_message called\n");
2084 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%x\n", key);
2085 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "resultp = %p\n", resultp);
2086 
2087 	do {
2088 		error = 0;
2089 
2090 		/*
2091 		 * Lock the flags if they aren't locked already.
2092 		 */
2093 		if (!lock_held) {
2094 			error = mboxsc_lock_flags(TRUE, 0);
2095 			if (error == 0) {
2096 				lock_held = 1;
2097 			}
2098 		}
2099 
2100 		/*
2101 		 * If the flags were locked successfully, reread the data-valid
2102 		 * flag.
2103 		 */
2104 		if (error == 0) {
2105 			error = iosram_get_flag(key, &data_valid, NULL);
2106 		}
2107 
2108 		/*
2109 		 * If the data-valid flag was read successfully, see if it has
2110 		 * been cleared or not, as the other side may have finally read
2111 		 * the message.
2112 		 */
2113 		if (error == 0) {
2114 			if (data_valid == IOSRAM_DATA_INVALID) {
2115 				/*
2116 				 * Surprise!  The SC finally picked up the
2117 				 * message, so delivery succeeded after all.
2118 				 */
2119 				if (*resultp == ETIMEDOUT) {
2120 					*resultp = 0;
2121 				}
2122 			} else {
2123 				/*
2124 				 * The message still hasn't been read, so try to
2125 				 * clear the data-valid flag.
2126 				 */
2127 				error = iosram_set_flag(key,
2128 				    IOSRAM_DATA_INVALID, IOSRAM_INT_NONE);
2129 			}
2130 		}
2131 
2132 		/*
2133 		 * If the flags were locked, unlock them, no matter what else
2134 		 * has or has not succeeded.  Don't overwrite the existing value
2135 		 * of "error" unless no errors other than EAGAIN have been
2136 		 * encountered previously.  If we hit EAGAIN at some point,
2137 		 * unlocking the flags here is optional.  In all other cases, it
2138 		 * is mandatory.
2139 		 */
2140 		if (lock_held) {
2141 			int unlock_err;
2142 
2143 			if (error == EAGAIN) {
2144 				unlock_err = mboxsc_unlock_flags(FALSE);
2145 			} else {
2146 				unlock_err = mboxsc_unlock_flags(TRUE);
2147 			}
2148 
2149 			if (unlock_err == 0) {
2150 				lock_held = 0;
2151 			} else if ((error == 0) || (error == EAGAIN)) {
2152 				error = unlock_err;
2153 			}
2154 		}
2155 
2156 		/*
2157 		 * Did we hit a tunnel switch? (iosram driver returns EAGAIN)
2158 		 * If so, sleep for a while before trying the whole process
2159 		 * again.
2160 		 */
2161 		if (error == EAGAIN) {
2162 			/*
2163 			 * If we've been stuck in this loop for a while,
2164 			 * something is probably wrong, and we should display a
2165 			 * warning.
2166 			 */
2167 			if (warning_time - ddi_get_lbolt() <= 0) {
2168 				if (!warned) {
2169 					warned = 1;
2170 					cmn_err(CE_WARN, "Unable to clear flag "
2171 					    "(iosram EAGAIN)");
2172 				} else {
2173 					cmn_err(CE_WARN,
2174 					    "Still unable to clear flag");
2175 				}
2176 				warning_time = ddi_get_lbolt() +
2177 				    LOOP_WARN_INTERVAL;
2178 			}
2179 
2180 			delay(EAGAIN_POLL);
2181 		}
2182 	} while (error == EAGAIN);
2183 
2184 	/*
2185 	 * If the data-valid flag was not successfully cleared due to some sort
2186 	 * of problem, report it.  Otherwise, if we looped for a while on EAGAIN
2187 	 * and generated a warning about it, indicate that everything is okay
2188 	 * now.
2189 	 */
2190 	if (error != 0) {
2191 		cmn_err(CE_WARN, "Message expiration failure! (%d)", error);
2192 	} else if (warned) {
2193 		cmn_err(CE_WARN, "Flag cleared");
2194 	}
2195 
2196 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2197 	    "mboxsc_expire_message ret: 0x%08x\n", error);
2198 	return (error);
2199 }
2200 
2201 
2202 /*
2203  * mboxsc_generate_transid
2204  *
2205  * This function generates unique transaction IDs using an incrementing counter.
2206  * The value generated is guaranteed not to be the same as the prev_transid
2207  * value passed in by the caller.
2208  */
2209 static uint64_t
2210 mboxsc_generate_transid(uint64_t prev_transid)
2211 {
2212 	uint64_t	new_transid;
2213 	static uint64_t	transid_counter = 0;
2214 
2215 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_generate_transid called");
2216 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "prev_transid = 0x%016llx\n",
2217 	    prev_transid);
2218 
2219 	do {
2220 		new_transid = TRANSID_GEN_MASK | transid_counter++;
2221 		if (transid_counter & TRANSID_GEN_MASK) {
2222 			transid_counter = 0;
2223 		}
2224 	} while (new_transid == prev_transid);
2225 
2226 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2227 	    "mboxsc_generate_transid ret: 0x%016llx", new_transid);
2228 	return (new_transid);
2229 }
2230 
2231 
2232 /*
2233  * mboxsc_reference_mailbox
2234  *
2235  * Increment the mailbox's reference count to prevent it from being closed.
2236  * This really doesn't deserve to be a function, but since a dereference
2237  * function is needed, having a corresponding reference function makes the code
2238  * clearer.
2239  */
2240 static void
2241 mboxsc_reference_mailbox(mboxsc_mbox_t *mailboxp)
2242 {
2243 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_reference_mailbox called");
2244 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n", mailboxp);
2245 
2246 	ASSERT(mutex_owned(&mboxsc_lock));
2247 
2248 	mailboxp->mbox_refcount++;
2249 
2250 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_reference_mailbox ret");
2251 }
2252 
2253 
2254 /*
2255  * mboxsc_dereference_mailbox
2256  *
2257  * Decrement the mailbox's reference count, and if the count has gone to zero,
2258  * signal any threads waiting for mailboxes to be completely dereferenced.
2259  */
2260 static void
2261 mboxsc_dereference_mailbox(mboxsc_mbox_t *mailboxp)
2262 {
2263 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT,
2264 	    "mboxsc_dereference_mailbox called");
2265 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = 0x%p\n", mailboxp);
2266 
2267 	ASSERT(mutex_owned(&mboxsc_lock));
2268 
2269 	mailboxp->mbox_refcount--;
2270 	if (mailboxp->mbox_refcount == 0) {
2271 		cv_broadcast(&mboxsc_dereference_cv);
2272 	}
2273 
2274 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_dereference_mailbox ret");
2275 }
2276 
2277 
2278 #ifndef DEBUG
2279 /* ARGSUSED */
2280 int
2281 mboxsc_debug(int cmd, void *arg)
2282 {
2283 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called");
2284 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret");
2285 	return (ENOTSUP);
2286 }
2287 #else	/* DEBUG */
2288 
2289 static void	print_hash_table(void);
2290 static int	print_mailbox_by_key(uint32_t key);
2291 static void	print_mailbox(mboxsc_mbox_t *mailboxp);
2292 
2293 int
2294 mboxsc_debug(int cmd, void *arg)
2295 {
2296 	int		error = 0;
2297 
2298 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "mboxsc_debug called\n");
2299 
2300 	switch (cmd) {
2301 		case MBOXSC_PRNMBOX:
2302 			error = print_mailbox_by_key((uint32_t)(uintptr_t)arg);
2303 			break;
2304 
2305 		case MBOXSC_PRNHASHTBL:
2306 			print_hash_table();
2307 			break;
2308 
2309 		case MBOXSC_SETDBGMASK:
2310 			mboxsc_debug_mask = (uint32_t)(uintptr_t)arg;
2311 			break;
2312 
2313 		default:
2314 			DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
2315 			    "Error: unknown mboxsc debug cmd (%d)\n", cmd);
2316 			error = ENOTTY;
2317 			break;
2318 	}
2319 
2320 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT, "mboxsc_debug ret: 0x%08x\n", error);
2321 
2322 	return (error);
2323 }
2324 
2325 /*PRINTFLIKE5*/
2326 static void
2327 mboxsc_dprintf(
2328 	const char	*file,
2329 	int		line,
2330 	uint32_t	class,
2331 	uint32_t	action,
2332 	const char	*fmt,
2333 	...)
2334 {
2335 	int		i;
2336 	char		indent_buf[64];
2337 	char		msg_buf[256];
2338 	va_list		adx;
2339 	static uint32_t	indent = 0;
2340 
2341 	if (action & DBGACT_SHOWPOS) {
2342 		cmn_err(CE_CONT, "%s at line %d:\n", file, line);
2343 	}
2344 
2345 	if (class & DBG_RETS) {
2346 		indent--;
2347 	}
2348 
2349 	if (class & mboxsc_debug_mask) {
2350 		indent_buf[0] = '\0';
2351 		for (i = 0; i < indent; i++) {
2352 			strcat(indent_buf, "  ");
2353 		}
2354 
2355 		va_start(adx, fmt);
2356 		vsprintf(msg_buf, fmt, adx);
2357 		va_end(adx);
2358 
2359 		cmn_err(CE_CONT, "%s%s", indent_buf, msg_buf);
2360 	}
2361 
2362 	if (class & DBG_CALLS) {
2363 		indent++;
2364 	}
2365 
2366 	if (action & DBGACT_BREAK) {
2367 		debug_enter("");
2368 	}
2369 }
2370 
2371 static void
2372 print_hash_table(void)
2373 {
2374 	int		i;
2375 	mboxsc_mbox_t	*mailboxp;
2376 
2377 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_hash_table called\n");
2378 
2379 	mutex_enter(&mboxsc_lock);
2380 
2381 	for (i = 0; i < HASHTBL_SIZE; i++) {
2382 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hash[%02d]:\n", i);
2383 
2384 		for (mailboxp = mboxsc_hash_table[i]; mailboxp != NULL;
2385 		    mailboxp = mailboxp->mbox_hash_next) {
2386 			DPRINTF2(DBG_DEV, DBGACT_DEFAULT,
2387 			    "    key: 0x%08x, dir: %d\n", mailboxp->mbox_key,
2388 			    mailboxp->mbox_direction);
2389 		}
2390 	}
2391 
2392 	mutex_exit(&mboxsc_lock);
2393 
2394 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_hash_table ret\n");
2395 }
2396 
2397 static int
2398 print_mailbox_by_key(uint32_t key)
2399 {
2400 	int		error = 0;
2401 	mboxsc_mbox_t	*mailboxp;
2402 
2403 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox_by_key called\n");
2404 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "key = 0x%08x\n", key);
2405 
2406 	mutex_enter(&mboxsc_lock);
2407 
2408 	mailboxp = mboxsc_hashfind_mailbox_by_key(key);
2409 	if (mailboxp != NULL) {
2410 		print_mailbox(mailboxp);
2411 		error = 0;
2412 	} else {
2413 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT,
2414 		    "print_mailbox_by_key: no such mbox 0x%08x\n", key);
2415 		error = EBADF;
2416 	}
2417 
2418 	mutex_exit(&mboxsc_lock);
2419 	DPRINTF1(DBG_RETS, DBGACT_DEFAULT,
2420 	    "print_mailbox_by_key ret: 0x%08x\n", error);
2421 
2422 	return (error);
2423 }
2424 
2425 /* ARGSUSED */
2426 static void
2427 print_mailbox(mboxsc_mbox_t *mailboxp)
2428 {
2429 	DPRINTF0(DBG_CALLS, DBGACT_DEFAULT, "print_mailbox called\n");
2430 	DPRINTF1(DBG_ARGS, DBGACT_DEFAULT, "mailboxp = %p\n", mailboxp);
2431 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
2432 		DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
2433 		    "key = 0x%08x, dir = %d, callback = %p\n",
2434 		    mailboxp->mbox_key, mailboxp->mbox_direction,
2435 		    mailboxp->mbox_callback);
2436 	} else {
2437 		DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "key = 0x%08x, dir = %d\n",
2438 		    mailboxp->mbox_key, mailboxp->mbox_direction);
2439 	}
2440 	DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
2441 	    "length = %d, refcount = %d, state = %d\n",
2442 	    mailboxp->mbox_length, mailboxp->mbox_refcount,
2443 	    mailboxp->mbox_state);
2444 	DPRINTF2(DBG_DEV, DBGACT_DEFAULT, "waitcv = 0x%x, hashnext = %p\n",
2445 	    mailboxp->mbox_wait, mailboxp->mbox_hash_next);
2446 	if (mailboxp->mbox_direction == MBOXSC_MBOX_IN) {
2447 		DPRINTF3(DBG_DEV, DBGACT_DEFAULT,
2448 		    "hdr.type = 0x%x, hdr.cmd = 0x%x, hdr.len = 0x%x\n",
2449 		    mailboxp->mbox_header.msg_type,
2450 		    mailboxp->mbox_header.msg_cmd,
2451 		    mailboxp->mbox_header.msg_length);
2452 		DPRINTF1(DBG_DEV, DBGACT_DEFAULT, "hdr.tid = 0x%016llx\n",
2453 		    mailboxp->mbox_header.msg_transid);
2454 	}
2455 	DPRINTF0(DBG_RETS, DBGACT_DEFAULT, "print_mailbox ret\n");
2456 }
2457 #endif	/* DEBUG */
2458