pci_fm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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"
#include <sys/ddi_impldefs.h>
/*LINTLIBRARY*/
/*
* The routines below are generic sun4u PCI interfaces to support
* Fault Management.
*
* pci_dma_check, pci_acc_check, pci_handle_lookup are functions used
*
* pci_fm_acc_setup, pci_fm_init_child, pci_fm_create,
* pci_fm_destroy are constructors/destructors used to setup and teardown
* necessary resources.
*
* pci_bus_enter, pci_bus_exit are registered via busops and are used to
* provide exclusive access to the PCI bus.
*
* pci_err_callback is the registered callback for PCI which is called
*
* pbm_ereport_post is used by the PBM code to generically report all
* PBM errors.
*
*/
/*
* 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.
*/
/* ARGSUSED */
static int
const void *not_used)
{
int page;
/*
* The driver has to set DDI_DMA_FLAGERR to recover from dma faults.
*/
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)
{
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 status = DDI_FM_UNKNOWN;
return (status);
}
/*
* 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 used to initialize FMA for our children nodes. Called
* through pci busops when child node calls ddi_fm_init.
*/
/* ARGSUSED */
int
{
return (pci_p->pci_fm_cap);
}
/*
* Lock accesses to the pci bus, to be able to protect against bus errors.
*/
void
{
membar_sync();
}
/*
* Unlock access to bus and clear errors before exiting.
*/
/* ARGSUSED */
void
{
membar_sync();
if (pci_check_error(pci_p) != 0) {
(const void *)pci_p, PCI_BUS_EXIT_CALL);
}
}
}
/*
* PCI error callback which is registered with our parent to call
*/
int
const void *impl_data)
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
/*
* Check and log ecc and pbm errors
*/
fatal++;
else if (ret == DDI_FM_NONFATAL)
nonfatal++;
else if (ret == DDI_FM_UNKNOWN)
unknown++;
if (pci_check_error(pci_p) != 0) {
(const void *)pci_p, PCI_TRAP_CALL);
if (err == DDI_FM_FATAL)
fatal++;
else if (err == DDI_FM_NONFATAL)
nonfatal++;
else if (err == DDI_FM_UNKNOWN)
unknown++;
}
if (fatal)
return (DDI_FM_FATAL);
else if (nonfatal)
return (DDI_FM_NONFATAL);
else if (unknown)
return (DDI_FM_UNKNOWN);
else
return (DDI_FM_OK);
}
/*
* private version of walk_devs() that can be used during panic. No
* sleeping or locking required.
*/
static int
{
while (dip) {
case DDI_WALK_TERMINATE:
return (DDI_WALK_TERMINATE);
case DDI_WALK_CONTINUE:
arg) == DDI_WALK_TERMINATE)
return (DDI_WALK_TERMINATE);
break;
case DDI_WALK_PRUNECHILD:
break;
}
}
return (DDI_WALK_CONTINUE);
}
static int
{
int reglen;
int rn;
int totreg;
return (DDI_WALK_CONTINUE);
if (tgt_err->tgt_pci_space ==
(tgt_err->tgt_pci_addr >=
(tgt_err->tgt_pci_addr <
return (DDI_WALK_TERMINATE);
}
}
return (DDI_WALK_CONTINUE);
}
static int
{
int pci_ranges_length;
int nrange;
int i, size;
return (DDI_WALK_CONTINUE);
/*
* Get the ranges property.
*/
return (DDI_WALK_CONTINUE);
}
rangep = pci_ranges;
/* Not in range */
continue;
}
/* Config space address - check bus range */
DDI_PROP_DONTPASS, "bus-range",
continue;
}
/*
* Bus number not appropriate for this
* pci nexus.
*/
continue;
}
}
/* We have a match if we get here - compute pci address */
if (panicstr)
else
return (DDI_WALK_TERMINATE);
}
}
return (DDI_WALK_PRUNECHILD);
}
/*
* need special version of ddi_fm_ereport_post() as the leaf driver may
* not be hardened.
*/
void
{
char *name;
char device_path[MAXPATHLEN];
char ddi_error_class[FM_MAX_CLASS];
if (panicstr) {
return;
} else {
}
device_path, NULL);
if (panicstr) {
} else {
}
}
/*
* Function used to drain pci_target_queue, either during panic or after softint
* is generated, to generate target device ereports based on captured physical
* addresss
*/
static void
{
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.
*/
if (panicstr)
(void *)tgt_err);
else
(void *)tgt_err);
return;
}
void
{
/*
* PCI detected ECC errorq, to schedule async handling
* of ECC errors and logging.
* The errorq is created here but destroyed when _fini is called
* for the pci module.
*/
if (pci_ecc_queue == NULL) {
(void *)NULL,
ECC_MAX_ERRS, sizeof (ecc_errstate_t),
if (pci_ecc_queue == NULL)
panic("failed to create required system error queue");
}
/*
* 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) {
(void *)NULL,
TARGET_MAX_ERRS, sizeof (pci_target_err_t),
if (pci_target_queue == NULL)
panic("failed to create required system error queue");
}
/*
* Initialize FMA support
* The axq workaround prevents fault management of access errors
*/
else
/*
* Call parent to get it's capablity
*/
&pci_p->pci_fm_ibc);
/*
* Need to be ereport and error handler cabable
*/
/*
* Initialize error handling mutex.
*/
if (cmn_p->pci_common_refcnt == 0) {
(void *)pci_p->pci_fm_ibc);
}
/*
* Register error callback with our parent.
*/
pci_p);
}
void
{
/* schizo non-shared objects */
if (cmn_p->pci_common_refcnt != 0)
return;
}
/*
* Function used to post PCI block module specific ereports.
*/
void
{
char buf[FM_MAX_CLASS];
NULL);
}