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
30static bool __read_mostly pci_seg_supported = true;
31
32static 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)
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
130static 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
159static 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
182static struct notifier_block device_nb = {
183	.notifier_call = xen_pci_notifier,
184};
185
186static 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
194arch_initcall(register_xen_pci_notifier);
195