/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCIX_ECC_VER_CHECK(x) (((x) == PCI_PCIX_VER_1) ||\ ((x) == PCI_PCIX_VER_2)) /* * Expected PCI Express error mask values */ uint32_t pcie_expected_ce_mask = PCIE_AER_CE_AD_NFE; uint32_t pcie_expected_ue_mask = 0x0; uint32_t pcie_expected_sue_mask = 0x0; errorq_t *pci_target_queue = NULL; pci_fm_err_t pci_err_tbl[] = { PCI_DET_PERR, PCI_STAT_PERROR, NULL, DDI_FM_UNKNOWN, PCI_MDPE, PCI_STAT_S_PERROR, PCI_TARG_MDPE, DDI_FM_UNKNOWN, PCI_SIG_SERR, PCI_STAT_S_SYSERR, NULL, DDI_FM_FATAL, PCI_MA, PCI_STAT_R_MAST_AB, PCI_TARG_MA, DDI_FM_UNKNOWN, PCI_REC_TA, PCI_STAT_R_TARG_AB, PCI_TARG_REC_TA, DDI_FM_UNKNOWN, PCI_SIG_TA, PCI_STAT_S_TARG_AB, NULL, DDI_FM_UNKNOWN, NULL, NULL, NULL, NULL, }; pci_fm_err_t pci_bdg_err_tbl[] = { PCI_DET_PERR, PCI_STAT_PERROR, NULL, DDI_FM_UNKNOWN, PCI_MDPE, PCI_STAT_S_PERROR, PCI_TARG_MDPE, DDI_FM_UNKNOWN, PCI_REC_SERR, PCI_STAT_S_SYSERR, NULL, DDI_FM_UNKNOWN, PCI_MA, PCI_STAT_R_MAST_AB, PCI_TARG_MA, DDI_FM_UNKNOWN, PCI_REC_TA, PCI_STAT_R_TARG_AB, PCI_TARG_REC_TA, DDI_FM_UNKNOWN, PCI_SIG_TA, PCI_STAT_S_TARG_AB, NULL, DDI_FM_UNKNOWN, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pciex_ce_err_tbl[] = { PCIEX_RE, PCIE_AER_CE_RECEIVER_ERR, NULL, DDI_FM_NONFATAL, PCIEX_RNR, PCIE_AER_CE_REPLAY_ROLLOVER, NULL, DDI_FM_NONFATAL, PCIEX_RTO, PCIE_AER_CE_REPLAY_TO, NULL, DDI_FM_NONFATAL, PCIEX_BDP, PCIE_AER_CE_BAD_DLLP, NULL, DDI_FM_NONFATAL, PCIEX_BTP, PCIE_AER_CE_BAD_TLP, NULL, DDI_FM_NONFATAL, PCIEX_ANFE, PCIE_AER_CE_AD_NFE, NULL, DDI_FM_NONFATAL, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pciex_ue_err_tbl[] = { PCIEX_TE, PCIE_AER_UCE_TRAINING, NULL, DDI_FM_FATAL, PCIEX_DLP, PCIE_AER_UCE_DLP, NULL, DDI_FM_FATAL, PCIEX_SD, PCIE_AER_UCE_SD, NULL, DDI_FM_FATAL, PCIEX_ROF, PCIE_AER_UCE_RO, NULL, DDI_FM_FATAL, PCIEX_FCP, PCIE_AER_UCE_FCP, NULL, DDI_FM_FATAL, PCIEX_MFP, PCIE_AER_UCE_MTLP, NULL, DDI_FM_FATAL, PCIEX_CTO, PCIE_AER_UCE_TO, NULL, DDI_FM_NONFATAL, PCIEX_UC, PCIE_AER_UCE_UC, NULL, DDI_FM_NONFATAL, PCIEX_ECRC, PCIE_AER_UCE_ECRC, NULL, DDI_FM_UNKNOWN, PCIEX_CA, PCIE_AER_UCE_CA, NULL, DDI_FM_UNKNOWN, PCIEX_UR, PCIE_AER_UCE_UR, NULL, DDI_FM_NONFATAL, PCIEX_POIS, PCIE_AER_UCE_PTLP, NULL, DDI_FM_UNKNOWN, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pcie_sue_err_tbl[] = { PCIEX_S_TA_SC, PCIE_AER_SUCE_TA_ON_SC, NULL, DDI_FM_UNKNOWN, PCIEX_S_MA_SC, PCIE_AER_SUCE_MA_ON_SC, NULL, DDI_FM_UNKNOWN, PCIEX_S_RTA, PCIE_AER_SUCE_RCVD_TA, NULL, DDI_FM_UNKNOWN, PCIEX_S_RMA, PCIE_AER_SUCE_RCVD_MA, NULL, DDI_FM_UNKNOWN, PCIEX_S_USC, PCIE_AER_SUCE_USC_ERR, NULL, DDI_FM_UNKNOWN, PCIEX_S_USCMD, PCIE_AER_SUCE_USC_MSG_DATA_ERR, NULL, DDI_FM_FATAL, PCIEX_S_UDE, PCIE_AER_SUCE_UC_DATA_ERR, NULL, DDI_FM_UNKNOWN, PCIEX_S_UAT, PCIE_AER_SUCE_UC_ATTR_ERR, NULL, DDI_FM_FATAL, PCIEX_S_UADR, PCIE_AER_SUCE_UC_ADDR_ERR, NULL, DDI_FM_FATAL, PCIEX_S_TEX, PCIE_AER_SUCE_TIMER_EXPIRED, NULL, DDI_FM_FATAL, PCIEX_S_PERR, PCIE_AER_SUCE_PERR_ASSERT, NULL, DDI_FM_UNKNOWN, PCIEX_S_SERR, PCIE_AER_SUCE_SERR_ASSERT, NULL, DDI_FM_FATAL, PCIEX_INTERR, PCIE_AER_SUCE_INTERNAL_ERR, NULL, DDI_FM_FATAL, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pcix_err_tbl[] = { PCIX_SPL_DIS, PCI_PCIX_SPL_DSCD, NULL, DDI_FM_UNKNOWN, PCIX_UNEX_SPL, PCI_PCIX_UNEX_SPL, NULL, DDI_FM_UNKNOWN, PCIX_RX_SPL_MSG, PCI_PCIX_RX_SPL_MSG, NULL, DDI_FM_UNKNOWN, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pcix_sec_err_tbl[] = { PCIX_SPL_DIS, PCI_PCIX_BSS_SPL_DSCD, NULL, DDI_FM_UNKNOWN, PCIX_UNEX_SPL, PCI_PCIX_BSS_UNEX_SPL, NULL, DDI_FM_UNKNOWN, PCIX_BSS_SPL_OR, PCI_PCIX_BSS_SPL_OR, NULL, DDI_FM_NONFATAL, PCIX_BSS_SPL_DLY, PCI_PCIX_BSS_SPL_DLY, NULL, DDI_FM_NONFATAL, NULL, NULL, NULL, NULL, }; static pci_fm_err_t pciex_nadv_err_tbl[] = { PCIEX_UR, PCIE_DEVSTS_UR_DETECTED, NULL, DDI_FM_UNKNOWN, PCIEX_FAT, PCIE_DEVSTS_FE_DETECTED, NULL, DDI_FM_FATAL, PCIEX_NONFAT, PCIE_DEVSTS_NFE_DETECTED, NULL, DDI_FM_UNKNOWN, PCIEX_CORR, PCIE_DEVSTS_CE_DETECTED, NULL, DDI_FM_NONFATAL, NULL, NULL, NULL, NULL, }; static int pci_config_check(ddi_acc_handle_t handle) { ddi_acc_hdl_t *hp = impl_acc_hdl_get(handle); ddi_fm_error_t de; if (!(DDI_FM_ACC_ERR_CAP(ddi_fm_capable(hp->ah_dip)))) return (DDI_FM_OK); de.fme_version = DDI_FME_VERSION; ddi_fm_acc_err_get(handle, &de, de.fme_version); if (de.fme_status != DDI_FM_OK) { char buf[FM_MAX_CLASS]; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS, PCI_NR); ddi_fm_ereport_post(hp->ah_dip, buf, de.fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, NULL); ddi_fm_acc_err_clear(handle, de.fme_version); } return (de.fme_status); } static void pcix_ecc_regs_gather(pci_erpt_t *erpt_p, pcix_ecc_regs_t *pcix_ecc_regs, uint8_t pcix_cap_ptr) { int bdg = erpt_p->pe_dflags & PCI_BRIDGE_DEV; pcix_ecc_regs->pcix_ecc_ctlstat = pci_config_get32(erpt_p->pe_hdl, (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_STATUS : PCI_PCIX_ECC_STATUS))); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcix_ecc_regs->pcix_ecc_vflags |= PCIX_ERR_ECC_STS_VALID; else return; pcix_ecc_regs->pcix_ecc_fstaddr = pci_config_get32(erpt_p->pe_hdl, (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_FST_AD : PCI_PCIX_ECC_FST_AD))); pcix_ecc_regs->pcix_ecc_secaddr = pci_config_get32(erpt_p->pe_hdl, (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_SEC_AD : PCI_PCIX_ECC_SEC_AD))); pcix_ecc_regs->pcix_ecc_attr = pci_config_get32(( ddi_acc_handle_t)erpt_p->pe_hdl, (pcix_cap_ptr + (bdg ? PCI_PCIX_BDG_ECC_ATTR : PCI_PCIX_ECC_ATTR))); } static void pcix_regs_gather(pci_erpt_t *erpt_p, void *pe_regs) { if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pcix_bdg_error_regs_t *pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs; uint8_t pcix_bdg_cap_ptr; int i; pcix_bdg_cap_ptr = pcix_bdg_regs->pcix_bdg_cap_ptr; pcix_bdg_regs->pcix_bdg_sec_stat = pci_config_get16( erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_SEC_STATUS)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcix_bdg_regs->pcix_bdg_vflags |= PCIX_BDG_SEC_STATUS_VALID; else return; pcix_bdg_regs->pcix_bdg_stat = pci_config_get32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_STATUS)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcix_bdg_regs->pcix_bdg_vflags |= PCIX_BDG_STATUS_VALID; else return; if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) { pcix_ecc_regs_t *pcix_bdg_ecc_regs; /* * PCI Express to PCI-X bridges only implement the * secondary side of the PCI-X ECC registers, bit one is * read-only so we make sure we do not write to it. */ if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) { pcix_bdg_ecc_regs = pcix_bdg_regs->pcix_bdg_ecc_regs[1]; pcix_ecc_regs_gather(erpt_p, pcix_bdg_ecc_regs, pcix_bdg_cap_ptr); } else { for (i = 0; i < 2; i++) { pcix_bdg_ecc_regs = pcix_bdg_regs->pcix_bdg_ecc_regs[i]; pci_config_put32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_ECC_STATUS), i); pcix_ecc_regs_gather(erpt_p, pcix_bdg_ecc_regs, pcix_bdg_cap_ptr); } } } } else { pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)pe_regs; uint8_t pcix_cap_ptr; pcix_cap_ptr = pcix_regs->pcix_cap_ptr; pcix_regs->pcix_command = pci_config_get16(erpt_p->pe_hdl, (pcix_cap_ptr + PCI_PCIX_COMMAND)); pcix_regs->pcix_status = pci_config_get32(erpt_p->pe_hdl, (pcix_cap_ptr + PCI_PCIX_STATUS)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcix_regs->pcix_vflags |= PCIX_ERR_STATUS_VALID; else return; if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) { pcix_ecc_regs_t *pcix_ecc_regs = pcix_regs->pcix_ecc_regs; pcix_ecc_regs_gather(erpt_p, pcix_ecc_regs, pcix_cap_ptr); } } } static void pcie_regs_gather(pci_erpt_t *erpt_p) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; uint8_t pcie_cap_ptr; pcie_adv_error_regs_t *pcie_adv_regs; uint16_t pcie_ecap_ptr; pcie_cap_ptr = pcie_regs->pcie_cap_ptr; pcie_regs->pcie_err_status = pci_config_get16(erpt_p->pe_hdl, pcie_cap_ptr + PCIE_DEVSTS); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_regs->pcie_vflags |= PCIE_ERR_STATUS_VALID; else return; pcie_regs->pcie_err_ctl = pci_config_get16(erpt_p->pe_hdl, (pcie_cap_ptr + PCIE_DEVCTL)); if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV) && (erpt_p->pe_dflags & PCIX_DEV)) pcix_regs_gather(erpt_p, pcie_regs->pcix_bdg_regs); if (erpt_p->pe_dflags & PCIEX_RC_DEV) { pcie_rc_error_regs_t *pcie_rc_regs = pcie_regs->pcie_rc_regs; pcie_rc_regs->pcie_rc_status = pci_config_get32(erpt_p->pe_hdl, (pcie_cap_ptr + PCIE_ROOTSTS)); pcie_rc_regs->pcie_rc_ctl = pci_config_get16(erpt_p->pe_hdl, (pcie_cap_ptr + PCIE_ROOTCTL)); } if (!(erpt_p->pe_dflags & PCIEX_ADV_DEV)) return; pcie_adv_regs = pcie_regs->pcie_adv_regs; pcie_ecap_ptr = pcie_adv_regs->pcie_adv_cap_ptr; pcie_adv_regs->pcie_ue_status = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_UCE_STS); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_adv_regs->pcie_adv_vflags |= PCIE_UE_STATUS_VALID; pcie_adv_regs->pcie_ue_mask = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_UCE_MASK); pcie_adv_regs->pcie_ue_sev = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_UCE_SERV); pcie_adv_regs->pcie_adv_ctl = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_CTL); pcie_adv_regs->pcie_ue_hdr0 = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_HDR_LOG); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) { int i; pcie_adv_regs->pcie_adv_vflags |= PCIE_UE_HDR_VALID; for (i = 0; i < 3; i++) { pcie_adv_regs->pcie_ue_hdr[i] = pci_config_get32( erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_HDR_LOG + (4 * (i + 1))); } } pcie_adv_regs->pcie_ce_status = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_CE_STS); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_adv_regs->pcie_adv_vflags |= PCIE_CE_STATUS_VALID; pcie_adv_regs->pcie_ce_mask = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_CE_MASK); /* * If pci express to pci bridge then grab the bridge * error registers. */ if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) { pcie_adv_bdg_error_regs_t *pcie_bdg_regs = pcie_adv_regs->pcie_adv_bdg_regs; pcie_bdg_regs->pcie_sue_status = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_SUCE_STS); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_adv_regs->pcie_adv_vflags |= PCIE_SUE_STATUS_VALID; pcie_bdg_regs->pcie_sue_hdr0 = pci_config_get32(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_SHDR_LOG)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) { int i; pcie_adv_regs->pcie_adv_vflags |= PCIE_SUE_HDR_VALID; for (i = 0; i < 3; i++) { pcie_bdg_regs->pcie_sue_hdr[i] = pci_config_get32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_SHDR_LOG + (4 * (i + 1))); } } } /* * If PCI Express root complex then grab the root complex * error registers. */ if (erpt_p->pe_dflags & PCIEX_RC_DEV) { pcie_adv_rc_error_regs_t *pcie_rc_regs = pcie_adv_regs->pcie_adv_rc_regs; pcie_rc_regs->pcie_rc_err_cmd = pci_config_get32(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_RE_CMD)); pcie_rc_regs->pcie_rc_err_status = pci_config_get32(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_RE_STS)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_adv_regs->pcie_adv_vflags |= PCIE_RC_ERR_STATUS_VALID; pcie_rc_regs->pcie_rc_ce_src_id = pci_config_get16(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_CE_SRC_ID)); pcie_rc_regs->pcie_rc_ue_src_id = pci_config_get16(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_ERR_SRC_ID)); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pcie_adv_regs->pcie_adv_vflags |= PCIE_SRC_ID_VALID; } } /*ARGSUSED*/ static void pci_regs_gather(dev_info_t *dip, pci_erpt_t *erpt_p) { pci_error_regs_t *pci_regs = erpt_p->pe_pci_regs; /* * Start by reading all the error registers that are available for * pci and pci express and for leaf devices and bridges/switches */ pci_regs->pci_err_status = pci_config_get16(erpt_p->pe_hdl, PCI_CONF_STAT); if (pci_config_check(erpt_p->pe_hdl) != DDI_FM_OK) return; pci_regs->pci_vflags |= PCI_ERR_STATUS_VALID; pci_regs->pci_cfg_comm = pci_config_get16(erpt_p->pe_hdl, PCI_CONF_COMM); if (pci_config_check(erpt_p->pe_hdl) != DDI_FM_OK) return; /* * If pci-pci bridge grab PCI bridge specific error registers. */ if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pci_regs->pci_bdg_regs->pci_bdg_sec_stat = pci_config_get16(erpt_p->pe_hdl, PCI_BCNF_SEC_STATUS); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pci_regs->pci_bdg_regs->pci_bdg_vflags |= PCI_BDG_SEC_STAT_VALID; pci_regs->pci_bdg_regs->pci_bdg_ctrl = pci_config_get16(erpt_p->pe_hdl, PCI_BCNF_BCNTRL); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) pci_regs->pci_bdg_regs->pci_bdg_vflags |= PCI_BDG_CTRL_VALID; } /* * If pci express device grab pci express error registers and * check for advanced error reporting features and grab them if * available. */ if (erpt_p->pe_dflags & PCIEX_DEV) pcie_regs_gather(erpt_p); else if (erpt_p->pe_dflags & PCIX_DEV) pcix_regs_gather(erpt_p, erpt_p->pe_regs); } static void pcix_regs_clear(pci_erpt_t *erpt_p, void *pe_regs) { if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pcix_bdg_error_regs_t *pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs; uint8_t pcix_bdg_cap_ptr; int i; pcix_bdg_cap_ptr = pcix_bdg_regs->pcix_bdg_cap_ptr; if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_SEC_STATUS_VALID) pci_config_put16(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_SEC_STATUS), pcix_bdg_regs->pcix_bdg_sec_stat); if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_STATUS), pcix_bdg_regs->pcix_bdg_stat); pcix_bdg_regs->pcix_bdg_vflags = 0x0; if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) { pcix_ecc_regs_t *pcix_bdg_ecc_regs; /* * PCI Express to PCI-X bridges only implement the * secondary side of the PCI-X ECC registers, bit one is * read-only so we make sure we do not write to it. */ if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) { pcix_bdg_ecc_regs = pcix_bdg_regs->pcix_bdg_ecc_regs[1]; if (pcix_bdg_ecc_regs->pcix_ecc_vflags & PCIX_ERR_ECC_STS_VALID) { pci_config_put32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_ECC_STATUS), pcix_bdg_ecc_regs-> pcix_ecc_ctlstat); } pcix_bdg_ecc_regs->pcix_ecc_vflags = 0x0; } else { for (i = 0; i < 2; i++) { pcix_bdg_ecc_regs = pcix_bdg_regs->pcix_bdg_ecc_regs[i]; if (pcix_bdg_ecc_regs->pcix_ecc_vflags & PCIX_ERR_ECC_STS_VALID) { pci_config_put32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_ECC_STATUS), i); pci_config_put32(erpt_p->pe_hdl, (pcix_bdg_cap_ptr + PCI_PCIX_BDG_ECC_STATUS), pcix_bdg_ecc_regs-> pcix_ecc_ctlstat); } pcix_bdg_ecc_regs->pcix_ecc_vflags = 0x0; } } } } else { pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)pe_regs; uint8_t pcix_cap_ptr; pcix_cap_ptr = pcix_regs->pcix_cap_ptr; if (pcix_regs->pcix_vflags & PCIX_ERR_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, (pcix_cap_ptr + PCI_PCIX_STATUS), pcix_regs->pcix_status); pcix_regs->pcix_vflags = 0x0; if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) { pcix_ecc_regs_t *pcix_ecc_regs = pcix_regs->pcix_ecc_regs; if (pcix_ecc_regs->pcix_ecc_vflags & PCIX_ERR_ECC_STS_VALID) pci_config_put32(erpt_p->pe_hdl, (pcix_cap_ptr + PCI_PCIX_ECC_STATUS), pcix_ecc_regs->pcix_ecc_ctlstat); pcix_ecc_regs->pcix_ecc_vflags = 0x0; } } } static void pcie_regs_clear(pci_erpt_t *erpt_p) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; uint8_t pcie_cap_ptr; pcie_adv_error_regs_t *pcie_adv_regs; uint16_t pcie_ecap_ptr; pcie_cap_ptr = pcie_regs->pcie_cap_ptr; if (pcie_regs->pcie_vflags & PCIE_ERR_STATUS_VALID) pci_config_put16(erpt_p->pe_hdl, pcie_cap_ptr + PCIE_DEVSTS, pcie_regs->pcie_err_status); pcie_regs->pcie_vflags = 0x0; if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV) && (erpt_p->pe_dflags & PCIX_DEV)) pcix_regs_clear(erpt_p, pcie_regs->pcix_bdg_regs); if (!(erpt_p->pe_dflags & PCIEX_ADV_DEV)) return; pcie_adv_regs = pcie_regs->pcie_adv_regs; pcie_ecap_ptr = pcie_adv_regs->pcie_adv_cap_ptr; if (pcie_adv_regs->pcie_adv_vflags & PCIE_UE_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_UCE_STS, pcie_adv_regs->pcie_ue_status); if (pcie_adv_regs->pcie_adv_vflags & PCIE_CE_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_CE_STS, pcie_adv_regs->pcie_ce_status); if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) { pcie_adv_bdg_error_regs_t *pcie_bdg_regs = pcie_adv_regs->pcie_adv_bdg_regs; if (pcie_adv_regs->pcie_adv_vflags & PCIE_SUE_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_SUCE_STS, pcie_bdg_regs->pcie_sue_status); } /* * If PCI Express root complex then clear the root complex * error registers. */ if (erpt_p->pe_dflags & PCIEX_RC_DEV) { pcie_adv_rc_error_regs_t *pcie_rc_regs = pcie_adv_regs->pcie_adv_rc_regs; if (pcie_adv_regs->pcie_adv_vflags & PCIE_RC_ERR_STATUS_VALID) pci_config_put32(erpt_p->pe_hdl, (pcie_ecap_ptr + PCIE_AER_RE_STS), pcie_rc_regs->pcie_rc_err_status); } pcie_adv_regs->pcie_adv_vflags = 0x0; } static void pci_regs_clear(pci_erpt_t *erpt_p) { /* * Finally clear the error bits */ if (erpt_p->pe_dflags & PCIEX_DEV) pcie_regs_clear(erpt_p); else if (erpt_p->pe_dflags & PCIX_DEV) pcix_regs_clear(erpt_p, erpt_p->pe_regs); if (erpt_p->pe_pci_regs->pci_vflags & PCI_ERR_STATUS_VALID) pci_config_put16(erpt_p->pe_hdl, PCI_CONF_STAT, erpt_p->pe_pci_regs->pci_err_status); erpt_p->pe_pci_regs->pci_vflags = 0x0; if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { if (erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags & PCI_BDG_SEC_STAT_VALID) pci_config_put16(erpt_p->pe_hdl, PCI_BCNF_SEC_STATUS, erpt_p->pe_pci_regs->pci_bdg_regs-> pci_bdg_sec_stat); if (erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags & PCI_BDG_CTRL_VALID) pci_config_put16(erpt_p->pe_hdl, PCI_BCNF_BCNTRL, erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_ctrl); erpt_p->pe_pci_regs->pci_bdg_regs->pci_bdg_vflags = 0x0; } } /* * pcix_ereport_setup: Allocate structures for PCI-X error handling and ereport * generation. */ /* ARGSUSED */ static void pcix_ereport_setup(dev_info_t *dip, pci_erpt_t *erpt_p) { uint8_t pcix_cap_ptr; int i; pcix_cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "pcix-capid-pointer", PCI_CAP_NEXT_PTR_NULL); if (pcix_cap_ptr != PCI_CAP_NEXT_PTR_NULL) erpt_p->pe_dflags |= PCIX_DEV; else return; if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pcix_bdg_error_regs_t *pcix_bdg_regs; erpt_p->pe_regs = kmem_zalloc(sizeof (pcix_bdg_error_regs_t), KM_SLEEP); pcix_bdg_regs = (pcix_bdg_error_regs_t *)erpt_p->pe_regs; pcix_bdg_regs->pcix_bdg_cap_ptr = pcix_cap_ptr; pcix_bdg_regs->pcix_bdg_ver = pci_config_get16(erpt_p->pe_hdl, pcix_cap_ptr + PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK; if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) { for (i = 0; i < 2; i++) { pcix_bdg_regs->pcix_bdg_ecc_regs[i] = kmem_zalloc(sizeof (pcix_ecc_regs_t), KM_SLEEP); } } } else { pcix_error_regs_t *pcix_regs; erpt_p->pe_regs = kmem_zalloc(sizeof (pcix_error_regs_t), KM_SLEEP); pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs; pcix_regs->pcix_cap_ptr = pcix_cap_ptr; pcix_regs->pcix_ver = pci_config_get16(erpt_p->pe_hdl, pcix_cap_ptr + PCI_PCIX_COMMAND) & PCI_PCIX_VER_MASK; if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) { pcix_regs->pcix_ecc_regs = kmem_zalloc( sizeof (pcix_ecc_regs_t), KM_SLEEP); } } } static void pcie_ereport_setup(dev_info_t *dip, pci_erpt_t *erpt_p) { pcie_error_regs_t *pcie_regs; pcie_adv_error_regs_t *pcie_adv_regs; char buf[FM_MAX_CLASS]; uint8_t pcix_cap_ptr; uint8_t pcie_cap_ptr; uint16_t pcie_ecap_ptr; uint16_t dev_type = 0; uint32_t mask = pcie_expected_ue_mask; /* * The following sparc specific code should be removed once the pci_cap * interfaces create the necessary properties for us. */ #if defined(__sparc) ushort_t status; uint32_t slot_cap; uint8_t cap_ptr = 0; uint8_t cap_id = 0; uint32_t hdr, hdr_next_ptr, hdr_cap_id; uint16_t offset = P2ALIGN(PCIE_EXT_CAP, 4); uint16_t aer_ptr = 0; cap_ptr = pci_config_get8(erpt_p->pe_hdl, PCI_CONF_CAP_PTR); if (pci_config_check(erpt_p->pe_hdl) == DDI_FM_OK) { while ((cap_id = pci_config_get8(erpt_p->pe_hdl, cap_ptr)) != 0xff) { if (cap_id == PCI_CAP_ID_PCIX) { (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pcix-capid-pointer", cap_ptr); } if (cap_id == PCI_CAP_ID_PCI_E) { status = pci_config_get16(erpt_p->pe_hdl, cap_ptr + 2); if (status & PCIE_PCIECAP_SLOT_IMPL) { /* offset 14h is Slot Cap Register */ slot_cap = pci_config_get32(erpt_p->pe_hdl, cap_ptr + PCIE_SLOTCAP); (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pcie-slotcap-reg", slot_cap); } (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pcie-capid-reg", pci_config_get16(erpt_p->pe_hdl, cap_ptr + PCIE_PCIECAP)); (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pcie-capid-pointer", cap_ptr); } if ((cap_ptr = pci_config_get8(erpt_p->pe_hdl, cap_ptr + 1)) == 0xff || cap_ptr == 0 || (pci_config_check(erpt_p->pe_hdl) != DDI_FM_OK)) break; } } #endif pcix_cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "pcix-capid-pointer", PCI_CAP_NEXT_PTR_NULL); if (pcix_cap_ptr != PCI_CAP_NEXT_PTR_NULL) erpt_p->pe_dflags |= PCIX_DEV; pcie_cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "pcie-capid-pointer", PCI_CAP_NEXT_PTR_NULL); if (pcie_cap_ptr != PCI_CAP_NEXT_PTR_NULL) { erpt_p->pe_dflags |= PCIEX_DEV; erpt_p->pe_regs = kmem_zalloc(sizeof (pcie_error_regs_t), KM_SLEEP); pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; pcie_regs->pcie_cap_ptr = pcie_cap_ptr; } if (!(erpt_p->pe_dflags & PCIEX_DEV)) return; /* * Don't currently need to check for version here because we are * compliant with PCIE 1.0a which is version 0 and is guaranteed * software compatibility with future versions. We will need to * add errors for new detectors/features which are added in newer * revisions [sec 7.8.2]. */ pcie_regs->pcie_cap = pci_config_get16(erpt_p->pe_hdl, pcie_regs->pcie_cap_ptr + PCIE_PCIECAP); dev_type = pcie_regs->pcie_cap & PCIE_PCIECAP_DEV_TYPE_MASK; if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV) && (erpt_p->pe_dflags & PCIX_DEV)) { int i; pcie_regs->pcix_bdg_regs = kmem_zalloc(sizeof (pcix_bdg_error_regs_t), KM_SLEEP); pcie_regs->pcix_bdg_regs->pcix_bdg_cap_ptr = pcix_cap_ptr; pcie_regs->pcix_bdg_regs->pcix_bdg_ver = pci_config_get16(erpt_p->pe_hdl, pcix_cap_ptr + PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK; if (PCIX_ECC_VER_CHECK(pcie_regs->pcix_bdg_regs->pcix_bdg_ver)) for (i = 0; i < 2; i++) pcie_regs->pcix_bdg_regs->pcix_bdg_ecc_regs[i] = kmem_zalloc(sizeof (pcix_ecc_regs_t), KM_SLEEP); } if (dev_type == PCIE_PCIECAP_DEV_TYPE_ROOT) { erpt_p->pe_dflags |= PCIEX_RC_DEV; pcie_regs->pcie_rc_regs = kmem_zalloc( sizeof (pcie_rc_error_regs_t), KM_SLEEP); } /* * The following sparc specific code should be removed once the pci_cap * interfaces create the necessary properties for us. */ #if defined(__sparc) hdr = pci_config_get32(erpt_p->pe_hdl, offset); hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK; hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK; while ((hdr_next_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) && (hdr_cap_id != PCIE_EXT_CAP_ID_AER)) { offset = P2ALIGN(hdr_next_ptr, 4); hdr = pci_config_get32(erpt_p->pe_hdl, offset); hdr_next_ptr = (hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK; hdr_cap_id = (hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK; } if (hdr_cap_id == PCIE_EXT_CAP_ID_AER) aer_ptr = P2ALIGN(offset, 4); if (aer_ptr != PCI_CAP_NEXT_PTR_NULL) (void) ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pcie-aer-pointer", aer_ptr); #endif /* * Find and store if this device is capable of pci express * advanced errors, if not report an error against the device. */ pcie_ecap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "pcie-aer-pointer", PCI_CAP_NEXT_PTR_NULL); if (pcie_ecap_ptr != PCI_CAP_NEXT_PTR_NULL) { erpt_p->pe_dflags |= PCIEX_ADV_DEV; pcie_regs->pcie_adv_regs = kmem_zalloc( sizeof (pcie_adv_error_regs_t), KM_SLEEP); pcie_regs->pcie_adv_regs->pcie_adv_cap_ptr = pcie_ecap_ptr; } if (!(erpt_p->pe_dflags & PCIEX_ADV_DEV)) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_NADV); ddi_fm_ereport_post(dip, buf, NULL, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, NULL); return; } pcie_adv_regs = pcie_regs->pcie_adv_regs; if (pcie_adv_regs == NULL) return; /* * Initialize structures for advanced PCI Express devices. */ /* * Advanced error registers exist for PCI Express to PCI(X) Bridges and * may also exist for PCI(X) to PCI Express Bridges, the latter is not * well explained in the PCI Express to PCI/PCI-X Bridge Specification * 1.0 and will be left out of the current gathering of these registers. */ if (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) { erpt_p->pe_dflags |= PCIEX_2PCI_DEV; pcie_adv_regs->pcie_adv_bdg_regs = kmem_zalloc( sizeof (pcie_adv_bdg_error_regs_t), KM_SLEEP); } if (erpt_p->pe_dflags & PCIEX_RC_DEV) pcie_adv_regs->pcie_adv_rc_regs = kmem_zalloc( sizeof (pcie_adv_rc_error_regs_t), KM_SLEEP); /* * Check that mask values are as expected, if not * change them to what we desire. */ pci_regs_gather(dip, erpt_p); pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; if (pcie_regs->pcie_adv_regs->pcie_ce_mask != pcie_expected_ce_mask) { pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_CE_MASK, pcie_expected_ce_mask); } /* Disable PTLP/ECRC (or mask these two) for Switches */ if (dev_type == PCIE_PCIECAP_DEV_TYPE_UP || dev_type == PCIE_PCIECAP_DEV_TYPE_DOWN) mask |= PCIE_AER_UCE_PTLP | PCIE_AER_UCE_ECRC; if (pcie_regs->pcie_adv_regs->pcie_ue_mask != mask) { pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_UCE_MASK, mask); } if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) { if (pcie_regs->pcie_adv_regs->pcie_adv_bdg_regs->pcie_sue_mask != pcie_expected_sue_mask) { pci_config_put32(erpt_p->pe_hdl, pcie_ecap_ptr + PCIE_AER_SUCE_MASK, pcie_expected_sue_mask); } } } /* * pci_ereport_setup: Detect PCI device type and initialize structures to be * used to generate ereports based on detected generic device errors. */ void pci_ereport_setup(dev_info_t *dip) { struct dev_info *devi = DEVI(dip); struct i_ddi_fmhdl *fmhdl = devi->devi_fmhdl; pci_erpt_t *erpt_p; uint8_t pci_hdr_type; uint16_t pci_status; pci_regspec_t *pci_rp; int32_t len; uint32_t phys_hi; /* * If device is not ereport capbable then report an error against the * driver for using this interface, */ if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) && !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) { i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); return; } /* * ASSERT fmhdl exists and fh_bus_specific is NULL. */ ASSERT(fmhdl && (fmhdl->fh_bus_specific == NULL)); erpt_p = kmem_zalloc(sizeof (pci_erpt_t), KM_SLEEP); if (pci_config_setup(dip, &erpt_p->pe_hdl) != DDI_SUCCESS) goto error; erpt_p->pe_pci_regs = kmem_zalloc(sizeof (pci_error_regs_t), KM_SLEEP); pci_status = pci_config_get16(erpt_p->pe_hdl, PCI_CONF_STAT); if (pci_config_check(erpt_p->pe_hdl) != DDI_FM_OK) goto error; /* * Get header type and record if device is a bridge. */ pci_hdr_type = pci_config_get8(erpt_p->pe_hdl, PCI_CONF_HEADER); if (pci_config_check(erpt_p->pe_hdl) != DDI_FM_OK) goto error; /* * Check to see if PCI device is a bridge, if so allocate pci bridge * error register structure. */ if ((pci_hdr_type & PCI_HEADER_TYPE_M) == PCI_HEADER_PPB) { erpt_p->pe_dflags |= PCI_BRIDGE_DEV; erpt_p->pe_pci_regs->pci_bdg_regs = kmem_zalloc( sizeof (pci_bdg_error_regs_t), KM_SLEEP); } if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&pci_rp, &len) == DDI_SUCCESS) { phys_hi = pci_rp->pci_phys_hi; kmem_free(pci_rp, len); erpt_p->pe_bdf = (uint16_t)(PCI_REG_BDFR_G(phys_hi) >> PCI_REG_FUNC_SHIFT); } if (!(pci_status & PCI_STAT_CAP)) { goto done; } /* * Initialize structures for PCI Express and PCI-X devices. * Order matters below and pcie_ereport_setup should preceed * pcix_ereport_setup. */ pcie_ereport_setup(dip, erpt_p); if (!(erpt_p->pe_dflags & PCIEX_DEV)) { pcix_ereport_setup(dip, erpt_p); } done: pci_regs_gather(dip, erpt_p); pci_regs_clear(erpt_p); /* * Before returning set fh_bus_specific to completed pci_erpt_t * structure */ fmhdl->fh_bus_specific = (void *)erpt_p; return; error: if (erpt_p->pe_pci_regs) kmem_free(erpt_p->pe_pci_regs, sizeof (pci_error_regs_t)); kmem_free(erpt_p, sizeof (pci_erpt_t)); erpt_p = NULL; } static void pcix_ereport_teardown(pci_erpt_t *erpt_p) { if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pcix_bdg_error_regs_t *pcix_bdg_regs; uint16_t pcix_ver; pcix_bdg_regs = (pcix_bdg_error_regs_t *)erpt_p->pe_regs; pcix_ver = pcix_bdg_regs->pcix_bdg_ver; if (PCIX_ECC_VER_CHECK(pcix_ver)) { int i; for (i = 0; i < 2; i++) kmem_free(pcix_bdg_regs->pcix_bdg_ecc_regs[i], sizeof (pcix_ecc_regs_t)); } kmem_free(erpt_p->pe_regs, sizeof (pcix_bdg_error_regs_t)); } else { pcix_error_regs_t *pcix_regs; uint16_t pcix_ver; pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs; pcix_ver = pcix_regs->pcix_ver; if (PCIX_ECC_VER_CHECK(pcix_ver)) { kmem_free(pcix_regs->pcix_ecc_regs, sizeof (pcix_ecc_regs_t)); } kmem_free(erpt_p->pe_regs, sizeof (pcix_error_regs_t)); } } static void pcie_ereport_teardown(pci_erpt_t *erpt_p) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; if (erpt_p->pe_dflags & PCIEX_ADV_DEV) { pcie_adv_error_regs_t *pcie_adv = pcie_regs->pcie_adv_regs; if (erpt_p->pe_dflags & PCIEX_2PCI_DEV) kmem_free(pcie_adv->pcie_adv_bdg_regs, sizeof (pcie_adv_bdg_error_regs_t)); if (erpt_p->pe_dflags & PCIEX_RC_DEV) kmem_free(pcie_adv->pcie_adv_rc_regs, sizeof (pcie_adv_rc_error_regs_t)); kmem_free(pcie_adv, sizeof (pcie_adv_error_regs_t)); } if (erpt_p->pe_dflags & PCIEX_RC_DEV) kmem_free(pcie_regs->pcie_rc_regs, sizeof (pcie_rc_error_regs_t)); if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { if (erpt_p->pe_dflags & PCIX_DEV) { uint16_t pcix_ver = pcie_regs->pcix_bdg_regs-> pcix_bdg_ver; if (PCIX_ECC_VER_CHECK(pcix_ver)) { int i; for (i = 0; i < 2; i++) kmem_free(pcie_regs->pcix_bdg_regs-> pcix_bdg_ecc_regs[i], sizeof (pcix_ecc_regs_t)); } kmem_free(pcie_regs->pcix_bdg_regs, sizeof (pcix_bdg_error_regs_t)); } } kmem_free(erpt_p->pe_regs, sizeof (pcie_error_regs_t)); } void pci_ereport_teardown(dev_info_t *dip) { struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; pci_erpt_t *erpt_p; if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) && !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) { i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_SLEEP); } ASSERT(fmhdl); erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; if (erpt_p == NULL) return; if (erpt_p->pe_dflags & PCIEX_DEV) pcie_ereport_teardown(erpt_p); else if (erpt_p->pe_dflags & PCIX_DEV) pcix_ereport_teardown(erpt_p); pci_config_teardown((ddi_acc_handle_t *)&erpt_p->pe_hdl); if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) kmem_free(erpt_p->pe_pci_regs->pci_bdg_regs, sizeof (pci_bdg_error_regs_t)); kmem_free(erpt_p->pe_pci_regs, sizeof (pci_error_regs_t)); kmem_free(erpt_p, sizeof (pci_erpt_t)); fmhdl->fh_bus_specific = NULL; /* * The following sparc specific code should be removed once the pci_cap * interfaces create the necessary properties for us. */ #if defined(__sparc) (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "pcix-capid-pointer"); (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "pcie-slotcap-reg"); (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "pcie-capid-reg"); (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "pcie-capid-pointer"); (void) ndi_prop_remove(DDI_DEV_T_NONE, dip, "pcie-aer-pointer"); #endif } /* * Function used by PCI device and nexus error handlers to check if a * captured address resides in their DMA or ACC handle caches or the caches of * their children devices, respectively. */ static int pci_dev_hdl_lookup(dev_info_t *dip, int type, ddi_fm_error_t *derr, void *addr) { struct i_ddi_fmhdl *fmhdl = DEVI(dip)->devi_fmhdl; pci_erpt_t *erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) return (ndi_fmc_error(dip, NULL, type, derr->fme_ena, addr)); else return (ndi_fmc_entry_error(dip, type, derr, addr)); } static void pcie_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p, char *buf, int errtype) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; pcie_adv_error_regs_t *pcie_adv_regs = pcie_regs->pcie_adv_regs; pcie_adv_rc_error_regs_t *pcie_adv_rc_regs = pcie_adv_regs->pcie_adv_rc_regs; switch (errtype) { case PCIEX_TYPE_CE: ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_DEVSTS_REG, DATA_TYPE_UINT16, pcie_regs->pcie_err_status, PCIEX_CE_STATUS_REG, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ce_status, NULL); break; case PCIEX_TYPE_UE: ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_DEVSTS_REG, DATA_TYPE_UINT16, pcie_regs->pcie_err_status, PCIEX_UE_STATUS_REG, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_status, PCIEX_UE_SEV_REG, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_sev, PCIEX_ADV_CTL, DATA_TYPE_UINT32, pcie_adv_regs->pcie_adv_ctl, PCIEX_SRC_ID, DATA_TYPE_UINT16, pcie_adv_regs->pcie_adv_bdf, PCIEX_SRC_VALID, DATA_TYPE_BOOLEAN_VALUE, (pcie_adv_regs->pcie_adv_bdf != NULL) ? 1 : NULL, #ifdef DEBUG PCIEX_UE_HDR0, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_hdr0, PCIEX_UE_HDR1, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_hdr[0], PCIEX_UE_HDR2, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_hdr[1], PCIEX_UE_HDR3, DATA_TYPE_UINT32, pcie_adv_regs->pcie_ue_hdr[2], #endif NULL); break; case PCIEX_TYPE_GEN: ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_DEVSTS_REG, DATA_TYPE_UINT16, pcie_regs->pcie_err_status, NULL); break; case PCIEX_TYPE_RC_UE_MSG: case PCIEX_TYPE_RC_CE_MSG: ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_ROOT_ERRSTS_REG, DATA_TYPE_UINT32, pcie_adv_rc_regs->pcie_rc_err_status, PCIEX_SRC_ID, DATA_TYPE_UINT16, (errtype == PCIEX_TYPE_RC_UE_MSG) ? pcie_adv_rc_regs->pcie_rc_ue_src_id : pcie_adv_rc_regs->pcie_rc_ce_src_id, PCIEX_SRC_VALID, DATA_TYPE_BOOLEAN_VALUE, (errtype == PCIEX_TYPE_RC_UE_MSG) ? (pcie_adv_regs->pcie_adv_vflags & PCIE_SRC_ID_VALID && pcie_adv_rc_regs->pcie_rc_ue_src_id != 0) : (pcie_adv_regs->pcie_adv_vflags & PCIE_SRC_ID_VALID && pcie_adv_rc_regs->pcie_rc_ce_src_id != 0), NULL); break; case PCIEX_TYPE_RC_MULT_MSG: ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_ROOT_ERRSTS_REG, DATA_TYPE_UINT32, pcie_adv_rc_regs->pcie_rc_err_status, NULL); break; default: break; } } static void pcie_check_addr(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *eprt_p) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)eprt_p->pe_regs; pcie_adv_error_regs_t *pcie_adv_regs = pcie_regs->pcie_adv_regs; pcie_tlp_hdr_t *ue_hdr0; uint32_t *ue_hdr; uint64_t addr = NULL; if (!(pcie_adv_regs->pcie_adv_vflags & PCIE_UE_HDR_VALID)) { derr->fme_status = DDI_FM_UNKNOWN; return; } ue_hdr0 = (pcie_tlp_hdr_t *)&pcie_adv_regs->pcie_ue_hdr0; ue_hdr = pcie_adv_regs->pcie_ue_hdr; switch (ue_hdr0->type) { case PCIE_TLP_TYPE_MEM: case PCIE_TLP_TYPE_MEMLK: if ((ue_hdr0->fmt & 0x1) == 0x1) { pcie_mem64_t *mem64_tlp = (pcie_mem64_t *)ue_hdr; addr = (uint64_t)mem64_tlp->addr1 << 32 | (uint32_t)mem64_tlp->addr0 << 2; pcie_adv_regs->pcie_adv_bdf = mem64_tlp->rid; } else { pcie_memio32_t *memio32_tlp = (pcie_memio32_t *)ue_hdr; addr = (uint32_t)memio32_tlp->addr0 << 2; pcie_adv_regs->pcie_adv_bdf = memio32_tlp->rid; } derr->fme_status = pci_dev_hdl_lookup(dip, DMA_HANDLE, derr, (void *) &addr); /* * If DMA handle is not found error could have been a memory * mapped IO address so check in the access cache */ if (derr->fme_status == DDI_FM_UNKNOWN) derr->fme_status = pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr); break; case PCIE_TLP_TYPE_IO: { pcie_memio32_t *memio32_tlp = (pcie_memio32_t *)ue_hdr; addr = (uint32_t)memio32_tlp->addr0 << 2; pcie_adv_regs->pcie_adv_bdf = memio32_tlp->rid; derr->fme_status = pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr); break; } case PCIE_TLP_TYPE_CFG0: case PCIE_TLP_TYPE_CFG1: { pcie_cfg_t *cfg_tlp = (pcie_cfg_t *)ue_hdr; pcie_adv_regs->pcie_adv_bdf = (uint16_t)cfg_tlp->bus << 8 | (uint16_t)cfg_tlp->dev << 3 | cfg_tlp->func; derr->fme_status = DDI_FM_UNKNOWN; break; } case PCIE_TLP_TYPE_MSG: { pcie_msg_t *msg_tlp = (pcie_msg_t *)ue_hdr; pcie_adv_regs->pcie_adv_bdf = msg_tlp->rid; derr->fme_status = DDI_FM_UNKNOWN; break; } case PCIE_TLP_TYPE_CPL: case PCIE_TLP_TYPE_CPLLK: { pcie_cpl_t *cpl_tlp = (pcie_cpl_t *)ue_hdr; pcie_adv_regs->pcie_adv_bdf = cpl_tlp->cid; derr->fme_status = DDI_FM_UNKNOWN; break; } case PCIE_TLP_TYPE_MSI: default: derr->fme_status = DDI_FM_UNKNOWN; } /* * If no handle was found in the children caches and their is no * address infomation already stored and we have a captured address * then we need to store it away so that intermediate bridges can * check if the address exists in their handle caches. */ if (derr->fme_status == DDI_FM_UNKNOWN && derr->fme_bus_specific == NULL && addr != NULL) derr->fme_bus_specific = (void *)(uintptr_t)addr; } static void pcie_pci_check_addr(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *eprt_p) { pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)eprt_p->pe_regs; pcie_adv_error_regs_t *pcie_adv_regs = pcie_regs->pcie_adv_regs; pcie_adv_bdg_error_regs_t *pcie_bdg_regs = pcie_adv_regs->pcie_adv_bdg_regs; uint64_t addr = NULL; pcix_attr_t *pcie_pci_sue_attr; int cmd; int dual_addr = 0; if (!(pcie_adv_regs->pcie_adv_vflags & PCIE_SUE_HDR_VALID)) { derr->fme_status = DDI_FM_UNKNOWN; return; } pcie_pci_sue_attr = (pcix_attr_t *)&pcie_bdg_regs->pcie_sue_hdr0; cmd = (pcie_bdg_regs->pcie_sue_hdr[0] >> PCIE_AER_SUCE_HDR_CMD_LWR_SHIFT) & PCIE_AER_SUCE_HDR_CMD_LWR_MASK; cmd_switch: switch (cmd) { case PCI_PCIX_CMD_IORD: case PCI_PCIX_CMD_IOWR: pcie_adv_regs->pcie_adv_bdf = pcie_pci_sue_attr->rid; addr = pcie_bdg_regs->pcie_sue_hdr[2]; addr = (addr << PCIE_AER_SUCE_HDR_ADDR_SHIFT) | pcie_bdg_regs->pcie_sue_hdr[1]; derr->fme_status = pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr); break; case PCI_PCIX_CMD_MEMRD_DW: case PCI_PCIX_CMD_MEMWR: case PCI_PCIX_CMD_MEMRD_BL: case PCI_PCIX_CMD_MEMWR_BL: case PCI_PCIX_CMD_MEMRDBL: case PCI_PCIX_CMD_MEMWRBL: pcie_adv_regs->pcie_adv_bdf = pcie_pci_sue_attr->rid; addr = pcie_bdg_regs->pcie_sue_hdr[2]; addr = (addr << PCIE_AER_SUCE_HDR_ADDR_SHIFT) | pcie_bdg_regs->pcie_sue_hdr[1]; derr->fme_status = pci_dev_hdl_lookup(dip, DMA_HANDLE, derr, (void *) &addr); if (derr->fme_status == DDI_FM_UNKNOWN) derr->fme_status = pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr); break; case PCI_PCIX_CMD_CFRD: case PCI_PCIX_CMD_CFWR: /* * If we want to store the bdf of the device being addressed we * will need to do some surgery */ derr->fme_status = DDI_FM_UNKNOWN; break; case PCI_PCIX_CMD_DADR: cmd = (pcie_bdg_regs->pcie_sue_hdr[0] >> PCIE_AER_SUCE_HDR_CMD_UP_SHIFT) & PCIE_AER_SUCE_HDR_CMD_UP_MASK; if (dual_addr) break; ++dual_addr; goto cmd_switch; default: derr->fme_status = DDI_FM_UNKNOWN; } /* * If no handle was found in the children caches and their is no * address infomation already stored and we have a captured address * then we need to store it away so that intermediate bridges can * check if the address exists in their handle caches. */ if (derr->fme_status == DDI_FM_UNKNOWN && derr->fme_bus_specific == NULL && addr != NULL) derr->fme_bus_specific = (void *)(uintptr_t)addr; } static int pcix_check_addr(dev_info_t *dip, ddi_fm_error_t *derr, pcix_ecc_regs_t *pcix_ecc_regs) { int cmd = (pcix_ecc_regs->pcix_ecc_ctlstat >> 16) & 0xf; uint64_t addr; addr = pcix_ecc_regs->pcix_ecc_secaddr; addr = addr << 32; addr |= pcix_ecc_regs->pcix_ecc_fstaddr; switch (cmd) { case PCI_PCIX_CMD_INTR: case PCI_PCIX_CMD_SPEC: return (DDI_FM_FATAL); case PCI_PCIX_CMD_IORD: case PCI_PCIX_CMD_IOWR: return (pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr)); case PCI_PCIX_CMD_DEVID: return (DDI_FM_FATAL); case PCI_PCIX_CMD_MEMRD_DW: case PCI_PCIX_CMD_MEMWR: case PCI_PCIX_CMD_MEMRD_BL: case PCI_PCIX_CMD_MEMWR_BL: return (pci_dev_hdl_lookup(dip, DMA_HANDLE, derr, (void *) &addr)); case PCI_PCIX_CMD_CFRD: case PCI_PCIX_CMD_CFWR: return (pci_dev_hdl_lookup(dip, ACC_HANDLE, derr, (void *) &addr)); case PCI_PCIX_CMD_SPL: case PCI_PCIX_CMD_DADR: return (DDI_FM_FATAL); case PCI_PCIX_CMD_MEMRDBL: case PCI_PCIX_CMD_MEMWRBL: return (pci_dev_hdl_lookup(dip, DMA_HANDLE, derr, (void *) &addr)); default: return (DDI_FM_FATAL); } } /*ARGSUSED*/ static int pci_bdg_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p) { pci_bdg_error_regs_t *pci_bdg_regs = erpt_p->pe_pci_regs->pci_bdg_regs; int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; int ret = DDI_FM_OK; char buf[FM_MAX_CLASS]; int i; if (derr->fme_flag != DDI_FM_ERR_UNEXPECTED) goto done; if ((pci_bdg_regs->pci_bdg_vflags & PCI_BDG_CTRL_VALID) && (pci_bdg_regs->pci_bdg_ctrl & PCI_BCNF_BCNTRL_DTO_STAT)) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS, PCI_DTO); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16, pci_bdg_regs->pci_bdg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16, pci_bdg_regs->pci_bdg_ctrl, NULL); unknown++; } if (pci_bdg_regs->pci_bdg_vflags & PCI_BDG_SEC_STAT_VALID) { for (i = 0; pci_bdg_err_tbl[i].err_class != NULL; i++) { if (pci_bdg_regs->pci_bdg_sec_stat & pci_bdg_err_tbl[i].reg_bit) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", PCI_ERROR_SUBCLASS, PCI_SEC_ERROR_SUBCLASS, pci_bdg_err_tbl[i].err_class); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCI_SEC_CONFIG_STATUS, DATA_TYPE_UINT16, pci_bdg_regs->pci_bdg_sec_stat, PCI_BCNTRL, DATA_TYPE_UINT16, pci_bdg_regs->pci_bdg_ctrl, NULL); /* * Increment severity based on flag if bridge * is PCI or PCI-X, if PCI Express and this is a * master abort then treat as nonfatal. * XXFM May need to check if all other errors * are related to MA? */ if (!(erpt_p->pe_dflags & PCIEX_DEV)) { PCI_FM_SEV_INC( pci_bdg_err_tbl[i].flags); } else if (pci_bdg_err_tbl[i].reg_bit == PCI_STAT_R_MAST_AB) { nonfatal++; } if (derr->fme_bus_specific && pci_bdg_err_tbl[i].terr_class) pci_target_enqueue(derr->fme_ena, pci_bdg_err_tbl[i].terr_class, PCI_ERROR_SUBCLASS, (uintptr_t)derr->fme_bus_specific); } } } done: /* * Need to check for poke and cautious put. We already know peek * and cautious get errors occurred (as we got a trap) and we know * they are nonfatal. */ if (derr->fme_flag == DDI_FM_ERR_EXPECTED) { /* * for cautious puts we treat all errors as nonfatal. Actually * we set nonfatal for cautious gets as well - doesn't do any * harm */ if (pci_bdg_regs->pci_bdg_sec_stat & (PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB | PCI_STAT_S_PERROR | PCI_STAT_S_SYSERR)) nonfatal++; /* * for cautious accesses we already have the acc_handle. Just * need to call children to clear their error bits */ ret = ndi_fm_handler_dispatch(dip, NULL, derr); PCI_FM_SEV_INC(ret); return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } if (derr->fme_flag == DDI_FM_ERR_POKE) { /* * special case for pokes - we only consider master abort * and target abort as nonfatal. Sserr with no master abort is * fatal, but master/target abort can come in on separate * instance, so return unknown and parent will determine if * nonfatal (if another child returned nonfatal - ie master * or target abort) or fatal otherwise */ if (pci_bdg_regs->pci_bdg_sec_stat & (PCI_STAT_R_TARG_AB | PCI_STAT_R_MAST_AB)) nonfatal++; if (erpt_p->pe_pci_regs->pci_err_status & PCI_STAT_S_SYSERR) unknown++; } /* * If errant address is passed in then attempt to find * ACC/DMA handle in caches. */ if (derr->fme_bus_specific) { int i; for (i = 0; i < 2; i++) { ret = ndi_fmc_error(dip, NULL, i ? ACC_HANDLE : DMA_HANDLE, derr->fme_ena, (void *)&derr->fme_bus_specific); PCI_FM_SEV_INC(ret); } } /* * now check children below the bridge, only if errant handle was not * found */ if (!derr->fme_acc_handle && !derr->fme_dma_handle) { ret = ndi_fm_handler_dispatch(dip, NULL, derr); PCI_FM_SEV_INC(ret); } return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static int pcix_ecc_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p, void *pe_regs) { pcix_error_regs_t *pcix_regs; pcix_bdg_error_regs_t *pcix_bdg_regs; pcix_ecc_regs_t *pcix_ecc_regs; int bridge; int i; int ecc_phase; int ecc_corr; int sec_ue; int sec_ce; int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; char buf[FM_MAX_CLASS]; if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs; bridge = 1; } else { pcix_regs = (pcix_error_regs_t *)pe_regs; bridge = 0; } for (i = 0; i < (bridge ? 2 : 1); i++) { int ret = DDI_FM_OK; pcix_ecc_regs = bridge ? pcix_bdg_regs->pcix_bdg_ecc_regs[i] : pcix_regs->pcix_ecc_regs; if (pcix_ecc_regs->pcix_ecc_vflags & PCIX_ERR_ECC_STS_VALID) { ecc_phase = (pcix_ecc_regs->pcix_ecc_ctlstat & PCI_PCIX_ECC_PHASE) >> 0x4; ecc_corr = (pcix_ecc_regs->pcix_ecc_ctlstat & PCI_PCIX_ECC_CORR); sec_ue = (pcix_ecc_regs->pcix_ecc_ctlstat & PCI_PCIX_ECC_S_UE); sec_ce = (pcix_ecc_regs->pcix_ecc_ctlstat & PCI_PCIX_ECC_S_CE); switch (ecc_phase) { case PCI_PCIX_ECC_PHASE_NOERR: break; case PCI_PCIX_ECC_PHASE_FADDR: case PCI_PCIX_ECC_PHASE_SADDR: PCI_FM_SEV_INC(ecc_corr ? DDI_FM_NONFATAL : DDI_FM_FATAL); (void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s", PCIX_ERROR_SUBCLASS, i ? PCIX_SEC_ERROR_SUBCLASS : "", ecc_corr ? PCIX_ECC_CE_ADDR : PCIX_ECC_UE_ADDR); break; case PCI_PCIX_ECC_PHASE_ATTR: PCI_FM_SEV_INC(ecc_corr ? DDI_FM_NONFATAL : DDI_FM_FATAL); (void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s", PCIX_ERROR_SUBCLASS, i ? PCIX_SEC_ERROR_SUBCLASS : "", ecc_corr ? PCIX_ECC_CE_ATTR : PCIX_ECC_UE_ATTR); break; case PCI_PCIX_ECC_PHASE_DATA32: case PCI_PCIX_ECC_PHASE_DATA64: if (ecc_corr) ret = DDI_FM_NONFATAL; else ret = pcix_check_addr(dip, derr, pcix_ecc_regs); PCI_FM_SEV_INC(ret); (void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s", PCIX_ERROR_SUBCLASS, i ? PCIX_SEC_ERROR_SUBCLASS : "", ecc_corr ? PCIX_ECC_CE_DATA : PCIX_ECC_UE_DATA); break; } if (ecc_phase) if (bridge) ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_SEC_STATUS, DATA_TYPE_UINT16, pcix_bdg_regs->pcix_bdg_sec_stat, PCIX_BDG_STAT, DATA_TYPE_UINT32, pcix_bdg_regs->pcix_bdg_stat, PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_ctlstat, PCIX_ECC_ATTR, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_attr, NULL); else ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_COMMAND, DATA_TYPE_UINT16, pcix_regs->pcix_command, PCIX_STATUS, DATA_TYPE_UINT32, pcix_regs->pcix_status, PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_ctlstat, PCIX_ECC_ATTR, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_attr, NULL); if (sec_ce || sec_ue) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s", PCIX_ERROR_SUBCLASS, i ? PCIX_SEC_ERROR_SUBCLASS : "", sec_ce ? PCIX_ECC_S_CE : PCIX_ECC_S_UE); if (bridge) ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_SEC_STATUS, DATA_TYPE_UINT16, pcix_bdg_regs->pcix_bdg_sec_stat, PCIX_BDG_STAT, DATA_TYPE_UINT32, pcix_bdg_regs->pcix_bdg_stat, PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_ctlstat, PCIX_ECC_ATTR, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_attr, NULL); else ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_COMMAND, DATA_TYPE_UINT16, pcix_regs->pcix_command, PCIX_STATUS, DATA_TYPE_UINT32, pcix_regs->pcix_status, PCIX_ECC_CTLSTAT, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_ctlstat, PCIX_ECC_ATTR, DATA_TYPE_UINT32, pcix_ecc_regs->pcix_ecc_attr, NULL); PCI_FM_SEV_INC(sec_ue ? DDI_FM_FATAL : DDI_FM_NONFATAL); } } } return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static int pcix_bdg_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p, void *pe_regs) { pcix_bdg_error_regs_t *pcix_bdg_regs = (pcix_bdg_error_regs_t *)pe_regs; int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; char buf[FM_MAX_CLASS]; int i; if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_STATUS_VALID) { for (i = 0; pcix_err_tbl[i].err_class != NULL; i++) { if ((pcix_bdg_regs->pcix_bdg_stat & pcix_err_tbl[i].reg_bit)) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIX_ERROR_SUBCLASS, pcix_err_tbl[i].err_class); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_SEC_STATUS, DATA_TYPE_UINT16, pcix_bdg_regs->pcix_bdg_sec_stat, PCIX_BDG_STAT, DATA_TYPE_UINT32, pcix_bdg_regs->pcix_bdg_stat, NULL); PCI_FM_SEV_INC(pcix_err_tbl[i].flags); } } } if (pcix_bdg_regs->pcix_bdg_vflags & PCIX_BDG_SEC_STATUS_VALID) { for (i = 0; pcix_sec_err_tbl[i].err_class != NULL; i++) { if ((pcix_bdg_regs->pcix_bdg_sec_stat & pcix_sec_err_tbl[i].reg_bit)) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s%s", PCIX_ERROR_SUBCLASS, PCIX_SEC_ERROR_SUBCLASS, pcix_sec_err_tbl[i].err_class); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_SEC_STATUS, DATA_TYPE_UINT16, pcix_bdg_regs->pcix_bdg_sec_stat, PCIX_BDG_STAT, DATA_TYPE_UINT32, pcix_bdg_regs->pcix_bdg_stat, NULL); PCI_FM_SEV_INC(pcix_sec_err_tbl[i].flags); } } } /* Log/Handle ECC errors */ if (PCIX_ECC_VER_CHECK(pcix_bdg_regs->pcix_bdg_ver)) { int ret; ret = pcix_ecc_error_report(dip, derr, erpt_p, (void *)pcix_bdg_regs); PCI_FM_SEV_INC(ret); } return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static int pcix_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p) { pcix_error_regs_t *pcix_regs = (pcix_error_regs_t *)erpt_p->pe_regs; int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; char buf[FM_MAX_CLASS]; int i; if (pcix_regs->pcix_vflags & PCIX_ERR_STATUS_VALID) { for (i = 0; pcix_err_tbl[i].err_class != NULL; i++) { if (!(pcix_regs->pcix_status & pcix_err_tbl[i].reg_bit)) continue; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIX_ERROR_SUBCLASS, pcix_err_tbl[i].err_class); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIX_COMMAND, DATA_TYPE_UINT16, pcix_regs->pcix_command, PCIX_STATUS, DATA_TYPE_UINT32, pcix_regs->pcix_status, NULL); PCI_FM_SEV_INC(pcix_err_tbl[i].flags); } } /* Log/Handle ECC errors */ if (PCIX_ECC_VER_CHECK(pcix_regs->pcix_ver)) { int ret = pcix_ecc_error_report(dip, derr, erpt_p, (void *)pcix_regs); PCI_FM_SEV_INC(ret); } return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static int pcie_rc_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p, void *pe_regs) { pcie_adv_error_regs_t *pcie_adv_regs = (pcie_adv_error_regs_t *)pe_regs; int fatal = 0; int nonfatal = 0; int unknown = 0; char buf[FM_MAX_CLASS]; if (pcie_adv_regs->pcie_adv_vflags & PCIE_RC_ERR_STATUS_VALID) { pcie_adv_rc_error_regs_t *pcie_rc_regs = pcie_adv_regs->pcie_adv_rc_regs; int ce, ue, mult_ce, mult_ue, first_ue_fatal, nfe, fe; ce = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_CE_RCVD; ue = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_FE_NFE_RCVD; mult_ce = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_MUL_CE_RCVD; mult_ue = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_MUL_FE_NFE_RCVD; first_ue_fatal = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_FIRST_UC_FATAL; nfe = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_NFE_MSGS_RCVD; fe = pcie_rc_regs->pcie_rc_err_status & PCIE_AER_RE_STS_FE_MSGS_RCVD; /* * log fatal/nonfatal/corrected messages * recieved by root complex */ if (ue && fe) fatal++; else if (ce && !ue) nonfatal++; if (fe && first_ue_fatal) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_RC_FE_MSG); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_RC_UE_MSG); } if (nfe && !first_ue_fatal) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_RC_NFE_MSG); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_RC_UE_MSG); } if (ce) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_RC_CE_MSG); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_RC_CE_MSG); } if (mult_ce) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_RC_MCE_MSG); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_RC_MULT_MSG); } if (mult_ue) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, PCIEX_RC_MUE_MSG); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_RC_MULT_MSG); } } return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static int pcie_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p) { int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; char buf[FM_MAX_CLASS]; int i; pcie_error_regs_t *pcie_regs = (pcie_error_regs_t *)erpt_p->pe_regs; pcie_adv_error_regs_t *pcie_adv_regs; pcie_adv_bdg_error_regs_t *pcie_bdg_regs; if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV) && (erpt_p->pe_dflags & PCIX_DEV)) { int ret = pcix_bdg_error_report(dip, derr, erpt_p, (void *)pcie_regs->pcix_bdg_regs); PCI_FM_SEV_INC(ret); } if (!(erpt_p->pe_dflags & PCIEX_ADV_DEV)) { if (!(pcie_regs->pcie_vflags & PCIE_ERR_STATUS_VALID)) goto done; for (i = 0; pciex_nadv_err_tbl[i].err_class != NULL; i++) { if (!(pcie_regs->pcie_err_status & pciex_nadv_err_tbl[i].reg_bit)) continue; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, pciex_nadv_err_tbl[i].err_class); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_GEN); PCI_FM_SEV_INC(pciex_nadv_err_tbl[i].flags); } goto done; } pcie_adv_regs = pcie_regs->pcie_adv_regs; /* * Log PCI Express uncorrectable errors */ if (pcie_adv_regs->pcie_adv_vflags & PCIE_UE_STATUS_VALID) { for (i = 0; pciex_ue_err_tbl[i].err_class != NULL; i++) { if (!(pcie_adv_regs->pcie_ue_status & pciex_ue_err_tbl[i].reg_bit)) continue; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, pciex_ue_err_tbl[i].err_class); pcie_adv_regs->pcie_adv_bdf = 0; if ((pcie_adv_regs->pcie_ue_status & PCIE_AER_UCE_LOG_BITS) != pciex_ue_err_tbl[i].reg_bit) { PCI_FM_SEV_INC(pciex_ue_err_tbl[i].flags); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_UE); } else { pcie_check_addr(dip, derr, erpt_p); /* * fatal/nonfatal errors are fatal/nonfatal * regardless of if we find a handle */ if (pciex_ue_err_tbl[i].flags == DDI_FM_FATAL) derr->fme_status = DDI_FM_FATAL; else if (pciex_ue_err_tbl[i].flags == DDI_FM_NONFATAL) derr->fme_status = DDI_FM_NONFATAL; pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_UE); PCI_FM_SEV_INC(derr->fme_status); } } } /* * Log PCI Express correctable errors */ if (pcie_adv_regs->pcie_adv_vflags & PCIE_CE_STATUS_VALID) { for (i = 0; pciex_ce_err_tbl[i].err_class != NULL; i++) { if (!(pcie_adv_regs->pcie_ce_status & pciex_ce_err_tbl[i].reg_bit)) continue; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, pciex_ce_err_tbl[i].err_class); pcie_ereport_post(dip, derr, erpt_p, buf, PCIEX_TYPE_CE); if (!fatal && !unknown) PCI_FM_SEV_INC(pciex_ce_err_tbl[i].flags); } } if (!(erpt_p->pe_dflags & PCI_BRIDGE_DEV)) goto done; if (erpt_p->pe_dflags & PCIEX_RC_DEV) { int ret = pcie_rc_error_report(dip, derr, erpt_p, (void *)pcie_adv_regs); PCI_FM_SEV_INC(ret); } if (!((erpt_p->pe_dflags & PCIEX_2PCI_DEV) && (pcie_adv_regs->pcie_adv_vflags & PCIE_SUE_STATUS_VALID))) goto done; pcie_bdg_regs = pcie_adv_regs->pcie_adv_bdg_regs; for (i = 0; pcie_sue_err_tbl[i].err_class != NULL; i++) { if ((pcie_bdg_regs->pcie_sue_status & pcie_sue_err_tbl[i].reg_bit)) { (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCIEX_ERROR_SUBCLASS, pcie_sue_err_tbl[i].err_class); if ((pcie_bdg_regs->pcie_sue_status & PCIE_AER_SUCE_LOG_BITS) != pcie_sue_err_tbl[i].reg_bit) { PCI_FM_SEV_INC(pcie_sue_err_tbl[i].flags); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_SEC_UE_STATUS, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_status, #ifdef DEBUG PCIEX_SUE_HDR0, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr0, PCIEX_SUE_HDR1, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[0], PCIEX_SUE_HDR2, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[1], PCIEX_SUE_HDR3, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[2], #endif NULL); } else { pcie_adv_regs->pcie_adv_bdf = 0; pcie_pci_check_addr(dip, derr, erpt_p); /* * fatal/nonfatal errors are fatal/nonfatal * regardless of if we find a handle */ if (pcie_sue_err_tbl[i].flags == DDI_FM_FATAL) derr->fme_status = DDI_FM_FATAL; else if (pcie_sue_err_tbl[i].flags == DDI_FM_NONFATAL) derr->fme_status = DDI_FM_NONFATAL; ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCIEX_SEC_UE_STATUS, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_status, PCIEX_SRC_ID, DATA_TYPE_UINT16, pcie_adv_regs->pcie_adv_bdf, PCIEX_SRC_VALID, DATA_TYPE_BOOLEAN_VALUE, (pcie_adv_regs->pcie_adv_bdf != NULL) ? 1 : NULL, #ifdef DEBUG PCIEX_SUE_HDR0, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr0, PCIEX_SUE_HDR1, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[0], PCIEX_SUE_HDR2, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[1], PCIEX_SUE_HDR3, DATA_TYPE_UINT32, pcie_bdg_regs->pcie_sue_hdr[2], #endif NULL); PCI_FM_SEV_INC(derr->fme_status); } } } done: return (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } static void pci_error_report(dev_info_t *dip, ddi_fm_error_t *derr, pci_erpt_t *erpt_p) { int fatal = 0; int nonfatal = 0; int unknown = 0; int ok = 0; char buf[FM_MAX_CLASS]; int i; if (derr->fme_flag == DDI_FM_ERR_UNEXPECTED) { /* * Log generic PCI errors. */ for (i = 0; pci_err_tbl[i].err_class != NULL; i++) { if (!(erpt_p->pe_pci_regs->pci_err_status & pci_err_tbl[i].reg_bit) || !(erpt_p->pe_pci_regs->pci_vflags & PCI_ERR_STATUS_VALID)) continue; /* * Generate an ereport for this error bit. */ (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", PCI_ERROR_SUBCLASS, pci_err_tbl[i].err_class); ddi_fm_ereport_post(dip, buf, derr->fme_ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0, PCI_CONFIG_STATUS, DATA_TYPE_UINT16, erpt_p->pe_pci_regs->pci_err_status, PCI_CONFIG_COMMAND, DATA_TYPE_UINT16, erpt_p->pe_pci_regs->pci_cfg_comm, NULL); if (!(erpt_p->pe_dflags & PCIEX_DEV)) PCI_FM_SEV_INC(pci_err_tbl[i].flags); } if (erpt_p->pe_dflags & PCIEX_DEV) { int ret = pcie_error_report(dip, derr, erpt_p); PCI_FM_SEV_INC(ret); } else if (erpt_p->pe_dflags & PCIX_DEV) { if (erpt_p->pe_dflags & PCI_BRIDGE_DEV) { int ret = pcix_bdg_error_report(dip, derr, erpt_p, erpt_p->pe_regs); PCI_FM_SEV_INC(ret); } else { int ret = pcix_error_report(dip, derr, erpt_p); PCI_FM_SEV_INC(ret); } } } if ((erpt_p->pe_dflags & PCI_BRIDGE_DEV)) { int ret = pci_bdg_error_report(dip, derr, erpt_p); PCI_FM_SEV_INC(ret); } derr->fme_status = (fatal ? DDI_FM_FATAL : (nonfatal ? DDI_FM_NONFATAL : (unknown ? DDI_FM_UNKNOWN : DDI_FM_OK))); } void pci_ereport_post(dev_info_t *dip, ddi_fm_error_t *derr, uint16_t *xx_status) { struct i_ddi_fmhdl *fmhdl; pci_erpt_t *erpt_p; fmhdl = DEVI(dip)->devi_fmhdl; if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(dip)) && !DDI_FM_ERRCB_CAP(ddi_fm_capable(dip))) { i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); return; } ASSERT(fmhdl); if (derr->fme_ena == NULL) derr->fme_ena = fm_ena_generate(0, FM_ENA_FMT1); erpt_p = (pci_erpt_t *)fmhdl->fh_bus_specific; if (erpt_p == NULL) { i_ddi_drv_ereport_post(dip, DVR_EFMCAP, NULL, DDI_NOSLEEP); return; } pci_regs_gather(dip, erpt_p); pci_error_report(dip, derr, erpt_p); pci_regs_clear(erpt_p); if (xx_status != NULL) *xx_status = erpt_p->pe_pci_regs->pci_err_status; } /* * private version of walk_devs() that can be used during panic. No * sleeping or locking required. */ static int pci_fm_walk_devs(dev_info_t *dip, int (*f)(dev_info_t *, void *), void *arg) { while (dip) { switch ((*f)(dip, arg)) { case DDI_WALK_TERMINATE: return (DDI_WALK_TERMINATE); case DDI_WALK_CONTINUE: if (pci_fm_walk_devs(ddi_get_child(dip), f, arg) == DDI_WALK_TERMINATE) return (DDI_WALK_TERMINATE); break; case DDI_WALK_PRUNECHILD: break; } dip = ddi_get_next_sibling(dip); } return (DDI_WALK_CONTINUE); } /* * need special version of ddi_fm_ereport_post() as the leaf driver may * not be hardened. */ static void pci_fm_ereport_post(dev_info_t *dip, const char *error_class, uint64_t ena, uint8_t version, ...) { char *name; char device_path[MAXPATHLEN]; char ddi_error_class[FM_MAX_CLASS]; nvlist_t *ereport, *detector; nv_alloc_t *nva; errorq_elem_t *eqep; va_list ap; if (panicstr) { eqep = errorq_reserve(ereport_errorq); if (eqep == NULL) return; ereport = errorq_elem_nvl(ereport_errorq, eqep); nva = errorq_elem_nva(ereport_errorq, eqep); detector = fm_nvlist_create(nva); } else { ereport = fm_nvlist_create(NULL); detector = fm_nvlist_create(NULL); } (void) ddi_pathname(dip, device_path); fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, device_path, NULL); (void) snprintf(ddi_error_class, FM_MAX_CLASS, "%s.%s", DDI_IO_CLASS, error_class); fm_ereport_set(ereport, version, ddi_error_class, ena, detector, NULL); va_start(ap, version); name = va_arg(ap, char *); (void) i_fm_payload_set(ereport, name, ap); va_end(ap); if (panicstr) { errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); } else { (void) fm_ereport_post(ereport, EVCH_TRYHARD); fm_nvlist_destroy(ereport, FM_NVA_FREE); fm_nvlist_destroy(detector, FM_NVA_FREE); } } static int pci_check_regs(dev_info_t *dip, void *arg) { int reglen; int rn; int totreg; pci_regspec_t *drv_regp; pci_target_err_t *tgt_err = (pci_target_err_t *)arg; if (tgt_err->tgt_pci_space == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { /* * for config space, we need to check if the given address * is a valid config space address for this device - based * on pci_phys_hi of the config space entry in reg property. */ if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) return (DDI_WALK_CONTINUE); totreg = reglen / sizeof (pci_regspec_t); for (rn = 0; rn < totreg; rn++) { if (tgt_err->tgt_pci_space == PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi) && (tgt_err->tgt_pci_addr & (PCI_REG_BUS_M | PCI_REG_DEV_M | PCI_REG_FUNC_M)) == (drv_regp[rn].pci_phys_hi & (PCI_REG_BUS_M | PCI_REG_DEV_M | PCI_REG_FUNC_M))) { tgt_err->tgt_dip = dip; kmem_free(drv_regp, reglen); return (DDI_WALK_TERMINATE); } } kmem_free(drv_regp, reglen); } else { /* * for non config space, need to check reg to look * for any non-relocable mapping, otherwise check * assigned-addresses. */ if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "reg", (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) return (DDI_WALK_CONTINUE); totreg = reglen / sizeof (pci_regspec_t); for (rn = 0; rn < totreg; rn++) { if ((drv_regp[rn].pci_phys_hi & PCI_RELOCAT_B) && (tgt_err->tgt_pci_space == TGT_PCI_SPACE_UNKNOWN || tgt_err->tgt_pci_space == PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi)) && (tgt_err->tgt_pci_addr >= (uint64_t)drv_regp[rn].pci_phys_low + ((uint64_t)drv_regp[rn].pci_phys_mid << 32)) && (tgt_err->tgt_pci_addr < (uint64_t)drv_regp[rn].pci_phys_low + ((uint64_t)drv_regp[rn].pci_phys_mid << 32) + (uint64_t)drv_regp[rn].pci_size_low + ((uint64_t)drv_regp[rn].pci_size_hi << 32))) { tgt_err->tgt_dip = dip; kmem_free(drv_regp, reglen); return (DDI_WALK_TERMINATE); } } kmem_free(drv_regp, reglen); if (ddi_getlongprop(DDI_DEV_T_NONE, dip, DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)&drv_regp, ®len) != DDI_SUCCESS) return (DDI_WALK_CONTINUE); totreg = reglen / sizeof (pci_regspec_t); for (rn = 0; rn < totreg; rn++) { if ((tgt_err->tgt_pci_space == TGT_PCI_SPACE_UNKNOWN || tgt_err->tgt_pci_space == PCI_REG_ADDR_G(drv_regp[rn].pci_phys_hi)) && (tgt_err->tgt_pci_addr >= (uint64_t)drv_regp[rn].pci_phys_low + ((uint64_t)drv_regp[rn].pci_phys_mid << 32)) && (tgt_err->tgt_pci_addr < (uint64_t)drv_regp[rn].pci_phys_low + ((uint64_t)drv_regp[rn].pci_phys_mid << 32) + (uint64_t)drv_regp[rn].pci_size_low + ((uint64_t)drv_regp[rn].pci_size_hi << 32))) { tgt_err->tgt_dip = dip; kmem_free(drv_regp, reglen); return (DDI_WALK_TERMINATE); } } kmem_free(drv_regp, reglen); } return (DDI_WALK_CONTINUE); } /* * impl_fix_ranges - fixes the config space entry of the "ranges" * property on psycho+ platforms. (if changing this function please make sure * to change the pci_fix_ranges function in pcipsy.c) */ /*ARGSUSED*/ static void pci_fix_ranges(dev_info_t *dip, pci_ranges_t *pci_ranges, int nrange) { #if defined(__sparc) char *name = ddi_binding_name(dip); if ((strcmp(name, "pci108e,8000") == 0) || (strcmp(name, "pci108e,a000") == 0) || (strcmp(name, "pci108e,a001") == 0)) { int i; for (i = 0; i < nrange; i++, pci_ranges++) if ((pci_ranges->child_high & PCI_REG_ADDR_M) == PCI_ADDR_CONFIG) pci_ranges->parent_low |= pci_ranges->child_high; } #endif } static int pci_check_ranges(dev_info_t *dip, void *arg) { uint64_t range_parent_begin; uint64_t range_parent_size; uint64_t range_parent_end; uint32_t space_type; uint32_t bus_num; uint32_t range_offset; pci_ranges_t *pci_ranges, *rangep; pci_bus_range_t *pci_bus_rangep; int pci_ranges_length; int nrange; pci_target_err_t *tgt_err = (pci_target_err_t *)arg; int i, size; if (strcmp(ddi_node_name(dip), "pci") != 0 && strcmp(ddi_node_name(dip), "pciex") != 0) return (DDI_WALK_CONTINUE); /* * Get the ranges property. Note we only look at the top level pci * node (hostbridge) which has a ranges property of type pci_ranges_t * not at pci-pci bridges. */ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "ranges", (caddr_t)&pci_ranges, &pci_ranges_length) != DDI_SUCCESS) { /* * no ranges property - no translation needed */ tgt_err->tgt_pci_addr = tgt_err->tgt_err_addr; tgt_err->tgt_pci_space = TGT_PCI_SPACE_UNKNOWN; if (panicstr) (void) pci_fm_walk_devs(ddi_get_child(dip), pci_check_regs, (void *)tgt_err); else { int circ = 0; ndi_devi_enter(dip, &circ); ddi_walk_devs(ddi_get_child(dip), pci_check_regs, (void *)tgt_err); ndi_devi_exit(dip, circ); } if (tgt_err->tgt_dip != NULL) return (DDI_WALK_TERMINATE); return (DDI_WALK_PRUNECHILD); } nrange = pci_ranges_length / sizeof (pci_ranges_t); rangep = pci_ranges; /* Need to fix the pci ranges property for psycho based systems */ pci_fix_ranges(dip, pci_ranges, nrange); for (i = 0; i < nrange; i++, rangep++) { range_parent_begin = ((uint64_t)rangep->parent_high << 32) + rangep->parent_low; range_parent_size = ((uint64_t)rangep->size_high << 32) + rangep->size_low; range_parent_end = range_parent_begin + range_parent_size - 1; if ((tgt_err->tgt_err_addr < range_parent_begin) || (tgt_err->tgt_err_addr > range_parent_end)) { /* Not in range */ continue; } space_type = PCI_REG_ADDR_G(rangep->child_high); if (space_type == PCI_REG_ADDR_G(PCI_ADDR_CONFIG)) { /* Config space address - check bus range */ range_offset = tgt_err->tgt_err_addr - range_parent_begin; bus_num = PCI_REG_BUS_G(range_offset); if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "bus-range", (caddr_t)&pci_bus_rangep, &size) != DDI_SUCCESS) { continue; } if ((bus_num < pci_bus_rangep->lo) || (bus_num > pci_bus_rangep->hi)) { /* * Bus number not appropriate for this * pci nexus. */ kmem_free(pci_bus_rangep, size); continue; } kmem_free(pci_bus_rangep, size); } /* We have a match if we get here - compute pci address */ tgt_err->tgt_pci_addr = tgt_err->tgt_err_addr - range_parent_begin; tgt_err->tgt_pci_addr += (((uint64_t)rangep->child_mid << 32) + rangep->child_low); tgt_err->tgt_pci_space = space_type; if (panicstr) (void) pci_fm_walk_devs(ddi_get_child(dip), pci_check_regs, (void *)tgt_err); else { int circ = 0; ndi_devi_enter(dip, &circ); ddi_walk_devs(ddi_get_child(dip), pci_check_regs, (void *)tgt_err); ndi_devi_exit(dip, circ); } if (tgt_err->tgt_dip != NULL) { kmem_free(pci_ranges, pci_ranges_length); return (DDI_WALK_TERMINATE); } } kmem_free(pci_ranges, pci_ranges_length); return (DDI_WALK_PRUNECHILD); } /* * Function used to drain pci_target_queue, either during panic or after softint * is generated, to generate target device ereports based on captured physical * addresses */ /*ARGSUSED*/ static void pci_target_drain(void *private_p, pci_target_err_t *tgt_err) { char buf[FM_MAX_CLASS]; /* * The following assumes that all pci_pci bridge devices * are configured as transparant. Find the top-level pci * nexus which has tgt_err_addr in one of its ranges, converting this * to a pci address in the process. Then starting at this node do * another tree walk to find a device with the pci address we've * found within range of one of it's assigned-addresses properties. */ tgt_err->tgt_dip = NULL; if (panicstr) (void) pci_fm_walk_devs(ddi_root_node(), pci_check_ranges, (void *)tgt_err); else ddi_walk_devs(ddi_root_node(), pci_check_ranges, (void *)tgt_err); if (tgt_err->tgt_dip == NULL) return; (void) snprintf(buf, FM_MAX_CLASS, "%s.%s", tgt_err->tgt_bridge_type, tgt_err->tgt_err_class); pci_fm_ereport_post(tgt_err->tgt_dip, buf, tgt_err->tgt_err_ena, 0, PCI_PA, DATA_TYPE_UINT64, tgt_err->tgt_err_addr, NULL); } void pci_target_enqueue(uint64_t ena, char *class, char *bridge_type, uint64_t addr) { pci_target_err_t tgt_err; tgt_err.tgt_err_ena = ena; tgt_err.tgt_err_class = class; tgt_err.tgt_bridge_type = bridge_type; tgt_err.tgt_err_addr = addr; errorq_dispatch(pci_target_queue, (void *)&tgt_err, sizeof (pci_target_err_t), ERRORQ_ASYNC); } void pci_targetq_init(void) { /* * PCI target errorq, to schedule async handling of generation of * target device ereports based on captured physical address. * The errorq is created here but destroyed when _fini is called * for the pci module. */ if (pci_target_queue == NULL) { pci_target_queue = errorq_create("pci_target_queue", (errorq_func_t)pci_target_drain, (void *)NULL, TARGET_MAX_ERRS, sizeof (pci_target_err_t), FM_ERR_PIL, ERRORQ_VITAL); if (pci_target_queue == NULL) panic("failed to create required system error queue"); } }