14df55fdeSJanie Lu /*
24df55fdeSJanie Lu  * CDDL HEADER START
34df55fdeSJanie Lu  *
44df55fdeSJanie Lu  * The contents of this file are subject to the terms of the
54df55fdeSJanie Lu  * Common Development and Distribution License (the "License").
64df55fdeSJanie Lu  * You may not use this file except in compliance with the License.
74df55fdeSJanie Lu  *
84df55fdeSJanie Lu  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94df55fdeSJanie Lu  * or http://www.opensolaris.org/os/licensing.
104df55fdeSJanie Lu  * See the License for the specific language governing permissions
114df55fdeSJanie Lu  * and limitations under the License.
124df55fdeSJanie Lu  *
134df55fdeSJanie Lu  * When distributing Covered Code, include this CDDL HEADER in each
144df55fdeSJanie Lu  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154df55fdeSJanie Lu  * If applicable, add the following below this CDDL HEADER, with the
164df55fdeSJanie Lu  * fields enclosed by brackets "[]" replaced with your own identifying
174df55fdeSJanie Lu  * information: Portions Copyright [yyyy] [name of copyright owner]
184df55fdeSJanie Lu  *
194df55fdeSJanie Lu  * CDDL HEADER END
204df55fdeSJanie Lu  */
214df55fdeSJanie Lu 
224df55fdeSJanie Lu /*
23*4f764f91SCheng Sean Ye  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
244df55fdeSJanie Lu  */
254df55fdeSJanie Lu #include <sys/types.h>
264df55fdeSJanie Lu #include <px_err.h>
274df55fdeSJanie Lu 
284df55fdeSJanie Lu #include "fabric-xlate.h"
294df55fdeSJanie Lu 
304df55fdeSJanie Lu #define	EPKT_DESC(b, o, p, c, d) (BLOCK_##b << 16 | OP_##o << 12 | \
314df55fdeSJanie Lu     PH_##p << 8 | CND_##c << 4 | DIR_##d)
324df55fdeSJanie Lu 
334df55fdeSJanie Lu /* EPKT Table used only for RC/RP errors */
344df55fdeSJanie Lu typedef struct fab_epkt_tbl {
354df55fdeSJanie Lu 	uint32_t	epkt_desc;
364df55fdeSJanie Lu 	uint32_t	pcie_ue_sts;	/* Equivalent PCIe UE Status */
374df55fdeSJanie Lu 	uint16_t	pci_err_sts;	/* Equivalent PCI Error Status */
384df55fdeSJanie Lu 	uint16_t	pci_bdg_sts;	/* Equivalent PCI Bridge Status */
394df55fdeSJanie Lu 	const char	*tgt_class;	/* Target Ereport Class */
404df55fdeSJanie Lu } fab_epkt_tbl_t;
414df55fdeSJanie Lu 
424df55fdeSJanie Lu static fab_epkt_tbl_t fab_epkt_tbl[] = {
434df55fdeSJanie Lu 	EPKT_DESC(MMU, XLAT, DATA, INV, RDWR),
444df55fdeSJanie Lu 	PCIE_AER_UCE_CA, 0, PCI_STAT_S_TARG_AB, 0,
454df55fdeSJanie Lu 	EPKT_DESC(MMU, XLAT, ADDR, UNMAP, RDWR),
464df55fdeSJanie Lu 	PCIE_AER_UCE_CA, 0, PCI_STAT_S_TARG_AB, 0,
474df55fdeSJanie Lu 	EPKT_DESC(MMU, XLAT, DATA, PROT, RDWR),
484df55fdeSJanie Lu 	PCIE_AER_UCE_CA, 0, PCI_STAT_S_TARG_AB, 0,
494df55fdeSJanie Lu 
504df55fdeSJanie Lu 	EPKT_DESC(INTR, MSI32, DATA, ILL, IRR),
514df55fdeSJanie Lu 	PCIE_AER_UCE_MTLP, PCI_STAT_S_SYSERR, 0, 0,
524df55fdeSJanie Lu 
534df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, RCA, WRITE),
5471b428beSCheng Sean Ye 	0, PCI_STAT_S_SYSERR, PCI_STAT_R_TARG_AB, 0,
554df55fdeSJanie Lu 
564df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, RUR, WRITE),
5771b428beSCheng Sean Ye 	0, PCI_STAT_S_SYSERR, PCI_STAT_R_MAST_AB, 0,
584df55fdeSJanie Lu 
594df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, INV, RDWR),
604df55fdeSJanie Lu 	PCIE_AER_UCE_MTLP, PCI_STAT_S_SYSERR, 0, 0,
614df55fdeSJanie Lu 
624df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, TO, READ),
634df55fdeSJanie Lu 	PCIE_AER_UCE_TO, PCI_STAT_S_SYSERR, 0, PCI_TARG_MA,
644df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, TO, WRITE),
654df55fdeSJanie Lu 	PCIE_AER_UCE_TO, PCI_STAT_S_SYSERR, 0, PCI_TARG_MA,
664df55fdeSJanie Lu 
674df55fdeSJanie Lu 	EPKT_DESC(PORT, PIO, IRR, UC, IRR),
684df55fdeSJanie Lu 	PCIE_AER_UCE_UC, PCI_STAT_S_SYSERR, 0, 0,
694df55fdeSJanie Lu 
704df55fdeSJanie Lu 	EPKT_DESC(PORT, LINK, FC, TO, IRR),
714df55fdeSJanie Lu 	PCIE_AER_UCE_FCP, PCI_STAT_S_SYSERR, 0, 0,
724df55fdeSJanie Lu 
734df55fdeSJanie Lu 	0, 0, 0, 0, 0
744df55fdeSJanie Lu };
754df55fdeSJanie Lu 
764df55fdeSJanie Lu /* ARGSUSED */
774df55fdeSJanie Lu void
fab_epkt_to_data(fmd_hdl_t * hdl,nvlist_t * nvl,fab_data_t * data)784df55fdeSJanie Lu fab_epkt_to_data(fmd_hdl_t *hdl, nvlist_t *nvl, fab_data_t *data)
794df55fdeSJanie Lu {
804df55fdeSJanie Lu 	data->nvl = nvl;
814df55fdeSJanie Lu 
824df55fdeSJanie Lu 	/* Always Root Complex */
834df55fdeSJanie Lu 	data->dev_type = PCIE_PCIECAP_DEV_TYPE_ROOT;
844df55fdeSJanie Lu 
854df55fdeSJanie Lu 	data->pcie_ue_sev = (PCIE_AER_UCE_DLP | PCIE_AER_UCE_SD |
864df55fdeSJanie Lu 	    PCIE_AER_UCE_FCP | PCIE_AER_UCE_RO | PCIE_AER_UCE_MTLP);
874df55fdeSJanie Lu }
884df55fdeSJanie Lu 
894df55fdeSJanie Lu static int
fab_xlate_epkt(fmd_hdl_t * hdl,fab_data_t * data,px_rc_err_t * epktp)904df55fdeSJanie Lu fab_xlate_epkt(fmd_hdl_t *hdl, fab_data_t *data, px_rc_err_t *epktp)
914df55fdeSJanie Lu {
924df55fdeSJanie Lu 	fab_epkt_tbl_t *entry;
934df55fdeSJanie Lu 	uint32_t temp;
944df55fdeSJanie Lu 
954df55fdeSJanie Lu 	for (entry = fab_epkt_tbl; entry->epkt_desc != 0; entry++) {
964df55fdeSJanie Lu 		temp = *(uint32_t *)&epktp->rc_descr >> 12;
974df55fdeSJanie Lu 		if (entry->epkt_desc == temp)
984df55fdeSJanie Lu 			goto send;
994df55fdeSJanie Lu 	}
1004df55fdeSJanie Lu 
1014df55fdeSJanie Lu 	return (0);
1024df55fdeSJanie Lu 
1034df55fdeSJanie Lu send:
1044df55fdeSJanie Lu 	fmd_hdl_debug(hdl, "Translate epkt DESC = %#x\n", temp);
1054df55fdeSJanie Lu 
1064df55fdeSJanie Lu 	/* Fill in PCI Status Register */
1074df55fdeSJanie Lu 	data->pci_err_status = entry->pci_err_sts;
1084df55fdeSJanie Lu 	data->pci_bdg_sec_stat = entry->pci_bdg_sts;
1094df55fdeSJanie Lu 
1104df55fdeSJanie Lu 	/* Fill in the device status register */
1114df55fdeSJanie Lu 	if (epktp->rc_descr.STOP)
1124df55fdeSJanie Lu 		data->pcie_err_status = PCIE_DEVSTS_FE_DETECTED;
1134df55fdeSJanie Lu 	else if (epktp->rc_descr.C)
1144df55fdeSJanie Lu 		data->pcie_err_status = PCIE_DEVSTS_CE_DETECTED;
1154df55fdeSJanie Lu 	else
1164df55fdeSJanie Lu 		data->pcie_err_status = PCIE_DEVSTS_NFE_DETECTED;
1174df55fdeSJanie Lu 
1184df55fdeSJanie Lu 	/* Fill in the AER UE register */
1194df55fdeSJanie Lu 	data->pcie_ue_status = entry->pcie_ue_sts;
1204df55fdeSJanie Lu 
1214df55fdeSJanie Lu 	/* Fill in the AER Control register */
1224df55fdeSJanie Lu 	temp = entry->pcie_ue_sts;
1234df55fdeSJanie Lu 	for (data->pcie_adv_ctl = (uint32_t)-1; temp; data->pcie_adv_ctl++)
1244df55fdeSJanie Lu 		temp = temp >> 1;
1254df55fdeSJanie Lu 
1264df55fdeSJanie Lu 	/* Send target ereports */
1274df55fdeSJanie Lu 	data->pcie_ue_no_tgt_erpt = B_TRUE;
1284df55fdeSJanie Lu 	if (entry->tgt_class && !epktp->rc_descr.STOP) {
1294df55fdeSJanie Lu 		if (epktp->rc_descr.D) {
1304df55fdeSJanie Lu 			data->pcie_ue_tgt_trans = PF_ADDR_DMA;
1314df55fdeSJanie Lu 			data->pcie_ue_tgt_addr = epktp->addr;
1324df55fdeSJanie Lu 		} else if (epktp->rc_descr.M) {
1334df55fdeSJanie Lu 			data->pcie_ue_tgt_trans = PF_ADDR_PIO;
1344df55fdeSJanie Lu 			data->pcie_ue_tgt_addr = epktp->addr;
1354df55fdeSJanie Lu 		}
1364df55fdeSJanie Lu 
1374df55fdeSJanie Lu 		if (data->pcie_ue_tgt_trans)
1384df55fdeSJanie Lu 			fab_send_tgt_erpt(hdl, data, entry->tgt_class,
1394df55fdeSJanie Lu 			    B_TRUE);
1404df55fdeSJanie Lu 	}
1414df55fdeSJanie Lu 	return (1);
1424df55fdeSJanie Lu }
1434df55fdeSJanie Lu 
1444df55fdeSJanie Lu void
fab_xlate_epkt_erpts(fmd_hdl_t * hdl,nvlist_t * nvl,const char * class)1454df55fdeSJanie Lu fab_xlate_epkt_erpts(fmd_hdl_t *hdl, nvlist_t *nvl, const char *class)
1464df55fdeSJanie Lu {
1474df55fdeSJanie Lu 	fab_data_t data = {0};
1484df55fdeSJanie Lu 	px_rc_err_t epkt = {0};
1494df55fdeSJanie Lu 	pcie_tlp_hdr_t *tlp_hdr;
1504df55fdeSJanie Lu 	void *ptr;
1514df55fdeSJanie Lu 	uint8_t ver;
1524df55fdeSJanie Lu 	int err;
153*4f764f91SCheng Sean Ye 	char *devpath, *rppath = NULL;
1544df55fdeSJanie Lu 	nvlist_t *detector;
1554df55fdeSJanie Lu 
1564df55fdeSJanie Lu 	fmd_hdl_debug(hdl, "epkt ereport received: %s\n", class);
1574df55fdeSJanie Lu 	fab_epkt_to_data(hdl, nvl, &data);
1584df55fdeSJanie Lu 
1594df55fdeSJanie Lu 	err = nvlist_lookup_uint8(nvl, "epkt_ver", &ver);
1604df55fdeSJanie Lu 	err |= nvlist_lookup_uint32(nvl, "desc", (uint32_t *)&epkt.rc_descr);
1614df55fdeSJanie Lu 	err |= nvlist_lookup_uint32(nvl, "size", &epkt.size);
1624df55fdeSJanie Lu 	err |= nvlist_lookup_uint64(nvl, "addr", &epkt.addr);
1634df55fdeSJanie Lu 	err |= nvlist_lookup_uint64(nvl, "hdr1", &epkt.hdr[0]);
1644df55fdeSJanie Lu 	err |= nvlist_lookup_uint64(nvl, "hdr2", &epkt.hdr[1]);
1654df55fdeSJanie Lu 	err |= nvlist_lookup_uint64(nvl, "reserved", &epkt.reserved);
1664df55fdeSJanie Lu 
1674df55fdeSJanie Lu 	if (err != 0) {
1684df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "Failed to retrieve all epkt payloads");
1694df55fdeSJanie Lu 		return;
1704df55fdeSJanie Lu 	}
1714df55fdeSJanie Lu 
1724df55fdeSJanie Lu 	fmd_hdl_debug(hdl, "epkt flags: %c%c%c%c%c%c%c%c%c %s",
1734df55fdeSJanie Lu 	    epkt.rc_descr.S ? 'S' : '-', epkt.rc_descr.M ? 'M' : '-',
1744df55fdeSJanie Lu 	    epkt.rc_descr.S ? 'Q' : '-', epkt.rc_descr.D ? 'D' : '-',
1754df55fdeSJanie Lu 	    epkt.rc_descr.R ? 'R' : '-', epkt.rc_descr.H ? 'H' : '-',
1764df55fdeSJanie Lu 	    epkt.rc_descr.C ? 'C' : '-', epkt.rc_descr.I ? 'I' : '-',
1774df55fdeSJanie Lu 	    epkt.rc_descr.B ? 'B' : '-', epkt.rc_descr.STOP ? "STOP" : "");
1784df55fdeSJanie Lu 
1794df55fdeSJanie Lu 	/*
1804df55fdeSJanie Lu 	 * If the least byte of the 'reserved' is non zero, it is device
1814df55fdeSJanie Lu 	 * and function of the port
1824df55fdeSJanie Lu 	 */
1834df55fdeSJanie Lu 	if (epkt.reserved && 0xff)
1844df55fdeSJanie Lu 		rppath = fab_find_rppath_by_df(hdl, nvl, epkt.reserved & 0xff);
1854df55fdeSJanie Lu 
1864df55fdeSJanie Lu 	if (epkt.rc_descr.H) {
1874df55fdeSJanie Lu 		data.pcie_ue_hdr[0] = (uint32_t)(epkt.hdr[0] >> 32);
1884df55fdeSJanie Lu 		data.pcie_ue_hdr[1] = (uint32_t)epkt.hdr[0];
1894df55fdeSJanie Lu 		data.pcie_ue_hdr[2] = (uint32_t)(epkt.hdr[1] >> 32);
1904df55fdeSJanie Lu 		data.pcie_ue_hdr[3] = (uint32_t)(epkt.hdr[1]);
1914df55fdeSJanie Lu 
1924df55fdeSJanie Lu 		tlp_hdr = (pcie_tlp_hdr_t *)&data.pcie_ue_hdr[0];
1934df55fdeSJanie Lu 		ptr = &data.pcie_ue_hdr[1];
1944df55fdeSJanie Lu 		switch (tlp_hdr->type) {
1954df55fdeSJanie Lu 		case PCIE_TLP_TYPE_IO:
1964df55fdeSJanie Lu 		case PCIE_TLP_TYPE_MEM:
1974df55fdeSJanie Lu 		case PCIE_TLP_TYPE_MEMLK:
1984df55fdeSJanie Lu 		{
1994df55fdeSJanie Lu 			pcie_mem64_t *pmp = ptr;
2004df55fdeSJanie Lu 			data.pcie_ue_tgt_trans = PF_ADDR_PIO;
2014df55fdeSJanie Lu 			data.pcie_ue_tgt_bdf = pmp->rid;
2024df55fdeSJanie Lu 			if (tlp_hdr->fmt & 0x1)
2034df55fdeSJanie Lu 				data.pcie_ue_tgt_addr =
2044df55fdeSJanie Lu 				    ((uint64_t)pmp->addr1 << 32) | pmp->addr0;
2054df55fdeSJanie Lu 			else
2064df55fdeSJanie Lu 				data.pcie_ue_tgt_addr =
2074df55fdeSJanie Lu 				    ((pcie_memio32_t *)ptr)->addr0;
2084df55fdeSJanie Lu 
2094df55fdeSJanie Lu 			break;
2104df55fdeSJanie Lu 		}
2114df55fdeSJanie Lu 
2124df55fdeSJanie Lu 		case PCIE_TLP_TYPE_CFG0:
2134df55fdeSJanie Lu 		case PCIE_TLP_TYPE_CFG1:
2144df55fdeSJanie Lu 		{
2154df55fdeSJanie Lu 			pcie_cfg_t *pcp = ptr;
2164df55fdeSJanie Lu 
2174df55fdeSJanie Lu 			data.pcie_ue_tgt_trans = PF_ADDR_CFG;
2184df55fdeSJanie Lu 			data.pcie_ue_tgt_bdf =
2194df55fdeSJanie Lu 			    (pcp->bus << 8) | (pcp->dev << 3) | pcp->func;
2204df55fdeSJanie Lu 			break;
2214df55fdeSJanie Lu 		}
2224df55fdeSJanie Lu 
2234df55fdeSJanie Lu 		case PCIE_TLP_TYPE_CPL:
2244df55fdeSJanie Lu 		case PCIE_TLP_TYPE_CPLLK:
2254df55fdeSJanie Lu 			data.pcie_ue_tgt_bdf = ((pcie_cpl_t *)ptr)->rid;
2264df55fdeSJanie Lu 			break;
2274df55fdeSJanie Lu 		}
2284df55fdeSJanie Lu 
2294df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "HEADER 0 0x%x", data.pcie_ue_hdr[0]);
2304df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "HEADER 1 0x%x", data.pcie_ue_hdr[1]);
2314df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "HEADER 2 0x%x", data.pcie_ue_hdr[2]);
2324df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "HEADER 3 0x%x", data.pcie_ue_hdr[3]);
2334df55fdeSJanie Lu 		fmd_hdl_debug(hdl, "In header bdf = %#hx addr = %#llx",
2344df55fdeSJanie Lu 		    data.pcie_ue_tgt_bdf,
2354df55fdeSJanie Lu 		    (uint64_t)data.pcie_ue_tgt_addr);
2364df55fdeSJanie Lu 
2374df55fdeSJanie Lu 		/* find the root port to which this error is related */
238*4f764f91SCheng Sean Ye 		if (rppath == NULL && data.pcie_ue_tgt_bdf)
2394df55fdeSJanie Lu 			rppath = fab_find_rppath_by_devbdf(hdl, nvl,
2404df55fdeSJanie Lu 			    data.pcie_ue_tgt_bdf);
2414df55fdeSJanie Lu 	}
2424df55fdeSJanie Lu 
243*4f764f91SCheng Sean Ye 	/* find the root port by address */
244*4f764f91SCheng Sean Ye 	if (rppath == NULL && epkt.rc_descr.M != 0) {
245*4f764f91SCheng Sean Ye 		devpath = fab_find_addr(hdl, nvl, epkt.addr);
246*4f764f91SCheng Sean Ye 		if (devpath) {
247*4f764f91SCheng Sean Ye 			rppath = fab_find_rppath_by_devpath(hdl, devpath);
248*4f764f91SCheng Sean Ye 			fmd_hdl_strfree(hdl, devpath);
249*4f764f91SCheng Sean Ye 		}
250*4f764f91SCheng Sean Ye 	}
251*4f764f91SCheng Sean Ye 
2524df55fdeSJanie Lu 	/*
2534df55fdeSJanie Lu 	 * reset the detector in the original ereport to the root port
2544df55fdeSJanie Lu 	 */
255036ec191SCheng Sean Ye 	if (rppath) {
256036ec191SCheng Sean Ye 		if (nvlist_alloc(&detector, NV_UNIQUE_NAME, 0) != 0) {
257036ec191SCheng Sean Ye 			fmd_hdl_error(hdl, "failed to allocate nvlist");
258036ec191SCheng Sean Ye 			fmd_hdl_strfree(hdl, rppath);
259036ec191SCheng Sean Ye 			return;
260036ec191SCheng Sean Ye 		}
2614df55fdeSJanie Lu 		(void) nvlist_add_string(detector, FM_VERSION,
2624df55fdeSJanie Lu 		    FM_DEV_SCHEME_VERSION);
2634df55fdeSJanie Lu 		(void) nvlist_add_string(detector, FM_FMRI_SCHEME,
2644df55fdeSJanie Lu 		    FM_FMRI_SCHEME_DEV);
2654df55fdeSJanie Lu 		(void) nvlist_add_string(detector, FM_FMRI_DEV_PATH, rppath);
2664df55fdeSJanie Lu 		(void) nvlist_remove_all(nvl, FM_EREPORT_DETECTOR);
2674df55fdeSJanie Lu 		(void) nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector);
2684df55fdeSJanie Lu 		nvlist_free(detector);
269036ec191SCheng Sean Ye 		fmd_hdl_strfree(hdl, rppath);
270036ec191SCheng Sean Ye 	} else {
271036ec191SCheng Sean Ye 		/*
272036ec191SCheng Sean Ye 		 * We can not locate the root port the error originated from.
273036ec191SCheng Sean Ye 		 * Likely this is because the original ereport is malformed or
274036ec191SCheng Sean Ye 		 * the hw error register has corrupted contents.  In this case,
275036ec191SCheng Sean Ye 		 * the best we can do is send ereports on all root ports.
276036ec191SCheng Sean Ye 		 *
277036ec191SCheng Sean Ye 		 * Set pcie_rp_send_all for fab_send_erpt() to process later.
278036ec191SCheng Sean Ye 		 */
279036ec191SCheng Sean Ye 		fmd_hdl_debug(hdl, "RP not fond. Will translate on all RPs.\n");
280036ec191SCheng Sean Ye 		data.pcie_rp_send_all = B_TRUE;
2814df55fdeSJanie Lu 	}
2824df55fdeSJanie Lu 
2834df55fdeSJanie Lu 	(void) fab_xlate_epkt(hdl, &data, &epkt);
2844df55fdeSJanie Lu 	fab_xlate_pcie_erpts(hdl, &data);
2854df55fdeSJanie Lu }
286