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
53sbbc_mailbox_t	*master_mbox = NULL;
54
55/*
56 * Panic Shutdown event support
57 */
58static	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 */
64static ddi_softintr_t	panic_softintr_id = 0;
65
66static sg_panic_shutdown_t	panic_payload;
67static 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 */
73static kcondvar_t	outbox_queue;
74static kmutex_t		outbox_queue_lock;
75
76/*
77 * Handle unsolicited capability message.
78 */
79static plat_capability_data_t	cap_payload;
80static sbbc_msg_t		cap_payload_msg;
81static 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 */
87typedef 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 */
98static	ddi_softintr_t		dp_softintr_id = 0;
99static	kmutex_t		dp_hdlr_lock;
100
101static	plat_datapath_info_t	dp_payload;
102static	sbbc_msg_t		dp_payload_msg;
103
104static 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 */
114static int		outbox_busy = 0;
115
116/*
117 * local stuff
118 */
119static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t);
120static int sbbc_mbox_recv_msg();
121static int mbox_write(struct sbbc_mbox_header *,
122	struct sbbc_fragment *, sbbc_msg_t *);
123static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *,
124	sbbc_msg_t *);
125static int mbox_has_free_space(struct sbbc_mbox_header *);
126static void mbox_skip_next_msg(struct sbbc_mbox_header *);
127static int mbox_read_header(uint32_t, struct sbbc_mbox_header *);
128static void mbox_update_header(uint32_t, struct sbbc_mbox_header *);
129static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *);
130static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t);
131static void wakeup_next(void);
132static uint_t sbbc_panic_shutdown_handler(char *arg);
133static uint_t sbbc_do_fast_shutdown(char *arg);
134static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp);
135static uint_t cap_ecc_msg_handler(char *);
136static uint_t sbbc_datapath_error_msg_handler(char *arg);
137static uint_t sbbc_datapath_fault_msg_handler(char *arg);
138static uint_t sbbc_dp_trans_event(char *arg);
139
140
141/*
142 * Interrupt handlers
143 */
144static int sbbc_mbox_msgin(void);
145static int sbbc_mbox_msgout(void);
146static int sbbc_mbox_spacein(void);
147static int sbbc_mbox_spaceout(void);
148
149/*
150 * ECC event mailbox message taskq and parameters
151 */
152static taskq_t	*sbbc_ecc_mbox_taskq = NULL;
153static int	sbbc_ecc_mbox_taskq_errs = 0;
154static int	sbbc_ecc_mbox_send_errs = 0;
155static int	sbbc_ecc_mbox_inval_errs = 0;
156static int	sbbc_ecc_mbox_other_errs = 0;
157int	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 */
163void
164sbbc_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 */
231void
232sbbc_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 */
320int
321sbbc_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 */
409int
410sbbc_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 */
553static void
554sbbc_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 */
587int
588sbbc_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 */
669int
670sbbc_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 */
738static int
739sbbc_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 */
795static int
796sbbc_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 */
808static int
809sbbc_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 */
821static int
822sbbc_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 */
882int
883sbbc_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
1121static void
1122wakeup_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 */
1136int
1137sbbc_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 */
1268int
1269sbbc_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	}
1530done:
1531	mbox_skip_next_msg(&header);
1532	return (rc);
1533}
1534
1535/*
1536 * available free space in the outbox
1537 */
1538static int
1539mbox_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 */
1596static int
1597mbox_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 */
1807static int
1808mbox_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
1951done:
1952	msg->msg_bytes += f_frag_len;
1953
1954	return (rc);
1955}
1956
1957/*
1958 * move past the next message in the inbox
1959 */
1960static void
1961mbox_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
1992static struct sbbc_msg_waiter *
1993mbox_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
2016static int
2017mbox_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 */
2074static void
2075mbox_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
2139static int
2140mbox_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*/
2195static uint_t
2196sbbc_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 */
2220static uint_t
2221sbbc_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 */
2284static int
2285dp_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 */
2312static int
2313dp_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*/
2452static uint_t
2453sbbc_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
2575static uint_t
2576sbbc_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
2601static uint_t
2602sbbc_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 */
2634int
2635sbbc_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 */
2768void
2769sbbc_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) == TASKQID_INVALID) {
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
2836static uint_t
2837cap_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