px_fm.c revision 4fbb58f6b7ca6192a6857d169ee41c3e09dab64a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PX Fault Management Architecture
*/
#include "px_obj.h"
typedef struct px_fabric_cfgspace {
/* Error information */
/* Config space header and device type */
/* Register pointers */
/* PCI register values */
/* PCIE register values */
/* PCIE Header Log Registers */
/*
* Initialize px FMA support
*/
int
{
/*
* check parents' capability
*/
/*
* parents need to be ereport and error handling capable
*/
/*
* register error callback in parent
*/
return (DDI_SUCCESS);
}
/*
* Deregister FMA
*/
void
{
}
/*
* Function used to setup access functions depending on level of desired
* protection.
*/
void
{
switch (fflag) {
case DDI_FLAGERR_ACC:
break;
case DDI_CAUTIOUS_ACC :
break;
default:
break;
}
}
}
/*
* Function called after a dma fault occurred to find out whether the
* fault address is associated with a driver that is able to handle faults
* and recover from faults. The driver has to set DDI_DMA_FLAGERR and
* cache dma handles in order to make this checking effective to help
* recovery from dma faults.
*/
/* ARGSUSED */
static int
const void *not_used)
{
int page;
/*
* Assertion failure if DDI_FM_DMACHK_CAPABLE capability has not
* been effectively initialized during attach.
*/
return (DDI_FM_NONFATAL);
}
return (DDI_FM_UNKNOWN);
}
/*
* Function used to check if a given access handle owns the failing address.
* Called by ndi_fmc_error, when we detect a PIO error.
*/
/* ARGSUSED */
static int
const void *not_used)
{
/*
* Assertion failure if DDI_FM_ACCCHK_CAPABLE capability has not
* been effectively initialized during attach.
*/
return (DDI_FM_NONFATAL);
return (DDI_FM_UNKNOWN);
}
/*
* Function used by PCI error handlers to check if captured address is stored
* in the DMA or ACC handle caches.
*/
int
{
int (*f)() = type == DMA_HANDLE ?
}
/*
* Function used to initialize FMA for our children nodes. Called
* through pci busops when child node calls ddi_fm_init.
*/
/*ARGSUSED*/
int
{
}
/*
* lock access for exclusive PCIe access
*/
void
{
/*
* 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
* membar #Sync - a difference from what's in pci_bus_enter().
*/
}
/*
* unlock access for exclusive PCIe access
*/
/* ARGSUSED */
void
{
}
/*
* PCI error callback which is registered with our parent to call
* for PCIe logging when the CPU traps due to PCIe Uncorrectable Errors
*
* Dispatch on all known leaves of this fire device because we cannot tell
* which side the error came from.
*/
/*ARGSUSED*/
int
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int i;
for (i = 0; i < PX_CB_MAX_LEAF; i++) {
(i == 0));
}
for (i = 0; i < PX_CB_MAX_LEAF; i++) {
switch (ret) {
case DDI_FM_FATAL:
fatal++;
break;
case DDI_FM_NONFATAL:
nonfatal++;
break;
case DDI_FM_UNKNOWN:
unknown++;
break;
default:
break;
}
}
}
((nonfatal != 0) ? DDI_FM_NONFATAL :
/* fire fatal error overrides device error */
ret = DDI_FM_FATAL;
/* if fire encounts no error, then take whatever device error */
return (ret);
}
static uint16_t
{
int deadcount = 0;
/* Find the Advanced Error Register */
while ((hdr_next_ptr != PCIE_EXT_CAP_NEXT_PTR_NULL) &&
(hdr_cap_id != PCIE_EXT_CAP_ID_AER)) {
if (deadcount++ > 100)
break;
}
if (hdr_cap_id == PCIE_EXT_CAP_ID_AER)
return (offset);
return (0);
}
static uint16_t
{
int deadcount = 0;
if (!(hdr & PCI_STAT_CAP)) {
/* This is not a PCIE device */
return (0);
}
hdr_cap_id = 0;
while ((hdr_next_ptr != PCI_CAP_NEXT_PTR_NULL) &&
(hdr_cap_id != PCI_CAP_ID_PCI_E)) {
if (hdr_next_ptr < 0x40) {
break;
}
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
int ret = PX_NONFATAL;
if (sts_reg & pci_status)
/* Target Abort == Completer Abort */
if (sts_reg & pci_status)
/* Master Abort == Unsupport Request */
if (sts_reg & pci_status)
/* System Error == Uncorrectable Error */
pcie_status = -1;
if (sts_reg & pci_status)
return (ret);
}
/*
* This function checks the secondary status registers.
* Switches and Bridges have a different behavior.
*/
static int
int ret = PX_NONFATAL;
/*
* This is a PCIE-PCI bridge, but only check the severity
* if this device doesn't support AERs.
*/
} else {
/* This is most likely a PCIE switch */
}
return (ret);
}
/*
* This function checks and clears the primary AER.
*/
static int
int ret = PX_NONFATAL;
/* Determine severity and clear the AER */
case PCIE_MSG_CODE_ERR_COR:
break;
break;
case PCIE_MSG_CODE_ERR_FATAL:
break;
default:
/* Major error force a panic */
return (PX_FATAL_GOS);
}
return (ret);
}
/*
* This function checks and clears the secondary AER.
*/
static int
int ret = PX_NONFATAL;
/* Determine severity and clear the AER */
case PCIE_MSG_CODE_ERR_COR:
/* Ignore Correctable Errors */
sev = 0;
break;
break;
case PCIE_MSG_CODE_ERR_FATAL:
break;
default:
/* Major error force a panic */
return (DDI_FM_FATAL);
}
return (ret);
}
static int
{
int ret = PX_NONFATAL;
if (hdr_type == PCI_HEADER_PPB) {
}
if (!aer_off) {
}
if (aer_off) {
}
}
/* Clear the standard PCIe error registers */
/* Clear the legacy error registers */
/* Clear the legacy secondary error registers */
if (hdr_type == PCI_HEADER_PPB) {
}
return (ret);
}
static void
{
/* Gather Basic Device Information */
if (!cap_off)
return;
/* Get the Primary Sts Reg */
/* Get the PCIe Dev Sts Reg */
cap_off + PCIE_DEVCTL);
if (!aer_off)
return;
/* Get the AER register information */
return;
/* If this is a bridge check secondary aer */
}
/*
* 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
{
char buf[FM_MAX_CLASS];
int ret;
/* clear cs */
else
ret = PX_FATAL_GOS;
NULL);
/* Check for protected access */
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 = 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 */
{
/* Create the derr */
/* Check and clear the fabric error */
/* Check all child devices for errors */
/*
* PX_FATAL_HW indicates a condition recovered from Fatal-Reset,
* therefore it does not cause panic.
*/
PX_FM_PANIC("Fatal PCIe Fabric Error has occurred\n");
return (DDI_INTR_CLAIMED);
}
/*
* px_err_safeacc_check:
* 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
* 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
{
return;
}
/* safe access checking */
switch (acctype) {
case DDI_FM_ERR_EXPECTED:
/*
* cautious access protection, protected from all err.
*/
break;
case DDI_FM_ERR_POKE:
/*
* ddi_poke protection, check nexus and children for
* expected errors.
*/
membar_sync();
break;
case DDI_FM_ERR_PEEK:
break;
}
}