xref: /titanic_50/usr/src/uts/common/io/pciex/pciev.c (revision fc256490629fe68815f7e0f23cf9b3545720cfac)
1*fc256490SJason Beloro /*
2*fc256490SJason Beloro  * CDDL HEADER START
3*fc256490SJason Beloro  *
4*fc256490SJason Beloro  * The contents of this file are subject to the terms of the
5*fc256490SJason Beloro  * Common Development and Distribution License (the "License").
6*fc256490SJason Beloro  * You may not use this file except in compliance with the License.
7*fc256490SJason Beloro  *
8*fc256490SJason Beloro  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*fc256490SJason Beloro  * or http://www.opensolaris.org/os/licensing.
10*fc256490SJason Beloro  * See the License for the specific language governing permissions
11*fc256490SJason Beloro  * and limitations under the License.
12*fc256490SJason Beloro  *
13*fc256490SJason Beloro  * When distributing Covered Code, include this CDDL HEADER in each
14*fc256490SJason Beloro  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*fc256490SJason Beloro  * If applicable, add the following below this CDDL HEADER, with the
16*fc256490SJason Beloro  * fields enclosed by brackets "[]" replaced with your own identifying
17*fc256490SJason Beloro  * information: Portions Copyright [yyyy] [name of copyright owner]
18*fc256490SJason Beloro  *
19*fc256490SJason Beloro  * CDDL HEADER END
20*fc256490SJason Beloro  */
21*fc256490SJason Beloro /*
22*fc256490SJason Beloro  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23*fc256490SJason Beloro  * Use is subject to license terms.
24*fc256490SJason Beloro  */
25*fc256490SJason Beloro 
26*fc256490SJason Beloro #include <sys/types.h>
27*fc256490SJason Beloro #include <sys/ddi.h>
28*fc256490SJason Beloro #include <sys/dditypes.h>
29*fc256490SJason Beloro #include <sys/ddifm.h>
30*fc256490SJason Beloro #include <sys/sunndi.h>
31*fc256490SJason Beloro #include <sys/devops.h>
32*fc256490SJason Beloro #include <sys/pcie.h>
33*fc256490SJason Beloro #include <sys/pci_cap.h>
34*fc256490SJason Beloro #include <sys/pcie_impl.h>
35*fc256490SJason Beloro #include <sys/pathname.h>
36*fc256490SJason Beloro 
37*fc256490SJason Beloro /*
38*fc256490SJason Beloro  * The below 2 global variables are for PCIe IOV Error Handling.  They must only
39*fc256490SJason Beloro  * be accessed during error handling under the protection of a error mutex.
40*fc256490SJason Beloro  */
41*fc256490SJason Beloro static pcie_domains_t *pcie_faulty_domains = NULL;
42*fc256490SJason Beloro static boolean_t pcie_faulty_all = B_FALSE;
43*fc256490SJason Beloro 
44*fc256490SJason Beloro static void pcie_domain_list_destroy(pcie_domains_t *domain_ids);
45*fc256490SJason Beloro static void pcie_bdf_list_add(pcie_req_id_t bdf,
46*fc256490SJason Beloro     pcie_req_id_list_t **rlist_p);
47*fc256490SJason Beloro static void pcie_bdf_list_remove(pcie_req_id_t bdf,
48*fc256490SJason Beloro     pcie_req_id_list_t **rlist_p);
49*fc256490SJason Beloro static void pcie_cache_domain_info(pcie_bus_t *bus_p);
50*fc256490SJason Beloro static void pcie_uncache_domain_info(pcie_bus_t *bus_p);
51*fc256490SJason Beloro 
52*fc256490SJason Beloro static void pcie_faulty_list_clear();
53*fc256490SJason Beloro static void pcie_faulty_list_update(pcie_domains_t *pd,
54*fc256490SJason Beloro     pcie_domains_t **headp);
55*fc256490SJason Beloro 
56*fc256490SJason Beloro dev_info_t *
pcie_find_dip_by_bdf(dev_info_t * rootp,pcie_req_id_t bdf)57*fc256490SJason Beloro pcie_find_dip_by_bdf(dev_info_t *rootp, pcie_req_id_t bdf)
58*fc256490SJason Beloro {
59*fc256490SJason Beloro 	dev_info_t *dip;
60*fc256490SJason Beloro 	pcie_bus_t *bus_p;
61*fc256490SJason Beloro 	int bus_num;
62*fc256490SJason Beloro 
63*fc256490SJason Beloro 	dip = ddi_get_child(rootp);
64*fc256490SJason Beloro 	while (dip) {
65*fc256490SJason Beloro 		bus_p = PCIE_DIP2BUS(dip);
66*fc256490SJason Beloro 		if (bus_p && (bus_p->bus_bdf == bdf))
67*fc256490SJason Beloro 			return (dip);
68*fc256490SJason Beloro 		if (bus_p) {
69*fc256490SJason Beloro 			bus_num = (bdf >> 8) & 0xff;
70*fc256490SJason Beloro 			if ((bus_num >= bus_p->bus_bus_range.lo &&
71*fc256490SJason Beloro 			    bus_num <= bus_p->bus_bus_range.hi) ||
72*fc256490SJason Beloro 			    bus_p->bus_bus_range.hi == 0)
73*fc256490SJason Beloro 				return (pcie_find_dip_by_bdf(dip, bdf));
74*fc256490SJason Beloro 		}
75*fc256490SJason Beloro 		dip = ddi_get_next_sibling(dip);
76*fc256490SJason Beloro 	}
77*fc256490SJason Beloro 	return (NULL);
78*fc256490SJason Beloro }
79*fc256490SJason Beloro 
80*fc256490SJason Beloro /*
81*fc256490SJason Beloro  * Add a device bdf to the bdf list.
82*fc256490SJason Beloro  */
83*fc256490SJason Beloro static void
pcie_bdf_list_add(pcie_req_id_t bdf,pcie_req_id_list_t ** rlist_p)84*fc256490SJason Beloro pcie_bdf_list_add(pcie_req_id_t bdf, pcie_req_id_list_t **rlist_p)
85*fc256490SJason Beloro {
86*fc256490SJason Beloro 	pcie_req_id_list_t *rl = PCIE_ZALLOC(pcie_req_id_list_t);
87*fc256490SJason Beloro 
88*fc256490SJason Beloro 	rl->bdf = bdf;
89*fc256490SJason Beloro 	rl->next = *rlist_p;
90*fc256490SJason Beloro 	*rlist_p = rl;
91*fc256490SJason Beloro }
92*fc256490SJason Beloro 
93*fc256490SJason Beloro /*
94*fc256490SJason Beloro  * Remove a bdf from the bdf list.
95*fc256490SJason Beloro  */
96*fc256490SJason Beloro static void
pcie_bdf_list_remove(pcie_req_id_t bdf,pcie_req_id_list_t ** rlist_p)97*fc256490SJason Beloro pcie_bdf_list_remove(pcie_req_id_t bdf, pcie_req_id_list_t **rlist_p)
98*fc256490SJason Beloro {
99*fc256490SJason Beloro 	pcie_req_id_list_t *rl_pre, *rl_next;
100*fc256490SJason Beloro 
101*fc256490SJason Beloro 	rl_pre = *rlist_p;
102*fc256490SJason Beloro 	if (rl_pre->bdf == bdf) {
103*fc256490SJason Beloro 		*rlist_p = rl_pre->next;
104*fc256490SJason Beloro 		kmem_free(rl_pre, sizeof (pcie_req_id_list_t));
105*fc256490SJason Beloro 		return;
106*fc256490SJason Beloro 	}
107*fc256490SJason Beloro 
108*fc256490SJason Beloro 	while (rl_pre->next) {
109*fc256490SJason Beloro 		rl_next = rl_pre->next;
110*fc256490SJason Beloro 		if (rl_next->bdf == bdf) {
111*fc256490SJason Beloro 			rl_pre->next = rl_next->next;
112*fc256490SJason Beloro 			kmem_free(rl_next, sizeof (pcie_req_id_list_t));
113*fc256490SJason Beloro 			break;
114*fc256490SJason Beloro 		} else
115*fc256490SJason Beloro 			rl_pre = rl_next;
116*fc256490SJason Beloro 	}
117*fc256490SJason Beloro }
118*fc256490SJason Beloro 
119*fc256490SJason Beloro /*
120*fc256490SJason Beloro  * Cache IOV domain info in all it's parent's pcie_domain_t
121*fc256490SJason Beloro  *
122*fc256490SJason Beloro  * The leaf devices's domain info must be set before calling this function.
123*fc256490SJason Beloro  */
124*fc256490SJason Beloro void
pcie_cache_domain_info(pcie_bus_t * bus_p)125*fc256490SJason Beloro pcie_cache_domain_info(pcie_bus_t *bus_p)
126*fc256490SJason Beloro {
127*fc256490SJason Beloro 	boolean_t 	assigned = PCIE_IS_ASSIGNED(bus_p);
128*fc256490SJason Beloro 	boolean_t 	fma_dom = PCIE_ASSIGNED_TO_FMA_DOM(bus_p);
129*fc256490SJason Beloro 	uint_t		domain_id = PCIE_DOMAIN_ID_GET(bus_p);
130*fc256490SJason Beloro 	pcie_req_id_t	bdf = bus_p->bus_bdf;
131*fc256490SJason Beloro 	dev_info_t	*pdip;
132*fc256490SJason Beloro 	pcie_bus_t	*pbus_p;
133*fc256490SJason Beloro 	pcie_domain_t	*pdom_p;
134*fc256490SJason Beloro 
135*fc256490SJason Beloro 	ASSERT(!PCIE_IS_BDG(bus_p));
136*fc256490SJason Beloro 
137*fc256490SJason Beloro 	for (pdip = ddi_get_parent(PCIE_BUS2DIP(bus_p)); PCIE_DIP2BUS(pdip);
138*fc256490SJason Beloro 	    pdip = ddi_get_parent(pdip)) {
139*fc256490SJason Beloro 		pbus_p = PCIE_DIP2BUS(pdip);
140*fc256490SJason Beloro 		pdom_p = PCIE_BUS2DOM(pbus_p);
141*fc256490SJason Beloro 
142*fc256490SJason Beloro 		if (assigned) {
143*fc256490SJason Beloro 			if (domain_id)
144*fc256490SJason Beloro 				PCIE_DOMAIN_LIST_ADD(pbus_p, domain_id);
145*fc256490SJason Beloro 
146*fc256490SJason Beloro 			if (fma_dom)
147*fc256490SJason Beloro 				pdom_p->fmadom_count++;
148*fc256490SJason Beloro 			else {
149*fc256490SJason Beloro 				PCIE_BDF_LIST_ADD(pbus_p, bdf);
150*fc256490SJason Beloro 				pdom_p->nfmadom_count++;
151*fc256490SJason Beloro 			}
152*fc256490SJason Beloro 		} else
153*fc256490SJason Beloro 			pdom_p->rootdom_count++;
154*fc256490SJason Beloro 	}
155*fc256490SJason Beloro }
156*fc256490SJason Beloro 
157*fc256490SJason Beloro /*
158*fc256490SJason Beloro  * Clear the leaf device's domain info and uncache IOV domain info in all it's
159*fc256490SJason Beloro  * parent's pcie_domain_t
160*fc256490SJason Beloro  *
161*fc256490SJason Beloro  * The leaf devices's domain info is also cleared by calling this function.
162*fc256490SJason Beloro  */
163*fc256490SJason Beloro void
pcie_uncache_domain_info(pcie_bus_t * bus_p)164*fc256490SJason Beloro pcie_uncache_domain_info(pcie_bus_t *bus_p)
165*fc256490SJason Beloro {
166*fc256490SJason Beloro 	boolean_t 	assigned = PCIE_IS_ASSIGNED(bus_p);
167*fc256490SJason Beloro 	boolean_t 	fma_dom = PCIE_ASSIGNED_TO_FMA_DOM(bus_p);
168*fc256490SJason Beloro 	uint_t		domain_id = PCIE_DOMAIN_ID_GET(bus_p);
169*fc256490SJason Beloro 	pcie_domain_t	*dom_p = PCIE_BUS2DOM(bus_p), *pdom_p;
170*fc256490SJason Beloro 	pcie_bus_t	*pbus_p;
171*fc256490SJason Beloro 	dev_info_t	*pdip;
172*fc256490SJason Beloro 
173*fc256490SJason Beloro 	ASSERT(!PCIE_IS_BDG(bus_p));
174*fc256490SJason Beloro 	ASSERT((dom_p->fmadom_count + dom_p->nfmadom_count +
175*fc256490SJason Beloro 	    dom_p->rootdom_count) == 1);
176*fc256490SJason Beloro 
177*fc256490SJason Beloro 	/* Clear the domain information */
178*fc256490SJason Beloro 	if (domain_id) {
179*fc256490SJason Beloro 		PCIE_DOMAIN_ID_SET(bus_p, NULL);
180*fc256490SJason Beloro 		PCIE_DOMAIN_ID_DECR_REF_COUNT(bus_p);
181*fc256490SJason Beloro 	}
182*fc256490SJason Beloro 
183*fc256490SJason Beloro 	dom_p->fmadom_count = 0;
184*fc256490SJason Beloro 	dom_p->nfmadom_count = 0;
185*fc256490SJason Beloro 	dom_p->rootdom_count = 0;
186*fc256490SJason Beloro 
187*fc256490SJason Beloro 	for (pdip = ddi_get_parent(PCIE_BUS2DIP(bus_p)); PCIE_DIP2BUS(pdip);
188*fc256490SJason Beloro 	    pdip = ddi_get_parent(pdip)) {
189*fc256490SJason Beloro 		pbus_p = PCIE_DIP2BUS(pdip);
190*fc256490SJason Beloro 		pdom_p = PCIE_BUS2DOM(pbus_p);
191*fc256490SJason Beloro 
192*fc256490SJason Beloro 		if (assigned) {
193*fc256490SJason Beloro 			if (domain_id)
194*fc256490SJason Beloro 				PCIE_DOMAIN_LIST_REMOVE(pbus_p, domain_id);
195*fc256490SJason Beloro 
196*fc256490SJason Beloro 			if (fma_dom)
197*fc256490SJason Beloro 				pdom_p->fmadom_count--;
198*fc256490SJason Beloro 			else {
199*fc256490SJason Beloro 				pdom_p->nfmadom_count--;
200*fc256490SJason Beloro 				PCIE_BDF_LIST_REMOVE(pbus_p, bus_p->bus_bdf);
201*fc256490SJason Beloro 			}
202*fc256490SJason Beloro 		} else
203*fc256490SJason Beloro 			pdom_p->rootdom_count--;
204*fc256490SJason Beloro 	}
205*fc256490SJason Beloro }
206*fc256490SJason Beloro 
207*fc256490SJason Beloro 
208*fc256490SJason Beloro /*
209*fc256490SJason Beloro  * Initialize private data structure for IOV environments.
210*fc256490SJason Beloro  * o Allocate memory for iov data
211*fc256490SJason Beloro  * o Cache Domain ids.
212*fc256490SJason Beloro  */
213*fc256490SJason Beloro void
pcie_init_dom(dev_info_t * dip)214*fc256490SJason Beloro pcie_init_dom(dev_info_t *dip)
215*fc256490SJason Beloro {
216*fc256490SJason Beloro 	pcie_domain_t	*dom_p = PCIE_ZALLOC(pcie_domain_t);
217*fc256490SJason Beloro 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
218*fc256490SJason Beloro 
219*fc256490SJason Beloro 	PCIE_BUS2DOM(bus_p) = dom_p;
220*fc256490SJason Beloro 
221*fc256490SJason Beloro 	/* Only leaf devices are assignable to IO Domains */
222*fc256490SJason Beloro 	if (PCIE_IS_BDG(bus_p))
223*fc256490SJason Beloro 		return;
224*fc256490SJason Beloro 
225*fc256490SJason Beloro 	/*
226*fc256490SJason Beloro 	 * At the time of init_dom in the root domain a device may or may not
227*fc256490SJason Beloro 	 * have been assigned to an IO Domain.
228*fc256490SJason Beloro 	 *
229*fc256490SJason Beloro 	 * LDOMS: the property "ddi-assigned" will be set for devices that is
230*fc256490SJason Beloro 	 * assignable to an IO domain and unusable in the root domain.  If the
231*fc256490SJason Beloro 	 * property exist assume it has been assigned to a non-fma domain until
232*fc256490SJason Beloro 	 * otherwise notified.  The domain id is unknown on LDOMS.
233*fc256490SJason Beloro 	 *
234*fc256490SJason Beloro 	 * Xen: the "ddi-assigned" property won't be set until Xen store calls
235*fc256490SJason Beloro 	 * pcie_loan_device is called.  In this function this will always look
236*fc256490SJason Beloro 	 * like the device is assigned to the root domain.  Domain ID caching
237*fc256490SJason Beloro 	 * will occur in pcie_loan_device function.
238*fc256490SJason Beloro 	 */
239*fc256490SJason Beloro 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
240*fc256490SJason Beloro 	    "ddi-assigned", -1) != -1) {
241*fc256490SJason Beloro 		dom_p->nfmadom_count = 1;
242*fc256490SJason Beloro 
243*fc256490SJason Beloro 		/* Prevent "assigned" device from detaching */
244*fc256490SJason Beloro 		ndi_hold_devi(dip);
245*fc256490SJason Beloro 	} else
246*fc256490SJason Beloro 		dom_p->rootdom_count = 1;
247*fc256490SJason Beloro 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "ddi-assigned");
248*fc256490SJason Beloro 
249*fc256490SJason Beloro 	pcie_cache_domain_info(bus_p);
250*fc256490SJason Beloro }
251*fc256490SJason Beloro 
252*fc256490SJason Beloro void
pcie_fini_dom(dev_info_t * dip)253*fc256490SJason Beloro pcie_fini_dom(dev_info_t *dip)
254*fc256490SJason Beloro {
255*fc256490SJason Beloro 	pcie_domain_t	*dom_p = PCIE_DIP2DOM(dip);
256*fc256490SJason Beloro 	pcie_bus_t	*bus_p = PCIE_DIP2BUS(dip);
257*fc256490SJason Beloro 
258*fc256490SJason Beloro 	if (PCIE_IS_BDG(bus_p))
259*fc256490SJason Beloro 		pcie_domain_list_destroy(PCIE_DOMAIN_LIST_GET(bus_p));
260*fc256490SJason Beloro 	else
261*fc256490SJason Beloro 		pcie_uncache_domain_info(bus_p);
262*fc256490SJason Beloro 
263*fc256490SJason Beloro 	kmem_free(dom_p, sizeof (pcie_domain_t));
264*fc256490SJason Beloro }
265*fc256490SJason Beloro 
266*fc256490SJason Beloro /*
267*fc256490SJason Beloro  * PCIe Severity:
268*fc256490SJason Beloro  *
269*fc256490SJason Beloro  * PF_ERR_NO_ERROR	: no IOV Action
270*fc256490SJason Beloro  * PF_ERR_CE		: no IOV Action
271*fc256490SJason Beloro  * PF_ERR_NO_PANIC	: contains error telemetry, log domain info
272*fc256490SJason Beloro  * PF_ERR_MATCHED_DEVICE: contains error telemetry, log domain info
273*fc256490SJason Beloro  * PF_ERR_MATCHED_RC	: Error already taken care of, no further IOV Action
274*fc256490SJason Beloro  * PF_ERR_MATCHED_PARENT: Error already taken care of, no further IOV Action
275*fc256490SJason Beloro  * PF_ERR_PANIC		: contains error telemetry, log domain info
276*fc256490SJason Beloro  *
277*fc256490SJason Beloro  * For NO_PANIC, MATCHED_DEVICE and PANIC, IOV wants to look at the affected
278*fc256490SJason Beloro  * devices and find the domains involved.
279*fc256490SJason Beloro  *
280*fc256490SJason Beloro  * If root domain does not own an affected device, IOV EH should change
281*fc256490SJason Beloro  * PF_ERR_PANIC to PF_ERR_MATCH_DOM.
282*fc256490SJason Beloro  */
283*fc256490SJason Beloro int
pciev_eh(pf_data_t * pfd_p,pf_impl_t * impl)284*fc256490SJason Beloro pciev_eh(pf_data_t *pfd_p, pf_impl_t *impl)
285*fc256490SJason Beloro {
286*fc256490SJason Beloro 	int severity = pfd_p->pe_severity_flags;
287*fc256490SJason Beloro 	int iov_severity = severity;
288*fc256490SJason Beloro 	pcie_bus_t *a_bus_p;	/* Affected device's pcie_bus_t */
289*fc256490SJason Beloro 	pf_data_t *root_pfd_p = impl->pf_dq_head_p;
290*fc256490SJason Beloro 	pcie_bus_t *root_bus_p;
291*fc256490SJason Beloro 
292*fc256490SJason Beloro 	/*
293*fc256490SJason Beloro 	 * check if all devices under the root device are unassigned.
294*fc256490SJason Beloro 	 * this function should quickly return in non-IOV environment.
295*fc256490SJason Beloro 	 */
296*fc256490SJason Beloro 	ASSERT(root_pfd_p != NULL);
297*fc256490SJason Beloro 	root_bus_p = PCIE_PFD2BUS(root_pfd_p);
298*fc256490SJason Beloro 	if (PCIE_BDG_IS_UNASSIGNED(root_bus_p))
299*fc256490SJason Beloro 		return (severity);
300*fc256490SJason Beloro 
301*fc256490SJason Beloro 	if (severity & PF_ERR_PANIC_DEADLOCK) {
302*fc256490SJason Beloro 		pcie_faulty_all = B_TRUE;
303*fc256490SJason Beloro 
304*fc256490SJason Beloro 	} else if (severity & (PF_ERR_NO_PANIC | PF_ERR_MATCHED_DEVICE |
305*fc256490SJason Beloro 	    PF_ERR_PANIC | PF_ERR_PANIC_BAD_RESPONSE)) {
306*fc256490SJason Beloro 
307*fc256490SJason Beloro 		uint16_t affected_flag, dev_affected_flags;
308*fc256490SJason Beloro 		uint_t is_panic = 0, is_aff_dev_found = 0;
309*fc256490SJason Beloro 
310*fc256490SJason Beloro 		dev_affected_flags = PFD_AFFECTED_DEV(pfd_p)->pe_affected_flags;
311*fc256490SJason Beloro 		/* adjust affected flags to leverage cached domain ids */
312*fc256490SJason Beloro 		if (dev_affected_flags & PF_AFFECTED_CHILDREN) {
313*fc256490SJason Beloro 			dev_affected_flags |= PF_AFFECTED_SELF;
314*fc256490SJason Beloro 			dev_affected_flags &= ~PF_AFFECTED_CHILDREN;
315*fc256490SJason Beloro 		}
316*fc256490SJason Beloro 
317*fc256490SJason Beloro 		for (affected_flag = 1;
318*fc256490SJason Beloro 		    affected_flag <= PF_MAX_AFFECTED_FLAG;
319*fc256490SJason Beloro 		    affected_flag <<= 1) {
320*fc256490SJason Beloro 			a_bus_p = pciev_get_affected_dev(impl, pfd_p,
321*fc256490SJason Beloro 			    affected_flag, dev_affected_flags);
322*fc256490SJason Beloro 
323*fc256490SJason Beloro 			if (a_bus_p == NULL)
324*fc256490SJason Beloro 				continue;
325*fc256490SJason Beloro 
326*fc256490SJason Beloro 			is_aff_dev_found++;
327*fc256490SJason Beloro 			PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf =
328*fc256490SJason Beloro 			    a_bus_p->bus_bdf;
329*fc256490SJason Beloro 
330*fc256490SJason Beloro 			/*
331*fc256490SJason Beloro 			 * If a leaf device is assigned to the root domain or if
332*fc256490SJason Beloro 			 * a bridge has children assigned to a root domain
333*fc256490SJason Beloro 			 * panic.
334*fc256490SJason Beloro 			 *
335*fc256490SJason Beloro 			 * If a leaf device or a child of a bridge is assigned
336*fc256490SJason Beloro 			 * to NFMA domain mark it for panic.  If assigned to FMA
337*fc256490SJason Beloro 			 * domain save the domain id.
338*fc256490SJason Beloro 			 */
339*fc256490SJason Beloro 			if (!PCIE_IS_BDG(a_bus_p) &&
340*fc256490SJason Beloro 			    !PCIE_IS_ASSIGNED(a_bus_p)) {
341*fc256490SJason Beloro 				if (severity & PF_ERR_FATAL_FLAGS)
342*fc256490SJason Beloro 					is_panic++;
343*fc256490SJason Beloro 				continue;
344*fc256490SJason Beloro 			}
345*fc256490SJason Beloro 
346*fc256490SJason Beloro 			if (PCIE_BDG_HAS_CHILDREN_ROOT_DOM(a_bus_p)) {
347*fc256490SJason Beloro 				if (severity & PF_ERR_FATAL_FLAGS)
348*fc256490SJason Beloro 					is_panic++;
349*fc256490SJason Beloro 			}
350*fc256490SJason Beloro 
351*fc256490SJason Beloro 			if ((PCIE_ASSIGNED_TO_NFMA_DOM(a_bus_p) ||
352*fc256490SJason Beloro 			    PCIE_BDG_HAS_CHILDREN_NFMA_DOM(a_bus_p)) &&
353*fc256490SJason Beloro 			    (severity & PF_ERR_FATAL_FLAGS)) {
354*fc256490SJason Beloro 				PCIE_BUS2DOM(a_bus_p)->nfma_panic = B_TRUE;
355*fc256490SJason Beloro 				iov_severity |= PF_ERR_MATCH_DOM;
356*fc256490SJason Beloro 			}
357*fc256490SJason Beloro 
358*fc256490SJason Beloro 			if (PCIE_ASSIGNED_TO_FMA_DOM(a_bus_p)) {
359*fc256490SJason Beloro 				pcie_save_domain_id(
360*fc256490SJason Beloro 				    &PCIE_BUS2DOM(a_bus_p)->domain.id);
361*fc256490SJason Beloro 				iov_severity |= PF_ERR_MATCH_DOM;
362*fc256490SJason Beloro 			}
363*fc256490SJason Beloro 
364*fc256490SJason Beloro 			if (PCIE_BDG_HAS_CHILDREN_FMA_DOM(a_bus_p)) {
365*fc256490SJason Beloro 				pcie_save_domain_id(
366*fc256490SJason Beloro 				    PCIE_DOMAIN_LIST_GET(a_bus_p));
367*fc256490SJason Beloro 				iov_severity |= PF_ERR_MATCH_DOM;
368*fc256490SJason Beloro 			}
369*fc256490SJason Beloro 		}
370*fc256490SJason Beloro 
371*fc256490SJason Beloro 		/*
372*fc256490SJason Beloro 		 * Overwrite the severity only if affected device can be
373*fc256490SJason Beloro 		 * identified and root domain does not need to panic.
374*fc256490SJason Beloro 		 */
375*fc256490SJason Beloro 		if ((!is_panic) && is_aff_dev_found) {
376*fc256490SJason Beloro 			iov_severity &= ~PF_ERR_FATAL_FLAGS;
377*fc256490SJason Beloro 		}
378*fc256490SJason Beloro 	}
379*fc256490SJason Beloro 
380*fc256490SJason Beloro 	return (iov_severity);
381*fc256490SJason Beloro }
382*fc256490SJason Beloro 
383*fc256490SJason Beloro /* ARGSUSED */
384*fc256490SJason Beloro void
pciev_eh_exit(pf_data_t * root_pfd_p,uint_t intr_type)385*fc256490SJason Beloro pciev_eh_exit(pf_data_t *root_pfd_p, uint_t intr_type)
386*fc256490SJason Beloro {
387*fc256490SJason Beloro 	pcie_bus_t *root_bus_p;
388*fc256490SJason Beloro 
389*fc256490SJason Beloro 	/*
390*fc256490SJason Beloro 	 * check if all devices under the root device are unassigned.
391*fc256490SJason Beloro 	 * this function should quickly return in non-IOV environment.
392*fc256490SJason Beloro 	 */
393*fc256490SJason Beloro 	root_bus_p = PCIE_PFD2BUS(root_pfd_p);
394*fc256490SJason Beloro 	if (PCIE_BDG_IS_UNASSIGNED(root_bus_p))
395*fc256490SJason Beloro 		return;
396*fc256490SJason Beloro 
397*fc256490SJason Beloro 	pcie_faulty_list_clear();
398*fc256490SJason Beloro }
399*fc256490SJason Beloro 
400*fc256490SJason Beloro pcie_bus_t *
pciev_get_affected_dev(pf_impl_t * impl,pf_data_t * pfd_p,uint16_t affected_flag,uint16_t dev_affected_flags)401*fc256490SJason Beloro pciev_get_affected_dev(pf_impl_t *impl, pf_data_t *pfd_p,
402*fc256490SJason Beloro     uint16_t affected_flag, uint16_t dev_affected_flags)
403*fc256490SJason Beloro {
404*fc256490SJason Beloro 	pcie_bus_t *bus_p = PCIE_PFD2BUS(pfd_p);
405*fc256490SJason Beloro 	uint16_t flag = affected_flag & dev_affected_flags;
406*fc256490SJason Beloro 	pcie_bus_t *temp_bus_p;
407*fc256490SJason Beloro 	pcie_req_id_t a_bdf;
408*fc256490SJason Beloro 	uint64_t a_addr;
409*fc256490SJason Beloro 	uint16_t cmd;
410*fc256490SJason Beloro 
411*fc256490SJason Beloro 	if (!flag)
412*fc256490SJason Beloro 		return (NULL);
413*fc256490SJason Beloro 
414*fc256490SJason Beloro 	switch (flag) {
415*fc256490SJason Beloro 	case PF_AFFECTED_ROOT:
416*fc256490SJason Beloro 		return (PCIE_DIP2BUS(bus_p->bus_rp_dip));
417*fc256490SJason Beloro 	case PF_AFFECTED_SELF:
418*fc256490SJason Beloro 		return (bus_p);
419*fc256490SJason Beloro 	case PF_AFFECTED_PARENT:
420*fc256490SJason Beloro 		return (PCIE_DIP2BUS(ddi_get_parent(PCIE_BUS2DIP(bus_p))));
421*fc256490SJason Beloro 	case PF_AFFECTED_BDF: /* may only be used for RC */
422*fc256490SJason Beloro 		a_bdf = PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf;
423*fc256490SJason Beloro 		if (!PCIE_CHECK_VALID_BDF(a_bdf))
424*fc256490SJason Beloro 			return (NULL);
425*fc256490SJason Beloro 
426*fc256490SJason Beloro 		temp_bus_p = pf_find_busp_by_bdf(impl, a_bdf);
427*fc256490SJason Beloro 		return (temp_bus_p);
428*fc256490SJason Beloro 	case PF_AFFECTED_AER:
429*fc256490SJason Beloro 		if (pf_tlp_decode(bus_p, PCIE_ADV_REG(pfd_p)) == DDI_SUCCESS) {
430*fc256490SJason Beloro 			temp_bus_p = pf_find_busp_by_aer(impl, pfd_p);
431*fc256490SJason Beloro 			return (temp_bus_p);
432*fc256490SJason Beloro 		}
433*fc256490SJason Beloro 		break;
434*fc256490SJason Beloro 	case PF_AFFECTED_SAER:
435*fc256490SJason Beloro 		if (pf_pci_decode(pfd_p, &cmd) == DDI_SUCCESS) {
436*fc256490SJason Beloro 			temp_bus_p = pf_find_busp_by_saer(impl, pfd_p);
437*fc256490SJason Beloro 			return (temp_bus_p);
438*fc256490SJason Beloro 		}
439*fc256490SJason Beloro 		break;
440*fc256490SJason Beloro 	case PF_AFFECTED_ADDR: /* ROOT only */
441*fc256490SJason Beloro 		a_addr = PCIE_ROOT_FAULT(pfd_p)->scan_addr;
442*fc256490SJason Beloro 		temp_bus_p = pf_find_busp_by_addr(impl, a_addr);
443*fc256490SJason Beloro 		return (temp_bus_p);
444*fc256490SJason Beloro 	}
445*fc256490SJason Beloro 
446*fc256490SJason Beloro 	return (NULL);
447*fc256490SJason Beloro }
448*fc256490SJason Beloro 
449*fc256490SJason Beloro /* type used for pcie_domain_list_find() function */
450*fc256490SJason Beloro typedef enum {
451*fc256490SJason Beloro 	PCIE_DOM_LIST_TYPE_CACHE = 1,
452*fc256490SJason Beloro 	PCIE_DOM_LIST_TYPE_FAULT = 2
453*fc256490SJason Beloro } pcie_dom_list_type_t;
454*fc256490SJason Beloro 
455*fc256490SJason Beloro /*
456*fc256490SJason Beloro  * Check if a domain id is already in the linked list
457*fc256490SJason Beloro  */
458*fc256490SJason Beloro static pcie_domains_t *
pcie_domain_list_find(uint_t domain_id,pcie_domains_t * pd_list_p,pcie_dom_list_type_t type)459*fc256490SJason Beloro pcie_domain_list_find(uint_t domain_id, pcie_domains_t *pd_list_p,
460*fc256490SJason Beloro     pcie_dom_list_type_t type)
461*fc256490SJason Beloro {
462*fc256490SJason Beloro 	while (pd_list_p) {
463*fc256490SJason Beloro 		if (pd_list_p->domain_id == domain_id)
464*fc256490SJason Beloro 			return (pd_list_p);
465*fc256490SJason Beloro 
466*fc256490SJason Beloro 		if (type == PCIE_DOM_LIST_TYPE_CACHE) {
467*fc256490SJason Beloro 			pd_list_p = pd_list_p->cached_next;
468*fc256490SJason Beloro 		} else if (type == PCIE_DOM_LIST_TYPE_FAULT) {
469*fc256490SJason Beloro 			pd_list_p = pd_list_p->faulty_next;
470*fc256490SJason Beloro 		} else {
471*fc256490SJason Beloro 			return (NULL);
472*fc256490SJason Beloro 		}
473*fc256490SJason Beloro 	}
474*fc256490SJason Beloro 
475*fc256490SJason Beloro 	return (NULL);
476*fc256490SJason Beloro }
477*fc256490SJason Beloro 
478*fc256490SJason Beloro /*
479*fc256490SJason Beloro  * Return true if a leaf device is assigned to a domain or a bridge device
480*fc256490SJason Beloro  * has children assigned to the domain
481*fc256490SJason Beloro  */
482*fc256490SJason Beloro boolean_t
pcie_in_domain(pcie_bus_t * bus_p,uint_t domain_id)483*fc256490SJason Beloro pcie_in_domain(pcie_bus_t *bus_p, uint_t domain_id)
484*fc256490SJason Beloro {
485*fc256490SJason Beloro 	if (PCIE_IS_BDG(bus_p)) {
486*fc256490SJason Beloro 		pcie_domains_t *pd;
487*fc256490SJason Beloro 		pd = pcie_domain_list_find(domain_id,
488*fc256490SJason Beloro 		    PCIE_DOMAIN_LIST_GET(bus_p), PCIE_DOM_LIST_TYPE_CACHE);
489*fc256490SJason Beloro 		if (pd && pd->cached_count)
490*fc256490SJason Beloro 			return (B_TRUE);
491*fc256490SJason Beloro 		return (B_FALSE);
492*fc256490SJason Beloro 	} else {
493*fc256490SJason Beloro 		return (PCIE_DOMAIN_ID_GET(bus_p) == domain_id);
494*fc256490SJason Beloro 	}
495*fc256490SJason Beloro }
496*fc256490SJason Beloro 
497*fc256490SJason Beloro /*
498*fc256490SJason Beloro  * Add a domain id to a cached domain id list.
499*fc256490SJason Beloro  * If the domain already exists in the list, increment the reference count.
500*fc256490SJason Beloro  */
501*fc256490SJason Beloro void
pcie_domain_list_add(uint_t domain_id,pcie_domains_t ** pd_list_p)502*fc256490SJason Beloro pcie_domain_list_add(uint_t domain_id, pcie_domains_t **pd_list_p)
503*fc256490SJason Beloro {
504*fc256490SJason Beloro 	pcie_domains_t *pd;
505*fc256490SJason Beloro 
506*fc256490SJason Beloro 	pd = pcie_domain_list_find(domain_id, *pd_list_p,
507*fc256490SJason Beloro 	    PCIE_DOM_LIST_TYPE_CACHE);
508*fc256490SJason Beloro 
509*fc256490SJason Beloro 	if (pd == NULL) {
510*fc256490SJason Beloro 		pd = PCIE_ZALLOC(pcie_domains_t);
511*fc256490SJason Beloro 		pd->domain_id = domain_id;
512*fc256490SJason Beloro 		pd->cached_count = 1;
513*fc256490SJason Beloro 		pd->cached_next = *pd_list_p;
514*fc256490SJason Beloro 		*pd_list_p = pd;
515*fc256490SJason Beloro 	} else
516*fc256490SJason Beloro 		pd->cached_count++;
517*fc256490SJason Beloro }
518*fc256490SJason Beloro 
519*fc256490SJason Beloro /*
520*fc256490SJason Beloro  * Remove a domain id from a cached domain id list.
521*fc256490SJason Beloro  * Decrement the reference count.
522*fc256490SJason Beloro  */
523*fc256490SJason Beloro void
pcie_domain_list_remove(uint_t domain_id,pcie_domains_t * pd_list_p)524*fc256490SJason Beloro pcie_domain_list_remove(uint_t domain_id, pcie_domains_t *pd_list_p)
525*fc256490SJason Beloro {
526*fc256490SJason Beloro 	pcie_domains_t *pd;
527*fc256490SJason Beloro 
528*fc256490SJason Beloro 	pd = pcie_domain_list_find(domain_id, pd_list_p,
529*fc256490SJason Beloro 	    PCIE_DOM_LIST_TYPE_CACHE);
530*fc256490SJason Beloro 
531*fc256490SJason Beloro 	if (pd) {
532*fc256490SJason Beloro 		ASSERT((pd->cached_count)--);
533*fc256490SJason Beloro 	}
534*fc256490SJason Beloro }
535*fc256490SJason Beloro 
536*fc256490SJason Beloro /* destroy cached domain id list */
537*fc256490SJason Beloro static void
pcie_domain_list_destroy(pcie_domains_t * domain_ids)538*fc256490SJason Beloro pcie_domain_list_destroy(pcie_domains_t *domain_ids)
539*fc256490SJason Beloro {
540*fc256490SJason Beloro 	pcie_domains_t *p = domain_ids;
541*fc256490SJason Beloro 	pcie_domains_t *next;
542*fc256490SJason Beloro 
543*fc256490SJason Beloro 	while (p) {
544*fc256490SJason Beloro 		next = p->cached_next;
545*fc256490SJason Beloro 		kmem_free(p, sizeof (pcie_domains_t));
546*fc256490SJason Beloro 		p = next;
547*fc256490SJason Beloro 	}
548*fc256490SJason Beloro }
549*fc256490SJason Beloro 
550*fc256490SJason Beloro static void
pcie_faulty_list_update(pcie_domains_t * pd,pcie_domains_t ** headp)551*fc256490SJason Beloro pcie_faulty_list_update(pcie_domains_t *pd,
552*fc256490SJason Beloro     pcie_domains_t **headp)
553*fc256490SJason Beloro {
554*fc256490SJason Beloro 	if (pd == NULL)
555*fc256490SJason Beloro 		return;
556*fc256490SJason Beloro 
557*fc256490SJason Beloro 	if (*headp == NULL) {
558*fc256490SJason Beloro 		*headp = pd;
559*fc256490SJason Beloro 		pd->faulty_prev = NULL;
560*fc256490SJason Beloro 		pd->faulty_next = NULL;
561*fc256490SJason Beloro 		pd->faulty_count = 1;
562*fc256490SJason Beloro 	} else {
563*fc256490SJason Beloro 		pd->faulty_next = *headp;
564*fc256490SJason Beloro 		(*headp)->faulty_prev = pd;
565*fc256490SJason Beloro 		pd->faulty_prev = NULL;
566*fc256490SJason Beloro 		pd->faulty_count = 1;
567*fc256490SJason Beloro 		*headp = pd;
568*fc256490SJason Beloro 	}
569*fc256490SJason Beloro }
570*fc256490SJason Beloro 
571*fc256490SJason Beloro static void
pcie_faulty_list_clear()572*fc256490SJason Beloro pcie_faulty_list_clear()
573*fc256490SJason Beloro {
574*fc256490SJason Beloro 	pcie_domains_t *pd = pcie_faulty_domains;
575*fc256490SJason Beloro 	pcie_domains_t *next;
576*fc256490SJason Beloro 
577*fc256490SJason Beloro 	/* unlink all domain structures from the faulty list */
578*fc256490SJason Beloro 	while (pd) {
579*fc256490SJason Beloro 		next = pd->faulty_next;
580*fc256490SJason Beloro 		pd->faulty_prev = NULL;
581*fc256490SJason Beloro 		pd->faulty_next = NULL;
582*fc256490SJason Beloro 		pd->faulty_count = 0;
583*fc256490SJason Beloro 		pd = next;
584*fc256490SJason Beloro 	}
585*fc256490SJason Beloro 	pcie_faulty_domains = NULL;
586*fc256490SJason Beloro 	pcie_faulty_all = B_FALSE;
587*fc256490SJason Beloro }
588*fc256490SJason Beloro 
589*fc256490SJason Beloro void
pcie_save_domain_id(pcie_domains_t * domain_ids)590*fc256490SJason Beloro pcie_save_domain_id(pcie_domains_t *domain_ids)
591*fc256490SJason Beloro {
592*fc256490SJason Beloro 	pcie_domains_t *old_list_p, *new_list_p, *pd;
593*fc256490SJason Beloro 
594*fc256490SJason Beloro 	if (pcie_faulty_all)
595*fc256490SJason Beloro 		return;
596*fc256490SJason Beloro 
597*fc256490SJason Beloro 	if (domain_ids == NULL)
598*fc256490SJason Beloro 		return;
599*fc256490SJason Beloro 
600*fc256490SJason Beloro 	old_list_p = pcie_faulty_domains;
601*fc256490SJason Beloro 	for (new_list_p = domain_ids; new_list_p;
602*fc256490SJason Beloro 	    new_list_p = new_list_p->cached_next) {
603*fc256490SJason Beloro 		if (!new_list_p->cached_count)
604*fc256490SJason Beloro 			continue;
605*fc256490SJason Beloro 
606*fc256490SJason Beloro 		/* search domain id in the faulty domain list */
607*fc256490SJason Beloro 		pd = pcie_domain_list_find(new_list_p->domain_id,
608*fc256490SJason Beloro 		    old_list_p, PCIE_DOM_LIST_TYPE_FAULT);
609*fc256490SJason Beloro 		if (pd)
610*fc256490SJason Beloro 			pd->faulty_count++;
611*fc256490SJason Beloro 		else
612*fc256490SJason Beloro 			pcie_faulty_list_update(new_list_p,
613*fc256490SJason Beloro 			    &pcie_faulty_domains);
614*fc256490SJason Beloro 	}
615*fc256490SJason Beloro }
616