xref: /openbmc/linux/drivers/xen/pci.c (revision a5b2c10c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2009, Intel Corporation.
4  *
5  * Author: Weidong Han <weidong.han@intel.com>
6  */
7 
8 #include <linux/pci.h>
9 #include <linux/acpi.h>
10 #include <linux/pci-acpi.h>
11 #include <xen/xen.h>
12 #include <xen/interface/physdev.h>
13 #include <xen/interface/xen.h>
14 
15 #include <asm/xen/hypervisor.h>
16 #include <asm/xen/hypercall.h>
17 #include "../pci/pci.h"
18 #ifdef CONFIG_PCI_MMCONFIG
19 #include <asm/pci_x86.h>
20 
21 static int xen_mcfg_late(void);
22 #endif
23 
24 static bool __read_mostly pci_seg_supported = true;
25 
26 static int xen_add_device(struct device *dev)
27 {
28 	int r;
29 	struct pci_dev *pci_dev = to_pci_dev(dev);
30 #ifdef CONFIG_PCI_IOV
31 	struct pci_dev *physfn = pci_dev->physfn;
32 #endif
33 #ifdef CONFIG_PCI_MMCONFIG
34 	static bool pci_mcfg_reserved = false;
35 	/*
36 	 * Reserve MCFG areas in Xen on first invocation due to this being
37 	 * potentially called from inside of acpi_init immediately after
38 	 * MCFG table has been finally parsed.
39 	 */
40 	if (!pci_mcfg_reserved) {
41 		xen_mcfg_late();
42 		pci_mcfg_reserved = true;
43 	}
44 #endif
45 	if (pci_seg_supported) {
46 		struct {
47 			struct physdev_pci_device_add add;
48 			uint32_t pxm;
49 		} add_ext = {
50 			.add.seg = pci_domain_nr(pci_dev->bus),
51 			.add.bus = pci_dev->bus->number,
52 			.add.devfn = pci_dev->devfn
53 		};
54 		struct physdev_pci_device_add *add = &add_ext.add;
55 
56 #ifdef CONFIG_ACPI
57 		acpi_handle handle;
58 #endif
59 
60 #ifdef CONFIG_PCI_IOV
61 		if (pci_dev->is_virtfn) {
62 			add->flags = XEN_PCI_DEV_VIRTFN;
63 			add->physfn.bus = physfn->bus->number;
64 			add->physfn.devfn = physfn->devfn;
65 		} else
66 #endif
67 		if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
68 			add->flags = XEN_PCI_DEV_EXTFN;
69 
70 #ifdef CONFIG_ACPI
71 		handle = ACPI_HANDLE(&pci_dev->dev);
72 #ifdef CONFIG_PCI_IOV
73 		if (!handle && pci_dev->is_virtfn)
74 			handle = ACPI_HANDLE(physfn->bus->bridge);
75 #endif
76 		if (!handle) {
77 			/*
78 			 * This device was not listed in the ACPI name space at
79 			 * all. Try to get acpi handle of parent pci bus.
80 			 */
81 			struct pci_bus *pbus;
82 			for (pbus = pci_dev->bus; pbus; pbus = pbus->parent) {
83 				handle = acpi_pci_get_bridge_handle(pbus);
84 				if (handle)
85 					break;
86 			}
87 		}
88 		if (handle) {
89 			acpi_status status;
90 
91 			do {
92 				unsigned long long pxm;
93 
94 				status = acpi_evaluate_integer(handle, "_PXM",
95 							       NULL, &pxm);
96 				if (ACPI_SUCCESS(status)) {
97 					add->optarr[0] = pxm;
98 					add->flags |= XEN_PCI_DEV_PXM;
99 					break;
100 				}
101 				status = acpi_get_parent(handle, &handle);
102 			} while (ACPI_SUCCESS(status));
103 		}
104 #endif /* CONFIG_ACPI */
105 
106 		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, add);
107 		if (r != -ENOSYS)
108 			return r;
109 		pci_seg_supported = false;
110 	}
111 
112 	if (pci_domain_nr(pci_dev->bus))
113 		r = -ENOSYS;
114 #ifdef CONFIG_PCI_IOV
115 	else if (pci_dev->is_virtfn) {
116 		struct physdev_manage_pci_ext manage_pci_ext = {
117 			.bus		= pci_dev->bus->number,
118 			.devfn		= pci_dev->devfn,
119 			.is_virtfn 	= 1,
120 			.physfn.bus	= physfn->bus->number,
121 			.physfn.devfn	= physfn->devfn,
122 		};
123 
124 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
125 			&manage_pci_ext);
126 	}
127 #endif
128 	else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
129 		struct physdev_manage_pci_ext manage_pci_ext = {
130 			.bus		= pci_dev->bus->number,
131 			.devfn		= pci_dev->devfn,
132 			.is_extfn	= 1,
133 		};
134 
135 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
136 			&manage_pci_ext);
137 	} else {
138 		struct physdev_manage_pci manage_pci = {
139 			.bus	= pci_dev->bus->number,
140 			.devfn	= pci_dev->devfn,
141 		};
142 
143 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
144 			&manage_pci);
145 	}
146 
147 	return r;
148 }
149 
150 static int xen_remove_device(struct device *dev)
151 {
152 	int r;
153 	struct pci_dev *pci_dev = to_pci_dev(dev);
154 
155 	if (pci_seg_supported) {
156 		struct physdev_pci_device device = {
157 			.seg = pci_domain_nr(pci_dev->bus),
158 			.bus = pci_dev->bus->number,
159 			.devfn = pci_dev->devfn
160 		};
161 
162 		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
163 					  &device);
164 	} else if (pci_domain_nr(pci_dev->bus))
165 		r = -ENOSYS;
166 	else {
167 		struct physdev_manage_pci manage_pci = {
168 			.bus = pci_dev->bus->number,
169 			.devfn = pci_dev->devfn
170 		};
171 
172 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
173 					  &manage_pci);
174 	}
175 
176 	return r;
177 }
178 
179 static int xen_pci_notifier(struct notifier_block *nb,
180 			    unsigned long action, void *data)
181 {
182 	struct device *dev = data;
183 	int r = 0;
184 
185 	switch (action) {
186 	case BUS_NOTIFY_ADD_DEVICE:
187 		r = xen_add_device(dev);
188 		break;
189 	case BUS_NOTIFY_DEL_DEVICE:
190 		r = xen_remove_device(dev);
191 		break;
192 	default:
193 		return NOTIFY_DONE;
194 	}
195 	if (r)
196 		dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
197 			action == BUS_NOTIFY_ADD_DEVICE ? "add" :
198 			(action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
199 	return NOTIFY_OK;
200 }
201 
202 static struct notifier_block device_nb = {
203 	.notifier_call = xen_pci_notifier,
204 };
205 
206 static int __init register_xen_pci_notifier(void)
207 {
208 	if (!xen_initial_domain())
209 		return 0;
210 
211 	return bus_register_notifier(&pci_bus_type, &device_nb);
212 }
213 
214 arch_initcall(register_xen_pci_notifier);
215 
216 #ifdef CONFIG_PCI_MMCONFIG
217 static int xen_mcfg_late(void)
218 {
219 	struct pci_mmcfg_region *cfg;
220 	int rc;
221 
222 	if (!xen_initial_domain())
223 		return 0;
224 
225 	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
226 		return 0;
227 
228 	if (list_empty(&pci_mmcfg_list))
229 		return 0;
230 
231 	/* Check whether they are in the right area. */
232 	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
233 		struct physdev_pci_mmcfg_reserved r;
234 
235 		r.address = cfg->address;
236 		r.segment = cfg->segment;
237 		r.start_bus = cfg->start_bus;
238 		r.end_bus = cfg->end_bus;
239 		r.flags = XEN_PCI_MMCFG_RESERVED;
240 
241 		rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r);
242 		switch (rc) {
243 		case 0:
244 		case -ENOSYS:
245 			continue;
246 
247 		default:
248 			pr_warn("Failed to report MMCONFIG reservation"
249 				" state for %s to hypervisor"
250 				" (%d)\n",
251 				cfg->name, rc);
252 		}
253 	}
254 	return 0;
255 }
256 #endif
257