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