xref: /titanic_41/usr/src/uts/sun4/io/px/px_fm.c (revision e6b21d58662dc0b7b176616916c2aa3c6bcc2622)
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * PX Fault Management Architecture
28  */
29 #include <sys/types.h>
30 #include <sys/sunndi.h>
31 #include <sys/sunddi.h>
32 #include <sys/fm/protocol.h>
33 #include <sys/fm/util.h>
34 #include <sys/fm/io/pci.h>
35 #include <sys/membar.h>
36 #include "px_obj.h"
37 
38 extern uint_t px_ranges_phi_mask;
39 
40 #define	PX_PCIE_PANIC_BITS \
41 	(PCIE_AER_UCE_DLP | PCIE_AER_UCE_FCP | PCIE_AER_UCE_TO | \
42 	PCIE_AER_UCE_RO | PCIE_AER_UCE_MTLP | PCIE_AER_UCE_ECRC)
43 #define	PX_PCIE_NO_PANIC_BITS \
44 	(PCIE_AER_UCE_TRAINING | PCIE_AER_UCE_SD | PCIE_AER_UCE_CA | \
45 	PCIE_AER_UCE_UC | PCIE_AER_UCE_UR)
46 
47 /*
48  * Global panicing state variabled used to control if further error handling
49  * should occur.  If the system is already panic'ing or if PX itself has
50  * recommended panic'ing the system, no further error handling should occur to
51  * prevent the system from hanging.
52  */
53 boolean_t px_panicing = B_FALSE;
54 
55 static int px_pcie_ptlp(dev_info_t *dip, ddi_fm_error_t *derr,
56     px_err_pcie_t *regs);
57 
58 #if defined(DEBUG)
59 static void px_pcie_log(dev_info_t *dip, px_err_pcie_t *regs);
60 #else	/* DEBUG */
61 #define	px_pcie_log 0 &&
62 #endif	/* DEBUG */
63 
64 /*
65  * Initialize px FMA support
66  */
67 int
px_fm_attach(px_t * px_p)68 px_fm_attach(px_t *px_p)
69 {
70 	int		i;
71 	dev_info_t	*dip = px_p->px_dip;
72 	pcie_bus_t	*bus_p;
73 
74 	px_p->px_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
75 	    DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
76 
77 	/*
78 	 * check parents' capability
79 	 */
80 	ddi_fm_init(dip, &px_p->px_fm_cap, &px_p->px_fm_ibc);
81 
82 	/*
83 	 * parents need to be ereport and error handling capable
84 	 */
85 	ASSERT(px_p->px_fm_cap &&
86 	    (DDI_FM_ERRCB_CAPABLE | DDI_FM_EREPORT_CAPABLE));
87 
88 	/*
89 	 * Initialize lock to synchronize fabric error handling
90 	 */
91 	mutex_init(&px_p->px_fm_mutex, NULL, MUTEX_DRIVER,
92 	    (void *)px_p->px_fm_ibc);
93 
94 	px_p->px_pfd_idx = 0;
95 	for (i = 0; i < 5; i++)
96 		pcie_rc_init_pfd(dip, &px_p->px_pfd_arr[i]);
97 	PCIE_DIP2PFD(dip) = px_p->px_pfd_arr;
98 
99 	bus_p = PCIE_DIP2BUS(dip);
100 	bus_p->bus_rp_bdf = px_p->px_bdf;
101 	bus_p->bus_rp_dip = dip;
102 
103 	return (DDI_SUCCESS);
104 }
105 
106 /*
107  * Deregister FMA
108  */
109 void
px_fm_detach(px_t * px_p)110 px_fm_detach(px_t *px_p)
111 {
112 	int i;
113 
114 	mutex_destroy(&px_p->px_fm_mutex);
115 	ddi_fm_fini(px_p->px_dip);
116 	for (i = 0; i < 5; i++)
117 		pcie_rc_fini_pfd(&px_p->px_pfd_arr[i]);
118 }
119 
120 /*
121  * register error callback in parent
122  */
123 void
px_fm_cb_enable(px_t * px_p)124 px_fm_cb_enable(px_t *px_p)
125 {
126 	ddi_fm_handler_register(px_p->px_dip, px_fm_callback, px_p);
127 }
128 
129 void
px_fm_cb_disable(px_t * px_p)130 px_fm_cb_disable(px_t *px_p)
131 {
132 	ddi_fm_handler_unregister(px_p->px_dip);
133 }
134 
135 /*
136  * Function used to setup access functions depending on level of desired
137  * protection.
138  */
139 void
px_fm_acc_setup(ddi_map_req_t * mp,dev_info_t * rdip,pci_regspec_t * rp)140 px_fm_acc_setup(ddi_map_req_t *mp, dev_info_t *rdip, pci_regspec_t *rp)
141 {
142 	uchar_t fflag;
143 	ndi_err_t *errp;
144 	ddi_acc_hdl_t *hp;
145 	ddi_acc_impl_t *ap;
146 
147 	hp = mp->map_handlep;
148 	ap = (ddi_acc_impl_t *)hp->ah_platform_private;
149 	fflag = ap->ahi_common.ah_acc.devacc_attr_access;
150 
151 	if (mp->map_op == DDI_MO_MAP_LOCKED) {
152 		ndi_fmc_insert(rdip, ACC_HANDLE, (void *)hp, NULL);
153 		switch (fflag) {
154 		case DDI_FLAGERR_ACC:
155 			ap->ahi_get8 = i_ddi_prot_get8;
156 			ap->ahi_get16 = i_ddi_prot_get16;
157 			ap->ahi_get32 = i_ddi_prot_get32;
158 			ap->ahi_get64 = i_ddi_prot_get64;
159 			ap->ahi_put8 = i_ddi_prot_put8;
160 			ap->ahi_put16 = i_ddi_prot_put16;
161 			ap->ahi_put32 = i_ddi_prot_put32;
162 			ap->ahi_put64 = i_ddi_prot_put64;
163 			ap->ahi_rep_get8 = i_ddi_prot_rep_get8;
164 			ap->ahi_rep_get16 = i_ddi_prot_rep_get16;
165 			ap->ahi_rep_get32 = i_ddi_prot_rep_get32;
166 			ap->ahi_rep_get64 = i_ddi_prot_rep_get64;
167 			ap->ahi_rep_put8 = i_ddi_prot_rep_put8;
168 			ap->ahi_rep_put16 = i_ddi_prot_rep_put16;
169 			ap->ahi_rep_put32 = i_ddi_prot_rep_put32;
170 			ap->ahi_rep_put64 = i_ddi_prot_rep_put64;
171 			impl_acc_err_init(hp);
172 			errp = ((ddi_acc_impl_t *)hp)->ahi_err;
173 			if ((rp->pci_phys_hi & PCI_REG_ADDR_M) ==
174 			    PCI_ADDR_CONFIG)
175 				errp->err_cf = px_err_cfg_hdl_check;
176 			else
177 				errp->err_cf = px_err_pio_hdl_check;
178 			break;
179 		case DDI_CAUTIOUS_ACC :
180 			ap->ahi_get8 = i_ddi_caut_get8;
181 			ap->ahi_get16 = i_ddi_caut_get16;
182 			ap->ahi_get32 = i_ddi_caut_get32;
183 			ap->ahi_get64 = i_ddi_caut_get64;
184 			ap->ahi_put8 = i_ddi_caut_put8;
185 			ap->ahi_put16 = i_ddi_caut_put16;
186 			ap->ahi_put32 = i_ddi_caut_put32;
187 			ap->ahi_put64 = i_ddi_caut_put64;
188 			ap->ahi_rep_get8 = i_ddi_caut_rep_get8;
189 			ap->ahi_rep_get16 = i_ddi_caut_rep_get16;
190 			ap->ahi_rep_get32 = i_ddi_caut_rep_get32;
191 			ap->ahi_rep_get64 = i_ddi_caut_rep_get64;
192 			ap->ahi_rep_put8 = i_ddi_caut_rep_put8;
193 			ap->ahi_rep_put16 = i_ddi_caut_rep_put16;
194 			ap->ahi_rep_put32 = i_ddi_caut_rep_put32;
195 			ap->ahi_rep_put64 = i_ddi_caut_rep_put64;
196 			impl_acc_err_init(hp);
197 			errp = ((ddi_acc_impl_t *)hp)->ahi_err;
198 			if ((rp->pci_phys_hi & PCI_REG_ADDR_M) ==
199 			    PCI_ADDR_CONFIG)
200 				errp->err_cf = px_err_cfg_hdl_check;
201 			else
202 				errp->err_cf = px_err_pio_hdl_check;
203 			break;
204 		default:
205 			/* Illegal state, remove the handle from cache */
206 			ndi_fmc_remove(rdip, ACC_HANDLE, (void *)hp);
207 			break;
208 		}
209 	} else if (mp->map_op == DDI_MO_UNMAP) {
210 		ndi_fmc_remove(rdip, ACC_HANDLE, (void *)hp);
211 	}
212 }
213 
214 /*
215  * Function used to initialize FMA for our children nodes. Called
216  * through pci busops when child node calls ddi_fm_init.
217  */
218 /*ARGSUSED*/
219 int
px_fm_init_child(dev_info_t * dip,dev_info_t * cdip,int cap,ddi_iblock_cookie_t * ibc_p)220 px_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap,
221     ddi_iblock_cookie_t *ibc_p)
222 {
223 	px_t *px_p = DIP_TO_STATE(dip);
224 
225 	ASSERT(ibc_p != NULL);
226 	*ibc_p = px_p->px_fm_ibc;
227 
228 	return (px_p->px_fm_cap);
229 }
230 
231 /*
232  * lock access for exclusive PCIe access
233  */
234 void
px_bus_enter(dev_info_t * dip,ddi_acc_handle_t handle)235 px_bus_enter(dev_info_t *dip, ddi_acc_handle_t handle)
236 {
237 	px_pec_t	*pec_p = ((px_t *)DIP_TO_STATE(dip))->px_pec_p;
238 
239 	/*
240 	 * Exclusive access has been used for cautious put/get,
241 	 * Both utilize i_ddi_ontrap which, on sparcv9, implements
242 	 * similar protection as what on_trap() does, and which calls
243 	 * membar  #Sync to flush out all cpu deferred errors
244 	 * prior to get/put operation, so here we're not calling
245 	 * membar  #Sync - a difference from what's in pci_bus_enter().
246 	 */
247 	mutex_enter(&pec_p->pec_pokefault_mutex);
248 	pec_p->pec_acc_hdl = handle;
249 }
250 
251 /*
252  * unlock access for exclusive PCIe access
253  */
254 /* ARGSUSED */
255 void
px_bus_exit(dev_info_t * dip,ddi_acc_handle_t handle)256 px_bus_exit(dev_info_t *dip, ddi_acc_handle_t handle)
257 {
258 	px_t		*px_p = DIP_TO_STATE(dip);
259 	px_pec_t	*pec_p = px_p->px_pec_p;
260 
261 	pec_p->pec_acc_hdl = NULL;
262 	mutex_exit(&pec_p->pec_pokefault_mutex);
263 }
264 
265 static uint64_t
px_in_addr_range(dev_info_t * dip,pci_ranges_t * ranges_p,uint64_t addr)266 px_in_addr_range(dev_info_t *dip, pci_ranges_t *ranges_p, uint64_t addr)
267 {
268 	uint64_t	addr_low, addr_high;
269 
270 	addr_low = (uint64_t)(ranges_p->parent_high & px_ranges_phi_mask) << 32;
271 	addr_low |= (uint64_t)ranges_p->parent_low;
272 	addr_high = addr_low + ((uint64_t)ranges_p->size_high << 32) +
273 	    (uint64_t)ranges_p->size_low;
274 
275 	DBG(DBG_ERR_INTR, dip, "Addr: 0x%llx high: 0x%llx low: 0x%llx\n",
276 	    addr, addr_high, addr_low);
277 
278 	if ((addr < addr_high) && (addr >= addr_low))
279 		return (addr_low);
280 
281 	return (0);
282 }
283 
284 /*
285  * PCI error callback which is registered with our parent to call
286  * for PCIe logging when the CPU traps due to PCIe Uncorrectable Errors
287  * and PCI BERR/TO/UE on IO Loads.
288  */
289 /*ARGSUSED*/
290 int
px_fm_callback(dev_info_t * dip,ddi_fm_error_t * derr,const void * impl_data)291 px_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
292 {
293 	dev_info_t	*pdip = ddi_get_parent(dip);
294 	px_t		*px_p = (px_t *)impl_data;
295 	int		i, acc_type = 0;
296 	int		lookup, rc_err, fab_err;
297 	uint64_t	addr, base_addr;
298 	uint64_t	fault_addr = (uint64_t)derr->fme_bus_specific;
299 	pcie_req_id_t	bdf = PCIE_INVALID_BDF;
300 	pci_ranges_t	*ranges_p;
301 	int		range_len;
302 	pf_data_t	*pfd_p;
303 
304 	/*
305 	 * If the current thread already owns the px_fm_mutex, then we
306 	 * have encountered an error while processing a previous
307 	 * error.  Attempting to take the mutex again will cause the
308 	 * system to deadlock.
309 	 */
310 	if (px_p->px_fm_mutex_owner == curthread)
311 		return (DDI_FM_FATAL);
312 
313 	i_ddi_fm_handler_exit(pdip);
314 
315 	if (px_fm_enter(px_p) != DDI_SUCCESS) {
316 		i_ddi_fm_handler_enter(pdip);
317 		return (DDI_FM_FATAL);
318 	}
319 
320 	/*
321 	 * Make sure this failed load came from this PCIe port.	 Check by
322 	 * matching the upper 32 bits of the address with the ranges property.
323 	 */
324 	range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
325 	i = 0;
326 	for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
327 		base_addr = px_in_addr_range(dip, ranges_p, fault_addr);
328 		if (base_addr) {
329 			switch (ranges_p->child_high & PCI_ADDR_MASK) {
330 			case PCI_ADDR_CONFIG:
331 				acc_type = PF_ADDR_CFG;
332 				addr = NULL;
333 				bdf = (pcie_req_id_t)((fault_addr >> 12) &
334 				    0xFFFF);
335 				break;
336 			case PCI_ADDR_IO:
337 			case PCI_ADDR_MEM64:
338 			case PCI_ADDR_MEM32:
339 				acc_type = PF_ADDR_PIO;
340 				addr = fault_addr - base_addr;
341 				bdf = PCIE_INVALID_BDF;
342 				break;
343 			}
344 			break;
345 		}
346 	}
347 
348 	/* This address doesn't belong to this leaf, just return with OK */
349 	if (!acc_type) {
350 		px_fm_exit(px_p);
351 		i_ddi_fm_handler_enter(pdip);
352 		return (DDI_FM_OK);
353 	}
354 
355 	rc_err = px_err_cmn_intr(px_p, derr, PX_TRAP_CALL, PX_FM_BLOCK_ALL);
356 	lookup = pf_hdl_lookup(dip, derr->fme_ena, acc_type, (uint64_t)addr,
357 	    bdf);
358 
359 	pfd_p = px_rp_en_q(px_p, bdf, addr,
360 	    (PCI_STAT_R_MAST_AB | PCI_STAT_R_TARG_AB));
361 	PCIE_ROOT_EH_SRC(pfd_p)->intr_type = PF_INTR_TYPE_DATA;
362 
363 	/* Update affected info, either addr or bdf is not NULL */
364 	if (addr) {
365 		PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags = PF_AFFECTED_ADDR;
366 	} else if (PCIE_CHECK_VALID_BDF(bdf)) {
367 		PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags = PF_AFFECTED_BDF;
368 		PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf = bdf;
369 	}
370 
371 	fab_err = px_scan_fabric(px_p, dip, derr);
372 
373 	px_fm_exit(px_p);
374 	i_ddi_fm_handler_enter(pdip);
375 
376 	if (!px_die)
377 		return (DDI_FM_OK);
378 
379 	if ((rc_err & (PX_PANIC | PX_PROTECTED)) ||
380 	    (fab_err & PF_ERR_FATAL_FLAGS) ||
381 	    (lookup == PF_HDL_NOTFOUND))
382 		return (DDI_FM_FATAL);
383 	else if ((rc_err == PX_NO_ERROR) && (fab_err == PF_ERR_NO_ERROR))
384 		return (DDI_FM_OK);
385 
386 	return (DDI_FM_NONFATAL);
387 }
388 
389 /*
390  * px_err_fabric_intr:
391  * Interrupt handler for PCIE fabric block.
392  * o lock
393  * o create derr
394  * o px_err_cmn_intr(leaf, with jbc)
395  * o send ereport(fire fmri, derr, payload = BDF)
396  * o dispatch (leaf)
397  * o unlock
398  * o handle error: fatal? fm_panic() : return INTR_CLAIMED)
399  */
400 /* ARGSUSED */
401 uint_t
px_err_fabric_intr(px_t * px_p,msgcode_t msg_code,pcie_req_id_t rid)402 px_err_fabric_intr(px_t *px_p, msgcode_t msg_code, pcie_req_id_t rid)
403 {
404 	dev_info_t	*rpdip = px_p->px_dip;
405 	int		rc_err, fab_err;
406 	ddi_fm_error_t	derr;
407 	uint32_t	rp_status;
408 	uint16_t	ce_source, ue_source;
409 	pf_data_t	*pfd_p;
410 
411 	if (px_fm_enter(px_p) != DDI_SUCCESS)
412 		goto done;
413 
414 	/* Create the derr */
415 	bzero(&derr, sizeof (ddi_fm_error_t));
416 	derr.fme_version = DDI_FME_VERSION;
417 	derr.fme_ena = fm_ena_generate(0, FM_ENA_FMT1);
418 	derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
419 
420 	px_err_safeacc_check(px_p, &derr);
421 
422 	if (msg_code == PCIE_MSG_CODE_ERR_COR) {
423 		rp_status = PCIE_AER_RE_STS_CE_RCVD;
424 		ce_source = rid;
425 		ue_source = 0;
426 	} else {
427 		rp_status = PCIE_AER_RE_STS_FE_NFE_RCVD;
428 		ce_source = 0;
429 		ue_source = rid;
430 		if (msg_code == PCIE_MSG_CODE_ERR_NONFATAL)
431 			rp_status |= PCIE_AER_RE_STS_NFE_MSGS_RCVD;
432 		else {
433 			rp_status |= PCIE_AER_RE_STS_FE_MSGS_RCVD;
434 			rp_status |= PCIE_AER_RE_STS_FIRST_UC_FATAL;
435 		}
436 	}
437 
438 	if (derr.fme_flag == DDI_FM_ERR_UNEXPECTED) {
439 		ddi_fm_ereport_post(rpdip, PCI_ERROR_SUBCLASS "." PCIEX_FABRIC,
440 		    derr.fme_ena,
441 		    DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
442 		    FIRE_PRIMARY, DATA_TYPE_BOOLEAN_VALUE, B_TRUE,
443 		    "pcie_adv_rp_status", DATA_TYPE_UINT32, rp_status,
444 		    "pcie_adv_rp_command", DATA_TYPE_UINT32, 0,
445 		    "pcie_adv_rp_ce_src_id", DATA_TYPE_UINT16, ce_source,
446 		    "pcie_adv_rp_ue_src_id", DATA_TYPE_UINT16, ue_source,
447 		    NULL);
448 	}
449 
450 	/* Ensure that the rid of the fabric message will get scanned. */
451 	pfd_p = px_rp_en_q(px_p, rid, NULL, NULL);
452 	PCIE_ROOT_EH_SRC(pfd_p)->intr_type = PF_INTR_TYPE_FABRIC;
453 
454 	rc_err = px_err_cmn_intr(px_p, &derr, PX_INTR_CALL, PX_FM_BLOCK_PCIE);
455 
456 	/* call rootport dispatch */
457 	fab_err = px_scan_fabric(px_p, rpdip, &derr);
458 
459 	px_err_panic(rc_err, PX_RC, fab_err, B_TRUE);
460 	px_fm_exit(px_p);
461 	px_err_panic(rc_err, PX_RC, fab_err, B_FALSE);
462 
463 done:
464 	return (DDI_INTR_CLAIMED);
465 }
466 
467 /*
468  * px_scan_fabric:
469  *
470  * Check for drain state and if there is anything to scan.
471  *
472  * Note on pfd: Different interrupts will populate the pfd's differently.  The
473  * px driver can have a total of 5 different error sources, so it has a queue of
474  * 5 pfds.  Each valid PDF is linked together and passed to pf_scan_fabric.
475  *
476  * Each error handling will populate the following info in the pfd
477  *
478  *			Root Fault	 Intr Src	 Affected BDF
479  *			----------------+---------------+------------
480  * Callback/CPU Trap	Address/BDF	|DATA		|Lookup Addr
481  * Mondo 62/63 (sun4u)	decode error	|N/A		|N/A
482  * EPKT (sun4v)		decode epkt	|INTERNAL	|decode epkt
483  * Fabric Message	fabric payload	|FABRIC		|NULL
484  * Peek/Poke		Address/BDF	|NULL		|NULL
485  *			----------------+---------------+------------
486  */
487 int
px_scan_fabric(px_t * px_p,dev_info_t * rpdip,ddi_fm_error_t * derr)488 px_scan_fabric(px_t *px_p, dev_info_t *rpdip, ddi_fm_error_t *derr) {
489 	int fab_err = 0;
490 
491 	ASSERT(MUTEX_HELD(&px_p->px_fm_mutex));
492 
493 	if (!px_lib_is_in_drain_state(px_p) && px_p->px_pfd_idx) {
494 		fab_err = pf_scan_fabric(rpdip, derr, px_p->px_pfd_arr);
495 	}
496 
497 	return (fab_err);
498 }
499 
500 /*
501  * px_err_safeacc_check:
502  * Check to see if a peek/poke and cautious access is currently being
503  * done on a particular leaf.
504  *
505  * Safe access reads induced fire errors will be handled by cpu trap handler
506  * which will call px_fm_callback() which calls this function. In that
507  * case, the derr fields will be set by trap handler with the correct values.
508  *
509  * Safe access writes induced errors will be handled by px interrupt
510  * handlers, this function will fill in the derr fields.
511  *
512  * If a cpu trap does occur, it will quiesce all other interrupts allowing
513  * the cpu trap error handling to finish before Fire receives an interrupt.
514  *
515  * If fire does indeed have an error when a cpu trap occurs as a result of
516  * a safe access, a trap followed by a Mondo/Fabric interrupt will occur.
517  * In which case derr will be initialized as "UNEXPECTED" by the interrupt
518  * handler and this function will need to find if this error occured in the
519  * middle of a safe access operation.
520  *
521  * @param px_p		leaf in which to check access
522  * @param derr		fm err data structure to be updated
523  */
524 void
px_err_safeacc_check(px_t * px_p,ddi_fm_error_t * derr)525 px_err_safeacc_check(px_t *px_p, ddi_fm_error_t *derr)
526 {
527 	px_pec_t 	*pec_p = px_p->px_pec_p;
528 	int		acctype = pec_p->pec_safeacc_type;
529 
530 	ASSERT(MUTEX_HELD(&px_p->px_fm_mutex));
531 
532 	if (derr->fme_flag != DDI_FM_ERR_UNEXPECTED) {
533 		return;
534 	}
535 
536 	/* safe access checking */
537 	switch (acctype) {
538 	case DDI_FM_ERR_EXPECTED:
539 		/*
540 		 * cautious access protection, protected from all err.
541 		 */
542 		ddi_fm_acc_err_get(pec_p->pec_acc_hdl, derr,
543 		    DDI_FME_VERSION);
544 		derr->fme_flag = acctype;
545 		derr->fme_acc_handle = pec_p->pec_acc_hdl;
546 		break;
547 	case DDI_FM_ERR_POKE:
548 		/*
549 		 * ddi_poke protection, check nexus and children for
550 		 * expected errors.
551 		 */
552 		membar_sync();
553 		derr->fme_flag = acctype;
554 		break;
555 	case DDI_FM_ERR_PEEK:
556 		derr->fme_flag = acctype;
557 		break;
558 	}
559 }
560 
561 /*
562  * Suggest panic if any EQ (except CE q) has overflown.
563  */
564 int
px_err_check_eq(dev_info_t * dip)565 px_err_check_eq(dev_info_t *dip)
566 {
567 	px_t			*px_p = DIP_TO_STATE(dip);
568 	px_msiq_state_t 	*msiq_state_p = &px_p->px_ib_p->ib_msiq_state;
569 	px_pec_t		*pec_p = px_p->px_pec_p;
570 	msiqid_t		eq_no = msiq_state_p->msiq_1st_msiq_id;
571 	pci_msiq_state_t	msiq_state;
572 	int			i;
573 
574 	for (i = 0; i < msiq_state_p->msiq_cnt; i++) {
575 		if (i + eq_no == pec_p->pec_corr_msg_msiq_id) /* skip CE q */
576 			continue;
577 		if ((px_lib_msiq_getstate(dip, i + eq_no, &msiq_state) !=
578 		    DDI_SUCCESS) || msiq_state == PCI_MSIQ_STATE_ERROR)
579 			return (PX_PANIC);
580 	}
581 	return (PX_NO_PANIC);
582 }
583 
584 /* ARGSUSED */
585 int
px_err_check_pcie(dev_info_t * dip,ddi_fm_error_t * derr,px_err_pcie_t * regs,pf_intr_type_t intr_type)586 px_err_check_pcie(dev_info_t *dip, ddi_fm_error_t *derr, px_err_pcie_t *regs,
587     pf_intr_type_t intr_type)
588 {
589 	px_t		*px_p = DIP_TO_STATE(dip);
590 	pf_data_t	*pfd_p = px_get_pfd(px_p);
591 	int		i;
592 	pf_pcie_adv_err_regs_t *adv_reg = PCIE_ADV_REG(pfd_p);
593 
594 	PCIE_ROOT_EH_SRC(pfd_p)->intr_type = intr_type;
595 
596 	/*
597 	 * set RC s_status in PCI term to coordinate with downstream fabric
598 	 * errors ananlysis.
599 	 */
600 	if (regs->primary_ue & PCIE_AER_UCE_UR)
601 		PCI_BDG_ERR_REG(pfd_p)->pci_bdg_sec_stat = PCI_STAT_R_MAST_AB;
602 	if (regs->primary_ue & PCIE_AER_UCE_CA)
603 		PCI_BDG_ERR_REG(pfd_p)->pci_bdg_sec_stat = PCI_STAT_R_TARG_AB;
604 	if (regs->primary_ue & (PCIE_AER_UCE_PTLP | PCIE_AER_UCE_ECRC))
605 		PCI_BDG_ERR_REG(pfd_p)->pci_bdg_sec_stat = PCI_STAT_PERROR;
606 
607 	if (!regs->primary_ue)
608 		goto done;
609 
610 	adv_reg->pcie_ce_status = regs->ce_reg;
611 	adv_reg->pcie_ue_status = regs->ue_reg | regs->primary_ue;
612 	PCIE_ADV_HDR(pfd_p, 0) = regs->rx_hdr1;
613 	PCIE_ADV_HDR(pfd_p, 1) = regs->rx_hdr2;
614 	PCIE_ADV_HDR(pfd_p, 2) = regs->rx_hdr3;
615 	PCIE_ADV_HDR(pfd_p, 3) = regs->rx_hdr4;
616 	for (i = regs->primary_ue; i != 1; i = i >> 1)
617 		adv_reg->pcie_adv_ctl++;
618 
619 	if (regs->primary_ue & (PCIE_AER_UCE_UR | PCIE_AER_UCE_CA)) {
620 		if (pf_tlp_decode(PCIE_DIP2BUS(dip), adv_reg) == DDI_SUCCESS)
621 			PCIE_ROOT_FAULT(pfd_p)->scan_bdf =
622 			    adv_reg->pcie_ue_tgt_bdf;
623 	} else if (regs->primary_ue & PCIE_AER_UCE_PTLP) {
624 		if (pf_tlp_decode(PCIE_DIP2BUS(dip), adv_reg) == DDI_SUCCESS) {
625 			PCIE_ROOT_FAULT(pfd_p)->scan_bdf =
626 			    adv_reg->pcie_ue_tgt_bdf;
627 			if (adv_reg->pcie_ue_tgt_trans ==
628 			    PF_ADDR_PIO)
629 				PCIE_ROOT_FAULT(pfd_p)->scan_addr =
630 				    adv_reg->pcie_ue_tgt_addr;
631 		}
632 
633 		/*
634 		 * Normally for Poisoned Completion TLPs we can look at the
635 		 * transmit log header for the original request and the original
636 		 * address, however this doesn't seem to be working.  HW BUG.
637 		 */
638 	}
639 
640 done:
641 	px_pcie_log(dip, regs);
642 
643 	/* Return No Error here and let the pcie misc module analyse it */
644 	return (PX_NO_ERROR);
645 }
646 
647 #if defined(DEBUG)
648 static void
px_pcie_log(dev_info_t * dip,px_err_pcie_t * regs)649 px_pcie_log(dev_info_t *dip, px_err_pcie_t *regs)
650 {
651 	DBG(DBG_ERR_INTR, dip,
652 	    "A PCIe RC error has occured\n"
653 	    "\tCE: 0x%x UE: 0x%x Primary UE: 0x%x\n"
654 	    "\tTX Hdr: 0x%x 0x%x 0x%x 0x%x\n\tRX Hdr: 0x%x 0x%x 0x%x 0x%x\n",
655 	    regs->ce_reg, regs->ue_reg, regs->primary_ue,
656 	    regs->tx_hdr1, regs->tx_hdr2, regs->tx_hdr3, regs->tx_hdr4,
657 	    regs->rx_hdr1, regs->rx_hdr2, regs->rx_hdr3, regs->rx_hdr4);
658 }
659 #endif
660 
661 /*
662  * look through poisoned TLP cases and suggest panic/no panic depend on
663  * handle lookup.
664  */
665 static int
px_pcie_ptlp(dev_info_t * dip,ddi_fm_error_t * derr,px_err_pcie_t * regs)666 px_pcie_ptlp(dev_info_t *dip, ddi_fm_error_t *derr, px_err_pcie_t *regs)
667 {
668 	pf_pcie_adv_err_regs_t adv_reg;
669 	pcie_req_id_t	bdf;
670 	uint64_t	addr;
671 	uint32_t	trans_type;
672 	int		tlp_sts, tlp_cmd;
673 	int		lookup = PF_HDL_NOTFOUND;
674 
675 	if (regs->primary_ue != PCIE_AER_UCE_PTLP)
676 		return (PX_PANIC);
677 
678 	if (!regs->rx_hdr1)
679 		goto done;
680 
681 	adv_reg.pcie_ue_hdr[0] = regs->rx_hdr1;
682 	adv_reg.pcie_ue_hdr[1] = regs->rx_hdr2;
683 	adv_reg.pcie_ue_hdr[2] = regs->rx_hdr3;
684 	adv_reg.pcie_ue_hdr[3] = regs->rx_hdr4;
685 
686 	tlp_sts = pf_tlp_decode(PCIE_DIP2BUS(dip), &adv_reg);
687 	tlp_cmd = ((pcie_tlp_hdr_t *)(adv_reg.pcie_ue_hdr))->type;
688 
689 	if (tlp_sts == DDI_FAILURE)
690 		goto done;
691 
692 	bdf = adv_reg.pcie_ue_tgt_bdf;
693 	addr = adv_reg.pcie_ue_tgt_addr;
694 	trans_type = adv_reg.pcie_ue_tgt_trans;
695 
696 	switch (tlp_cmd) {
697 	case PCIE_TLP_TYPE_CPL:
698 	case PCIE_TLP_TYPE_CPLLK:
699 		/*
700 		 * Usually a PTLP is a CPL with data.  Grab the completer BDF
701 		 * from the RX TLP, and the original address from the TX TLP.
702 		 */
703 		if (regs->tx_hdr1) {
704 			adv_reg.pcie_ue_hdr[0] = regs->tx_hdr1;
705 			adv_reg.pcie_ue_hdr[1] = regs->tx_hdr2;
706 			adv_reg.pcie_ue_hdr[2] = regs->tx_hdr3;
707 			adv_reg.pcie_ue_hdr[3] = regs->tx_hdr4;
708 
709 			lookup = pf_tlp_decode(PCIE_DIP2BUS(dip), &adv_reg);
710 			if (lookup != DDI_SUCCESS)
711 				break;
712 			addr = adv_reg.pcie_ue_tgt_addr;
713 			trans_type = adv_reg.pcie_ue_tgt_trans;
714 		} /* FALLTHRU */
715 	case PCIE_TLP_TYPE_IO:
716 	case PCIE_TLP_TYPE_MEM:
717 	case PCIE_TLP_TYPE_MEMLK:
718 		lookup = pf_hdl_lookup(dip, derr->fme_ena, trans_type, addr,
719 		    bdf);
720 		break;
721 	default:
722 		lookup = PF_HDL_NOTFOUND;
723 	}
724 done:
725 	return (lookup == PF_HDL_FOUND ? PX_NO_PANIC : PX_PANIC);
726 }
727 
728 /*
729  * px_get_pdf automatically allocates a RC pf_data_t and returns a pointer to
730  * it.  This function should be used when an error requires a fabric scan.
731  */
732 pf_data_t *
px_get_pfd(px_t * px_p)733 px_get_pfd(px_t *px_p) {
734 	int		idx = px_p->px_pfd_idx++;
735 	pf_data_t	*pfd_p = &px_p->px_pfd_arr[idx];
736 
737 	/* Clear Old Data */
738 	PCIE_ROOT_FAULT(pfd_p)->scan_bdf = PCIE_INVALID_BDF;
739 	PCIE_ROOT_FAULT(pfd_p)->scan_addr = 0;
740 	PCIE_ROOT_EH_SRC(pfd_p)->intr_type = PF_INTR_TYPE_NONE;
741 	PCIE_ROOT_EH_SRC(pfd_p)->intr_data = NULL;
742 	PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags = NULL;
743 	PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf = PCIE_INVALID_BDF;
744 	PCI_BDG_ERR_REG(pfd_p)->pci_bdg_sec_stat = 0;
745 	PCIE_ADV_REG(pfd_p)->pcie_ce_status = 0;
746 	PCIE_ADV_REG(pfd_p)->pcie_ue_status = 0;
747 	PCIE_ADV_REG(pfd_p)->pcie_adv_ctl = 0;
748 
749 	pfd_p->pe_next = NULL;
750 
751 	if (idx > 0) {
752 		px_p->px_pfd_arr[idx - 1].pe_next = pfd_p;
753 		pfd_p->pe_prev = &px_p->px_pfd_arr[idx - 1];
754 	} else {
755 		pfd_p->pe_prev = NULL;
756 	}
757 
758 	pfd_p->pe_severity_flags = 0;
759 	pfd_p->pe_orig_severity_flags = 0;
760 	pfd_p->pe_valid = B_TRUE;
761 
762 	return (pfd_p);
763 }
764 
765 /*
766  * This function appends a pf_data structure to the error q which is used later
767  * during PCIe fabric scan.  It signifies:
768  * o errs rcvd in RC, that may have been propagated to/from the fabric
769  * o the fabric scan code should scan the device path of fault bdf/addr
770  *
771  * scan_bdf: The bdf that caused the fault, which may have error bits set.
772  * scan_addr: The PIO addr that caused the fault, such as failed PIO, but not
773  *	       failed DMAs.
774  * s_status: Secondary Status equivalent to why the fault occured.
775  *	     (ie S-TA/MA, R-TA)
776  * Either the scan bdf or addr may be NULL, but not both.
777  */
778 pf_data_t *
px_rp_en_q(px_t * px_p,pcie_req_id_t scan_bdf,uint32_t scan_addr,uint16_t s_status)779 px_rp_en_q(px_t *px_p, pcie_req_id_t scan_bdf, uint32_t scan_addr,
780     uint16_t s_status)
781 {
782 	pf_data_t	*pfd_p;
783 
784 	if (!PCIE_CHECK_VALID_BDF(scan_bdf) && !scan_addr)
785 		return (NULL);
786 
787 	pfd_p = px_get_pfd(px_p);
788 
789 	PCIE_ROOT_FAULT(pfd_p)->scan_bdf = scan_bdf;
790 	PCIE_ROOT_FAULT(pfd_p)->scan_addr = (uint64_t)scan_addr;
791 	PCI_BDG_ERR_REG(pfd_p)->pci_bdg_sec_stat = s_status;
792 
793 	return (pfd_p);
794 }
795 
796 
797 /*
798  * Find and Mark CFG Handles as failed associated with the given BDF. We should
799  * always know the BDF for CFG accesses, since it is encoded in the address of
800  * the TLP.  Since there can be multiple cfg handles, mark them all as failed.
801  */
802 /* ARGSUSED */
803 int
px_err_cfg_hdl_check(dev_info_t * dip,const void * handle,const void * arg1,const void * arg2)804 px_err_cfg_hdl_check(dev_info_t *dip, const void *handle, const void *arg1,
805     const void *arg2)
806 {
807 	int			status = DDI_FM_FATAL;
808 	uint32_t		addr = *(uint32_t *)arg1;
809 	uint16_t		bdf = *(uint16_t *)arg2;
810 	pcie_bus_t		*bus_p;
811 
812 	DBG(DBG_ERR_INTR, dip, "Check CFG Hdl: dip 0x%p addr 0x%x bdf=0x%x\n",
813 	    dip, addr, bdf);
814 
815 	bus_p = PCIE_DIP2BUS(dip);
816 
817 	/*
818 	 * Because CFG and IO Acc Handlers are on the same cache list and both
819 	 * types of hdls gets called for both types of errors.  For this checker
820 	 * only mark the device as "Non-Fatal" if the addr == NULL and bdf !=
821 	 * NULL.
822 	 */
823 	status = (!addr && (PCIE_CHECK_VALID_BDF(bdf) &&
824 	    (bus_p->bus_bdf == bdf))) ? DDI_FM_NONFATAL : DDI_FM_FATAL;
825 
826 	return (status);
827 }
828 
829 /*
830  * Find and Mark all ACC Handles associated with a give address and BDF as
831  * failed.  If the BDF != NULL, then check to see if the device has a ACC Handle
832  * associated with ADDR.  If the handle is not found, mark all the handles as
833  * failed.  If the BDF == NULL, mark the handle as failed if it is associated
834  * with ADDR.
835  */
836 int
px_err_pio_hdl_check(dev_info_t * dip,const void * handle,const void * arg1,const void * arg2)837 px_err_pio_hdl_check(dev_info_t *dip, const void *handle, const void *arg1,
838     const void *arg2)
839 {
840 	dev_info_t		*px_dip;
841 	px_t			*px_p;
842 	pci_ranges_t		*ranges_p;
843 	int			range_len;
844 	ddi_acc_handle_t	ap = (ddi_acc_handle_t)handle;
845 	ddi_acc_hdl_t		*hp = impl_acc_hdl_get(ap);
846 	int			i, status = DDI_FM_FATAL;
847 	uint64_t		fault_addr = *(uint64_t *)arg1;
848 	uint16_t		bdf = *(uint16_t *)arg2;
849 	uint64_t		base_addr, range_addr;
850 	uint_t			size;
851 
852 	/*
853 	 * Find the correct px dip.  On system with a real Root Port, it's the
854 	 * node above the root port.  On systems without a real Root Port the px
855 	 * dip is the bus_rp_dip.
856 	 */
857 	px_dip = PCIE_DIP2BUS(dip)->bus_rp_dip;
858 
859 	if (!PCIE_IS_RC(PCIE_DIP2BUS(px_dip)))
860 		px_dip = ddi_get_parent(px_dip);
861 
862 	ASSERT(PCIE_IS_RC(PCIE_DIP2BUS(px_dip)));
863 	px_p = INST_TO_STATE(ddi_get_instance(px_dip));
864 
865 	DBG(DBG_ERR_INTR, dip, "Check PIO Hdl: dip 0x%x addr 0x%x bdf=0x%x\n",
866 	    dip, fault_addr, bdf);
867 
868 	/* Normalize the base addr to the addr and strip off the HB info. */
869 	base_addr = (hp->ah_pfn << MMU_PAGESHIFT) + hp->ah_offset;
870 	range_len = px_p->px_ranges_length / sizeof (pci_ranges_t);
871 	i = 0;
872 	for (ranges_p = px_p->px_ranges_p; i < range_len; i++, ranges_p++) {
873 		range_addr = px_in_addr_range(dip, ranges_p, base_addr);
874 		if (range_addr) {
875 			switch (ranges_p->child_high & PCI_ADDR_MASK) {
876 			case PCI_ADDR_IO:
877 			case PCI_ADDR_MEM64:
878 			case PCI_ADDR_MEM32:
879 				base_addr = base_addr - range_addr;
880 				break;
881 			}
882 			break;
883 		}
884 	}
885 
886 	/*
887 	 * Mark the handle as failed if the ADDR is mapped, or if we
888 	 * know the BDF and ADDR == 0.
889 	 */
890 	size = hp->ah_len;
891 	if (((fault_addr >= base_addr) && (fault_addr < (base_addr + size))) ||
892 	    ((fault_addr == NULL) && (PCIE_CHECK_VALID_BDF(bdf) &&
893 	    (bdf == PCIE_DIP2BUS(dip)->bus_bdf))))
894 		status = DDI_FM_NONFATAL;
895 
896 	return (status);
897 }
898 
899 /*
900  * Find and Mark all DNA Handles associated with a give address and BDF as
901  * failed.  If the BDF != NULL, then check to see if the device has a DMA Handle
902  * associated with ADDR.  If the handle is not found, mark all the handles as
903  * failed.  If the BDF == NULL, mark the handle as failed if it is associated
904  * with ADDR.
905  */
906 int
px_err_dma_hdl_check(dev_info_t * dip,const void * handle,const void * arg1,const void * arg2)907 px_err_dma_hdl_check(dev_info_t *dip, const void *handle, const void *arg1,
908     const void *arg2)
909 {
910 	ddi_dma_impl_t		*pcie_dp;
911 	int			status = DDI_FM_FATAL;
912 	uint32_t		addr = *(uint32_t *)arg1;
913 	uint16_t		bdf = *(uint16_t *)arg2;
914 	uint32_t		base_addr;
915 	uint_t			size;
916 
917 	DBG(DBG_ERR_INTR, dip, "Check PIO Hdl: dip 0x%x addr 0x%x bdf=0x%x\n",
918 	    dip, addr, bdf);
919 
920 	pcie_dp = (ddi_dma_impl_t *)handle;
921 	base_addr = (uint32_t)pcie_dp->dmai_mapping;
922 	size = pcie_dp->dmai_size;
923 
924 	/*
925 	 * Mark the handle as failed if the ADDR is mapped, or if we
926 	 * know the BDF and ADDR == 0.
927 	 */
928 	if (((addr >= base_addr) && (addr < (base_addr + size))) ||
929 	    ((addr == NULL) && PCIE_CHECK_VALID_BDF(bdf)))
930 		status = DDI_FM_NONFATAL;
931 
932 	return (status);
933 }
934 
935 int
px_fm_enter(px_t * px_p)936 px_fm_enter(px_t *px_p) {
937 	if (px_panicing || (px_p->px_fm_mutex_owner == curthread))
938 		return (DDI_FAILURE);
939 
940 	mutex_enter(&px_p->px_fm_mutex);
941 	/*
942 	 * In rare cases when trap occurs and in the middle of scanning the
943 	 * fabric, a PIO will fail in the scan fabric.  The CPU error handling
944 	 * code will correctly panic the system, while a mondo for the failed
945 	 * PIO may also show up.  Normally the mondo will try to grab the mutex
946 	 * and wait until the callback finishes.  But in this rare case,
947 	 * mutex_enter actually suceeds also continues to scan the fabric.
948 	 *
949 	 * This code below is designed specifically to check for this case.  If
950 	 * we successfully grab the px_fm_mutex, the px_fm_mutex_owner better be
951 	 * NULL.  If it isn't that means we are in the rare corner case.  Return
952 	 * DDI_FAILURE, this should prevent PX from doing anymore error
953 	 * handling.
954 	 */
955 	if (px_p->px_fm_mutex_owner) {
956 		return (DDI_FAILURE);
957 	}
958 
959 	px_p->px_fm_mutex_owner = curthread;
960 
961 	if (px_panicing) {
962 		px_fm_exit(px_p);
963 		return (DDI_FAILURE);
964 	}
965 
966 	/* Signal the PCIe error handling module error handling is starting */
967 	pf_eh_enter(PCIE_DIP2BUS(px_p->px_dip));
968 
969 	return (DDI_SUCCESS);
970 }
971 
972 static void
px_guest_panic(px_t * px_p)973 px_guest_panic(px_t *px_p)
974 {
975 	pf_data_t *root_pfd_p = PCIE_DIP2PFD(px_p->px_dip);
976 	pf_data_t *pfd_p;
977 	pcie_bus_t *bus_p, *root_bus_p;
978 	pcie_req_id_list_t *rl;
979 
980 	/*
981 	 * check if all devices under the root device are unassigned.
982 	 * this function should quickly return in non-IOV environment.
983 	 */
984 	root_bus_p = PCIE_PFD2BUS(root_pfd_p);
985 	if (PCIE_BDG_IS_UNASSIGNED(root_bus_p))
986 		return;
987 
988 	for (pfd_p = root_pfd_p; pfd_p; pfd_p = pfd_p->pe_next) {
989 		bus_p = PCIE_PFD2BUS(pfd_p);
990 
991 		/* assume all affected devs were in the error Q */
992 		if (!PCIE_BUS2DOM(bus_p)->nfma_panic)
993 			continue;
994 
995 		if (PCIE_IS_BDG(bus_p)) {
996 			rl = PCIE_BDF_LIST_GET(bus_p);
997 			while (rl) {
998 				px_panic_domain(px_p, rl->bdf);
999 				rl = rl->next;
1000 			}
1001 		} else {
1002 			px_panic_domain(px_p, bus_p->bus_bdf);
1003 		}
1004 		/* clear panic flag */
1005 		PCIE_BUS2DOM(bus_p)->nfma_panic = B_FALSE;
1006 	}
1007 }
1008 
1009 void
px_fm_exit(px_t * px_p)1010 px_fm_exit(px_t *px_p) {
1011 	px_p->px_fm_mutex_owner = NULL;
1012 	if (px_p->px_pfd_idx == 0) {
1013 		mutex_exit(&px_p->px_fm_mutex);
1014 		return;
1015 	}
1016 	/* panic the affected domains that are non-fma-capable */
1017 	px_guest_panic(px_p);
1018 	/* Signal the PCIe error handling module error handling is ending */
1019 	pf_eh_exit(PCIE_DIP2BUS(px_p->px_dip));
1020 	px_p->px_pfd_idx = 0;
1021 	mutex_exit(&px_p->px_fm_mutex);
1022 }
1023 
1024 /*
1025  * Panic if the err tunable is set and that we are not already in the middle
1026  * of panic'ing.
1027  *
1028  * rc_err = Error severity of PX specific errors
1029  * msg = Where the error was detected
1030  * fabric_err = Error severity of PCIe Fabric errors
1031  * isTest = Test if error severity causes panic
1032  */
1033 #define	MSZ (sizeof (fm_msg) -strlen(fm_msg) - 1)
1034 void
px_err_panic(int rc_err,int msg,int fabric_err,boolean_t isTest)1035 px_err_panic(int rc_err, int msg, int fabric_err, boolean_t isTest)
1036 {
1037 	char fm_msg[96] = "";
1038 	int ferr = PX_NO_ERROR;
1039 
1040 	if (panicstr) {
1041 		px_panicing = B_TRUE;
1042 		return;
1043 	}
1044 
1045 	if (!(rc_err & px_die))
1046 		goto fabric;
1047 	if (msg & PX_RC)
1048 		(void) strncat(fm_msg, px_panic_rc_msg, MSZ);
1049 	if (msg & PX_RP)
1050 		(void) strncat(fm_msg, px_panic_rp_msg, MSZ);
1051 	if (msg & PX_HB)
1052 		(void) strncat(fm_msg, px_panic_hb_msg, MSZ);
1053 
1054 fabric:
1055 	if (fabric_err & PF_ERR_FATAL_FLAGS)
1056 		ferr = PX_PANIC;
1057 	else if (fabric_err & ~(PF_ERR_FATAL_FLAGS | PF_ERR_NO_ERROR))
1058 		ferr = PX_NO_PANIC;
1059 
1060 	if (ferr & px_die) {
1061 		if (strlen(fm_msg)) {
1062 			(void) strncat(fm_msg, " and", MSZ);
1063 		}
1064 		(void) strncat(fm_msg, px_panic_fab_msg, MSZ);
1065 	}
1066 
1067 	if (strlen(fm_msg)) {
1068 		px_panicing = B_TRUE;
1069 		if (!isTest)
1070 			fm_panic("Fatal error has occured in:%s.(0x%x)(0x%x)",
1071 			    fm_msg, rc_err, fabric_err);
1072 	}
1073 }
1074