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