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