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.
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.
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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
27 * PX Fault Management Architecture
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>
38 extern uint_t px_ranges_phi_mask
;
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)
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.
53 boolean_t px_panicing
= B_FALSE
;
55 static int px_pcie_ptlp(dev_info_t
*dip
, ddi_fm_error_t
*derr
,
59 static void px_pcie_log(dev_info_t
*dip
, px_err_pcie_t
*regs
);
61 #define px_pcie_log 0 &&
65 * Initialize px FMA support
68 px_fm_attach(px_t
*px_p
)
71 dev_info_t
*dip
= px_p
->px_dip
;
74 px_p
->px_fm_cap
= DDI_FM_EREPORT_CAPABLE
| DDI_FM_ERRCB_CAPABLE
|
75 DDI_FM_ACCCHK_CAPABLE
| DDI_FM_DMACHK_CAPABLE
;
78 * check parents' capability
80 ddi_fm_init(dip
, &px_p
->px_fm_cap
, &px_p
->px_fm_ibc
);
83 * parents need to be ereport and error handling capable
85 ASSERT(px_p
->px_fm_cap
&&
86 (DDI_FM_ERRCB_CAPABLE
| DDI_FM_EREPORT_CAPABLE
));
89 * Initialize lock to synchronize fabric error handling
91 mutex_init(&px_p
->px_fm_mutex
, NULL
, MUTEX_DRIVER
,
92 (void *)px_p
->px_fm_ibc
);
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
;
99 bus_p
= PCIE_DIP2BUS(dip
);
100 bus_p
->bus_rp_bdf
= px_p
->px_bdf
;
101 bus_p
->bus_rp_dip
= dip
;
103 return (DDI_SUCCESS
);
110 px_fm_detach(px_t
*px_p
)
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
]);
121 * register error callback in parent
124 px_fm_cb_enable(px_t
*px_p
)
126 ddi_fm_handler_register(px_p
->px_dip
, px_fm_callback
, px_p
);
130 px_fm_cb_disable(px_t
*px_p
)
132 ddi_fm_handler_unregister(px_p
->px_dip
);
136 * Function used to setup access functions depending on level of desired
140 px_fm_acc_setup(ddi_map_req_t
*mp
, dev_info_t
*rdip
, pci_regspec_t
*rp
)
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
;
151 if (mp
->map_op
== DDI_MO_MAP_LOCKED
) {
152 ndi_fmc_insert(rdip
, ACC_HANDLE
, (void *)hp
, NULL
);
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
) ==
175 errp
->err_cf
= px_err_cfg_hdl_check
;
177 errp
->err_cf
= px_err_pio_hdl_check
;
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
) ==
200 errp
->err_cf
= px_err_cfg_hdl_check
;
202 errp
->err_cf
= px_err_pio_hdl_check
;
205 /* Illegal state, remove the handle from cache */
206 ndi_fmc_remove(rdip
, ACC_HANDLE
, (void *)hp
);
209 } else if (mp
->map_op
== DDI_MO_UNMAP
) {
210 ndi_fmc_remove(rdip
, ACC_HANDLE
, (void *)hp
);
215 * Function used to initialize FMA for our children nodes. Called
216 * through pci busops when child node calls ddi_fm_init.
220 px_fm_init_child(dev_info_t
*dip
, dev_info_t
*cdip
, int cap
,
221 ddi_iblock_cookie_t
*ibc_p
)
223 px_t
*px_p
= DIP_TO_STATE(dip
);
225 ASSERT(ibc_p
!= NULL
);
226 *ibc_p
= px_p
->px_fm_ibc
;
228 return (px_p
->px_fm_cap
);
232 * lock access for exclusive PCIe access
235 px_bus_enter(dev_info_t
*dip
, ddi_acc_handle_t handle
)
237 px_pec_t
*pec_p
= ((px_t
*)DIP_TO_STATE(dip
))->px_pec_p
;
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().
247 mutex_enter(&pec_p
->pec_pokefault_mutex
);
248 pec_p
->pec_acc_hdl
= handle
;
252 * unlock access for exclusive PCIe access
256 px_bus_exit(dev_info_t
*dip
, ddi_acc_handle_t handle
)
258 px_t
*px_p
= DIP_TO_STATE(dip
);
259 px_pec_t
*pec_p
= px_p
->px_pec_p
;
261 pec_p
->pec_acc_hdl
= NULL
;
262 mutex_exit(&pec_p
->pec_pokefault_mutex
);
266 px_in_addr_range(dev_info_t
*dip
, pci_ranges_t
*ranges_p
, uint64_t addr
)
268 uint64_t addr_low
, addr_high
;
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
;
275 DBG(DBG_ERR_INTR
, dip
, "Addr: 0x%llx high: 0x%llx low: 0x%llx\n",
276 addr
, addr_high
, addr_low
);
278 if ((addr
< addr_high
) && (addr
>= addr_low
))
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.
291 px_fm_callback(dev_info_t
*dip
, ddi_fm_error_t
*derr
, const void *impl_data
)
293 dev_info_t
*pdip
= ddi_get_parent(dip
);
294 px_t
*px_p
= (px_t
*)impl_data
;
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
;
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.
310 if (px_p
->px_fm_mutex_owner
== curthread
)
311 return (DDI_FM_FATAL
);
313 i_ddi_fm_handler_exit(pdip
);
315 if (px_fm_enter(px_p
) != DDI_SUCCESS
) {
316 i_ddi_fm_handler_enter(pdip
);
317 return (DDI_FM_FATAL
);
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.
324 range_len
= px_p
->px_ranges_length
/ sizeof (pci_ranges_t
);
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
);
329 switch (ranges_p
->child_high
& PCI_ADDR_MASK
) {
330 case PCI_ADDR_CONFIG
:
331 acc_type
= PF_ADDR_CFG
;
333 bdf
= (pcie_req_id_t
)((fault_addr
>> 12) &
339 acc_type
= PF_ADDR_PIO
;
340 addr
= fault_addr
- base_addr
;
341 bdf
= PCIE_INVALID_BDF
;
348 /* This address doesn't belong to this leaf, just return with OK */
351 i_ddi_fm_handler_enter(pdip
);
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
,
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
;
363 /* Update affected info, either addr or bdf is not NULL */
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
;
371 fab_err
= px_scan_fabric(px_p
, dip
, derr
);
374 i_ddi_fm_handler_enter(pdip
);
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
))
386 return (DDI_FM_NONFATAL
);
390 * px_err_fabric_intr:
391 * Interrupt handler for PCIE fabric block.
394 * o px_err_cmn_intr(leaf, with jbc)
395 * o send ereport(fire fmri, derr, payload = BDF)
398 * o handle error: fatal? fm_panic() : return INTR_CLAIMED)
402 px_err_fabric_intr(px_t
*px_p
, msgcode_t msg_code
, pcie_req_id_t rid
)
404 dev_info_t
*rpdip
= px_p
->px_dip
;
408 uint16_t ce_source
, ue_source
;
411 if (px_fm_enter(px_p
) != DDI_SUCCESS
)
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
;
420 px_err_safeacc_check(px_p
, &derr
);
422 if (msg_code
== PCIE_MSG_CODE_ERR_COR
) {
423 rp_status
= PCIE_AER_RE_STS_CE_RCVD
;
427 rp_status
= PCIE_AER_RE_STS_FE_NFE_RCVD
;
430 if (msg_code
== PCIE_MSG_CODE_ERR_NONFATAL
)
431 rp_status
|= PCIE_AER_RE_STS_NFE_MSGS_RCVD
;
433 rp_status
|= PCIE_AER_RE_STS_FE_MSGS_RCVD
;
434 rp_status
|= PCIE_AER_RE_STS_FIRST_UC_FATAL
;
438 if (derr
.fme_flag
== DDI_FM_ERR_UNEXPECTED
) {
439 ddi_fm_ereport_post(rpdip
, PCI_ERROR_SUBCLASS
"." PCIEX_FABRIC
,
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
,
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
;
454 rc_err
= px_err_cmn_intr(px_p
, &derr
, PX_INTR_CALL
, PX_FM_BLOCK_PCIE
);
456 /* call rootport dispatch */
457 fab_err
= px_scan_fabric(px_p
, rpdip
, &derr
);
459 px_err_panic(rc_err
, PX_RC
, fab_err
, B_TRUE
);
461 px_err_panic(rc_err
, PX_RC
, fab_err
, B_FALSE
);
464 return (DDI_INTR_CLAIMED
);
470 * Check for drain state and if there is anything to scan.
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.
476 * Each error handling will populate the following info in the pfd
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 * ----------------+---------------+------------
488 px_scan_fabric(px_t
*px_p
, dev_info_t
*rpdip
, ddi_fm_error_t
*derr
) {
491 ASSERT(MUTEX_HELD(&px_p
->px_fm_mutex
));
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
);
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.
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.
509 * Safe access writes induced errors will be handled by px interrupt
510 * handlers, this function will fill in the derr fields.
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.
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.
521 * @param px_p leaf in which to check access
522 * @param derr fm err data structure to be updated
525 px_err_safeacc_check(px_t
*px_p
, ddi_fm_error_t
*derr
)
527 px_pec_t
*pec_p
= px_p
->px_pec_p
;
528 int acctype
= pec_p
->pec_safeacc_type
;
530 ASSERT(MUTEX_HELD(&px_p
->px_fm_mutex
));
532 if (derr
->fme_flag
!= DDI_FM_ERR_UNEXPECTED
) {
536 /* safe access checking */
538 case DDI_FM_ERR_EXPECTED
:
540 * cautious access protection, protected from all err.
542 ddi_fm_acc_err_get(pec_p
->pec_acc_hdl
, derr
,
544 derr
->fme_flag
= acctype
;
545 derr
->fme_acc_handle
= pec_p
->pec_acc_hdl
;
547 case DDI_FM_ERR_POKE
:
549 * ddi_poke protection, check nexus and children for
553 derr
->fme_flag
= acctype
;
555 case DDI_FM_ERR_PEEK
:
556 derr
->fme_flag
= acctype
;
562 * Suggest panic if any EQ (except CE q) has overflown.
565 px_err_check_eq(dev_info_t
*dip
)
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
;
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 */
577 if ((px_lib_msiq_getstate(dip
, i
+ eq_no
, &msiq_state
) !=
578 DDI_SUCCESS
) || msiq_state
== PCI_MSIQ_STATE_ERROR
)
581 return (PX_NO_PANIC
);
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
)
589 px_t
*px_p
= DIP_TO_STATE(dip
);
590 pf_data_t
*pfd_p
= px_get_pfd(px_p
);
592 pf_pcie_adv_err_regs_t
*adv_reg
= PCIE_ADV_REG(pfd_p
);
594 PCIE_ROOT_EH_SRC(pfd_p
)->intr_type
= intr_type
;
597 * set RC s_status in PCI term to coordinate with downstream fabric
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
;
607 if (!regs
->primary_ue
)
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
++;
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
==
629 PCIE_ROOT_FAULT(pfd_p
)->scan_addr
=
630 adv_reg
->pcie_ue_tgt_addr
;
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.
641 px_pcie_log(dip
, regs
);
643 /* Return No Error here and let the pcie misc module analyse it */
644 return (PX_NO_ERROR
);
649 px_pcie_log(dev_info_t
*dip
, px_err_pcie_t
*regs
)
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
);
662 * look through poisoned TLP cases and suggest panic/no panic depend on
666 px_pcie_ptlp(dev_info_t
*dip
, ddi_fm_error_t
*derr
, px_err_pcie_t
*regs
)
668 pf_pcie_adv_err_regs_t adv_reg
;
672 int tlp_sts
, tlp_cmd
;
673 int lookup
= PF_HDL_NOTFOUND
;
675 if (regs
->primary_ue
!= PCIE_AER_UCE_PTLP
)
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
;
686 tlp_sts
= pf_tlp_decode(PCIE_DIP2BUS(dip
), &adv_reg
);
687 tlp_cmd
= ((pcie_tlp_hdr_t
*)(adv_reg
.pcie_ue_hdr
))->type
;
689 if (tlp_sts
== DDI_FAILURE
)
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
;
697 case PCIE_TLP_TYPE_CPL
:
698 case PCIE_TLP_TYPE_CPLLK
:
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.
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
;
709 lookup
= pf_tlp_decode(PCIE_DIP2BUS(dip
), &adv_reg
);
710 if (lookup
!= DDI_SUCCESS
)
712 addr
= adv_reg
.pcie_ue_tgt_addr
;
713 trans_type
= adv_reg
.pcie_ue_tgt_trans
;
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
,
722 lookup
= PF_HDL_NOTFOUND
;
725 return (lookup
== PF_HDL_FOUND
? PX_NO_PANIC
: PX_PANIC
);
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.
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
];
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;
749 pfd_p
->pe_next
= NULL
;
752 px_p
->px_pfd_arr
[idx
- 1].pe_next
= pfd_p
;
753 pfd_p
->pe_prev
= &px_p
->px_pfd_arr
[idx
- 1];
755 pfd_p
->pe_prev
= NULL
;
758 pfd_p
->pe_severity_flags
= 0;
759 pfd_p
->pe_orig_severity_flags
= 0;
760 pfd_p
->pe_valid
= B_TRUE
;
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
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
774 * s_status: Secondary Status equivalent to why the fault occured.
776 * Either the scan bdf or addr may be NULL, but not both.
779 px_rp_en_q(px_t
*px_p
, pcie_req_id_t scan_bdf
, uint32_t scan_addr
,
784 if (!PCIE_CHECK_VALID_BDF(scan_bdf
) && !scan_addr
)
787 pfd_p
= px_get_pfd(px_p
);
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
;
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.
804 px_err_cfg_hdl_check(dev_info_t
*dip
, const void *handle
, const void *arg1
,
807 int status
= DDI_FM_FATAL
;
808 uint32_t addr
= *(uint32_t *)arg1
;
809 uint16_t bdf
= *(uint16_t *)arg2
;
812 DBG(DBG_ERR_INTR
, dip
, "Check CFG Hdl: dip 0x%p addr 0x%x bdf=0x%x\n",
815 bus_p
= PCIE_DIP2BUS(dip
);
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 !=
823 status
= (!addr
&& (PCIE_CHECK_VALID_BDF(bdf
) &&
824 (bus_p
->bus_bdf
== bdf
))) ? DDI_FM_NONFATAL
: DDI_FM_FATAL
;
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
837 px_err_pio_hdl_check(dev_info_t
*dip
, const void *handle
, const void *arg1
,
842 pci_ranges_t
*ranges_p
;
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
;
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.
857 px_dip
= PCIE_DIP2BUS(dip
)->bus_rp_dip
;
859 if (!PCIE_IS_RC(PCIE_DIP2BUS(px_dip
)))
860 px_dip
= ddi_get_parent(px_dip
);
862 ASSERT(PCIE_IS_RC(PCIE_DIP2BUS(px_dip
)));
863 px_p
= INST_TO_STATE(ddi_get_instance(px_dip
));
865 DBG(DBG_ERR_INTR
, dip
, "Check PIO Hdl: dip 0x%x addr 0x%x bdf=0x%x\n",
866 dip
, fault_addr
, bdf
);
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
);
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
);
875 switch (ranges_p
->child_high
& PCI_ADDR_MASK
) {
879 base_addr
= base_addr
- range_addr
;
887 * Mark the handle as failed if the ADDR is mapped, or if we
888 * know the BDF and ADDR == 0.
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
;
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
907 px_err_dma_hdl_check(dev_info_t
*dip
, const void *handle
, const void *arg1
,
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
;
917 DBG(DBG_ERR_INTR
, dip
, "Check PIO Hdl: dip 0x%x addr 0x%x bdf=0x%x\n",
920 pcie_dp
= (ddi_dma_impl_t
*)handle
;
921 base_addr
= (uint32_t)pcie_dp
->dmai_mapping
;
922 size
= pcie_dp
->dmai_size
;
925 * Mark the handle as failed if the ADDR is mapped, or if we
926 * know the BDF and ADDR == 0.
928 if (((addr
>= base_addr
) && (addr
< (base_addr
+ size
))) ||
929 ((addr
== NULL
) && PCIE_CHECK_VALID_BDF(bdf
)))
930 status
= DDI_FM_NONFATAL
;
936 px_fm_enter(px_t
*px_p
) {
937 if (px_panicing
|| (px_p
->px_fm_mutex_owner
== curthread
))
938 return (DDI_FAILURE
);
940 mutex_enter(&px_p
->px_fm_mutex
);
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.
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
955 if (px_p
->px_fm_mutex_owner
) {
956 return (DDI_FAILURE
);
959 px_p
->px_fm_mutex_owner
= curthread
;
963 return (DDI_FAILURE
);
966 /* Signal the PCIe error handling module error handling is starting */
967 pf_eh_enter(PCIE_DIP2BUS(px_p
->px_dip
));
969 return (DDI_SUCCESS
);
973 px_guest_panic(px_t
*px_p
)
975 pf_data_t
*root_pfd_p
= PCIE_DIP2PFD(px_p
->px_dip
);
977 pcie_bus_t
*bus_p
, *root_bus_p
;
978 pcie_req_id_list_t
*rl
;
981 * check if all devices under the root device are unassigned.
982 * this function should quickly return in non-IOV environment.
984 root_bus_p
= PCIE_PFD2BUS(root_pfd_p
);
985 if (PCIE_BDG_IS_UNASSIGNED(root_bus_p
))
988 for (pfd_p
= root_pfd_p
; pfd_p
; pfd_p
= pfd_p
->pe_next
) {
989 bus_p
= PCIE_PFD2BUS(pfd_p
);
991 /* assume all affected devs were in the error Q */
992 if (!PCIE_BUS2DOM(bus_p
)->nfma_panic
)
995 if (PCIE_IS_BDG(bus_p
)) {
996 rl
= PCIE_BDF_LIST_GET(bus_p
);
998 px_panic_domain(px_p
, rl
->bdf
);
1002 px_panic_domain(px_p
, bus_p
->bus_bdf
);
1004 /* clear panic flag */
1005 PCIE_BUS2DOM(bus_p
)->nfma_panic
= B_FALSE
;
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
);
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
);
1025 * Panic if the err tunable is set and that we are not already in the middle
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
1033 #define MSZ (sizeof (fm_msg) -strlen(fm_msg) - 1)
1035 px_err_panic(int rc_err
, int msg
, int fabric_err
, boolean_t isTest
)
1037 char fm_msg
[96] = "";
1038 int ferr
= PX_NO_ERROR
;
1041 px_panicing
= B_TRUE
;
1045 if (!(rc_err
& px_die
))
1048 (void) strncat(fm_msg
, px_panic_rc_msg
, MSZ
);
1050 (void) strncat(fm_msg
, px_panic_rp_msg
, MSZ
);
1052 (void) strncat(fm_msg
, px_panic_hb_msg
, MSZ
);
1055 if (fabric_err
& PF_ERR_FATAL_FLAGS
)
1057 else if (fabric_err
& ~(PF_ERR_FATAL_FLAGS
| PF_ERR_NO_ERROR
))
1060 if (ferr
& px_die
) {
1061 if (strlen(fm_msg
)) {
1062 (void) strncat(fm_msg
, " and", MSZ
);
1064 (void) strncat(fm_msg
, px_panic_fab_msg
, MSZ
);
1067 if (strlen(fm_msg
)) {
1068 px_panicing
= B_TRUE
;
1070 fm_panic("Fatal error has occured in:%s.(0x%x)(0x%x)",
1071 fm_msg
, rc_err
, fabric_err
);