xref: /titanic_52/usr/src/uts/sun4u/serengeti/io/sgsbbc_mailbox.c (revision 2bc987325e3ded1865bff043128661815c4690b9)
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  * Interface for Serengeti IOSRAM mailbox
29  * OS <-> SC communication protocol
30  */
31 
32 #include <sys/types.h>
33 #include <sys/systm.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/kmem.h>
37 #include <sys/uadmin.h>
38 #include <sys/machsystm.h>
39 #include <sys/disp.h>
40 #include <sys/taskq.h>
41 
42 #include <sys/sgevents.h>
43 #include <sys/sgsbbc_priv.h>
44 #include <sys/sgsbbc_iosram_priv.h>
45 #include <sys/sgsbbc_mailbox_priv.h>
46 #include <sys/plat_ecc_unum.h>
47 #include <sys/plat_ecc_dimm.h>
48 #include <sys/serengeti.h>
49 #include <sys/fm/util.h>
50 #include <sys/promif.h>
51 #include <sys/plat_datapath.h>
52 
53 sbbc_mailbox_t	*master_mbox = NULL;
54 
55 /*
56  * Panic Shutdown event support
57  */
58 static	kmutex_t	panic_hdlr_lock;
59 
60 /*
61  * The ID of the soft interrupt which triggers the bringing down of a Domain
62  * when a PANIC_SHUTDOWN event is received.
63  */
64 static ddi_softintr_t	panic_softintr_id = 0;
65 
66 static sg_panic_shutdown_t	panic_payload;
67 static sbbc_msg_t		panic_payload_msg;
68 
69 /*
70  * A queue for making sure outgoing messages are in order as ScApp
71  * does not support interleaving messages.
72  */
73 static kcondvar_t	outbox_queue;
74 static kmutex_t		outbox_queue_lock;
75 
76 /*
77  * Handle unsolicited capability message.
78  */
79 static plat_capability_data_t	cap_payload;
80 static sbbc_msg_t		cap_payload_msg;
81 static kmutex_t			cap_msg_hdlr_lock;
82 
83 /*
84  * Datapath error and fault messages arrive unsolicited.  The message data
85  * is contained in a plat_datapath_info_t structure.
86  */
87 typedef struct {
88 	uint8_t		type;		/* CDS, DX, CP */
89 	uint8_t		pad;		/* for alignment */
90 	uint16_t	cpuid;		/* Safari ID of base CPU */
91 	uint32_t	t_value;	/* SERD timeout threshold (seconds) */
92 } plat_datapath_info_t;
93 
94 /*
95  * Unsolicited datapath error messages are processed via a soft interrupt,
96  * triggered in unsolicited interrupt processing.
97  */
98 static	ddi_softintr_t		dp_softintr_id = 0;
99 static	kmutex_t		dp_hdlr_lock;
100 
101 static	plat_datapath_info_t	dp_payload;
102 static	sbbc_msg_t		dp_payload_msg;
103 
104 static char *dperrtype[] = {
105 	DP_ERROR_CDS,
106 	DP_ERROR_DX,
107 	DP_ERROR_RP
108 };
109 
110 /*
111  * Variable indicating if we are already processing requests.
112  * Setting this value must be protected by outbox_queue_lock.
113  */
114 static int		outbox_busy = 0;
115 
116 /*
117  * local stuff
118  */
119 static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t);
120 static int sbbc_mbox_recv_msg();
121 static int mbox_write(struct sbbc_mbox_header *,
122 	struct sbbc_fragment *, sbbc_msg_t *);
123 static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *,
124 	sbbc_msg_t *);
125 static int mbox_has_free_space(struct sbbc_mbox_header *);
126 static void mbox_skip_next_msg(struct sbbc_mbox_header *);
127 static int mbox_read_header(uint32_t, struct sbbc_mbox_header *);
128 static void mbox_update_header(uint32_t, struct sbbc_mbox_header *);
129 static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *);
130 static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t);
131 static void wakeup_next(void);
132 static uint_t sbbc_panic_shutdown_handler(char *arg);
133 static uint_t sbbc_do_fast_shutdown(char *arg);
134 static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp);
135 static uint_t cap_ecc_msg_handler(char *);
136 static uint_t sbbc_datapath_error_msg_handler(char *arg);
137 static uint_t sbbc_datapath_fault_msg_handler(char *arg);
138 static uint_t sbbc_dp_trans_event(char *arg);
139 
140 
141 /*
142  * Interrupt handlers
143  */
144 static int sbbc_mbox_msgin(void);
145 static int sbbc_mbox_msgout(void);
146 static int sbbc_mbox_spacein(void);
147 static int sbbc_mbox_spaceout(void);
148 
149 /*
150  * ECC event mailbox message taskq and parameters
151  */
152 static taskq_t	*sbbc_ecc_mbox_taskq = NULL;
153 static int	sbbc_ecc_mbox_taskq_errs = 0;
154 static int	sbbc_ecc_mbox_send_errs = 0;
155 static int	sbbc_ecc_mbox_inval_errs = 0;
156 static int	sbbc_ecc_mbox_other_errs = 0;
157 int	sbbc_ecc_mbox_err_throttle = ECC_MBOX_TASKQ_ERR_THROTTLE;
158 
159 /*
160  * Called when SBBC driver is loaded
161  * Initialise global mailbox stuff, etc
162  */
163 void
164 sbbc_mbox_init()
165 {
166 	int	i;
167 
168 	master_mbox = kmem_zalloc(sizeof (sbbc_mailbox_t), KM_NOSLEEP);
169 	if (master_mbox == NULL) {
170 		cmn_err(CE_PANIC, "Can't allocate memory for mailbox\n");
171 	}
172 
173 	/*
174 	 * mutex'es for the wait-lists
175 	 */
176 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
177 		mutex_init(&master_mbox->mbox_wait_lock[i],
178 			NULL, MUTEX_DEFAULT, NULL);
179 		master_mbox->mbox_wait_list[i] = NULL;
180 	}
181 
182 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++)
183 		master_mbox->intrs[i] = NULL;
184 
185 	/*
186 	 * Two mailbox channels SC -> OS , read-only
187 	 *			OS -> SC, read/write
188 	 */
189 	master_mbox->mbox_in = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
190 	if (master_mbox->mbox_in == NULL) {
191 		cmn_err(CE_PANIC,
192 			"Can't allocate memory for inbound mailbox\n");
193 	}
194 
195 	master_mbox->mbox_out = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
196 	if (master_mbox->mbox_out == NULL) {
197 		cmn_err(CE_PANIC,
198 			"Can't allocate memory for outbound mailbox\n");
199 	}
200 
201 	mutex_init(&master_mbox->mbox_in->mb_lock, NULL,
202 		MUTEX_DEFAULT, NULL);
203 	mutex_init(&master_mbox->mbox_out->mb_lock, NULL,
204 		MUTEX_DEFAULT, NULL);
205 
206 	/*
207 	 * Add PANIC_SHUTDOWN Event mutex
208 	 */
209 	mutex_init(&panic_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
210 
211 	/* Initialize datapath error message handler mutex */
212 	mutex_init(&dp_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
213 
214 	/* Initialize capability message handler event mutex */
215 	mutex_init(&cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
216 
217 	/*
218 	 * NOT USED YET
219 	 */
220 	master_mbox->mbox_in->mb_type =
221 		master_mbox->mbox_out->mb_type = 0;
222 
223 	cv_init(&outbox_queue, NULL, CV_DEFAULT, NULL);
224 	mutex_init(&outbox_queue_lock, NULL, MUTEX_DEFAULT, NULL);
225 
226 }
227 
228 /*
229  * called when the SBBC driver is unloaded
230  */
231 void
232 sbbc_mbox_fini()
233 {
234 	int	i;
235 	int	err;
236 
237 	/*
238 	 * destroy ECC event mailbox taskq
239 	 */
240 	if (sbbc_ecc_mbox_taskq != NULL) {
241 		taskq_destroy(sbbc_ecc_mbox_taskq);
242 		sbbc_ecc_mbox_taskq = NULL;
243 		sbbc_ecc_mbox_taskq_errs = 0;
244 	}
245 
246 	/*
247 	 * unregister interrupts
248 	 */
249 	(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
250 	(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
251 	(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_IN);
252 	(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_OUT);
253 
254 	/*
255 	 * Remove Panic Shutdown and Datapath Error event support.
256 	 *
257 	 * NOTE: If we have not added the soft interrupt handlers for these
258 	 * then we know that we have not registered the event handlers either.
259 	 */
260 	if (panic_softintr_id != 0) {
261 		ddi_remove_softintr(panic_softintr_id);
262 
263 		err = sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
264 			sbbc_panic_shutdown_handler);
265 		if (err != 0) {
266 			cmn_err(CE_WARN, "Failed to unreg Panic Shutdown "
267 				"handler. Err=%d", err);
268 		}
269 	}
270 	if (dp_softintr_id != 0) {
271 		ddi_remove_softintr(dp_softintr_id);
272 
273 		err = sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
274 			sbbc_datapath_error_msg_handler);
275 		err |= sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
276 			sbbc_datapath_fault_msg_handler);
277 		if (err != 0) {
278 			cmn_err(CE_WARN, "Failed to unreg Datapath Error "
279 				"handler. Err=%d", err);
280 		}
281 	}
282 
283 	/*
284 	 * destroy all its mutex'es, lists etc
285 	 */
286 
287 	/*
288 	 * mutex'es for the wait-lists
289 	 */
290 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
291 		mutex_destroy(&master_mbox->mbox_wait_lock[i]);
292 	}
293 
294 	mutex_destroy(&master_mbox->mbox_in->mb_lock);
295 	mutex_destroy(&master_mbox->mbox_out->mb_lock);
296 
297 	mutex_destroy(&panic_hdlr_lock);
298 	mutex_destroy(&dp_hdlr_lock);
299 
300 	kmem_free(master_mbox->mbox_in, sizeof (sbbc_mbox_t));
301 	kmem_free(master_mbox->mbox_out, sizeof (sbbc_mbox_t));
302 	kmem_free(master_mbox, sizeof (sbbc_mailbox_t));
303 
304 	cv_destroy(&outbox_queue);
305 	mutex_destroy(&outbox_queue_lock);
306 
307 	err = sbbc_mbox_unreg_intr(INFO_MBOX, cap_ecc_msg_handler);
308 	if (err != 0) {
309 		cmn_err(CE_WARN, "Failed to unregister capability message "
310 		    "handler. Err=%d", err);
311 	}
312 
313 	mutex_destroy(&cap_msg_hdlr_lock);
314 }
315 
316 /*
317  * Update iosram_sbbc to the new softstate after a tunnel switch.
318  * Move software interrupts from the old dip to the new dip.
319  */
320 int
321 sbbc_mbox_switch(sbbc_softstate_t *softsp)
322 {
323 	sbbc_intrs_t	*intr;
324 	int		msg_type;
325 	int		rc = 0;
326 	int		err;
327 
328 	if (master_mbox == NULL)
329 		return (ENXIO);
330 
331 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
332 
333 	for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
334 
335 		for (intr = master_mbox->intrs[msg_type]; intr != NULL;
336 			intr = intr->sbbc_intr_next) {
337 
338 			if (intr->sbbc_intr_id) {
339 				ddi_remove_softintr(intr->sbbc_intr_id);
340 
341 				if (ddi_add_softintr(softsp->dip,
342 					DDI_SOFTINT_HIGH,
343 					&intr->sbbc_intr_id, NULL, NULL,
344 					intr->sbbc_handler, intr->sbbc_arg)
345 					!= DDI_SUCCESS) {
346 
347 					cmn_err(CE_WARN,
348 						"Can't add SBBC mailbox "
349 						"softint for msg_type %x\n",
350 							msg_type);
351 					rc = ENXIO;
352 				}
353 			}
354 		}
355 	}
356 
357 	/*
358 	 * Add PANIC_SHUTDOWN Event handler
359 	 */
360 	if (panic_softintr_id) {
361 		ddi_remove_softintr(panic_softintr_id);
362 
363 		err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
364 			&panic_softintr_id, NULL, NULL,
365 			sbbc_do_fast_shutdown, NULL);
366 
367 		if (err != DDI_SUCCESS) {
368 			cmn_err(CE_WARN, "Failed to register Panic "
369 				"Shutdown handler. Err=%d", err);
370 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
371 				sbbc_panic_shutdown_handler);
372 			rc = ENXIO;
373 		}
374 
375 	}
376 	/*
377 	 * Add Datapath Error Event handler
378 	 */
379 	if (dp_softintr_id) {
380 		ddi_remove_softintr(dp_softintr_id);
381 
382 		err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
383 			&dp_softintr_id, NULL, NULL,
384 			sbbc_dp_trans_event, NULL);
385 
386 		if (err != DDI_SUCCESS) {
387 			cmn_err(CE_WARN, "Failed to register Datapath "
388 				"Error Event handler. Err=%d", err);
389 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
390 				sbbc_datapath_error_msg_handler);
391 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
392 				sbbc_datapath_fault_msg_handler);
393 			rc = ENXIO;
394 		}
395 
396 	}
397 
398 	return (rc);
399 }
400 
401 /*
402  * Called when the IOSRAM tunnel is created for the 'chosen' node.
403  *
404  * Read the mailbox header from the IOSRAM
405  * tunnel[SBBC_MAILBOX_KEY]
406  * Register the mailbox interrupt handlers
407  * for messages in/space etc
408  */
409 int
410 sbbc_mbox_create(sbbc_softstate_t *softsp)
411 {
412 	struct sbbc_mbox_header	header;
413 
414 	int	i;
415 	int	err;
416 	int	rc = 0;
417 
418 	/*
419 	 * This function should only be called once when
420 	 * the chosen node is initialized.
421 	 */
422 	ASSERT(MUTEX_HELD(&chosen_lock));
423 
424 	if (master_mbox == NULL)
425 		return (ENXIO);
426 
427 	/*
428 	 * read the header at offset 0
429 	 * check magic/version etc
430 	 */
431 	if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)&header,
432 	    sizeof (struct sbbc_mbox_header))) {
433 
434 		return (rc);
435 	}
436 
437 	/*
438 	 * add the interrupt handlers for the mailbox
439 	 * interrupts
440 	 */
441 	for (i = 0; i < MBOX_INTRS; i++) {
442 		sbbc_intrfunc_t		intr_handler;
443 		uint_t 			*state;
444 		kmutex_t 		*lock;
445 		uint32_t		intr_num;
446 
447 		switch (i) {
448 		case MBOX_MSGIN_INTR:
449 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgin;
450 			intr_num = SBBC_MAILBOX_IN;
451 			break;
452 		case MBOX_MSGOUT_INTR:
453 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgout;
454 			intr_num = SBBC_MAILBOX_OUT;
455 			break;
456 		case MBOX_SPACEIN_INTR:
457 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spacein;
458 			intr_num = SBBC_MAILBOX_SPACE_IN;
459 			break;
460 		case MBOX_SPACEOUT_INTR:
461 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spaceout;
462 			intr_num = SBBC_MAILBOX_SPACE_OUT;
463 			break;
464 		}
465 		state = (uint_t *)&master_mbox->intr_state[i].mbox_intr_state;
466 		lock = &master_mbox->intr_state[i].mbox_intr_lock;
467 		if (iosram_reg_intr(intr_num, intr_handler, (caddr_t)NULL,
468 			state, lock)) {
469 
470 			cmn_err(CE_WARN,
471 				"Can't register Mailbox interrupts \n");
472 		}
473 	}
474 
475 	/*
476 	 * Add PANIC_SHUTDOWN Event handler
477 	 */
478 	panic_payload_msg.msg_buf = (caddr_t)&panic_payload;
479 	panic_payload_msg.msg_len = sizeof (panic_payload);
480 
481 	err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &panic_softintr_id,
482 		NULL, NULL, sbbc_do_fast_shutdown, NULL);
483 
484 	if (err == DDI_SUCCESS) {
485 		err = sbbc_mbox_reg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
486 			sbbc_panic_shutdown_handler, &panic_payload_msg,
487 			NULL, &panic_hdlr_lock);
488 		if (err != 0) {
489 			cmn_err(CE_WARN, "Failed to register Panic "
490 				"Shutdown handler. Err=%d", err);
491 		}
492 
493 	} else {
494 		cmn_err(CE_WARN, "Failed to add Panic Shutdown "
495 			"softintr handler");
496 	}
497 
498 	/*
499 	 * Add Unsolicited Datapath Error Events handler
500 	 */
501 	dp_payload_msg.msg_buf = (caddr_t)&dp_payload;
502 	dp_payload_msg.msg_len = sizeof (dp_payload);
503 
504 	err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &dp_softintr_id,
505 		NULL, NULL, sbbc_dp_trans_event, NULL);
506 
507 	if (err == DDI_SUCCESS) {
508 		err = sbbc_mbox_reg_intr(MBOX_EVENT_DP_ERROR,
509 			sbbc_datapath_error_msg_handler, &dp_payload_msg,
510 			NULL, &dp_hdlr_lock);
511 		err |= sbbc_mbox_reg_intr(MBOX_EVENT_DP_FAULT,
512 			sbbc_datapath_fault_msg_handler, &dp_payload_msg,
513 			NULL, &dp_hdlr_lock);
514 		if (err != 0) {
515 			cmn_err(CE_WARN, "Failed to register Datapath "
516 				"error handler. Err=%d", err);
517 		}
518 
519 	} else {
520 		cmn_err(CE_WARN, "Failed to add Datapath error "
521 			"softintr handler");
522 	}
523 
524 	/*
525 	 * Register an interrupt handler with the sgbbc driver for the
526 	 * unsolicited INFO_MBOX response for the capability bitmap.
527 	 * This message is expected whenever the SC is (re)booted or
528 	 * failed over.
529 	 */
530 	cap_payload_msg.msg_buf = (caddr_t)&cap_payload;
531 	cap_payload_msg.msg_len = sizeof (cap_payload);
532 
533 	err = sbbc_mbox_reg_intr(INFO_MBOX, cap_ecc_msg_handler,
534 	    &cap_payload_msg, NULL, &cap_msg_hdlr_lock);
535 	if (err != 0) {
536 		cmn_err(CE_WARN, "Failed to register capability message"
537 		    " handler with Err=%d", err);
538 	}
539 
540 	/*
541 	 * Now is the opportunity to register
542 	 * the deferred mbox intrs.
543 	 */
544 	sbbc_mbox_post_reg(softsp);
545 
546 	return (rc);
547 }
548 
549 /*
550  * Called when chosen IOSRAM is initialized
551  * to register the deferred mbox intrs.
552  */
553 static void
554 sbbc_mbox_post_reg(sbbc_softstate_t *softsp)
555 {
556 	uint32_t msg_type;
557 	sbbc_intrs_t	*intr;
558 
559 	ASSERT(master_mbox);
560 	for (msg_type = 0;  msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
561 		intr = master_mbox->intrs[msg_type];
562 		while (intr != NULL) {
563 			if (!intr->registered) {
564 				SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_post_reg: "
565 					"postreg for msgtype=%x\n", msg_type);
566 				if (ddi_add_softintr(softsp->dip,
567 					DDI_SOFTINT_HIGH, &intr->sbbc_intr_id,
568 					NULL, NULL, intr->sbbc_handler,
569 					(caddr_t)intr->sbbc_arg)
570 						!= DDI_SUCCESS) {
571 					cmn_err(CE_WARN, "Can't add SBBC "
572 						"deferred mailbox softint \n");
573 				} else
574 					intr->registered = 1;
575 			}
576 			intr = intr->sbbc_intr_next;
577 		}
578 	}
579 }
580 
581 /*
582  * Register a handler for a message type
583  * NB NB NB
584  * arg must be either NULL or the address of a sbbc_fragment
585  * pointer
586  */
587 int
588 sbbc_mbox_reg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler,
589 		sbbc_msg_t *arg, uint_t *state, kmutex_t *lock)
590 {
591 	sbbc_intrs_t	*intr, *previntr;
592 	int		rc = 0;
593 
594 	/*
595 	 * Validate arguments
596 	 */
597 	if (msg_type >= SBBC_MBOX_MSG_TYPES)
598 		return (EINVAL);
599 
600 	/*
601 	 * Verify that we have already set up the master sbbc
602 	 */
603 	if (master_iosram == NULL || master_mbox == NULL)
604 		return (ENXIO);
605 
606 	mutex_enter(&master_iosram->iosram_lock);
607 	msg_type &= SBBC_MSG_TYPE_MASK;
608 	previntr = intr = master_mbox->intrs[msg_type];
609 
610 	/* Find the end of the link list */
611 	while (intr != NULL && intr->sbbc_handler != intr_handler) {
612 
613 		previntr = intr;
614 		intr = intr->sbbc_intr_next;
615 	}
616 
617 	/* Return if the handler has been registered */
618 	if (intr != NULL) {
619 		mutex_exit(&master_iosram->iosram_lock);
620 		return (EBUSY);
621 	}
622 
623 	/*
624 	 * The requested handler has not been installed.
625 	 * Allocate some memory.
626 	 */
627 	intr = kmem_zalloc(sizeof (sbbc_intrs_t), KM_SLEEP);
628 
629 	intr->sbbc_handler  = intr_handler;
630 	intr->sbbc_arg = (caddr_t)arg;
631 	intr->sbbc_intr_state = state;
632 	intr->sbbc_intr_lock = lock;
633 	intr->sbbc_intr_next = NULL;
634 	/* not registered yet */
635 	intr->registered = 0;
636 
637 	if (previntr != NULL)
638 		previntr->sbbc_intr_next = intr;
639 	else
640 		master_mbox->intrs[msg_type] = intr;
641 
642 	/*
643 	 * register only if the chosen IOSRAM is
644 	 * initialized, otherwise defer the registration
645 	 * until IOSRAM initialization.
646 	 */
647 	if (master_iosram->iosram_sbbc) {
648 		if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
649 			DDI_SOFTINT_HIGH,
650 			&intr->sbbc_intr_id, NULL, NULL,
651 			intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
652 			cmn_err(CE_WARN, "Can't add SBBC mailbox softint \n");
653 			rc = ENXIO;
654 		} else
655 			intr->registered = 1;
656 	} else {
657 		SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_reg_intr: "
658 				"deferring msg=%x registration\n", msg_type);
659 	}
660 
661 	mutex_exit(&master_iosram->iosram_lock);
662 
663 	return (rc);
664 }
665 
666 /*
667  * Unregister a handler for a message type
668  */
669 int
670 sbbc_mbox_unreg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler)
671 {
672 	sbbc_intrs_t		*intr, *previntr, *nextintr;
673 
674 	/*
675 	 * Verify that we have already set up the master sbbc
676 	 */
677 	if (master_iosram == NULL || master_mbox == NULL)
678 		return (ENXIO);
679 
680 	msg_type &= SBBC_MSG_TYPE_MASK;
681 
682 	if (msg_type >= SBBC_MBOX_MSG_TYPES ||
683 		intr_handler == (sbbc_intrfunc_t)NULL) {
684 
685 		return (EINVAL);
686 	}
687 
688 	mutex_enter(&master_iosram->iosram_lock);
689 
690 	previntr = intr = master_mbox->intrs[msg_type];
691 
692 	/*
693 	 * No handlers installed
694 	 */
695 	if (intr == NULL) {
696 		mutex_exit(&master_iosram->iosram_lock);
697 		return (EINVAL);
698 	}
699 
700 	while (intr != NULL) {
701 
702 		/* Save the next pointer */
703 		nextintr = intr->sbbc_intr_next;
704 
705 		/* Found a match.  Remove it from the link list */
706 		if (intr->sbbc_handler == intr_handler) {
707 
708 			if (intr->sbbc_intr_id)
709 				ddi_remove_softintr(intr->sbbc_intr_id);
710 
711 			kmem_free(intr, sizeof (sbbc_intrs_t));
712 
713 			if (previntr != master_mbox->intrs[msg_type])
714 				previntr->sbbc_intr_next = nextintr;
715 			else
716 				master_mbox->intrs[msg_type] = nextintr;
717 
718 			break;
719 		}
720 
721 		/* update pointers */
722 		previntr = intr;
723 		intr = nextintr;
724 	}
725 
726 	mutex_exit(&master_iosram->iosram_lock);
727 
728 	return (0);
729 }
730 /*
731  * Interrupt handlers - one for each mailbox
732  * interrupt type
733  */
734 
735 /*
736  * mailbox message received
737  */
738 static int
739 sbbc_mbox_msgin()
740 {
741 	mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
742 	master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_state =
743 		SBBC_INTR_RUNNING;
744 	mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
745 
746 	/*
747 	 * We are only locking the InBox here, not the whole
748 	 * mailbox. This is based on the assumption of
749 	 * complete separation of mailboxes - outbox is
750 	 * read/write, inbox is read-only.
751 	 * We only ever update the producer for the
752 	 * outbox and the consumer for the inbox.
753 	 */
754 	mutex_enter(&master_mbox->mbox_in->mb_lock);
755 
756 	for (;;) {
757 		/*
758 		 * Get as many incoming messages as possible
759 		 */
760 		while (sbbc_mbox_recv_msg() == 0)
761 			/* empty */;
762 
763 		/*
764 		 * send interrupt to SC to let it know that
765 		 * space is available over here
766 		 */
767 		(void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN);
768 
769 		mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].
770 			mbox_intr_lock);
771 		/*
772 		 * Read the inbox one more time to see if new messages
773 		 * has come in after we exit the loop.
774 		 */
775 		if (sbbc_mbox_recv_msg() == 0) {
776 			mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
777 				mbox_intr_lock);
778 		} else {
779 			master_mbox->intr_state[MBOX_MSGIN_INTR].
780 				mbox_intr_state = SBBC_INTR_IDLE;
781 			mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
782 				mbox_intr_lock);
783 			break;
784 		}
785 	}
786 
787 	mutex_exit(&master_mbox->mbox_in->mb_lock);
788 
789 	return (DDI_INTR_CLAIMED);
790 }
791 
792 /*
793  * mailbox message sent
794  */
795 static int
796 sbbc_mbox_msgout()
797 {
798 	/*
799 	 * Should never get this
800 	 */
801 
802 	return (DDI_INTR_CLAIMED);
803 }
804 
805 /*
806  * space in the inbox
807  */
808 static int
809 sbbc_mbox_spacein()
810 {
811 	/*
812 	 * Should never get this
813 	 */
814 
815 	return (DDI_INTR_CLAIMED);
816 }
817 
818 /*
819  * space in the outbox
820  */
821 static int
822 sbbc_mbox_spaceout()
823 {
824 	/*
825 	 * cv_broadcast() the threads waiting on the
826 	 * outbox's mb_full
827 	 */
828 
829 	mutex_enter(&master_mbox->mbox_out->mb_lock);
830 
831 	cv_broadcast(&master_mbox->mbox_out->mb_full);
832 
833 	mutex_exit(&master_mbox->mbox_out->mb_lock);
834 
835 	return (DDI_INTR_CLAIMED);
836 }
837 
838 /*
839  * Client Interface
840  *
841  * The main interface will be
842  *
843  * sbbc_mbox_request_response(sbbc_msg_t *request,
844  * 			sbbc_msg_t *response, time_t wait_time)
845  *
846  * 1) the client calls request_response
847  * 2) a new unique msg ID is assigned for that msg
848  * 3) if there is space available in the outbox
849  *    - the request msg is written to the mbox_out mailbox
850  *	and the mailbox info updated.
851  *    - allocate a sbbc_msg_waiter struct for this
852  *	message, initialise the w_cv condvar.
853  *    - get the mailbox mbox_wait_lock mutex for this
854  *      message type
855  *    - the response msg is put on the mbox_wait_list for
856  *	that message type to await the SC's response
857  *    - wait on the w_cv condvar protected by the
858  *	mbox_wait_lock
859  *    - SBBC_MAILBOX_OUT interrupt is sent to the SC
860  *
861  * 4) if no space in the outbox,
862  *    - the request message blocks waiting
863  *	for a SBBC_MAILBOX_SPACE_OUT interrupt
864  *      It will block on the mailbox mb_full condvar.
865  *    - go to (3) above
866  * 5) When we get a SBBC_MAILBOX_IN interrupt.
867  *    - read the message ID of the next message (FIFO)
868  *    - find that ID on the wait list
869  *    - no wait list entry => unsolicited message. If theres
870  *      a handler, trigger it
871  *    - if someone is waiting, read the message in from
872  *	SRAM, handling fragmentation, wraparound, etc
873  *    - if the whole message has been read, signal
874  *	the waiter
875  *    - read next message until mailbox empty
876  *    - send SBBC_MAILBOX_SPACE_IN interrupt to the SC
877  *
878  * 6) If a response is required and none is received, the client
879  *	will timeout after <wait_time> seconds and the message
880  *	status will be set to ETIMEDOUT.
881  */
882 int
883 sbbc_mbox_request_response(sbbc_msg_t *request,
884 		sbbc_msg_t *response, time_t wait_time)
885 {
886 
887 	struct sbbc_msg_waiter	*waiter;
888 	uint_t			msg_id;
889 	int			rc = 0;
890 	int			flags;
891 	uint16_t		msg_type;
892 	clock_t			stop_time;
893 	clock_t			clockleft;
894 	kmutex_t		*mbox_wait_lock;
895 	kmutex_t		*mb_lock;
896 	static fn_t		f = "sbbc_mbox_request_response";
897 
898 	if ((request == NULL) ||
899 		(request->msg_type.type >= SBBC_MBOX_MSG_TYPES) ||
900 		((response != NULL) &&
901 		(response->msg_type.type >= SBBC_MBOX_MSG_TYPES)))
902 		return (EINVAL);
903 
904 	msg_type = request->msg_type.type;
905 
906 	/*
907 	 * Verify that we have already set up the master sbbc
908 	 */
909 	if (master_mbox == NULL)
910 		return (ENXIO);
911 	mbox_wait_lock = &master_mbox->mbox_wait_lock[msg_type];
912 
913 	flags = WAIT_FOR_REPLY|WAIT_FOR_SPACE;
914 
915 	/*
916 	 * We want to place a lower limit on the shortest amount of time we
917 	 * will wait before timing out while communicating with the SC via
918 	 * the mailbox.
919 	 */
920 	if (wait_time < sbbc_mbox_min_timeout)
921 		wait_time = sbbc_mbox_default_timeout;
922 
923 	stop_time = ddi_get_lbolt() + wait_time * drv_usectohz(MICROSEC);
924 
925 	/*
926 	 * If there is a message being processed, sleep until it is our turn.
927 	 */
928 	mutex_enter(&outbox_queue_lock);
929 
930 	/*
931 	 * allocate an ID for this message, let it wrap
932 	 * around transparently.
933 	 * msg_id == 0 is unsolicited message
934 	 */
935 	msg_id = ++(master_mbox->mbox_msg_id);
936 	if (msg_id == 0)
937 		msg_id = ++(master_mbox->mbox_msg_id);
938 
939 	SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n",
940 		f, msg_id, request->msg_len);
941 
942 	/*
943 	 * A new message can actually grab the lock before the thread
944 	 * that has just been signaled.  Therefore, we need to double
945 	 * check to make sure that outbox_busy is not already set
946 	 * after we wake up.
947 	 *
948 	 * Potentially this could mean starvation for certain unfortunate
949 	 * threads that keep getting woken up and putting back to sleep.
950 	 * But the window of such contention is very small to begin with.
951 	 */
952 	while (outbox_busy) {
953 
954 		clockleft = cv_timedwait(&outbox_queue, &outbox_queue_lock,
955 			stop_time);
956 
957 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up\n", f, msg_id);
958 
959 		/*
960 		 * If we have timed out, set status to ETIMEOUT and return.
961 		 */
962 		if (clockleft < 0) {
963 			SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
964 				f, msg_id);
965 			cmn_err(CE_NOTE,
966 				"Timed out obtaining SBBC outbox lock");
967 			request->msg_status = ETIMEDOUT;
968 			if (response != NULL)
969 				response->msg_status = ETIMEDOUT;
970 			mutex_exit(&outbox_queue_lock);
971 			return (ETIMEDOUT);
972 		}
973 	}
974 
975 	outbox_busy = 1;
976 	mutex_exit(&outbox_queue_lock);
977 
978 	/*
979 	 * We are only locking the OutBox from here, not the whole
980 	 * mailbox. This is based on the assumption of
981 	 * complete separation of mailboxes - outbox is
982 	 * read/write, inbox is read-only.
983 	 * We only ever update the producer for the
984 	 * outbox and the consumer for the inbox.
985 	 */
986 	mb_lock = &master_mbox->mbox_out->mb_lock;
987 	mutex_enter(mb_lock);
988 
989 	/*
990 	 * No response expected ? Just send the message and return
991 	 */
992 	if (response == NULL) {
993 		rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time,
994 			stop_time);
995 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
996 		    f, msg_id, rc);
997 
998 		wakeup_next();
999 
1000 		mutex_exit(mb_lock);
1001 		request->msg_status = rc;
1002 		return (rc);
1003 	}
1004 
1005 	/*
1006 	 * allocate/initialise a waiter
1007 	 */
1008 	waiter = kmem_zalloc(sizeof (struct sbbc_msg_waiter), KM_NOSLEEP);
1009 
1010 	if (waiter == (struct sbbc_msg_waiter *)NULL) {
1011 		cmn_err(CE_WARN, "SBBC Mailbox can't allocate waiter\n");
1012 
1013 		wakeup_next();
1014 
1015 		mutex_exit(mb_lock);
1016 		return (ENOMEM);
1017 	}
1018 
1019 	waiter->w_id = 0;	/* Until we get an ID from the send */
1020 	waiter->w_msg = response;
1021 	waiter->w_msg->msg_status = EINPROGRESS;
1022 
1023 	cv_init(&waiter->w_cv, NULL, CV_DEFAULT, NULL);
1024 
1025 	rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, stop_time);
1026 
1027 	wakeup_next();
1028 
1029 	if (rc != 0) {
1030 
1031 		request->msg_status = response->msg_status = rc;
1032 		mutex_exit(mb_lock);
1033 
1034 		/* Free the waiter */
1035 		cv_destroy(&waiter->w_cv);
1036 		kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
1037 
1038 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
1039 		    f, msg_id, rc);
1040 
1041 		return (rc);
1042 	}
1043 
1044 	waiter->w_id = msg_id;
1045 
1046 	/*
1047 	 * Lock this waiter list and add the waiter
1048 	 */
1049 	mutex_enter(mbox_wait_lock);
1050 
1051 	if (master_mbox->mbox_wait_list[msg_type] == NULL) {
1052 		master_mbox->mbox_wait_list[msg_type] = waiter;
1053 		waiter->w_next = NULL;
1054 	} else {
1055 		struct sbbc_msg_waiter	*tmp;
1056 		tmp = master_mbox->mbox_wait_list[msg_type];
1057 		master_mbox->mbox_wait_list[msg_type] = waiter;
1058 		waiter->w_next = tmp;
1059 	}
1060 
1061 	mutex_exit(mb_lock);
1062 
1063 	/*
1064 	 * wait here for a response to our message
1065 	 * holding the mbox_wait_lock for the list ensures
1066 	 * that the interrupt handler can't get in before
1067 	 * we block.
1068 	 * NOTE: We use the request msg_type for the
1069 	 *	 the wait_list. This ensures that  the
1070 	 *	 msg_type won't change.
1071 	 */
1072 	clockleft = cv_timedwait(&waiter->w_cv, mbox_wait_lock, stop_time);
1073 
1074 	SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n",
1075 		f, msg_id);
1076 
1077 	/*
1078 	 * If we have timed out, set msg_status to ETIMEDOUT,
1079 	 * and remove the waiter from the waiter list.
1080 	 */
1081 	if (clockleft < 0) {
1082 		/*
1083 		 * Remove the waiter from the waiter list.
1084 		 * If we can't find the waiter in the list,
1085 		 * 1. msg_status == EINPROGRESS
1086 		 *    It is being processed.  We will give it
1087 		 *    a chance to finish.
1088 		 * 2. msg_status != EINPROGRESS
1089 		 *    It is done processing.  We can safely
1090 		 *    remove it.
1091 		 * If we can find the waiter, it has timed out.
1092 		 */
1093 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
1094 			f, msg_id);
1095 		if (mbox_find_waiter(msg_type, msg_id) == NULL) {
1096 			if (waiter->w_msg->msg_status == EINPROGRESS) {
1097 				SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x "
1098 					"complete.\n", f, msg_id);
1099 				cv_wait(&waiter->w_cv, mbox_wait_lock);
1100 			}
1101 		} else {
1102 			SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x "
1103 				"to ETIMEDOUT\n", f, msg_id);
1104 			cmn_err(CE_NOTE, "Timed out waiting for SC response");
1105 			rc = waiter->w_msg->msg_status = ETIMEDOUT;
1106 		}
1107 	}
1108 
1109 	/*
1110 	 * lose the waiter
1111 	 */
1112 	cv_destroy(&waiter->w_cv);
1113 	kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
1114 
1115 	mutex_exit(mbox_wait_lock);
1116 
1117 	return (rc);
1118 
1119 }
1120 
1121 static void
1122 wakeup_next()
1123 {
1124 	/*
1125 	 * Done sending the current message or encounter an error.
1126 	 * Wake up the one request in the outbox_queue.
1127 	 */
1128 	mutex_enter(&outbox_queue_lock);
1129 	outbox_busy = 0;
1130 	cv_signal(&outbox_queue);
1131 	mutex_exit(&outbox_queue_lock);
1132 }
1133 
1134 
1135 /* ARGSUSED */
1136 int
1137 sbbc_mbox_send_msg(sbbc_msg_t *msg, int flags, uint_t msg_id,
1138 	time_t wait_time, clock_t stop_time)
1139 {
1140 	struct sbbc_mbox_header	header;
1141 	struct sbbc_fragment	frag;
1142 	int			rc = 0;
1143 	int			bytes_written;
1144 	uint32_t		intr_enabled;
1145 	clock_t			clockleft;
1146 	static fn_t		f = "sbbc_mbox_send_msg";
1147 
1148 	/*
1149 	 * First check that the SC has enabled its mailbox
1150 	 */
1151 	rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
1152 		(caddr_t)&intr_enabled, sizeof (intr_enabled));
1153 
1154 	if (rc)
1155 		return (rc);
1156 
1157 	if (!(intr_enabled & SBBC_MAILBOX_OUT))
1158 		return (ENOTSUP);
1159 
1160 	/*
1161 	 * read the mailbox header
1162 	 */
1163 	if (rc = mbox_read_header(SBBC_OUTBOX, &header))
1164 		return (rc);
1165 
1166 	/*
1167 	 * Allocate/initialise a fragment for this message
1168 	 */
1169 	frag.f_id = msg_id;
1170 	frag.f_type = msg->msg_type;
1171 	frag.f_status = 0;
1172 	frag.f_total_len = msg->msg_len;
1173 	frag.f_frag_offset = 0;
1174 	/*
1175 	 * Throw in the message data
1176 	 */
1177 	bcopy(&msg->msg_data, &frag.f_data, sizeof (msg->msg_data));
1178 
1179 	/*
1180 	 * If not enough space is available
1181 	 * write what we can and wait for
1182 	 * an interrupt to tell us that more
1183 	 * space is available
1184 	 */
1185 
1186 	bytes_written = 0;
1187 	do {
1188 		rc = mbox_write(&header, &frag, msg);
1189 
1190 		if (rc != 0 && rc != ENOSPC) {
1191 			return (rc);
1192 		}
1193 
1194 		if (rc == 0) {
1195 			/*
1196 			 * Always tell the SC when there is a message.
1197 			 * Ignore returned value as not being able to
1198 			 * signal the SC about space available does
1199 			 * not stop the SC from processing input.
1200 			 */
1201 			(void) iosram_send_intr(SBBC_MAILBOX_OUT);
1202 		}
1203 
1204 		bytes_written += frag.f_frag_len;
1205 		frag.f_frag_offset += frag.f_frag_len;
1206 		if ((bytes_written < msg->msg_len) || (rc == ENOSPC)) {
1207 
1208 			if (mbox_has_free_space(&header) <=
1209 				sizeof (struct sbbc_fragment)) {
1210 
1211 				int tmprc;
1212 
1213 				clockleft = cv_timedwait(
1214 					&master_mbox->mbox_out->mb_full,
1215 					&master_mbox->mbox_out->mb_lock,
1216 					stop_time);
1217 
1218 				/* Return ETIMEDOUT if we timed out */
1219 				if (clockleft < 0) {
1220 					SGSBBC_DBG_MBOX("%s: msg_id = 0x%x "
1221 						"has timed out\n", f, msg_id);
1222 					cmn_err(CE_NOTE,
1223 						"Timed out sending message "
1224 						"to SC");
1225 					return (ETIMEDOUT);
1226 				}
1227 
1228 				/* Read updated header from IOSRAM */
1229 				if (tmprc = mbox_read_header(SBBC_OUTBOX,
1230 				    &header)) {
1231 
1232 					return (tmprc);
1233 				}
1234 			}
1235 		}
1236 
1237 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, "
1238 			"msg_len = 0x%x\n", f,
1239 				msg_id, bytes_written, msg->msg_len);
1240 	} while ((bytes_written < msg->msg_len) || (rc == ENOSPC));
1241 
1242 	/*
1243 	 * this could be a spurious interrupt
1244 	 * as the SC may be merrily readings its
1245 	 * mail even as send, but what can you do ? No
1246 	 * synchronization method between SC <-> OS
1247 	 * SRAM data eaters means that this is inevitable.
1248 	 * It would take a bigger brain to fix this.
1249 	 *
1250 	 */
1251 	(void) iosram_send_intr(SBBC_MAILBOX_OUT);
1252 
1253 	return (rc);
1254 }
1255 
1256 
1257 /*
1258  * get next message
1259  * Read the next message from SRAM
1260  * Check if theres an entry on the wait queue
1261  * for this message
1262  * If yes, read the message in and signal
1263  * the waiter (if all the message has been received)
1264  * No, its unsolicited, if theres a handler installed for
1265  * this message type trigger it, otherwise toss
1266  * the message
1267  */
1268 int
1269 sbbc_mbox_recv_msg()
1270 {
1271 	struct sbbc_mbox_header	header;
1272 	struct sbbc_fragment	frag;
1273 	sbbc_msg_t		tmpmsg;	/* Temporary msg storage */
1274 	int			rc = 0, i, first_hdlr, last_hdlr;
1275 	uint32_t		intr_enabled;
1276 	sbbc_intrs_t		*intr;
1277 	struct sbbc_msg_waiter	*waiter;
1278 	uint16_t		type;	/* frag.f_type.type */
1279 	uint32_t		f_id;	/* frag.f_id */
1280 	uint32_t		f_frag_offset, f_frag_len;
1281 	kmutex_t		*mbox_wait_lock;
1282 	static fn_t		f = "sbbc_mbox_recv_msg";
1283 
1284 	/*
1285 	 * First check that the OS has enabled its mailbox
1286 	 */
1287 	rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
1288 		(caddr_t)&intr_enabled, sizeof (intr_enabled));
1289 
1290 	if (rc) {
1291 		return (rc);
1292 	}
1293 
1294 	if (!(intr_enabled & SBBC_MAILBOX_IN))
1295 		return (ENOTSUP);
1296 
1297 	/*
1298 	 * read the mailbox header
1299 	 */
1300 	if (rc = mbox_read_header(SBBC_INBOX, &header))
1301 		return (rc);
1302 
1303 	/*
1304 	 * check if any messages available. If
1305 	 * consumer == producer then no more
1306 	 * messages
1307 	 */
1308 	if ((header.mailboxes[SBBC_INBOX].mbox_consumer ==
1309 		header.mailboxes[SBBC_INBOX].mbox_producer)) {
1310 
1311 		return (-1);
1312 	}
1313 
1314 	/*
1315 	 * read the fragment header for this message
1316 	 */
1317 	if (rc = mbox_read_frag(&header, &frag)) {
1318 
1319 		return (rc);
1320 	}
1321 
1322 	/* Save to local variable for easy reading */
1323 	type = frag.f_type.type;
1324 	f_id = frag.f_id;
1325 
1326 	SGSBBC_DBG_MBOX("%s: f_id = 0x%x\n", f, f_id);
1327 
1328 	/*
1329 	 * check the message type. If its invalid, we will
1330 	 * just toss the message
1331 	 */
1332 	if (type >= SBBC_MBOX_MSG_TYPES) {
1333 		goto done;
1334 	}
1335 
1336 	/*
1337 	 * if theres no waiters for this message type, and theres
1338 	 * no message handler installed, toss it.
1339 	 *
1340 	 * Unsolicited messages (f_id == 0) are tricky because we won't know
1341 	 * when the handler has finished so that we can
1342 	 * remove the message, so, given the small brains in operation
1343 	 * here, what we do is restrict junk mail to zero-length
1344 	 * messages, then we allocate a fragment using kmem,
1345 	 * make a copy of the fragment in this memory,
1346 	 * pass this pointer to the fragment, then skip the message.
1347 	 * So even if there is data associated with the junkmail,
1348 	 * the message handler doesn't get to see it
1349 	 * We expect the mesaage handler to free the memory.
1350 	 */
1351 	if (type == SBBC_BROADCAST_MSG) {
1352 		/*
1353 		 * Broadcast message, trigger all handlers
1354 		 */
1355 		first_hdlr = 0;
1356 		last_hdlr = SBBC_MBOX_MSG_TYPES - 1;
1357 	} else if ((master_mbox->mbox_wait_list[type] == NULL) || (f_id == 0)) {
1358 		/*
1359 		 * Theres no waiters, or its unsolicited anyway
1360 		 */
1361 		first_hdlr = last_hdlr = type;
1362 	} else {
1363 		/*
1364 		 * check the fragment message type, look at the wait list for
1365 		 * that type to find its associated message
1366 		 *
1367 		 * First find the message. If we get it, take it off
1368 		 * the waiter list and read the data. We will
1369 		 * put it back on the list if necessary.
1370 		 * This avoids the problem of a second message-in
1371 		 * interrupt playing with this waiter.
1372 		 * This will cut down on mutex spinning on the wait
1373 		 * list locks, also, expect the next fragment to be
1374 		 * for this messageso we might as well have it at the
1375 		 * start of the list.
1376 		 *
1377 		 * its possible that a return message has a different type,
1378 		 * (possible but not recommended!). So, if we don't find
1379 		 * it on the list pointed to by the request type,
1380 		 * go look at all the other lists
1381 		 */
1382 
1383 		mbox_wait_lock = &master_mbox->mbox_wait_lock[type];
1384 
1385 		mutex_enter(mbox_wait_lock);
1386 		if ((waiter = mbox_find_waiter(type, f_id)) == NULL) {
1387 			for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
1388 				if (i == type)
1389 					continue;
1390 				if ((waiter = mbox_find_waiter(i, f_id))
1391 					!= NULL)
1392 					break;
1393 			}
1394 		}
1395 		mutex_exit(mbox_wait_lock);
1396 
1397 		if (waiter == NULL) {
1398 			rc = -1;
1399 			/*
1400 			 * there's no waiter for this message, but that
1401 			 * could mean that this message is the start of
1402 			 * a send/receive to us, and every 'first' request
1403 			 * must by definition be unsolicited,
1404 			 * so trigger the handler
1405 			 */
1406 			first_hdlr = last_hdlr = type;
1407 		} else {
1408 			SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, "
1409 				"msg_len = 0x%x\n",
1410 					f, f_id, waiter->w_id,
1411 					waiter->w_msg->msg_len);
1412 
1413 			rc = mbox_read(&header, &frag, waiter->w_msg);
1414 
1415 			SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, "
1416 				"len = 0x%x, total_len = 0x%x\n",
1417 					f, frag.f_id, frag.f_frag_offset,
1418 					frag.f_frag_len, frag.f_total_len);
1419 
1420 			if (rc || ((frag.f_frag_offset + frag.f_frag_len) ==
1421 				frag.f_total_len)) {
1422 				/*
1423 				 * failed or all the message has been read in
1424 				 */
1425 				mutex_enter(mbox_wait_lock);
1426 				waiter->w_msg->msg_status = (rc == ENOMEM)?
1427 					rc : frag.f_status;
1428 				SGSBBC_DBG_MBOX("%s: msg_status = %d\n",
1429 					f, waiter->w_msg->msg_status);
1430 				cv_signal(&waiter->w_cv);
1431 				mutex_exit(mbox_wait_lock);
1432 
1433 			} else {
1434 				/*
1435 				 * back on the wait list
1436 				 */
1437 				mutex_enter(mbox_wait_lock);
1438 				if (waiter->w_msg->msg_status == ETIMEDOUT) {
1439 					cv_signal(&waiter->w_cv);
1440 					mutex_exit(mbox_wait_lock);
1441 					goto done;
1442 				}
1443 
1444 				if (master_mbox->mbox_wait_list[type] == NULL) {
1445 					master_mbox->mbox_wait_list[type] =
1446 						waiter;
1447 					waiter->w_next = NULL;
1448 				} else {
1449 					struct sbbc_msg_waiter	*tmp;
1450 					tmp = master_mbox->mbox_wait_list[type];
1451 					master_mbox->mbox_wait_list[type] =
1452 						waiter;
1453 					waiter->w_next = tmp;
1454 				}
1455 				mutex_exit(mbox_wait_lock);
1456 			}
1457 			goto done;
1458 		}
1459 	}
1460 
1461 	/*
1462 	 * Set msg_len to f_frag_len so msg_buf will be large enough
1463 	 * to contain what is in the fragment.
1464 	 */
1465 	f_frag_len = tmpmsg.msg_len = frag.f_frag_len;
1466 	/*
1467 	 * Save the f_frag_offset for copying into client's space.
1468 	 * Set frag.f_frag_offset to 0 so we don't have to allocate
1469 	 * too much space for reading in the message.
1470 	 */
1471 	f_frag_offset = frag.f_frag_offset;
1472 	frag.f_frag_offset = 0;
1473 
1474 	/* Allocate space for msg_buf */
1475 	if (f_frag_len != 0 && (tmpmsg.msg_buf =
1476 		kmem_alloc(f_frag_len, KM_NOSLEEP)) == NULL) {
1477 
1478 		rc = ENOMEM;
1479 		cmn_err(CE_WARN, "Can't allocate memory"
1480 			" for unsolicited messages\n");
1481 	} else {
1482 		/* Save the incoming message in tmpmsg */
1483 		rc = mbox_read(&header, &frag, &tmpmsg);
1484 
1485 		for (i = first_hdlr; rc == 0 && i <= last_hdlr; i++) {
1486 
1487 			intr = master_mbox->intrs[i];
1488 			if ((intr == NULL) || (intr->sbbc_intr_id == 0)) {
1489 				continue;
1490 			}
1491 
1492 			while (intr != NULL) {
1493 				/*
1494 				 * If the client has allocated enough space
1495 				 * for incoming message, copy into the
1496 				 * client buffer.
1497 				 */
1498 				sbbc_msg_t *arg = (sbbc_msg_t *)intr->sbbc_arg;
1499 				if (arg != (void *)NULL) {
1500 					if (arg->msg_len >= frag.f_total_len) {
1501 						if (f_frag_len > 0)
1502 							bcopy(tmpmsg.msg_buf,
1503 								arg->msg_buf +
1504 								f_frag_offset,
1505 								f_frag_len);
1506 					} else {
1507 						arg->msg_status = ENOMEM;
1508 					}
1509 				}
1510 
1511 				/*
1512 				 * Only trigger the interrupt when we
1513 				 * have received the whole message.
1514 				 */
1515 				if (f_frag_offset + f_frag_len ==
1516 					frag.f_total_len) {
1517 
1518 					ddi_trigger_softintr(
1519 						intr->sbbc_intr_id);
1520 				}
1521 				intr = intr->sbbc_intr_next;
1522 			}
1523 		}
1524 
1525 		if (f_frag_len != 0) {
1526 			/* Don't forget to free the buffer */
1527 			kmem_free(tmpmsg.msg_buf, f_frag_len);
1528 		}
1529 	}
1530 done:
1531 	mbox_skip_next_msg(&header);
1532 	return (rc);
1533 }
1534 
1535 /*
1536  * available free space in the outbox
1537  */
1538 static int
1539 mbox_has_free_space(struct sbbc_mbox_header *header)
1540 {
1541 	uint32_t	space = 0;
1542 
1543 	ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
1544 
1545 	if (header->mailboxes[SBBC_OUTBOX].mbox_producer ==
1546 		header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
1547 		/*
1548 		 * mailbox is empty
1549 		 */
1550 		space += header->mailboxes[SBBC_OUTBOX].mbox_len -
1551 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
1552 		space +=
1553 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
1554 	} else if (header->mailboxes[SBBC_OUTBOX].mbox_producer >
1555 		header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
1556 		space += header->mailboxes[SBBC_OUTBOX].mbox_len -
1557 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
1558 		space += header->mailboxes[SBBC_OUTBOX].mbox_consumer;
1559 	} else {
1560 		/*
1561 		 * mailbox wrapped around
1562 		 */
1563 		space += header->mailboxes[SBBC_OUTBOX].mbox_consumer -
1564 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
1565 	}
1566 
1567 	/*
1568 	 * Need to make sure that the mailbox never
1569 	 * gets completely full, as consumer == producer is
1570 	 * our test for empty, so we drop MBOX_ALIGN_BYTES.
1571 	 */
1572 
1573 	if (space >= MBOX_ALIGN_BYTES)
1574 		space -= MBOX_ALIGN_BYTES;
1575 	else
1576 		space = 0;
1577 
1578 	return (space);
1579 
1580 }
1581 /*
1582  * Write the data to IOSRAM
1583  * Update the SRAM mailbox header
1584  * Update the local mailbox pointers
1585  * Only write a single fragment. If possible,
1586  * put the whole message into a fragment.
1587  *
1588  * Note: We assume that there is no 'max' message
1589  *	 size. We will just keep fragmenting.
1590  * Note: We always write to SBBC_OUTBOX and
1591  *	 read from SBBC_INBOX
1592  *
1593  * If we get an error at any time, return immediately
1594  * without updating the mailbox header in SRAM
1595  */
1596 static int
1597 mbox_write(struct sbbc_mbox_header *header,
1598 	struct sbbc_fragment *frag, sbbc_msg_t *msg)
1599 {
1600 	int		bytes_written, bytes_remaining, free_space;
1601 	int		rc = 0;
1602 	caddr_t		src;
1603 	uint32_t	sram_dst;
1604 	int		space_at_end, space_at_start;
1605 	uint32_t	mbox_offset, mbox_len;
1606 	uint32_t	mbox_producer, mbox_consumer;
1607 	uint32_t	f_total_len, f_frag_offset;
1608 	uint32_t	frag_header_size;
1609 	static fn_t	f = "mbox_write";
1610 
1611 	ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
1612 
1613 	/*
1614 	 * Save to local variables to make code more readable
1615 	 */
1616 	mbox_offset = header->mailboxes[SBBC_OUTBOX].mbox_offset;
1617 	mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
1618 	mbox_producer = header->mailboxes[SBBC_OUTBOX].mbox_producer;
1619 	mbox_consumer = header->mailboxes[SBBC_OUTBOX].mbox_consumer;
1620 	f_total_len = frag->f_total_len;
1621 	f_frag_offset = frag->f_frag_offset;
1622 	frag_header_size = sizeof (struct sbbc_fragment);
1623 
1624 	SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, "
1625 		"mbox_producer = 0x%x\n", f, mbox_consumer, mbox_producer);
1626 
1627 	/*
1628 	 * Write pointer in SRAM
1629 	 */
1630 	sram_dst = mbox_offset + mbox_producer;
1631 
1632 	/*
1633 	 * NB We assume that the consumer stays constant
1634 	 *    during the write. It may not necessarily
1635 	 *    be the case but it won't cause us any problems, just means
1636 	 *    we fragment more than is absolutely necessary
1637 	 *
1638 	 * possible cases
1639 	 * 1) consumer == producer, mailbox empty
1640 	 *	space_at_end == mailbox end - producer
1641 	 *	space_at_start == producer - MBOX_ALIGN_BYTES
1642 	 * 2) producer < consumer
1643 	 *	space_at_end = (consumer - producer - MBOX_ALIGN_BYTES)
1644 	 *	space_at_start == 0
1645 	 * 3) producer > consumer
1646 	 *	space_at_end = mailbox end - producer
1647 	 *	space_at_start = consumer - MBOX_ALIGN_BYTES
1648 	 *
1649 	 * (space - MBOX_ALIGN_BYTES) because we need to avoid the
1650 	 * scenario where the producer wraps around completely and
1651 	 * producer == consumer, as this is our test for 'empty'.
1652 	 * Also we want it to be 8-byte aligned.
1653 	 * Note: start is assumed = 0
1654 	 */
1655 	if (mbox_producer < mbox_consumer) {
1656 		space_at_end = mbox_consumer - mbox_producer - MBOX_ALIGN_BYTES;
1657 		if (space_at_end < 0)
1658 			space_at_end = 0;
1659 		space_at_start = 0;
1660 	} else {
1661 		space_at_end = mbox_len - mbox_producer;
1662 		if (mbox_consumer == 0)
1663 			space_at_end -= MBOX_ALIGN_BYTES;
1664 		space_at_start = mbox_consumer - MBOX_ALIGN_BYTES;
1665 		if (space_at_start < 0)
1666 			space_at_start = 0;
1667 	}
1668 
1669 	SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
1670 		f, space_at_end, space_at_start);
1671 
1672 	free_space = space_at_end + space_at_start;
1673 
1674 	if (free_space < frag_header_size) {
1675 		/*
1676 		 * can't even write a fragment header, so just return
1677 		 * the caller will block waiting for space
1678 		 */
1679 		frag->f_frag_len = 0;
1680 		return (ENOSPC);
1681 	}
1682 
1683 	/*
1684 	 * How many bytes will be in the fragment ?
1685 	 */
1686 	bytes_remaining = f_total_len - f_frag_offset;
1687 	frag->f_frag_len = min(bytes_remaining, free_space - frag_header_size);
1688 
1689 	SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n",
1690 		f, sram_dst);
1691 
1692 	/*
1693 	 * we can write the fragment header and some data
1694 	 * First, the fragment header
1695 	 */
1696 	if (space_at_end >=  frag_header_size) {
1697 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, (caddr_t)frag,
1698 			frag_header_size);
1699 		if (rc)
1700 			return (rc);
1701 
1702 		sram_dst = (uint32_t)(sram_dst + frag_header_size);
1703 		/*
1704 		 * Wrap around if we reach the end
1705 		 */
1706 		if (sram_dst >= (mbox_len + mbox_offset)) {
1707 			sram_dst = mbox_offset;
1708 		}
1709 		space_at_end -= frag_header_size;
1710 	} else {
1711 		/* wraparound */
1712 		if (space_at_end) {
1713 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
1714 				(caddr_t)frag, space_at_end);
1715 			if (rc)
1716 				return (rc);
1717 			sram_dst = (uint32_t)mbox_offset;
1718 		}
1719 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
1720 			(caddr_t)((caddr_t)frag + space_at_end),
1721 			(frag_header_size - space_at_end));
1722 		if (rc)
1723 			return (rc);
1724 		sram_dst += frag_header_size - space_at_end;
1725 		space_at_start -= (frag_header_size - space_at_end);
1726 		space_at_end = 0;
1727 	}
1728 
1729 	SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
1730 		f, space_at_end, space_at_start);
1731 
1732 	/*
1733 	 * Now the fragment data
1734 	 */
1735 	free_space -= frag_header_size;
1736 	src = (caddr_t)(msg->msg_buf + f_frag_offset);
1737 	bytes_written = 0;
1738 	if (space_at_end) {
1739 		SGSBBC_DBG_MBOX("%s: writing data:sram_dst = 0x%x, "
1740 			"bytes_remaining = 0x%x\n",
1741 				f, sram_dst, bytes_remaining);
1742 
1743 		if (space_at_end < bytes_remaining)
1744 			bytes_written = space_at_end;
1745 		else
1746 			bytes_written = bytes_remaining;
1747 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
1748 			bytes_written);
1749 		if (rc)
1750 			return (rc);
1751 
1752 		sram_dst = (uint32_t)(sram_dst + bytes_written);
1753 		/*
1754 		 * Wrap around if we reach the end
1755 		 */
1756 		if (sram_dst >= (mbox_len + mbox_offset)) {
1757 			sram_dst = mbox_offset;
1758 		}
1759 		src = (caddr_t)(src + bytes_written);
1760 		bytes_remaining -= bytes_written;
1761 	}
1762 
1763 	if ((bytes_remaining > 0) && space_at_start) {
1764 		SGSBBC_DBG_MBOX("%s: writing the rest:sram_dst = 0x%x, "
1765 			"bytes_remaining = 0x%x\n",
1766 				f, sram_dst, bytes_remaining);
1767 		if (space_at_start < bytes_remaining) {
1768 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
1769 				space_at_start);
1770 			bytes_written += space_at_start;
1771 		} else {
1772 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
1773 				bytes_remaining);
1774 			bytes_written += bytes_remaining;
1775 		}
1776 		if (rc)
1777 			return (rc);
1778 	}
1779 
1780 	frag->f_frag_len = bytes_written;
1781 
1782 	/*
1783 	 * update header->mbox_producer (bytes_written + frag_size)
1784 	 */
1785 	sram_dst = mbox_producer + bytes_written + frag_header_size;
1786 	if (sram_dst >= mbox_len) {
1787 		sram_dst = sram_dst % mbox_len;
1788 	}
1789 
1790 	SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, "
1791 		"bytes_written = 0x%x\n", f, sram_dst, bytes_written);
1792 
1793 	header->mailboxes[SBBC_OUTBOX].mbox_producer = sram_dst;
1794 
1795 	mbox_update_header(SBBC_OUTBOX, header);
1796 
1797 
1798 	return (rc);
1799 }
1800 
1801 
1802 /*
1803  * Get the next frag from IOSRAM.
1804  * Write it to the corresponding msg buf.
1805  * The caller must update the SRAM pointers etc.
1806  */
1807 static int
1808 mbox_read(struct sbbc_mbox_header *header,
1809 	struct sbbc_fragment *frag, sbbc_msg_t *msg)
1810 {
1811 	int			rc = 0;
1812 	uint32_t		sram_src, sram_end;
1813 	caddr_t			msg_buf;
1814 	int			bytes_at_start, bytes_at_end;
1815 	int			bytes_to_read;
1816 	uint32_t		frag_header_size, frag_total_size;
1817 	uint32_t		f_frag_offset, f_frag_len;
1818 	uint32_t		mbox_producer, mbox_consumer;
1819 	uint32_t		mbox_len, mbox_offset;
1820 	static fn_t		f = "mbox_read";
1821 
1822 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
1823 
1824 	/*
1825 	 * Save to local variables to make code more readable
1826 	 */
1827 	mbox_producer = header->mailboxes[SBBC_INBOX].mbox_producer;
1828 	mbox_consumer = header->mailboxes[SBBC_INBOX].mbox_consumer;
1829 	mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
1830 	mbox_offset = header->mailboxes[SBBC_INBOX].mbox_offset;
1831 	frag_header_size = sizeof (struct sbbc_fragment);
1832 	f_frag_offset = frag->f_frag_offset;
1833 	f_frag_len = frag->f_frag_len;
1834 	frag_total_size = frag_header_size + f_frag_len;
1835 
1836 	/*
1837 	 * If the message buffer size is smaller than the fragment
1838 	 * size, return an error.
1839 	 */
1840 	if (msg->msg_len < f_frag_len)  {
1841 		rc = ENOMEM;
1842 		goto done;
1843 	}
1844 
1845 	msg_buf = (caddr_t)(msg->msg_buf + f_frag_offset);
1846 
1847 	/*
1848 	 * Throw in the message data
1849 	 */
1850 	bcopy(&frag->f_data, &msg->msg_data, sizeof (msg->msg_data));
1851 
1852 	/*
1853 	 * We have it all, waiter, message, so lets
1854 	 * go get that puppy!
1855 	 * Message could be in one or two chunks -
1856 	 * consumer < producer: 1 chunk, (producer - consumer)
1857 	 * consumer > producer: 2 chunks, (end - consumer)
1858 	 *				 (producer - start)
1859 	 */
1860 	sram_end =  (uint32_t)(mbox_offset + mbox_len);
1861 	sram_src = (uint32_t)(mbox_offset + mbox_consumer + frag_header_size);
1862 
1863 	/*
1864 	 * wraparound
1865 	 */
1866 	if (sram_src >= sram_end)
1867 		sram_src -= mbox_len;
1868 
1869 	/*
1870 	 * find where the data is
1871 	 * possible cases
1872 	 * 1) consumer == producer, mailbox empty
1873 	 *	error
1874 	 * 2) producer < consumer
1875 	 *	bytes_at_end =  mailbox end - consumer
1876 	 *	bytes_at_start = producer
1877 	 * 3) producer > consumer
1878 	 *	bytes_at_end =  producer - consumer
1879 	 *	bytes_at_start = 0
1880 	 */
1881 
1882 	SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, "
1883 		"frag_len = 0x%x\n",
1884 			f, mbox_consumer, mbox_producer, f_frag_len);
1885 
1886 	if (mbox_producer == mbox_consumer) {
1887 		bytes_at_end = bytes_at_start = 0;
1888 	} else if (mbox_producer < mbox_consumer) {
1889 		bytes_at_end = mbox_len - mbox_consumer;
1890 		bytes_at_start = mbox_producer;
1891 	} else {
1892 		bytes_at_end = mbox_producer - mbox_consumer;
1893 		bytes_at_start = 0;
1894 	}
1895 
1896 	SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, "
1897 		"bytes_at_start = 0x%x\n", f, bytes_at_end, bytes_at_start);
1898 
1899 	if ((bytes_at_end + bytes_at_start) < frag_total_size) {
1900 
1901 		/*
1902 		 * mailbox is corrupt
1903 		 * but what to do ?
1904 		 */
1905 		cmn_err(CE_PANIC, "Corrupt INBOX!\n"
1906 		    "producer = %x, consumer = %x, bytes_at_start = %x, "
1907 		    "bytes_at_end = %x\n", mbox_producer, mbox_consumer,
1908 		    bytes_at_start, bytes_at_end);
1909 	}
1910 
1911 	/*
1912 	 * If bytes_at_end is greater than header size, read the
1913 	 * part at the end of the mailbox, and then update the
1914 	 * pointers and bytes_to_read.
1915 	 */
1916 	if (bytes_at_end > frag_header_size) {
1917 		/*
1918 		 * We are only interested in the data segment.
1919 		 */
1920 		bytes_at_end -= frag_header_size;
1921 		bytes_to_read = (bytes_at_end >= f_frag_len)?
1922 			f_frag_len : bytes_at_end;
1923 		SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, "
1924 			"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
1925 		rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
1926 			bytes_to_read);
1927 		if (rc) {
1928 			goto done;
1929 		}
1930 
1931 		/*
1932 		 * Update pointers in SRAM and message buffer.
1933 		 */
1934 		sram_src = (uint32_t)mbox_offset;
1935 		msg_buf = (caddr_t)(msg_buf + bytes_to_read);
1936 		bytes_to_read = f_frag_len - bytes_to_read;
1937 	} else {
1938 		bytes_to_read = f_frag_len;
1939 	}
1940 
1941 	/*
1942 	 * wraparound to start of mailbox
1943 	 */
1944 	if (bytes_to_read > 0) {
1945 		SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, "
1946 			"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
1947 		rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
1948 			bytes_to_read);
1949 	}
1950 
1951 done:
1952 	msg->msg_bytes += f_frag_len;
1953 
1954 	return (rc);
1955 }
1956 
1957 /*
1958  * move past the next message in the inbox
1959  */
1960 static void
1961 mbox_skip_next_msg(struct sbbc_mbox_header *header)
1962 {
1963 	struct sbbc_fragment	frag;
1964 	uint32_t		next_msg;
1965 
1966 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
1967 
1968 	if (mbox_read_frag(header, &frag)) {
1969 		cmn_err(CE_PANIC, "INBOX is Corrupt !\n");
1970 	}
1971 
1972 	/*
1973 	 * Move on to the next message
1974 	 */
1975 	next_msg = header->mailboxes[SBBC_INBOX].mbox_consumer;
1976 	next_msg += sizeof (struct sbbc_fragment);
1977 	next_msg += frag.f_frag_len;
1978 	if (next_msg >= header->mailboxes[SBBC_INBOX].mbox_len) {
1979 		next_msg = (next_msg +
1980 			header->mailboxes[SBBC_INBOX].mbox_len) %
1981 			header->mailboxes[SBBC_INBOX].mbox_len;
1982 	}
1983 	header->mailboxes[SBBC_INBOX].mbox_consumer =
1984 		next_msg;
1985 
1986 	mbox_update_header(SBBC_INBOX, header);
1987 
1988 	return;
1989 
1990 }
1991 
1992 static struct sbbc_msg_waiter *
1993 mbox_find_waiter(uint16_t msg_type, uint32_t msg_id)
1994 {
1995 	struct	sbbc_msg_waiter	*waiter, *prev;
1996 
1997 	prev = NULL;
1998 	for (waiter = master_mbox->mbox_wait_list[msg_type];
1999 		waiter != NULL; waiter = waiter->w_next) {
2000 
2001 		if (waiter->w_id == msg_id) {
2002 			if (prev != NULL) {
2003 				prev->w_next = waiter->w_next;
2004 			} else {
2005 				master_mbox->mbox_wait_list[msg_type] =
2006 					waiter->w_next;
2007 			}
2008 			break;
2009 		}
2010 		prev = waiter;
2011 	}
2012 
2013 	return (waiter);
2014 }
2015 
2016 static int
2017 mbox_read_header(uint32_t mailbox, struct sbbc_mbox_header *header)
2018 {
2019 	struct sbbc_mbox_header *hd;
2020 	uint32_t	offset;
2021 	int		rc;
2022 
2023 	/*
2024 	 * Initialize a sbbc_mbox_header pointer to 0 so that we
2025 	 * can use it to calculate the offsets of fields inside
2026 	 * the structure.
2027 	 */
2028 	hd = (struct sbbc_mbox_header *)0;
2029 
2030 	if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)header,
2031 	    sizeof (struct sbbc_mbox_header)))
2032 		return (rc);
2033 
2034 	/*
2035 	 * Since the header is read in a byte-by-byte fashion
2036 	 * using ddi_rep_get8, we need to re-read the producer
2037 	 * or consumer pointer as integer in case it has changed
2038 	 * after part of the previous value has been read.
2039 	 */
2040 	switch (mailbox) {
2041 
2042 	case SBBC_INBOX:
2043 		offset = (uint32_t)(uintptr_t)
2044 		    (&hd->mailboxes[SBBC_INBOX].mbox_producer);
2045 		rc = iosram_read(SBBC_MAILBOX_KEY, offset,
2046 		    (caddr_t)&header->mailboxes[SBBC_INBOX].mbox_producer,
2047 		    sizeof (uint32_t));
2048 		break;
2049 	case SBBC_OUTBOX:
2050 		offset = (uint32_t)(uintptr_t)
2051 		    (&hd->mailboxes[SBBC_OUTBOX].mbox_consumer);
2052 		rc = iosram_read(SBBC_MAILBOX_KEY, offset,
2053 		    (caddr_t)&header->mailboxes[SBBC_OUTBOX].mbox_consumer,
2054 		    sizeof (uint32_t));
2055 		break;
2056 	default:
2057 		cmn_err(CE_PANIC, "Invalid Mbox header type\n");
2058 		break;
2059 
2060 	}
2061 
2062 	return (rc);
2063 }
2064 
2065 /*
2066  * There are only two fields updated by the  domain,
2067  * the inbox consumer field and the outbox producer
2068  * field. These fields are protected by the respective
2069  * mbox_{in|out}->mb_lock so that accesses will
2070  * be serialised. The only coherency issue is writing
2071  * back the header, so we do it here after grabbing
2072  * the global mailbox lock.
2073  */
2074 static void
2075 mbox_update_header(uint32_t mailbox, struct sbbc_mbox_header *header)
2076 {
2077 	struct sbbc_mbox_header	*hd;
2078 	uint32_t		value, offset, mbox_len;
2079 
2080 	/*
2081 	 * Initialize a sbbc_mbox_header pointer to 0 so that we
2082 	 * can use it to calculate the offsets of fields inside
2083 	 * the structure.
2084 	 */
2085 	hd = (struct sbbc_mbox_header *)0;
2086 
2087 	switch (mailbox) {
2088 
2089 	case SBBC_INBOX:
2090 		value = header->mailboxes[SBBC_INBOX].mbox_consumer;
2091 		offset = (uint32_t)(uintptr_t)
2092 			(&hd->mailboxes[SBBC_INBOX].mbox_consumer);
2093 
2094 		mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
2095 		break;
2096 	case SBBC_OUTBOX:
2097 		value = header->mailboxes[SBBC_OUTBOX].mbox_producer;
2098 		offset = (uint32_t)(uintptr_t)
2099 			(&hd->mailboxes[SBBC_OUTBOX].mbox_producer);
2100 		mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
2101 		break;
2102 	default:
2103 		cmn_err(CE_PANIC, "Invalid Mbox header type\n");
2104 		break;
2105 
2106 	}
2107 
2108 	/*
2109 	 * If the last read/write would cause the next read/write
2110 	 * to be unaligned, we skip on modulo MBOX_ALIGN_BYTES.
2111 	 * This is OK because all the mailbox handlers will
2112 	 * conform to this.
2113 	 */
2114 	if (value % MBOX_ALIGN_BYTES) {
2115 		value += (MBOX_ALIGN_BYTES - (value % MBOX_ALIGN_BYTES));
2116 		value %= mbox_len;
2117 	}
2118 
2119 	if (iosram_write(SBBC_MAILBOX_KEY, offset, (caddr_t)&value,
2120 		sizeof (uint32_t))) {
2121 		cmn_err(CE_PANIC, "Mailbox Corrupt ! \n");
2122 	}
2123 
2124 	/*
2125 	 * Update internal pointers so they won't be out of sync with
2126 	 * the values in IOSRAM.
2127 	 */
2128 	switch (mailbox) {
2129 
2130 	case SBBC_INBOX:
2131 		header->mailboxes[SBBC_INBOX].mbox_consumer = value;
2132 		break;
2133 	case SBBC_OUTBOX:
2134 		header->mailboxes[SBBC_OUTBOX].mbox_producer = value;
2135 		break;
2136 	}
2137 }
2138 
2139 static int
2140 mbox_read_frag(struct sbbc_mbox_header *header,
2141 	struct sbbc_fragment *frag)
2142 {
2143 	int			rc = 0;
2144 	uint32_t		sram_src, bytes;
2145 	caddr_t			dst;
2146 
2147 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
2148 	/*
2149 	 * read the fragment header for this message
2150 	 */
2151 	sram_src = (uint32_t)(header->mailboxes[SBBC_INBOX].mbox_offset +
2152 		header->mailboxes[SBBC_INBOX].mbox_consumer);
2153 
2154 	/*
2155 	 * wraparound ?
2156 	 */
2157 	if ((header->mailboxes[SBBC_INBOX].mbox_consumer +
2158 		sizeof (struct sbbc_fragment)) >=
2159 		header->mailboxes[SBBC_INBOX].mbox_len) {
2160 
2161 		dst = (caddr_t)frag;
2162 		bytes = header->mailboxes[SBBC_INBOX].mbox_len -
2163 			header->mailboxes[SBBC_INBOX].mbox_consumer;
2164 
2165 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, dst, bytes)) {
2166 			return (rc);
2167 		}
2168 
2169 		dst += bytes;
2170 		sram_src = header->mailboxes[SBBC_INBOX].mbox_offset;
2171 		bytes = (header->mailboxes[SBBC_INBOX].mbox_consumer +
2172 			sizeof (struct sbbc_fragment)) %
2173 			header->mailboxes[SBBC_INBOX].mbox_len;
2174 
2175 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src,
2176 			dst, bytes)) {
2177 			return (rc);
2178 		}
2179 	} else {
2180 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, (caddr_t)frag,
2181 			sizeof (struct sbbc_fragment))) {
2182 			return (rc);
2183 		}
2184 	}
2185 
2186 	return (0);
2187 }
2188 
2189 
2190 /*
2191  * This function is triggered by a soft interrupt and it's purpose is to call
2192  * to kadmin() to shutdown the Domain.
2193  */
2194 /*ARGSUSED0*/
2195 static uint_t
2196 sbbc_do_fast_shutdown(char *arg)
2197 {
2198 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
2199 
2200 	/*
2201 	 * If kadmin fails for some reason then we bring the system down
2202 	 * via power_down(), or failing that using halt().
2203 	 */
2204 	power_down("kadmin() failed, trying power_down()");
2205 
2206 	halt("power_down() failed, trying halt()");
2207 
2208 	/*
2209 	 * We should never make it this far, so something must have gone
2210 	 * horribly, horribly wrong.
2211 	 */
2212 	/*NOTREACHED*/
2213 	return (DDI_INTR_UNCLAIMED);
2214 }
2215 
2216 
2217 /*
2218  * This function handles unsolicited PANIC_SHUTDOWN events
2219  */
2220 static uint_t
2221 sbbc_panic_shutdown_handler(char *arg)
2222 {
2223 	static fn_t	f = "sbbc_panic_shutdown_handler()";
2224 
2225 	sg_panic_shutdown_t	*payload = NULL;
2226 	sbbc_msg_t		*msg = NULL;
2227 
2228 	if (arg == NULL) {
2229 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
2230 		return (DDI_INTR_UNCLAIMED);
2231 	}
2232 
2233 	msg = (sbbc_msg_t *)arg;
2234 
2235 	if (msg->msg_buf == NULL) {
2236 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
2237 		return (DDI_INTR_UNCLAIMED);
2238 	}
2239 
2240 	payload = (sg_panic_shutdown_t *)msg->msg_buf;
2241 
2242 	switch (*payload) {
2243 	case SC_EVENT_PANIC_ENV:
2244 
2245 		/*
2246 		 * Let the user know why the domain is going down.
2247 		 */
2248 		cmn_err(CE_WARN, "%s", PANIC_ENV_EVENT_MSG);
2249 
2250 		/*
2251 		 * trigger sbbc_do_fast_shutdown().
2252 		 */
2253 		ddi_trigger_softintr(panic_softintr_id);
2254 
2255 		/*NOTREACHED*/
2256 		break;
2257 
2258 	case SC_EVENT_PANIC_KEYSWITCH:
2259 		/*
2260 		 * The SC warns a user if they try a destructive keyswitch
2261 		 * command on a Domain which is currently running Solaris.
2262 		 * If the user chooses to continue despite our best advise
2263 		 * then we bring down the Domain immediately without trying
2264 		 * to shut the system down gracefully.
2265 		 */
2266 		break;
2267 
2268 	default:
2269 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: Unknown payload:%d", f,
2270 			*payload);
2271 		return (DDI_INTR_UNCLAIMED);
2272 	}
2273 
2274 	return (DDI_INTR_CLAIMED);
2275 }
2276 
2277 /*
2278  * dp_get_cores()
2279  *
2280  * Checks cpu implementation for the input cpuid and returns
2281  * the number of cores.
2282  * If implementation cannot be determined, returns 1
2283  */
2284 static int
2285 dp_get_cores(uint16_t cpuid)
2286 {
2287 	int	bd, ii, impl, nc;
2288 
2289 	bd = cpuid / 4;
2290 	nc = SG_MAX_CPUS_PER_BD;
2291 
2292 	/* find first with valid implementation */
2293 	for (ii = 0; ii < nc; ii++)
2294 		if (cpu[MAKE_CPUID(bd, ii)]) {
2295 			impl = cpunodes[MAKE_CPUID(bd, ii)].implementation;
2296 			break;
2297 		}
2298 
2299 	if (IS_JAGUAR(impl) || IS_PANTHER(impl))
2300 		return (2);
2301 	else
2302 		return (1);
2303 }
2304 
2305 /*
2306  * dp_payload_add_cpus()
2307  *
2308  * From datapath mailbox message, determines the number of and safari IDs
2309  * for affected cpus, then adds this info to the datapath ereport.
2310  *
2311  */
2312 static int
2313 dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp)
2314 {
2315 	int		jj = 0, numcpus = 0;
2316 	int		bd, procpos, ii, num, ncores, ret;
2317 	uint16_t	*dparray, cpuid;
2318 	uint64_t	*snarray;
2319 
2320 	/* check for multiple core architectures */
2321 	ncores = dp_get_cores(dpmsg->cpuid);
2322 
2323 	switch (dpmsg->type) {
2324 		case DP_CDS_TYPE:
2325 			numcpus = ncores;
2326 			break;
2327 
2328 		case DP_DX_TYPE:
2329 			numcpus = 2 * ncores;
2330 			break;
2331 
2332 		case DP_RP_TYPE:
2333 			numcpus = SG_MAX_CPUS_PER_BD;
2334 			break;
2335 
2336 		default:
2337 			ASSERT(0);
2338 			return (-1);
2339 	}
2340 
2341 	num = numcpus;
2342 
2343 	/*
2344 	 * populate dparray with impacted cores (only those present)
2345 	 */
2346 	dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP);
2347 	bd = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
2348 	procpos = SG_CPUID_TO_PORTID(dpmsg->cpuid) & 0x3;
2349 
2350 	mutex_enter(&cpu_lock);
2351 
2352 	switch (dpmsg->type) {
2353 
2354 		case DP_CDS_TYPE:
2355 			/*
2356 			 * For a CDS error, it's the reporting cpuid
2357 			 * and it's other core (if present)
2358 			 */
2359 			cpuid = dpmsg->cpuid & 0x1FF;	/* core 0 */
2360 			if (cpu[cpuid])
2361 				dparray[jj++] = cpuid;
2362 
2363 			cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;	/* core 1 */
2364 			if (cpu[cpuid])
2365 				dparray[jj++] = cpuid;
2366 			break;
2367 
2368 		case DP_DX_TYPE:
2369 			/*
2370 			 * For a DX error, it's the reporting cpuid (all
2371 			 * cores) and the other CPU sharing the same
2372 			 * DX<-->DCDS interface (all cores)
2373 			 */
2374 
2375 			/* reporting cpuid */
2376 			cpuid = dpmsg->cpuid & 0x1FF;	/* core 0 */
2377 			if (cpu[cpuid])
2378 				dparray[jj++] = cpuid;
2379 
2380 			cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;	/* core 1 */
2381 			if (cpu[cpuid])
2382 				dparray[jj++] = cpuid;
2383 
2384 			/* find partner cpuid */
2385 			if (procpos == 0 || procpos == 2)
2386 				cpuid = dpmsg->cpuid + 1;
2387 			else
2388 				cpuid = dpmsg->cpuid - 1;
2389 
2390 			/* add partner cpuid */
2391 			cpuid &= 0x1FF;			/* core 0 */
2392 			if (cpu[cpuid])
2393 				dparray[jj++] = cpuid;
2394 
2395 			cpuid |= SG_CORE_ID_MASK;	/* core 1 */
2396 			if (cpu[cpuid])
2397 				dparray[jj++] = cpuid;
2398 			break;
2399 
2400 		case DP_RP_TYPE:
2401 			/*
2402 			 * For a RP error, it's all cpuids (all cores) on
2403 			 * the reporting board
2404 			 */
2405 			for (ii = 0; ii < SG_MAX_CMPS_PER_BD; ii++) {
2406 				cpuid = MAKE_CPUID(bd, ii);
2407 				if (cpu[cpuid])		/* core 0 */
2408 					dparray[jj++] = cpuid;
2409 				cpuid |= SG_CORE_ID_MASK;
2410 				if (cpu[cpuid])		/* core 1 */
2411 					dparray[jj++] = cpuid;
2412 			}
2413 			break;
2414 	}
2415 
2416 	mutex_exit(&cpu_lock);
2417 
2418 	/*
2419 	 * The datapath message could not be associated with any
2420 	 * configured CPU.
2421 	 */
2422 	if (!jj) {
2423 		kmem_free(dparray, num * sizeof (uint16_t *));
2424 		ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
2425 		ASSERT(ret == 0);
2426 		return (-1);
2427 	}
2428 
2429 	snarray = kmem_zalloc(jj * sizeof (uint64_t), KM_SLEEP);
2430 	for (ii = 0; ii < jj; ii++)
2431 		snarray[ii] = cpunodes[dparray[ii]].device_id;
2432 
2433 	ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
2434 	ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj);
2435 	ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj);
2436 	ASSERT(ret == 0);
2437 
2438 	kmem_free(dparray, num * sizeof (uint16_t *));
2439 	kmem_free(snarray, jj * sizeof (uint64_t *));
2440 
2441 	return (0);
2442 }
2443 
2444 /*
2445  * sbbc_dp_trans_event() - datapath message handler.
2446  *
2447  * Process datapath error and fault messages received from the SC.  Checks
2448  * for, and disregards, messages associated with I/O boards.  Otherwise,
2449  * extracts message info to produce a datapath ereport.
2450  */
2451 /*ARGSUSED*/
2452 static uint_t
2453 sbbc_dp_trans_event(char *arg)
2454 {
2455 	const char	*f = "sbbc_dp_trans_event()";
2456 	nvlist_t	*erp, *detector, *hcelem;
2457 	char		buf[FM_MAX_CLASS];
2458 	int		board;
2459 	plat_datapath_info_t	*dpmsg;
2460 	sbbc_msg_t	*msg;
2461 	int		msgtype;
2462 
2463 	/* set i/f message and payload pointers */
2464 	msg = &dp_payload_msg;
2465 	dpmsg = &dp_payload;
2466 	msgtype = msg->msg_type.type;
2467 
2468 	cmn_err(CE_NOTE, "%s: msgtype=0x%x\n", f, msgtype);
2469 	cmn_err(CE_NOTE, "type=0x%x cpuid=0x%x t_value=0x%x\n", dpmsg->type,
2470 		dpmsg->cpuid, dpmsg->t_value);
2471 
2472 	/* check for valid type */
2473 	if (dpmsg->type > DP_RP_TYPE) {
2474 		cmn_err(CE_WARN, "%s: dpmsg type 0x%x invalid\n",
2475 			f, dpmsg->type);
2476 		return (DDI_INTR_CLAIMED);
2477 	}
2478 
2479 	/* check for I/O board message -  Schizo AIDs are 25 - 30 */
2480 	if (dpmsg->cpuid > 23) {
2481 		cmn_err(CE_NOTE, "%s: ignore I/O board msg\n", f);
2482 		return (DDI_INTR_CLAIMED);
2483 	}
2484 
2485 	/* allocate space for ereport */
2486 	erp = fm_nvlist_create(NULL);
2487 
2488 /*
2489  * Member Name	Data Type	   Comments
2490  * -----------	---------	   -----------
2491  * version	uint8		   0
2492  * class	string		   "asic"
2493  * ENA		uint64		   ENA Format 1
2494  * detector	fmri		   aggregated ID data for SC-DE
2495  *
2496  * Datapath ereport subclasses and data payloads:
2497  * There will be two types of ereports (error and fault) which will be
2498  * identified by the "type" member.
2499  *
2500  * ereport.asic.serengeti.cds.cds-dp
2501  * ereport.asic.serengeti.dx.dx-dp	(board)
2502  * ereport.asic.serengeti.rp.rp-dp	(centerplane)
2503  *
2504  * Member Name	Data Type	  Comments
2505  * -----------	---------	  -----------
2506  * erptype	uint16		  derived from message type: error or
2507  *				  fault
2508  * t-value	uint32		  SC's datapath SERD timeout threshold
2509  * dp-list-sz	uint8		  number of dp-list array elements
2510  * dp-list	array of uint16	  Safari IDs of affected cpus
2511  * sn-list	array of uint64	  Serial numbers of affected cpus
2512  */
2513 
2514 	/* compose common ereport elements */
2515 	detector = fm_nvlist_create(NULL);
2516 
2517 	/*
2518 	 *  Create legacy FMRI for the detector
2519 	 */
2520 	board = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
2521 	switch (dpmsg->type) {
2522 		case DP_CDS_TYPE:
2523 		case DP_DX_TYPE:
2524 			(void) snprintf(buf, FM_MAX_CLASS, "SB%d", board);
2525 			break;
2526 		case DP_RP_TYPE:
2527 			(void) snprintf(buf, FM_MAX_CLASS, "RP");
2528 			break;
2529 		default:
2530 			(void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN");
2531 			break;
2532 	}
2533 
2534 	hcelem = fm_nvlist_create(NULL);
2535 
2536 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC);
2537 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf);
2538 
2539 	(void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION);
2540 	(void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
2541 	(void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, "");
2542 	(void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1);
2543 	(void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1);
2544 
2545 	/* build ereport class name */
2546 	(void) snprintf(buf, FM_MAX_CLASS, "asic.serengeti.%s.%s-%s",
2547 		dperrtype[dpmsg->type], dperrtype[dpmsg->type],
2548 		FM_ERROR_DATAPATH);
2549 
2550 	fm_ereport_set(erp, FM_EREPORT_VERSION, buf,
2551 		fm_ena_generate(0, FM_ENA_FMT1), detector, NULL);
2552 
2553 	/* add payload elements */
2554 	if (msgtype == MBOX_EVENT_DP_ERROR)
2555 		fm_payload_set(erp,
2556 			DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL);
2557 	else
2558 		fm_payload_set(erp,
2559 			DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL);
2560 
2561 	fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL);
2562 
2563 	(void) dp_payload_add_cpus(dpmsg, erp);
2564 
2565 	/* post ereport */
2566 	fm_ereport_post(erp, EVCH_SLEEP);
2567 
2568 	/* free ereport memory */
2569 	fm_nvlist_destroy(erp, FM_NVA_FREE);
2570 	fm_nvlist_destroy(detector, FM_NVA_FREE);
2571 
2572 	return (DDI_INTR_CLAIMED);
2573 }
2574 
2575 static uint_t
2576 sbbc_datapath_error_msg_handler(char *arg)
2577 {
2578 	static fn_t	f = "sbbc_datapath_error_msg_handler()";
2579 	sbbc_msg_t	*msg = NULL;
2580 
2581 	if (arg == NULL) {
2582 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
2583 		return (DDI_INTR_UNCLAIMED);
2584 	}
2585 
2586 	msg = (sbbc_msg_t *)arg;
2587 
2588 	if (msg->msg_buf == NULL) {
2589 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
2590 		return (DDI_INTR_UNCLAIMED);
2591 	}
2592 
2593 	msg->msg_type.type = MBOX_EVENT_DP_ERROR;
2594 
2595 	/* trigger sbbc_dp_trans_event() */
2596 	ddi_trigger_softintr(dp_softintr_id);
2597 
2598 	return (DDI_INTR_CLAIMED);
2599 }
2600 
2601 static uint_t
2602 sbbc_datapath_fault_msg_handler(char *arg)
2603 {
2604 
2605 	static fn_t	f = "sbbc_datapath_fault_msg_handler()";
2606 
2607 	sbbc_msg_t		*msg = NULL;
2608 
2609 	if (arg == NULL) {
2610 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
2611 		return (DDI_INTR_UNCLAIMED);
2612 	}
2613 
2614 	msg = (sbbc_msg_t *)arg;
2615 
2616 	if (msg->msg_buf == NULL) {
2617 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
2618 		return (DDI_INTR_UNCLAIMED);
2619 	}
2620 
2621 	msg->msg_type.type = MBOX_EVENT_DP_FAULT;
2622 
2623 	/* trigger sbbc_dp_trans_event() */
2624 	ddi_trigger_softintr(dp_softintr_id);
2625 
2626 	return (DDI_INTR_CLAIMED);
2627 }
2628 
2629 /*
2630  * Log an ECC event message to the SC.  This is called from the
2631  * sbbc_ecc_mbox_taskq or directly from plat_send_ecc_mailbox_msg
2632  * for indictment messages.
2633  */
2634 int
2635 sbbc_mbox_ecc_output(sbbc_ecc_mbox_t *msgp)
2636 {
2637 	int				rv;
2638 	plat_capability_data_t		*cap;
2639 	plat_dimm_sid_board_data_t	*ddata;
2640 	plat_ecc_msg_hdr_t		*hdr;
2641 
2642 	rv = sbbc_mbox_request_response(&msgp->ecc_req, &msgp->ecc_resp,
2643 		sbbc_mbox_default_timeout);
2644 
2645 	if (rv != 0) {
2646 		/*
2647 		 * Indictment messages use the return value to indicate a
2648 		 * problem in the mailbox.  For Error mailbox messages, we'll
2649 		 * have to use a syslog message.
2650 		 */
2651 		if (msgp->ecc_log_error) {
2652 			if (sbbc_ecc_mbox_send_errs == 0) {
2653 				cmn_err(CE_NOTE, "!Solaris failed to send a "
2654 				    "message (0x%x/0x%x) to the System "
2655 				    "Controller. Error: %d, Message Status: %d",
2656 				    msgp->ecc_resp.msg_type.type,
2657 				    msgp->ecc_resp.msg_type.sub_type,
2658 				    rv, msgp->ecc_resp.msg_status);
2659 			}
2660 
2661 			if (++sbbc_ecc_mbox_send_errs >=
2662 			    sbbc_ecc_mbox_err_throttle) {
2663 				sbbc_ecc_mbox_send_errs = 0;
2664 			}
2665 		}
2666 
2667 	} else if (msgp->ecc_resp.msg_status != 0) {
2668 		if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
2669 			switch (msgp->ecc_resp.msg_type.sub_type) {
2670 			case INFO_MBOX_ECC:
2671 				hdr = (plat_ecc_msg_hdr_t *)
2672 				    msgp->ecc_req.msg_buf;
2673 				if (hdr->emh_msg_type ==
2674 				    PLAT_ECC_DIMM_SID_MESSAGE) {
2675 					rv = msgp->ecc_resp.msg_status;
2676 					break;
2677 				}
2678 			/*FALLTHROUGH*/
2679 			case INFO_MBOX_ECC_CAP:
2680 				/*
2681 				 * The positive response comes only
2682 				 * from the AVL FS1 updated SC.
2683 				 * If the firmware is either downgraded
2684 				 * or failover to an older version, then
2685 				 * lets reset the SC capability to
2686 				 * default.
2687 				 */
2688 				plat_ecc_capability_sc_set
2689 				    (PLAT_ECC_CAPABILITY_SC_DEFAULT);
2690 				break;
2691 			default:
2692 				break;
2693 			}
2694 		}
2695 		if (msgp->ecc_log_error) {
2696 			if (sbbc_ecc_mbox_inval_errs == 0) {
2697 				cmn_err(CE_NOTE, "!An internal error (%d) "
2698 				    "occurred in the System Controller while "
2699 				    "processing this message (0x%x/0x%x)",
2700 				    msgp->ecc_resp.msg_status,
2701 				    msgp->ecc_resp.msg_type.type,
2702 				    msgp->ecc_resp.msg_type.sub_type);
2703 			}
2704 			if (msgp->ecc_resp.msg_status == EINVAL) {
2705 				if (++sbbc_ecc_mbox_inval_errs >=
2706 				    sbbc_ecc_mbox_err_throttle) {
2707 					sbbc_ecc_mbox_inval_errs = 0;
2708 				}
2709 				rv = ENOMSG;
2710 			} else {
2711 				if (++sbbc_ecc_mbox_other_errs >=
2712 				    sbbc_ecc_mbox_err_throttle) {
2713 					sbbc_ecc_mbox_other_errs = 0;
2714 				}
2715 				rv = msgp->ecc_resp.msg_status;
2716 			}
2717 		}
2718 
2719 	} else {
2720 		if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
2721 			switch (msgp->ecc_resp.msg_type.sub_type) {
2722 			case INFO_MBOX_ECC_CAP:
2723 				/*
2724 				 * Successfully received the response
2725 				 * for the capability message, so updating
2726 				 * the SC ECC messaging capability.
2727 				 */
2728 				cap = (plat_capability_data_t *)
2729 				    msgp->ecc_resp.msg_buf;
2730 				plat_ecc_capability_sc_set
2731 				    (cap->capd_capability);
2732 				break;
2733 
2734 			case INFO_MBOX_ECC:
2735 				hdr = (plat_ecc_msg_hdr_t *)
2736 				    msgp->ecc_resp.msg_buf;
2737 				if (hdr && (hdr->emh_msg_type ==
2738 				    PLAT_ECC_DIMM_SID_MESSAGE)) {
2739 					/*
2740 					 * Successfully received a response
2741 					 * to a request for DIMM serial ids.
2742 					 */
2743 					ddata = (plat_dimm_sid_board_data_t *)
2744 					    msgp->ecc_resp.msg_buf;
2745 					(void) plat_store_mem_sids(ddata);
2746 				}
2747 				break;
2748 
2749 			default:
2750 				break;
2751 			}
2752 		}
2753 	}
2754 
2755 	if (msgp->ecc_resp.msg_buf)
2756 		kmem_free((void *)msgp->ecc_resp.msg_buf,
2757 		    (size_t)msgp->ecc_resp.msg_len);
2758 
2759 	kmem_free((void *)msgp->ecc_req.msg_buf, (size_t)msgp->ecc_req.msg_len);
2760 	kmem_free(msgp, sizeof (sbbc_ecc_mbox_t));
2761 	return (rv);
2762 }
2763 
2764 /*
2765  * Enqueue ECC event message on taskq to SC.  This is invoked from
2766  * plat_send_ecc_mailbox_msg() for each ECC event generating a message.
2767  */
2768 void
2769 sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t *sbbc_ecc_msgp)
2770 {
2771 	/*
2772 	 * Create the ECC event mailbox taskq, if it does not yet exist.
2773 	 * This must be done here rather than in sbbc_mbox_init().  The
2774 	 * sgsbbc driver is loaded very early in the boot flow.  Calling
2775 	 * taskq_create() from sbbc_mbox_init could lead to a boot deadlock.
2776 	 *
2777 	 * There might be a tiny probability that two ECC handlers on
2778 	 * different processors could arrive here simultaneously.  If
2779 	 * the taskq has not been created previously, then these two
2780 	 * simultaneous events could cause the creation of an extra taskq.
2781 	 * Given the extremely small likelihood (if not outright impossibility)
2782 	 * of this occurrence, sbbc_ecc_mbox_taskq is not protected by a lock.
2783 	 */
2784 
2785 	if (sbbc_ecc_mbox_taskq == NULL) {
2786 		sbbc_ecc_mbox_taskq = taskq_create("ECC_event_mailbox", 1,
2787 		    minclsyspri, ECC_MBOX_TASKQ_MIN, ECC_MBOX_TASKQ_MAX,
2788 		    TASKQ_PREPOPULATE);
2789 		if (sbbc_ecc_mbox_taskq == NULL) {
2790 			if (sbbc_ecc_mbox_taskq_errs == 0) {
2791 				cmn_err(CE_NOTE, "Unable to create mailbox "
2792 				    "task queue for ECC event logging to "
2793 				    "System Controller");
2794 			}
2795 			if (++sbbc_ecc_mbox_taskq_errs >=
2796 			    sbbc_ecc_mbox_err_throttle) {
2797 				sbbc_ecc_mbox_taskq_errs = 0;
2798 			}
2799 
2800 			kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
2801 				(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
2802 			kmem_free((void *)sbbc_ecc_msgp,
2803 				sizeof (sbbc_ecc_mbox_t));
2804 			return;
2805 		}
2806 
2807 		/*
2808 		 * Reset error counter so that first taskq_dispatch
2809 		 * error will be output
2810 		 */
2811 		sbbc_ecc_mbox_taskq_errs = 0;
2812 	}
2813 
2814 	/*
2815 	 * Enqueue the message
2816 	 */
2817 
2818 	if (taskq_dispatch(sbbc_ecc_mbox_taskq,
2819 	    (task_func_t *)sbbc_mbox_ecc_output, sbbc_ecc_msgp,
2820 	    TQ_NOSLEEP) == NULL) {
2821 
2822 		if (sbbc_ecc_mbox_taskq_errs == 0) {
2823 			cmn_err(CE_NOTE, "Unable to send ECC event "
2824 				"message to System Controller");
2825 		}
2826 		if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) {
2827 			sbbc_ecc_mbox_taskq_errs = 0;
2828 		}
2829 
2830 		kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
2831 				(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
2832 		kmem_free((void *)sbbc_ecc_msgp, sizeof (sbbc_ecc_mbox_t));
2833 	}
2834 }
2835 
2836 static uint_t
2837 cap_ecc_msg_handler(char *addr)
2838 {
2839 	sbbc_msg_t *msg = NULL;
2840 	plat_capability_data_t *cap = NULL;
2841 	static fn_t f = "cap_ecc_msg_handler";
2842 
2843 	msg = (sbbc_msg_t *)addr;
2844 
2845 	if (msg == NULL) {
2846 		SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
2847 		    "null addr");
2848 		return (DDI_INTR_CLAIMED);
2849 	}
2850 
2851 	if (msg->msg_buf == NULL) {
2852 		SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
2853 		    "null data buffer");
2854 		return (DDI_INTR_CLAIMED);
2855 	}
2856 
2857 	cap = (plat_capability_data_t *)msg->msg_buf;
2858 	switch (cap->capd_msg_type) {
2859 	case PLAT_ECC_CAPABILITY_MESSAGE:
2860 		SGSBBC_DBG_MBOX("%s: capability  0x%x\n", f,
2861 		    cap->capd_capability);
2862 		plat_ecc_capability_sc_set(cap->capd_capability);
2863 		break;
2864 	default:
2865 		SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f,
2866 		    cap->capd_msg_type);
2867 		break;
2868 	}
2869 
2870 	return (DDI_INTR_CLAIMED);
2871 }
2872