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