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