/*
* 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
* 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
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/ddifm_impl.h>
#include <sys/pci_impl.h>
((x) == PCI_PCIX_VER_2))
};
#if defined(__sparc)
#endif
};
};
};
static int
{
return (DDI_FM_OK);
if (fme_flag == DDI_FM_ERR_UNEXPECTED) {
}
}
return (de.fme_status);
}
static void
{
else
return;
}
static void
{
int i;
else
return;
else
return;
for (i = 0; i < 2; i++) {
PCI_PCIX_BDG_ECC_STATUS), i);
}
}
} else {
(pcix_cap_ptr + PCI_PCIX_COMMAND));
(pcix_cap_ptr + PCI_PCIX_STATUS));
else
return;
}
}
}
/*ARGSUSED*/
static void
{
/*
* Start by reading all the error registers that are available for
*/
return;
return;
/*
* If pci-pci bridge grab PCI bridge specific error registers.
*/
}
/* If pci-x device grab error registers */
}
static void
{
int i;
for (i = 0; i < 2; i++) {
if (pcix_bdg_ecc_regs->pcix_ecc_vflags &
i);
}
0x0;
}
}
} else {
if (pcix_ecc_regs->pcix_ecc_vflags &
}
}
}
static void
{
/*
* Finally clear the error bits
*/
}
}
/*
* pcix_ereport_setup: Allocate structures for PCI-X error handling and ereport
* generation.
*/
/* ARGSUSED */
static void
{
int i;
}
if (pcix_cap_ptr != PCI_CAP_NEXT_PTR_NULL)
else
return;
KM_SLEEP);
for (i = 0; i < 2; i++) {
kmem_zalloc(sizeof (pcix_ecc_regs_t),
KM_SLEEP);
}
}
} else {
KM_SLEEP);
sizeof (pcix_ecc_regs_t), KM_SLEEP);
}
}
}
/*
* pci_ereport_setup: Detect PCI device type and initialize structures to be
* used to generate ereports based on detected generic device errors.
*/
void
{
/*
* If device is not ereport capbable then report an error against the
* driver for using this interface,
*/
return;
}
/*
* ASSERT fmhdl exists and fh_bus_specific is NULL.
*/
goto error;
goto error;
/*
* Get header type and record if device is a bridge.
*/
goto error;
/*
* Check to see if PCI device is a bridge, if so allocate pci bridge
* error register structure.
*/
sizeof (pci_bdg_error_regs_t), KM_SLEEP);
}
}
if (!(pci_status & PCI_STAT_CAP)) {
goto done;
}
/* Initialize structures for PCI-X devices. */
done:
/*
* Before returning set fh_bus_specific to completed pci_erpt_t
* structure
*/
return;
if (erpt_p->pe_pci_regs)
}
static void
{
if (PCIX_ECC_VER_CHECK(pcix_ver)) {
int i;
for (i = 0; i < 2; i++)
sizeof (pcix_ecc_regs_t));
}
} else {
if (PCIX_ECC_VER_CHECK(pcix_ver)) {
sizeof (pcix_ecc_regs_t));
}
}
}
void
{
}
return;
sizeof (pci_bdg_error_regs_t));
/*
* The following sparc specific code should be removed once the pci_cap
* interfaces create the necessary properties for us.
*/
}
/*ARGSUSED*/
static int
{
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 (DDI_FM_UNKNOWN);
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 (DDI_FM_UNKNOWN);
case PCI_PCIX_CMD_CFRD:
case PCI_PCIX_CMD_CFWR:
/*
* for type 1 config transaction we can find bdf from address
*/
}
return (DDI_FM_UNKNOWN);
case PCI_PCIX_CMD_SPL:
case PCI_PCIX_CMD_DADR:
return (DDI_FM_UNKNOWN);
case PCI_PCIX_CMD_MEMRDBL:
case PCI_PCIX_CMD_MEMWRBL:
return (DDI_FM_UNKNOWN);
default:
return (DDI_FM_FATAL);
}
}
/*ARGSUSED*/
static int
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int ok = 0;
int i;
goto done;
unknown++;
}
if (pci_bdg_regs->pci_bdg_sec_stat &
pci_bdg_err_tbl[i].reg_bit) {
pci_bdg_err_tbl[i].err_class);
}
}
}
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.
*/
/*
* for cautious puts we treat all errors as nonfatal. Actually
* we set nonfatal for cautious gets as well - doesn't do any
* harm
*/
nonfatal++;
}
/*
* special case for pokes - we only consider master abort
* and target abort as nonfatal. Sserr with no master abort is
* instance, so return unknown and parent will determine if
* nonfatal (if another child returned nonfatal - ie master
* or target abort) or fatal otherwise
*/
nonfatal++;
unknown++;
}
/*
* now check children below the bridge
*/
}
static int
void *pe_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;
bridge = 1;
} else {
bridge = 0;
}
PCI_PCIX_ECC_PHASE) >> 0x4;
switch (ecc_phase) {
case PCI_PCIX_ECC_PHASE_NOERR:
break;
case PCI_PCIX_ECC_PHASE_FADDR:
case PCI_PCIX_ECC_PHASE_SADDR:
"%s.%s%s", PCIX_ERROR_SUBCLASS,
i ? PCIX_SEC_ERROR_SUBCLASS : "",
break;
case PCI_PCIX_ECC_PHASE_ATTR:
"%s.%s%s", PCIX_ERROR_SUBCLASS,
i ? PCIX_SEC_ERROR_SUBCLASS : "",
break;
if (ecc_corr)
else {
int type;
if (i) {
if (pci_regs->pci_bdg_regs->
type = ACC_HANDLE;
else
type = DMA_HANDLE;
} else {
if (pci_regs->pci_err_status &
type = DMA_HANDLE;
else
type = ACC_HANDLE;
}
}
"%s.%s%s", PCIX_ERROR_SUBCLASS,
i ? PCIX_SEC_ERROR_SUBCLASS : "",
break;
}
if (ecc_phase)
if (bridge)
DATA_TYPE_UINT8, 0,
else
DATA_TYPE_UINT8, 0,
"%s.%s%s", PCIX_ERROR_SUBCLASS,
i ? PCIX_SEC_ERROR_SUBCLASS : "",
if (bridge)
DATA_TYPE_UINT8, 0,
else
DATA_TYPE_UINT8, 0,
}
}
}
}
static int
void *pe_regs)
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int ok = 0;
int i;
if ((pcix_bdg_regs->pcix_bdg_stat &
pcix_err_tbl[i].reg_bit)) {
pcix_err_tbl[i].err_class);
}
}
}
if ((pcix_bdg_regs->pcix_bdg_sec_stat &
pcix_sec_err_tbl[i].reg_bit)) {
}
}
}
int ret;
(void *)pcix_bdg_regs);
}
}
static int
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int ok = 0;
int i;
continue;
NULL);
}
}
(void *)pcix_regs);
}
}
static void
{
int fatal = 0;
int nonfatal = 0;
int unknown = 0;
int ok = 0;
int i;
/*
* Log generic PCI errors.
*/
pci_err_tbl[i].reg_bit) ||
continue;
/*
* Generate an ereport for this error bit.
*/
}
} else {
}
}
}
}
(void *)&pci_fme_bsp->pci_bs_addr);
}
/*
* If we didn't find the handle using an addr, try using bdf.
* Note we don't do this where the bdf is for a
* fabricated the bdf.
*/
if (ret == DDI_FM_UNKNOWN &&
}
}
}
void
{
/*
* On PCI Express systems, all error handling and ereport are done via
* the PCIe misc module. This function is a no-op for PCIe Systems. In
* order to tell if a system is a PCI or PCIe system, check that the
* bus_private_data exists. If it exists, this is a PCIe system.
*/
*xx_status = 0x0;
return;
}
return;
}
/*
* copy in the ddi_fm_error_t structure in case it's VER0
*/
else
/*
* if this is the first pci device we've found convert
* fme_bus_specific to DDI_FME_BUS_TYPE_PCI
*/
if (de.fme_bus_specific) {
/*
* the cpu passed us an addr - this can be used to look
* up an access handle
*/
}
}
return;
}
/*
* 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);
}
/*
* need special version of ddi_fm_ereport_post() as the leaf driver may
* not be hardened.
*/
static void
{
char *name;
if (panicstr) {
return;
} else {
}
if (panicstr) {
} else {
}
}
static int
{
int reglen;
int rn;
int totreg;
/*
* 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.
*/
return (DDI_WALK_CONTINUE);
if (tgt_err->tgt_pci_space ==
PCI_REG_DEV_M | PCI_REG_FUNC_M)) ==
PCI_REG_DEV_M | PCI_REG_FUNC_M))) {
return (DDI_WALK_TERMINATE);
}
}
} else {
/*
* for non config space, need to check reg to look
* for any non-relocable mapping, otherwise check
* assigned-addresses.
*/
return (DDI_WALK_CONTINUE);
tgt_err->tgt_pci_space ==
(tgt_err->tgt_pci_addr >=
(tgt_err->tgt_pci_addr <
return (DDI_WALK_TERMINATE);
}
}
return (DDI_WALK_CONTINUE);
tgt_err->tgt_pci_space ==
(tgt_err->tgt_pci_addr >=
(tgt_err->tgt_pci_addr <
return (DDI_WALK_TERMINATE);
}
}
}
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
{
#if defined(__sparc)
int i;
for (i = 0; i < nrange; i++, pci_ranges++)
pci_ranges->parent_low |=
}
#endif
}
static int
{
int pci_ranges_length;
int nrange;
int i, size;
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.
*/
/*
* no ranges property - no translation needed
*/
if (panicstr)
else {
int circ = 0;
(void *)tgt_err);
}
return (DDI_WALK_TERMINATE);
return (DDI_WALK_PRUNECHILD);
}
rangep = pci_ranges;
/* Need to fix the pci ranges property for psycho based systems */
/* 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 {
int circ = 0;
(void *)tgt_err);
}
return (DDI_WALK_TERMINATE);
}
}
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
{
/*
* 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
{
}
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) {
if (pci_target_queue == NULL)
panic("failed to create required system error queue");
}
}