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