xref: /illumos-gate/usr/src/uts/sun4v/io/px/px_err.c (revision 01689544)
1f8d2de6bSjchu /*
2f8d2de6bSjchu  * CDDL HEADER START
3f8d2de6bSjchu  *
4f8d2de6bSjchu  * The contents of this file are subject to the terms of the
5*01689544Sjchu  * Common Development and Distribution License (the "License").
6*01689544Sjchu  * You may not use this file except in compliance with the License.
7f8d2de6bSjchu  *
8f8d2de6bSjchu  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f8d2de6bSjchu  * or http://www.opensolaris.org/os/licensing.
10f8d2de6bSjchu  * See the License for the specific language governing permissions
11f8d2de6bSjchu  * and limitations under the License.
12f8d2de6bSjchu  *
13f8d2de6bSjchu  * When distributing Covered Code, include this CDDL HEADER in each
14f8d2de6bSjchu  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f8d2de6bSjchu  * If applicable, add the following below this CDDL HEADER, with the
16f8d2de6bSjchu  * fields enclosed by brackets "[]" replaced with your own identifying
17f8d2de6bSjchu  * information: Portions Copyright [yyyy] [name of copyright owner]
18f8d2de6bSjchu  *
19f8d2de6bSjchu  * CDDL HEADER END
20f8d2de6bSjchu  */
21f8d2de6bSjchu /*
22*01689544Sjchu  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23f8d2de6bSjchu  * Use is subject to license terms.
24f8d2de6bSjchu  */
25f8d2de6bSjchu 
26f8d2de6bSjchu #pragma ident	"%Z%%M%	%I%	%E% SMI"
27f8d2de6bSjchu 
28f8d2de6bSjchu /*
29f8d2de6bSjchu  * sun4v Fire Error Handling
30f8d2de6bSjchu  */
31f8d2de6bSjchu 
32f8d2de6bSjchu #include <sys/types.h>
33f8d2de6bSjchu #include <sys/ddi.h>
34f8d2de6bSjchu #include <sys/sunddi.h>
35f8d2de6bSjchu #include <sys/fm/protocol.h>
36f8d2de6bSjchu #include <sys/fm/util.h>
37f8d2de6bSjchu #include <sys/membar.h>
38f8d2de6bSjchu #include "px_obj.h"
39f8d2de6bSjchu #include "px_err.h"
40f8d2de6bSjchu 
41f8d2de6bSjchu static uint_t px_err_common_intr(px_fault_t *fault_p, px_rc_err_t *epkt);
42f8d2de6bSjchu static int  px_err_check_severity(px_t *px_p, ddi_fm_error_t *derr,
43f8d2de6bSjchu     px_rc_err_t *epkt, int caller);
44f8d2de6bSjchu 
45f8d2de6bSjchu static int px_cb_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
46f8d2de6bSjchu     px_rc_err_t *epkt, int caller);
47f8d2de6bSjchu static int px_mmu_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
48f8d2de6bSjchu     px_rc_err_t *epkt, int caller);
49f8d2de6bSjchu static int px_pcie_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
50f8d2de6bSjchu     px_rc_err_t *epkt, int caller);
51f8d2de6bSjchu 
52f8d2de6bSjchu /*
53f8d2de6bSjchu  * px_err_cb_intr:
54f8d2de6bSjchu  * Interrupt handler for the Host Bus Block.
55f8d2de6bSjchu  */
56f8d2de6bSjchu uint_t
57f8d2de6bSjchu px_err_cb_intr(caddr_t arg)
58f8d2de6bSjchu {
59f8d2de6bSjchu 	px_fault_t	*fault_p = (px_fault_t *)arg;
60f8d2de6bSjchu 	px_rc_err_t	*epkt = (px_rc_err_t *)fault_p->px_intr_payload;
61f8d2de6bSjchu 
62f8d2de6bSjchu 	if (epkt != NULL) {
63f8d2de6bSjchu 		return (px_err_common_intr(fault_p, epkt));
64f8d2de6bSjchu 	}
65f8d2de6bSjchu 
66f8d2de6bSjchu 	return (DDI_INTR_UNCLAIMED);
67f8d2de6bSjchu }
68f8d2de6bSjchu 
69f8d2de6bSjchu /*
70f8d2de6bSjchu  * px_err_dmc_pec_intr:
71f8d2de6bSjchu  * Interrupt handler for the DMC/PEC block.
72f8d2de6bSjchu  */
73f8d2de6bSjchu uint_t
74f8d2de6bSjchu px_err_dmc_pec_intr(caddr_t arg)
75f8d2de6bSjchu {
76f8d2de6bSjchu 	px_fault_t	*fault_p = (px_fault_t *)arg;
77f8d2de6bSjchu 	px_rc_err_t	*epkt = (px_rc_err_t *)fault_p->px_intr_payload;
78f8d2de6bSjchu 
79f8d2de6bSjchu 	if (epkt != NULL) {
80f8d2de6bSjchu 		return (px_err_common_intr(fault_p, epkt));
81f8d2de6bSjchu 	}
82f8d2de6bSjchu 
83f8d2de6bSjchu 	return (DDI_INTR_UNCLAIMED);
84f8d2de6bSjchu }
85f8d2de6bSjchu 
86f8d2de6bSjchu /*
87f8d2de6bSjchu  * px_err_handle:
88f8d2de6bSjchu  * Common function called by trap, mondo and fabric intr.
89f8d2de6bSjchu  * This function is more meaningful in sun4u implementation.  Kept
90f8d2de6bSjchu  * to mirror sun4u call stack.
91f8d2de6bSjchu  * o check for safe access
92f8d2de6bSjchu  *
93f8d2de6bSjchu  * @param px_p		leaf in which to check access
94f8d2de6bSjchu  * @param derr		fm err data structure to be updated
95f8d2de6bSjchu  * @param caller	PX_TRAP_CALL | PX_INTR_CALL
96f8d2de6bSjchu  * @param chkjbc	whether to handle hostbus registers (ignored)
97f8d2de6bSjchu  * @return err		PX_OK | PX_NONFATAL |
98f8d2de6bSjchu  *                      PX_FATAL_GOS | PX_FATAL_HW | PX_STUCK_FATAL
99f8d2de6bSjchu  */
100f8d2de6bSjchu /* ARGSUSED */
101f8d2de6bSjchu int
102f8d2de6bSjchu px_err_handle(px_t *px_p, ddi_fm_error_t *derr, int caller,
103f8d2de6bSjchu     boolean_t chkxbc)
104f8d2de6bSjchu {
105f8d2de6bSjchu 	/* check for safe access */
106f8d2de6bSjchu 	px_err_safeacc_check(px_p, derr);
107f8d2de6bSjchu 
108f8d2de6bSjchu 	return (DDI_FM_OK);
109f8d2de6bSjchu }
110f8d2de6bSjchu 
111f8d2de6bSjchu /*
112f8d2de6bSjchu  * px_err_common_intr:
113f8d2de6bSjchu  * Interrupt handler for the JBC/DMC/PEC block.
114f8d2de6bSjchu  * o lock
115f8d2de6bSjchu  * o create derr
116f8d2de6bSjchu  * o check safe access
117f8d2de6bSjchu  * o px_err_check_severiy(epkt)
118f8d2de6bSjchu  * o dispatch
119f8d2de6bSjchu  * o Idle intr state
120f8d2de6bSjchu  * o unlock
121f8d2de6bSjchu  * o handle error: fatal? fm_panic() : return INTR_CLAIMED)
122f8d2de6bSjchu  */
123f8d2de6bSjchu static uint_t
124f8d2de6bSjchu px_err_common_intr(px_fault_t *fault_p, px_rc_err_t *epkt)
125f8d2de6bSjchu {
126f8d2de6bSjchu 	px_t		*px_p = DIP_TO_STATE(fault_p->px_fh_dip);
127f8d2de6bSjchu 	dev_info_t	*rpdip = px_p->px_dip;
128f8d2de6bSjchu 	int		err, ret;
129f8d2de6bSjchu 	ddi_fm_error_t	derr;
130f8d2de6bSjchu 
131*01689544Sjchu 	mutex_enter(&px_p->px_fm_mutex);
132f8d2de6bSjchu 
133f8d2de6bSjchu 	/* Create the derr */
134f8d2de6bSjchu 	bzero(&derr, sizeof (ddi_fm_error_t));
135f8d2de6bSjchu 	derr.fme_version = DDI_FME_VERSION;
136f8d2de6bSjchu 	derr.fme_ena = fm_ena_generate(epkt->stick, FM_ENA_FMT1);
137f8d2de6bSjchu 	derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
138f8d2de6bSjchu 
139f8d2de6bSjchu 	/* Basically check for safe access */
140f8d2de6bSjchu 	(void) px_err_handle(px_p, &derr, PX_INTR_CALL, B_FALSE);
141f8d2de6bSjchu 
142f8d2de6bSjchu 	/* Check the severity of this error */
143f8d2de6bSjchu 	err = px_err_check_severity(px_p, &derr, epkt, PX_INTR_CALL);
144f8d2de6bSjchu 
145f8d2de6bSjchu 	/* check for error severity */
146f8d2de6bSjchu 	ret = ndi_fm_handler_dispatch(rpdip, NULL, &derr);
147f8d2de6bSjchu 
148f8d2de6bSjchu 	/* Set the intr state to idle for the leaf that received the mondo */
149f8d2de6bSjchu 	if (px_lib_intr_setstate(rpdip, fault_p->px_fh_sysino,
150f8d2de6bSjchu 		INTR_IDLE_STATE) != DDI_SUCCESS) {
151*01689544Sjchu 		mutex_exit(&px_p->px_fm_mutex);
152f8d2de6bSjchu 		return (DDI_INTR_UNCLAIMED);
153f8d2de6bSjchu 	}
154f8d2de6bSjchu 
155*01689544Sjchu 	mutex_exit(&px_p->px_fm_mutex);
156f8d2de6bSjchu 
157f8d2de6bSjchu 	if ((err & (PX_FATAL_GOS | PX_FATAL_SW)) || (ret == DDI_FM_FATAL))
1584fbb58f6Sjchu 		PX_FM_PANIC("Fatal System Bus Error has occurred\n");
159f8d2de6bSjchu 
160f8d2de6bSjchu 	return (DDI_INTR_CLAIMED);
161f8d2de6bSjchu }
162f8d2de6bSjchu 
163f8d2de6bSjchu /*
164f8d2de6bSjchu  * px_err_check_severity:
165f8d2de6bSjchu  * Check the severity of the fire error based the epkt received
166f8d2de6bSjchu  *
167f8d2de6bSjchu  * @param px_p		leaf in which to take the snap shot.
168f8d2de6bSjchu  * @param derr		fm err in which the ereport is to be based on
169f8d2de6bSjchu  * @param epkt		epkt recevied from HV
170f8d2de6bSjchu  */
171f8d2de6bSjchu static int
172f8d2de6bSjchu px_err_check_severity(px_t *px_p, ddi_fm_error_t *derr, px_rc_err_t *epkt,
173f8d2de6bSjchu     int caller)
174f8d2de6bSjchu {
175f8d2de6bSjchu 	px_pec_t 	*pec_p = px_p->px_pec_p;
176f8d2de6bSjchu 	dev_info_t	*dip = px_p->px_dip;
177f8d2de6bSjchu 	int		err = 0;
178f8d2de6bSjchu 
179f8d2de6bSjchu 	/* Cautious access error handling  */
180f8d2de6bSjchu 	if (derr->fme_flag == DDI_FM_ERR_EXPECTED) {
181f8d2de6bSjchu 		if (caller == PX_TRAP_CALL) {
182f8d2de6bSjchu 			/*
183f8d2de6bSjchu 			 * for ddi_caut_get treat all events as nonfatal
184f8d2de6bSjchu 			 * The trampoline will set err_ena = 0,
185f8d2de6bSjchu 			 * err_status = NONFATAL.
186f8d2de6bSjchu 			 */
187f8d2de6bSjchu 			derr->fme_status = DDI_FM_NONFATAL;
188f8d2de6bSjchu 		} else {
189f8d2de6bSjchu 			/*
190f8d2de6bSjchu 			 * For ddi_caut_put treat all events as nonfatal. Here
191f8d2de6bSjchu 			 * we have the handle and can call ndi_fm_acc_err_set().
192f8d2de6bSjchu 			 */
193f8d2de6bSjchu 			derr->fme_status = DDI_FM_NONFATAL;
194f8d2de6bSjchu 			ndi_fm_acc_err_set(pec_p->pec_acc_hdl, derr);
195f8d2de6bSjchu 		}
196f8d2de6bSjchu 	}
197f8d2de6bSjchu 
198f8d2de6bSjchu 	switch (epkt->rc_descr.block) {
199f8d2de6bSjchu 	case BLOCK_HOSTBUS:
200f8d2de6bSjchu 		err = px_cb_check_errors(dip, derr, epkt, caller);
201f8d2de6bSjchu 		break;
202f8d2de6bSjchu 	case BLOCK_MMU:
203f8d2de6bSjchu 		err = px_mmu_check_errors(dip, derr, epkt, caller);
204f8d2de6bSjchu 		break;
205f8d2de6bSjchu 	case BLOCK_INTR:
206f8d2de6bSjchu 		err = PX_NONFATAL;
207f8d2de6bSjchu 		break;
208f8d2de6bSjchu 	case BLOCK_PCIE:
209f8d2de6bSjchu 		err = px_pcie_check_errors(dip, derr, epkt, caller);
210f8d2de6bSjchu 		break;
211f8d2de6bSjchu 	default:
212f8d2de6bSjchu 		err = PX_ERR_UNKNOWN;
213f8d2de6bSjchu 	}
214f8d2de6bSjchu 
215f8d2de6bSjchu 	return (err);
216f8d2de6bSjchu }
217f8d2de6bSjchu 
218f8d2de6bSjchu /* ARGSUSED */
219f8d2de6bSjchu static int
220f8d2de6bSjchu px_cb_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
221f8d2de6bSjchu     px_rc_err_t *epkt, int caller)
222f8d2de6bSjchu {
223f8d2de6bSjchu 	int		fme_flag = derr->fme_flag;
224f8d2de6bSjchu 	boolean_t	is_safeacc;
225f8d2de6bSjchu 	int		ret,  err = 0;
226f8d2de6bSjchu 
227f8d2de6bSjchu 	is_safeacc = (fme_flag == DDI_FM_ERR_EXPECTED) ||
228f8d2de6bSjchu 	    (fme_flag == DDI_FM_ERR_PEEK) ||
229f8d2de6bSjchu 	    (fme_flag == DDI_FM_ERR_POKE);
230f8d2de6bSjchu 
231f8d2de6bSjchu 	/* block/op/phase/cond/dir/flag... */
232f8d2de6bSjchu 	switch (epkt->rc_descr.op) {
233f8d2de6bSjchu 	case OP_PIO:
2348c334881Sjchu 		err = PX_NONFATAL;
235f8d2de6bSjchu 		/* check handle if affected memory address is captured */
236f8d2de6bSjchu 		if (epkt->rc_descr.M != 0) {
237f8d2de6bSjchu 			ret = px_handle_lookup(dip, ACC_HANDLE,
238f8d2de6bSjchu 			    derr->fme_ena, (void *)epkt->addr);
239f8d2de6bSjchu 		}
240f8d2de6bSjchu 		if (ret == DDI_FM_FATAL)
2418c334881Sjchu 			err |= PX_FATAL_GOS;
242f8d2de6bSjchu 		break;
243f8d2de6bSjchu 
244f8d2de6bSjchu 	case OP_DMA:
245f8d2de6bSjchu 		switch (epkt->rc_descr.phase) {
246f8d2de6bSjchu 		case PH_ADDR:
2478c334881Sjchu 			err = PX_FATAL_GOS;
248f8d2de6bSjchu 			break;
249f8d2de6bSjchu 		case PH_DATA:
250f8d2de6bSjchu 			if (epkt->rc_descr.cond == CND_UE) {
2518c334881Sjchu 				err = PX_FATAL_GOS;
252f8d2de6bSjchu 				break;
253f8d2de6bSjchu 			}
254f8d2de6bSjchu 
2558c334881Sjchu 			err = PX_NONFATAL;
256f8d2de6bSjchu 			if (epkt->rc_descr.M == 1) {
257f8d2de6bSjchu 				ret = px_handle_lookup(dip, DMA_HANDLE,
258f8d2de6bSjchu 				    derr->fme_ena, (void *)epkt->addr);
259f8d2de6bSjchu 				if (ret == DDI_FM_FATAL)
2608c334881Sjchu 					err |= PX_FATAL_GOS;
261f8d2de6bSjchu 			}
262f8d2de6bSjchu 			break;
263f8d2de6bSjchu 		default:
264f8d2de6bSjchu 			DBG(DBG_ERR_INTR, dip, "Unexpected epkt");
2658c334881Sjchu 			err = PX_FATAL_GOS;
266f8d2de6bSjchu 			break;
267f8d2de6bSjchu 		}
268f8d2de6bSjchu 		break;
269f8d2de6bSjchu 	case OP_UNKNOWN:
2708c334881Sjchu 		err = PX_NONFATAL;
271*01689544Sjchu 		if ((epkt->rc_descr.cond == CND_UNMAP) ||
272*01689544Sjchu 		    (epkt->rc_descr.cond == CND_UE) ||
273*01689544Sjchu 		    (epkt->rc_descr.cond == CND_INT) ||
274*01689544Sjchu 		    (epkt->rc_descr.cond == CND_ILL))
275*01689544Sjchu 			err |= PX_FATAL_GOS;
276*01689544Sjchu 
277f8d2de6bSjchu 		if (epkt->rc_descr.M == 1) {
278f8d2de6bSjchu 			int	ret1, ret2;
2798c334881Sjchu 
280f8d2de6bSjchu 			ret1 = px_handle_lookup(dip, DMA_HANDLE, derr->fme_ena,
281f8d2de6bSjchu 			    (void *)epkt->addr);
282f8d2de6bSjchu 			ret2 = px_handle_lookup(dip, ACC_HANDLE, derr->fme_ena,
283f8d2de6bSjchu 			    (void *)epkt->addr);
2848c334881Sjchu 
2858c334881Sjchu 			if (ret1 == DDI_FM_FATAL || ret2 == DDI_FM_FATAL)
2868c334881Sjchu 				err |= PX_FATAL_GOS;
287f8d2de6bSjchu 		}
288f8d2de6bSjchu 		break;
289f8d2de6bSjchu 
290f8d2de6bSjchu 	case OP_RESERVED:
291f8d2de6bSjchu 	default:
292f8d2de6bSjchu 		DBG(DBG_ERR_INTR, NULL, "Unrecognized JBC error.");
2938c334881Sjchu 		err = PX_FATAL_GOS;
294f8d2de6bSjchu 		break;
295f8d2de6bSjchu 	}
296f8d2de6bSjchu 
297f8d2de6bSjchu 	/*
298f8d2de6bSjchu 	 * For protected safe access, consider PX_FATAL_GOS as the only
299f8d2de6bSjchu 	 * exception for px to take immediate panic, else, treat errors
300f8d2de6bSjchu 	 * as nonfatal.
301f8d2de6bSjchu 	 */
302f8d2de6bSjchu 	if (is_safeacc) {
303f8d2de6bSjchu 		if (err & PX_FATAL_GOS)
304f8d2de6bSjchu 			err = PX_FATAL_GOS;
305f8d2de6bSjchu 		else
306f8d2de6bSjchu 			err = PX_NONFATAL;
307f8d2de6bSjchu 	}
308f8d2de6bSjchu 
309f8d2de6bSjchu 	return (err);
310f8d2de6bSjchu }
311f8d2de6bSjchu 
312f8d2de6bSjchu /* ARGSUSED */
313f8d2de6bSjchu static int
314f8d2de6bSjchu px_mmu_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
315f8d2de6bSjchu     px_rc_err_t *epkt, int caller)
316f8d2de6bSjchu {
317f8d2de6bSjchu 	int		ret, err = 0;
318f8d2de6bSjchu 
319f8d2de6bSjchu 	switch (epkt->rc_descr.op) {
320f8d2de6bSjchu 	case OP_BYPASS:	/* nonfatal */
321f8d2de6bSjchu 	case OP_XLAT:	/* nonfatal, stuck-fatal, fatal-reset */
322f8d2de6bSjchu 	case OP_TBW:	/* nonfatal, stuck-fatal */
323f8d2de6bSjchu 		err = PX_NONFATAL;
324f8d2de6bSjchu 		break;
325f8d2de6bSjchu 	default:
326f8d2de6bSjchu 		err = PX_ERR_UNKNOWN;
327f8d2de6bSjchu 		break;
328f8d2de6bSjchu 	}
329f8d2de6bSjchu 
330f8d2de6bSjchu 	if ((epkt->rc_descr.D != 0) || (epkt->rc_descr.M != 0)) {
331f8d2de6bSjchu 		ret = px_handle_lookup(dip, DMA_HANDLE, derr->fme_ena,
332f8d2de6bSjchu 		    (void *)epkt->addr);
3338c334881Sjchu 		if (ret == DDI_FM_FATAL)
3348c334881Sjchu 			err |= PX_FATAL_GOS;
3358c334881Sjchu 		else
3368c334881Sjchu 			err |= PX_NONFATAL;
3378c334881Sjchu 	} else
3388c334881Sjchu 		err |= PX_NONFATAL;
339f8d2de6bSjchu 
340f8d2de6bSjchu 	return (err);
341f8d2de6bSjchu }
342f8d2de6bSjchu 
343f8d2de6bSjchu /* ARGSUSED */
344f8d2de6bSjchu static int
345f8d2de6bSjchu px_pcie_check_errors(dev_info_t *dip, ddi_fm_error_t *derr,
346f8d2de6bSjchu     px_rc_err_t *epkt, int caller)
347f8d2de6bSjchu {
3488bc7d88aSet 	int		ret = PX_NONFATAL;
349f8d2de6bSjchu 	px_pec_err_t	*pec = (px_pec_err_t *)epkt;
350f8d2de6bSjchu 
351f8d2de6bSjchu 	switch (pec->pec_descr.dir) {
352f8d2de6bSjchu 	case DIR_INGRESS:
353f8d2de6bSjchu 	case DIR_EGRESS:
354f8d2de6bSjchu 	case DIR_LINK:
3558bc7d88aSet 		ret |= PX_FABRIC_ERR_SEV(pec->ue_reg_status,
3568bc7d88aSet 		    px_fabric_die_rc_ue, px_fabric_die_rc_ue_gos);
3578bc7d88aSet 		ret |= PX_FABRIC_ERR_SEV(pec->ue_reg_status,
3588bc7d88aSet 		    px_fabric_die_rc_ce, px_fabric_die_rc_ce_gos);
359f8d2de6bSjchu 		break;
360f8d2de6bSjchu 	default:
3618bc7d88aSet 		ret = PX_ERR_UNKNOWN;
362f8d2de6bSjchu 		break;
363f8d2de6bSjchu 	}
364f8d2de6bSjchu 
365f8d2de6bSjchu 	return (ret);
366f8d2de6bSjchu }
367