px_fm.c revision 2859562fdfd7ef8a00f5c8a5dc05117a732e7983
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * CDDL HEADER START
25cf1a301a396c38e8adf52c15f537b80d2483f7jl *
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * The contents of this file are subject to the terms of the
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Common Development and Distribution License (the "License").
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * You may not use this file except in compliance with the License.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl *
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * or http://www.opensolaris.org/os/licensing.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * See the License for the specific language governing permissions
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * and limitations under the License.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl *
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * When distributing Covered Code, include this CDDL HEADER in each
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * If applicable, add the following below this CDDL HEADER, with the
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * fields enclosed by brackets "[]" replaced with your own identifying
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * information: Portions Copyright [yyyy] [name of copyright owner]
25cf1a301a396c38e8adf52c15f537b80d2483f7jl *
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * CDDL HEADER END
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Use is subject to license terms.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#pragma ident "%Z%%M% %I% %E% SMI"
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * PX Fault Management Architecture
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/types.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/sunndi.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/sunddi.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/fm/protocol.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/fm/util.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include <sys/membar.h>
25cf1a301a396c38e8adf52c15f537b80d2483f7jl#include "px_obj.h"
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jltypedef struct px_fabric_cfgspace {
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* Error information */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl msgcode_t msg_code;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl pcie_req_id_t rid;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* Config space header and device type */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint8_t hdr_type;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint16_t dev_type;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* Register pointers */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint16_t cap_off;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint16_t aer_off;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* PCI register values */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t sts_reg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t sts_sreg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* PCIE register values */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t dev_sts_reg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_ce_reg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_ue_reg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sev_reg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_ue_sreg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sev_sreg;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /* PCIE Header Log Registers */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_h1;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_h2;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_h3;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_h4;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sh1;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sh2;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sh3;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uint32_t aer_sh4;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl} px_fabric_cfgspace_t;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic uint16_t px_fabric_get_aer(px_t *px_p, pcie_req_id_t rid);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic uint16_t px_fabric_get_pciecap(px_t *px_p, pcie_req_id_t rid);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic int px_fabric_handle_psts(px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic int px_fabric_handle_ssts(px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic int px_fabric_handle_paer(px_t *px_p, px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic int px_fabric_handle_saer(px_t *px_p, px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic int px_fabric_handle(px_t *px_p, px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic void px_fabric_fill_cs(px_t *px_p, px_fabric_cfgspace_t *cs);
25cf1a301a396c38e8adf52c15f537b80d2483f7jlstatic uint_t px_fabric_check(px_t *px_p, msgcode_t msg_code,
25cf1a301a396c38e8adf52c15f537b80d2483f7jl pcie_req_id_t rid, ddi_fm_error_t *derr);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Initialize px FMA support
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jlint
25cf1a301a396c38e8adf52c15f537b80d2483f7jlpx_fm_attach(px_t *px_p)
25cf1a301a396c38e8adf52c15f537b80d2483f7jl{
25cf1a301a396c38e8adf52c15f537b80d2483f7jl px_p->px_fm_cap = DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE |
25cf1a301a396c38e8adf52c15f537b80d2483f7jl DDI_FM_ACCCHK_CAPABLE | DDI_FM_DMACHK_CAPABLE;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Initialize pci_target_queue for FMA handling of
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * pci errors.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl pci_targetq_init();
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * check parents' capability
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_fm_init(px_p->px_dip, &px_p->px_fm_cap, &px_p->px_fm_ibc);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * parents need to be ereport and error handling capable
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ASSERT(px_p->px_fm_cap &&
25cf1a301a396c38e8adf52c15f537b80d2483f7jl (DDI_FM_ERRCB_CAPABLE | DDI_FM_EREPORT_CAPABLE));
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Initialize lock to synchronize fabric error handling
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl mutex_init(&px_p->px_fm_mutex, NULL, MUTEX_DRIVER,
25cf1a301a396c38e8adf52c15f537b80d2483f7jl (void *)px_p->px_fm_ibc);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl /*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * register error callback in parent
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_fm_handler_register(px_p->px_dip, px_fm_callback, px_p);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl return (DDI_SUCCESS);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl}
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Deregister FMA
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jlvoid
25cf1a301a396c38e8adf52c15f537b80d2483f7jlpx_fm_detach(px_t *px_p)
25cf1a301a396c38e8adf52c15f537b80d2483f7jl{
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_fm_handler_unregister(px_p->px_dip);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl mutex_destroy(&px_p->px_fm_mutex);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_fm_fini(px_p->px_dip);
25cf1a301a396c38e8adf52c15f537b80d2483f7jl}
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
25cf1a301a396c38e8adf52c15f537b80d2483f7jl/*
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * Function used to setup access functions depending on level of desired
25cf1a301a396c38e8adf52c15f537b80d2483f7jl * protection.
25cf1a301a396c38e8adf52c15f537b80d2483f7jl */
25cf1a301a396c38e8adf52c15f537b80d2483f7jlvoid
25cf1a301a396c38e8adf52c15f537b80d2483f7jlpx_fm_acc_setup(ddi_map_req_t *mp, dev_info_t *rdip)
25cf1a301a396c38e8adf52c15f537b80d2483f7jl{
25cf1a301a396c38e8adf52c15f537b80d2483f7jl uchar_t fflag;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_acc_hdl_t *hp;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl ddi_acc_impl_t *ap;
25cf1a301a396c38e8adf52c15f537b80d2483f7jl
hp = mp->map_handlep;
ap = (ddi_acc_impl_t *)hp->ah_platform_private;
fflag = ap->ahi_common.ah_acc.devacc_attr_access;
if (mp->map_op == DDI_MO_MAP_LOCKED) {
ndi_fmc_insert(rdip, ACC_HANDLE, (void *)hp, NULL);
switch (fflag) {
case DDI_FLAGERR_ACC:
ap->ahi_get8 = i_ddi_prot_get8;
ap->ahi_get16 = i_ddi_prot_get16;
ap->ahi_get32 = i_ddi_prot_get32;
ap->ahi_get64 = i_ddi_prot_get64;
ap->ahi_put8 = i_ddi_prot_put8;
ap->ahi_put16 = i_ddi_prot_put16;
ap->ahi_put32 = i_ddi_prot_put32;
ap->ahi_put64 = i_ddi_prot_put64;
ap->ahi_rep_get8 = i_ddi_prot_rep_get8;
ap->ahi_rep_get16 = i_ddi_prot_rep_get16;
ap->ahi_rep_get32 = i_ddi_prot_rep_get32;
ap->ahi_rep_get64 = i_ddi_prot_rep_get64;
ap->ahi_rep_put8 = i_ddi_prot_rep_put8;
ap->ahi_rep_put16 = i_ddi_prot_rep_put16;
ap->ahi_rep_put32 = i_ddi_prot_rep_put32;
ap->ahi_rep_put64 = i_ddi_prot_rep_put64;
break;
case DDI_CAUTIOUS_ACC :
ap->ahi_get8 = i_ddi_caut_get8;
ap->ahi_get16 = i_ddi_caut_get16;
ap->ahi_get32 = i_ddi_caut_get32;
ap->ahi_get64 = i_ddi_caut_get64;
ap->ahi_put8 = i_ddi_caut_put8;
ap->ahi_put16 = i_ddi_caut_put16;
ap->ahi_put32 = i_ddi_caut_put32;
ap->ahi_put64 = i_ddi_caut_put64;
ap->ahi_rep_get8 = i_ddi_caut_rep_get8;
ap->ahi_rep_get16 = i_ddi_caut_rep_get16;
ap->ahi_rep_get32 = i_ddi_caut_rep_get32;
ap->ahi_rep_get64 = i_ddi_caut_rep_get64;
ap->ahi_rep_put8 = i_ddi_caut_rep_put8;
ap->ahi_rep_put16 = i_ddi_caut_rep_put16;
ap->ahi_rep_put32 = i_ddi_caut_rep_put32;
ap->ahi_rep_put64 = i_ddi_caut_rep_put64;
break;
default:
break;
}
} else if (mp->map_op == DDI_MO_UNMAP) {
ndi_fmc_remove(rdip, ACC_HANDLE, (void *)hp);
}
}
/*
* Function used by PCI error handlers to check if captured address is stored
* in the DMA or ACC handle caches.
*/
int
px_handle_lookup(dev_info_t *dip, int type, uint64_t fme_ena, void *afar)
{
int ret = ndi_fmc_error(dip, NULL, type, fme_ena, afar);
return (ret == DDI_FM_UNKNOWN ? DDI_FM_FATAL : ret);
}
/*
* Function used to initialize FMA for our children nodes. Called
* through pci busops when child node calls ddi_fm_init.
*/
/*ARGSUSED*/
int
px_fm_init_child(dev_info_t *dip, dev_info_t *cdip, int cap,
ddi_iblock_cookie_t *ibc_p)
{
px_t *px_p = DIP_TO_STATE(dip);
ASSERT(ibc_p != NULL);
*ibc_p = px_p->px_fm_ibc;
return (px_p->px_fm_cap);
}
/*
* lock access for exclusive PCIe access
*/
void
px_bus_enter(dev_info_t *dip, ddi_acc_handle_t handle)
{
px_pec_t *pec_p = ((px_t *)DIP_TO_STATE(dip))->px_pec_p;
/*
* Exclusive access has been used for cautious put/get,
* Both utilize i_ddi_ontrap which, on sparcv9, implements
* similar protection as what on_trap() does, and which calls
* membar #Sync to flush out all cpu deferred errors
* prior to get/put operation, so here we're not calling
* membar #Sync - a difference from what's in pci_bus_enter().
*/
mutex_enter(&pec_p->pec_pokefault_mutex);
pec_p->pec_acc_hdl = handle;
}
/*
* unlock access for exclusive PCIe access
*/
/* ARGSUSED */
void
px_bus_exit(dev_info_t *dip, ddi_acc_handle_t handle)
{
px_t *px_p = DIP_TO_STATE(dip);
px_pec_t *pec_p = px_p->px_pec_p;
pec_p->pec_acc_hdl = NULL;
mutex_exit(&pec_p->pec_pokefault_mutex);
}
/*
* PCI error callback which is registered with our parent to call
* for PCIe logging when the CPU traps due to PCIe Uncorrectable Errors
* and PCI BERR/TO/UE
*
* Dispatch on all known leaves of this fire device because we cannot tell
* which side the error came from.
*/
/*ARGSUSED*/
int
px_fm_callback(dev_info_t *dip, ddi_fm_error_t *derr, const void *impl_data)
{
px_t *px_p = (px_t *)impl_data;
int err = PX_OK;
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int ret = DDI_FM_OK;
mutex_enter(&px_p->px_fm_mutex);
err = px_err_handle(px_p, derr, PX_TRAP_CALL, B_TRUE);
if (!px_lib_is_in_drain_state(px_p))
ret = ndi_fm_handler_dispatch(px_p->px_dip, NULL, derr);
mutex_exit(&px_p->px_fm_mutex);
switch (ret) {
case DDI_FM_FATAL:
fatal++;
break;
case DDI_FM_NONFATAL:
nonfatal++;
break;
case DDI_FM_UNKNOWN:
unknown++;
break;
default:
break;
}
ret = (fatal != 0) ? DDI_FM_FATAL :
((nonfatal != 0) ? DDI_FM_NONFATAL :
(((unknown != 0) ? DDI_FM_UNKNOWN : DDI_FM_OK)));
/* fire fatal error overrides device error */
if (err & (PX_FATAL_GOS | PX_FATAL_SW))
ret = DDI_FM_FATAL;
/* if fire encounts no error, then take whatever device error */
else if ((err != PX_OK) && (ret != DDI_FM_FATAL))
ret = DDI_FM_NONFATAL;
return (ret);
}
static uint16_t
px_fabric_get_aer(px_t *px_p, pcie_req_id_t rid)
{
uint32_t hdr, hdr_next_ptr, hdr_cap_id;
uint16_t offset = PCIE_EXT_CAP;
int deadcount = 0;
/* Find the Advanced Error Register */
hdr = px_fab_get(px_p, rid, 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 = hdr_next_ptr;
hdr = px_fab_get(px_p, rid, 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 (deadcount++ > 100)
break;
}
if (hdr_cap_id == PCIE_EXT_CAP_ID_AER)
return (offset);
return (0);
}
static uint16_t
px_fabric_get_pciecap(px_t *px_p, pcie_req_id_t rid)
{
uint32_t hdr, hdr_next_ptr, hdr_cap_id;
uint16_t offset = PCI_CONF_STAT;
int deadcount = 0;
hdr = px_fab_get(px_p, rid, PCI_CONF_COMM) >> 16;
if (!(hdr & PCI_STAT_CAP)) {
/* This is not a PCIE device */
return (0);
}
hdr = px_fab_get(px_p, rid, PCI_CONF_CAP_PTR);
hdr_next_ptr = hdr & 0xFF;
hdr_cap_id = 0;
while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
(hdr_cap_id != PCI_CAP_ID_PCI_E)) {
offset = hdr_next_ptr;
if (hdr_next_ptr < 0x40) {
break;
}
hdr = px_fab_get(px_p, rid, hdr_next_ptr);
hdr_next_ptr = (hdr >> 8) & 0xFF;
hdr_cap_id = hdr & 0xFF;
if (deadcount++ > 100)
break;
}
if (hdr_cap_id == PCI_CAP_ID_PCI_E)
return (offset);
return (0);
}
/*
* This function checks the primary status registers.
* Take the PCI status register and translate it to PCIe equivalent.
*/
static int
px_fabric_handle_psts(px_fabric_cfgspace_t *cs) {
uint16_t sts_reg = cs->sts_reg >> 16;
uint16_t pci_status;
uint32_t pcie_status;
int ret = PX_NONFATAL;
/* Parity Err == Send/Recv Poisoned TLP */
pci_status = PCI_STAT_S_PERROR | PCI_STAT_PERROR;
pcie_status = PCIE_AER_UCE_PTLP | PCIE_AER_UCE_ECRC;
if (sts_reg & pci_status)
ret |= PX_FABRIC_ERR_SEV(pcie_status,
px_fabric_die_ue, px_fabric_die_ue_gos);
/* Target Abort == Completer Abort */
pci_status = PCI_STAT_S_TARG_AB | PCI_STAT_R_TARG_AB;
pcie_status = PCIE_AER_UCE_CA;
if (sts_reg & pci_status)
ret |= PX_FABRIC_ERR_SEV(pcie_status,
px_fabric_die_ue, px_fabric_die_ue_gos);
/* Master Abort == Unsupport Request */
pci_status = PCI_STAT_R_MAST_AB;
pcie_status = PCIE_AER_UCE_UR;
if (sts_reg & pci_status)
ret |= PX_FABRIC_ERR_SEV(pcie_status,
px_fabric_die_ue, px_fabric_die_ue_gos);
/* System Error == Uncorrectable Error */
pci_status = PCI_STAT_S_SYSERR;
pcie_status = -1;
if (sts_reg & pci_status)
ret |= PX_FABRIC_ERR_SEV(pcie_status,
px_fabric_die_ue, px_fabric_die_ue_gos);
return (ret);
}
/*
* This function checks the secondary status registers.
* Switches and Bridges have a different behavior.
*/
static int
px_fabric_handle_ssts(px_fabric_cfgspace_t *cs) {
uint16_t sts_reg = cs->sts_sreg >> 16;
int ret = PX_NONFATAL;
if (cs->dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI) {
/*
* This is a PCIE-PCI bridge, but only check the severity
* if this device doesn't support AERs.
*/
if (!cs->aer_off)
ret |= PX_FABRIC_ERR_SEV(sts_reg, px_fabric_die_bdg_sts,
px_fabric_die_bdg_sts_gos);
} else {
/* This is most likely a PCIE switch */
ret |= PX_FABRIC_ERR_SEV(sts_reg, px_fabric_die_sw_sts,
px_fabric_die_sw_sts_gos);
}
return (ret);
}
/*
* This function checks and clears the primary AER.
*/
static int
px_fabric_handle_paer(px_t *px_p, px_fabric_cfgspace_t *cs) {
uint32_t chk_reg, chk_reg_gos, off_reg, reg;
int ret = PX_NONFATAL;
/* Determine severity and clear the AER */
switch (cs->msg_code) {
case PCIE_MSG_CODE_ERR_COR:
off_reg = PCIE_AER_CE_STS;
chk_reg = px_fabric_die_ce;
chk_reg_gos = px_fabric_die_ce_gos;
reg = cs->aer_ce_reg;
break;
case PCIE_MSG_CODE_ERR_NONFATAL:
off_reg = PCIE_AER_UCE_STS;
chk_reg = px_fabric_die_ue;
chk_reg_gos = px_fabric_die_ue_gos;
reg = cs->aer_ue_reg & ~(cs->aer_sev_reg);
break;
case PCIE_MSG_CODE_ERR_FATAL:
off_reg = PCIE_AER_UCE_STS;
chk_reg = px_fabric_die_ue;
chk_reg_gos = px_fabric_die_ue_gos;
reg = cs->aer_ue_reg & cs->aer_sev_reg;
break;
default:
/* Major error force a panic */
return (PX_FATAL_GOS);
}
px_fab_set(px_p, cs->rid, cs->aer_off + off_reg, reg);
ret |= PX_FABRIC_ERR_SEV(reg, chk_reg, chk_reg_gos);
return (ret);
}
/*
* This function checks and clears the secondary AER.
*/
static int
px_fabric_handle_saer(px_t *px_p, px_fabric_cfgspace_t *cs) {
uint32_t chk_reg, chk_reg_gos, off_reg, reg;
uint32_t sev;
int ret = PX_NONFATAL;
/* Determine severity and clear the AER */
switch (cs->msg_code) {
case PCIE_MSG_CODE_ERR_COR:
/* Ignore Correctable Errors */
sev = 0;
break;
case PCIE_MSG_CODE_ERR_NONFATAL:
sev = ~(cs->aer_sev_sreg);
break;
case PCIE_MSG_CODE_ERR_FATAL:
sev = cs->aer_sev_sreg;
break;
default:
/* Major error force a panic */
return (DDI_FM_FATAL);
}
off_reg = PCIE_AER_SUCE_STS;
chk_reg = px_fabric_die_sue;
chk_reg_gos = px_fabric_die_sue_gos;
reg = cs->aer_ue_sreg & sev;
px_fab_set(px_p, cs->rid, cs->aer_off + off_reg, reg);
ret |= PX_FABRIC_ERR_SEV(reg, chk_reg, chk_reg_gos);
return (ret);
}
static int
px_fabric_handle(px_t *px_p, px_fabric_cfgspace_t *cs)
{
pcie_req_id_t rid = cs->rid;
uint16_t cap_off = cs->cap_off;
uint16_t aer_off = cs->aer_off;
uint8_t hdr_type = cs->hdr_type;
uint16_t dev_type = cs->dev_type;
int ret = PX_NONFATAL;
if (hdr_type == PCI_HEADER_PPB) {
ret |= px_fabric_handle_ssts(cs);
}
if (!aer_off) {
ret |= px_fabric_handle_psts(cs);
}
if (aer_off) {
ret |= px_fabric_handle_paer(px_p, cs);
}
if (aer_off && (dev_type == PCIE_PCIECAP_DEV_TYPE_PCIE2PCI)) {
ret |= px_fabric_handle_saer(px_p, cs);
}
/* Clear the standard PCIe error registers */
px_fab_set(px_p, rid, cap_off + PCIE_DEVCTL, cs->dev_sts_reg);
/* Clear the legacy error registers */
px_fab_set(px_p, rid, PCI_CONF_COMM, cs->sts_reg);
/* Clear the legacy secondary error registers */
if (hdr_type == PCI_HEADER_PPB) {
px_fab_set(px_p, rid, PCI_BCNF_IO_BASE_LOW,
cs->sts_sreg);
}
return (ret);
}
static void
px_fabric_fill_cs(px_t *px_p, px_fabric_cfgspace_t *cs)
{
uint16_t cap_off, aer_off;
pcie_req_id_t rid = cs->rid;
/* Gather Basic Device Information */
cs->hdr_type = (px_fab_get(px_p, rid, PCI_CONF_CACHE_LINESZ) >> 16) &
PCI_HEADER_TYPE_M;
cs->cap_off = px_fabric_get_pciecap(px_p, rid);
cap_off = cs->cap_off;
if (!cap_off)
return;
cs->aer_off = px_fabric_get_aer(px_p, rid);
aer_off = cs->aer_off;
cs->dev_type = px_fab_get(px_p, rid, cap_off) >> 16;
cs->dev_type &= PCIE_PCIECAP_DEV_TYPE_MASK;
/* Get the Primary Sts Reg */
cs->sts_reg = px_fab_get(px_p, rid, PCI_CONF_COMM);
/* If it is a bridge/switch get the Secondary Sts Reg */
if (cs->hdr_type == PCI_HEADER_PPB)
cs->sts_sreg = px_fab_get(px_p, rid,
PCI_BCNF_IO_BASE_LOW);
/* Get the PCIe Dev Sts Reg */
cs->dev_sts_reg = px_fab_get(px_p, rid,
cap_off + PCIE_DEVCTL);
if (!aer_off)
return;
/* Get the AER register information */
cs->aer_ce_reg = px_fab_get(px_p, rid, aer_off + PCIE_AER_CE_STS);
cs->aer_ue_reg = px_fab_get(px_p, rid, aer_off + PCIE_AER_UCE_STS);
cs->aer_sev_reg = px_fab_get(px_p, rid, aer_off + PCIE_AER_UCE_SERV);
cs->aer_h1 = px_fab_get(px_p, rid, aer_off + PCIE_AER_HDR_LOG + 0x0);
cs->aer_h2 = px_fab_get(px_p, rid, aer_off + PCIE_AER_HDR_LOG + 0x4);
cs->aer_h3 = px_fab_get(px_p, rid, aer_off + PCIE_AER_HDR_LOG + 0x8);
cs->aer_h4 = px_fab_get(px_p, rid, aer_off + PCIE_AER_HDR_LOG + 0xC);
if (cs->dev_type != PCIE_PCIECAP_DEV_TYPE_PCIE2PCI)
return;
/* If this is a bridge check secondary aer */
cs->aer_ue_sreg = px_fab_get(px_p, rid, aer_off + PCIE_AER_SUCE_STS);
cs->aer_sev_sreg = px_fab_get(px_p, rid, aer_off + PCIE_AER_SUCE_SERV);
cs->aer_sh1 = px_fab_get(px_p, rid, aer_off + PCIE_AER_SHDR_LOG + 0x0);
cs->aer_sh2 = px_fab_get(px_p, rid, aer_off + PCIE_AER_SHDR_LOG + 0x4);
cs->aer_sh3 = px_fab_get(px_p, rid, aer_off + PCIE_AER_SHDR_LOG + 0x8);
cs->aer_sh4 = px_fab_get(px_p, rid, aer_off + PCIE_AER_SHDR_LOG + 0xC);
}
/*
* If a fabric intr occurs, query and clear the error registers on that device.
* Based on the error found return DDI_FM_OK or DDI_FM_FATAL.
*/
static uint_t
px_fabric_check(px_t *px_p, msgcode_t msg_code,
pcie_req_id_t rid, ddi_fm_error_t *derr)
{
dev_info_t *dip = px_p->px_dip;
char buf[FM_MAX_CLASS];
px_fabric_cfgspace_t cs;
int ret;
/* clear cs */
bzero(&cs, sizeof (px_fabric_cfgspace_t));
cs.msg_code = msg_code;
cs.rid = rid;
px_fabric_fill_cs(px_p, &cs);
if (cs.cap_off)
ret = px_fabric_handle(px_p, &cs);
else
ret = PX_FATAL_GOS;
(void) snprintf(buf, FM_MAX_CLASS, "%s", PX_FM_FABRIC_CLASS);
ddi_fm_ereport_post(dip, buf, derr->fme_ena,
DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, 0,
PX_FM_FABRIC_MSG_CODE, DATA_TYPE_UINT8, msg_code,
PX_FM_FABRIC_REQ_ID, DATA_TYPE_UINT16, rid,
"cap_off", DATA_TYPE_UINT16, cs.cap_off,
"aer_off", DATA_TYPE_UINT16, cs.aer_off,
"sts_reg", DATA_TYPE_UINT16, cs.sts_reg >> 16,
"sts_sreg", DATA_TYPE_UINT16, cs.sts_sreg >> 16,
"dev_sts_reg", DATA_TYPE_UINT16, cs.dev_sts_reg >> 16,
"aer_ce", DATA_TYPE_UINT32, cs.aer_ce_reg,
"aer_ue", DATA_TYPE_UINT32, cs.aer_ue_reg,
"aer_sev", DATA_TYPE_UINT32, cs.aer_sev_reg,
"aer_h1", DATA_TYPE_UINT32, cs.aer_h1,
"aer_h2", DATA_TYPE_UINT32, cs.aer_h2,
"aer_h3", DATA_TYPE_UINT32, cs.aer_h3,
"aer_h4", DATA_TYPE_UINT32, cs.aer_h4,
"saer_ue", DATA_TYPE_UINT32, cs.aer_ue_sreg,
"saer_sev", DATA_TYPE_UINT32, cs.aer_sev_sreg,
"saer_h1", DATA_TYPE_UINT32, cs.aer_sh1,
"saer_h2", DATA_TYPE_UINT32, cs.aer_sh2,
"saer_h3", DATA_TYPE_UINT32, cs.aer_sh3,
"saer_h4", DATA_TYPE_UINT32, cs.aer_sh4,
"severity", DATA_TYPE_UINT32, ret,
NULL);
/* Check for protected access */
switch (derr->fme_flag) {
case DDI_FM_ERR_EXPECTED:
case DDI_FM_ERR_PEEK:
case DDI_FM_ERR_POKE:
ret &= PX_FATAL_GOS;
break;
}
if (px_fabric_die &&
(ret & (PX_FATAL_GOS | PX_FATAL_SW)))
ret = DDI_FM_FATAL;
return (ret);
}
/*
* px_err_fabric_intr:
* Interrupt handler for PCIE fabric block.
* o lock
* o create derr
* o px_err_handle(leaf, with jbc)
* o send ereport(fire fmri, derr, payload = BDF)
* o dispatch (leaf)
* o unlock
* o handle error: fatal? fm_panic() : return INTR_CLAIMED)
*/
/* ARGSUSED */
uint_t
px_err_fabric_intr(px_t *px_p, msgcode_t msg_code,
pcie_req_id_t rid)
{
dev_info_t *rpdip = px_p->px_dip;
int err = PX_OK, ret = DDI_FM_OK, fab_err = DDI_FM_OK;
ddi_fm_error_t derr;
mutex_enter(&px_p->px_fm_mutex);
/* Create the derr */
bzero(&derr, sizeof (ddi_fm_error_t));
derr.fme_version = DDI_FME_VERSION;
derr.fme_ena = fm_ena_generate(0, FM_ENA_FMT1);
derr.fme_flag = DDI_FM_ERR_UNEXPECTED;
/* send ereport/handle/clear fire registers */
err |= px_err_handle(px_p, &derr, PX_INTR_CALL, B_TRUE);
/* Check and clear the fabric error */
fab_err = px_fabric_check(px_p, msg_code, rid, &derr);
/* Check all child devices for errors */
ret = ndi_fm_handler_dispatch(rpdip, NULL, &derr);
mutex_exit(&px_p->px_fm_mutex);
/*
* PX_FATAL_HW indicates a condition recovered from Fatal-Reset,
* therefore it does not cause panic.
*/
if ((err & (PX_FATAL_GOS | PX_FATAL_SW)) ||
(ret == DDI_FM_FATAL) || (fab_err == DDI_FM_FATAL))
PX_FM_PANIC("%s#%d: Fatal PCIe Fabric Error has occurred"
"(%x,%x,%x)\n", ddi_driver_name(rpdip),
ddi_get_instance(rpdip), err, fab_err, ret);
return (DDI_INTR_CLAIMED);
}
/*
* px_err_safeacc_check:
* Check to see if a peek/poke and cautious access is currently being
* done on a particular leaf.
*
* Safe access reads induced fire errors will be handled by cpu trap handler
* which will call px_fm_callback() which calls this function. In that
* case, the derr fields will be set by trap handler with the correct values.
*
* Safe access writes induced errors will be handled by px interrupt
* handlers, this function will fill in the derr fields.
*
* If a cpu trap does occur, it will quiesce all other interrupts allowing
* the cpu trap error handling to finish before Fire receives an interrupt.
*
* If fire does indeed have an error when a cpu trap occurs as a result of
* a safe access, a trap followed by a Mondo/Fabric interrupt will occur.
* In which case derr will be initialized as "UNEXPECTED" by the interrupt
* handler and this function will need to find if this error occured in the
* middle of a safe access operation.
*
* @param px_p leaf in which to check access
* @param derr fm err data structure to be updated
*/
void
px_err_safeacc_check(px_t *px_p, ddi_fm_error_t *derr)
{
px_pec_t *pec_p = px_p->px_pec_p;
int acctype = pec_p->pec_safeacc_type;
ASSERT(MUTEX_HELD(&px_p->px_fm_mutex));
if (derr->fme_flag != DDI_FM_ERR_UNEXPECTED) {
return;
}
/* safe access checking */
switch (acctype) {
case DDI_FM_ERR_EXPECTED:
/*
* cautious access protection, protected from all err.
*/
ASSERT(MUTEX_HELD(&pec_p->pec_pokefault_mutex));
ddi_fm_acc_err_get(pec_p->pec_acc_hdl, derr,
DDI_FME_VERSION);
derr->fme_flag = acctype;
derr->fme_acc_handle = pec_p->pec_acc_hdl;
break;
case DDI_FM_ERR_POKE:
/*
* ddi_poke protection, check nexus and children for
* expected errors.
*/
ASSERT(MUTEX_HELD(&pec_p->pec_pokefault_mutex));
membar_sync();
derr->fme_flag = acctype;
break;
case DDI_FM_ERR_PEEK:
derr->fme_flag = acctype;
break;
}
}