xref: /openbmc/linux/drivers/xen/pci.c (revision ca79522c)
1 /*
2  * Copyright (c) 2009, Intel Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Author: Weidong Han <weidong.han@intel.com>
18  */
19 
20 #include <linux/pci.h>
21 #include <linux/acpi.h>
22 #include <xen/xen.h>
23 #include <xen/interface/physdev.h>
24 #include <xen/interface/xen.h>
25 
26 #include <asm/xen/hypervisor.h>
27 #include <asm/xen/hypercall.h>
28 #include "../pci/pci.h"
29 
30 static bool __read_mostly pci_seg_supported = true;
31 
32 static int xen_add_device(struct device *dev)
33 {
34 	int r;
35 	struct pci_dev *pci_dev = to_pci_dev(dev);
36 #ifdef CONFIG_PCI_IOV
37 	struct pci_dev *physfn = pci_dev->physfn;
38 #endif
39 
40 	if (pci_seg_supported) {
41 		struct physdev_pci_device_add add = {
42 			.seg = pci_domain_nr(pci_dev->bus),
43 			.bus = pci_dev->bus->number,
44 			.devfn = pci_dev->devfn
45 		};
46 #ifdef CONFIG_ACPI
47 		acpi_handle handle;
48 #endif
49 
50 #ifdef CONFIG_PCI_IOV
51 		if (pci_dev->is_virtfn) {
52 			add.flags = XEN_PCI_DEV_VIRTFN;
53 			add.physfn.bus = physfn->bus->number;
54 			add.physfn.devfn = physfn->devfn;
55 		} else
56 #endif
57 		if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn))
58 			add.flags = XEN_PCI_DEV_EXTFN;
59 
60 #ifdef CONFIG_ACPI
61 		handle = DEVICE_ACPI_HANDLE(&pci_dev->dev);
62 		if (!handle && pci_dev->bus->bridge)
63 			handle = DEVICE_ACPI_HANDLE(pci_dev->bus->bridge);
64 #ifdef CONFIG_PCI_IOV
65 		if (!handle && pci_dev->is_virtfn)
66 			handle = DEVICE_ACPI_HANDLE(physfn->bus->bridge);
67 #endif
68 		if (handle) {
69 			acpi_status status;
70 
71 			do {
72 				unsigned long long pxm;
73 
74 				status = acpi_evaluate_integer(handle, "_PXM",
75 							       NULL, &pxm);
76 				if (ACPI_SUCCESS(status)) {
77 					add.optarr[0] = pxm;
78 					add.flags |= XEN_PCI_DEV_PXM;
79 					break;
80 				}
81 				status = acpi_get_parent(handle, &handle);
82 			} while (ACPI_SUCCESS(status));
83 		}
84 #endif /* CONFIG_ACPI */
85 
86 		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, &add);
87 		if (r != -ENOSYS)
88 			return r;
89 		pci_seg_supported = false;
90 	}
91 
92 	if (pci_domain_nr(pci_dev->bus))
93 		r = -ENOSYS;
94 #ifdef CONFIG_PCI_IOV
95 	else if (pci_dev->is_virtfn) {
96 		struct physdev_manage_pci_ext manage_pci_ext = {
97 			.bus		= pci_dev->bus->number,
98 			.devfn		= pci_dev->devfn,
99 			.is_virtfn 	= 1,
100 			.physfn.bus	= physfn->bus->number,
101 			.physfn.devfn	= physfn->devfn,
102 		};
103 
104 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
105 			&manage_pci_ext);
106 	}
107 #endif
108 	else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) {
109 		struct physdev_manage_pci_ext manage_pci_ext = {
110 			.bus		= pci_dev->bus->number,
111 			.devfn		= pci_dev->devfn,
112 			.is_extfn	= 1,
113 		};
114 
115 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext,
116 			&manage_pci_ext);
117 	} else {
118 		struct physdev_manage_pci manage_pci = {
119 			.bus	= pci_dev->bus->number,
120 			.devfn	= pci_dev->devfn,
121 		};
122 
123 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add,
124 			&manage_pci);
125 	}
126 
127 	return r;
128 }
129 
130 static int xen_remove_device(struct device *dev)
131 {
132 	int r;
133 	struct pci_dev *pci_dev = to_pci_dev(dev);
134 
135 	if (pci_seg_supported) {
136 		struct physdev_pci_device device = {
137 			.seg = pci_domain_nr(pci_dev->bus),
138 			.bus = pci_dev->bus->number,
139 			.devfn = pci_dev->devfn
140 		};
141 
142 		r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove,
143 					  &device);
144 	} else if (pci_domain_nr(pci_dev->bus))
145 		r = -ENOSYS;
146 	else {
147 		struct physdev_manage_pci manage_pci = {
148 			.bus = pci_dev->bus->number,
149 			.devfn = pci_dev->devfn
150 		};
151 
152 		r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove,
153 					  &manage_pci);
154 	}
155 
156 	return r;
157 }
158 
159 static int xen_pci_notifier(struct notifier_block *nb,
160 			    unsigned long action, void *data)
161 {
162 	struct device *dev = data;
163 	int r = 0;
164 
165 	switch (action) {
166 	case BUS_NOTIFY_ADD_DEVICE:
167 		r = xen_add_device(dev);
168 		break;
169 	case BUS_NOTIFY_DEL_DEVICE:
170 		r = xen_remove_device(dev);
171 		break;
172 	default:
173 		return NOTIFY_DONE;
174 	}
175 	if (r)
176 		dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n",
177 			action == BUS_NOTIFY_ADD_DEVICE ? "add" :
178 			(action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?"));
179 	return NOTIFY_OK;
180 }
181 
182 static struct notifier_block device_nb = {
183 	.notifier_call = xen_pci_notifier,
184 };
185 
186 static int __init register_xen_pci_notifier(void)
187 {
188 	if (!xen_initial_domain())
189 		return 0;
190 
191 	return bus_register_notifier(&pci_bus_type, &device_nb);
192 }
193 
194 arch_initcall(register_xen_pci_notifier);
195