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