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