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 33 static bool __read_mostly pci_seg_supported = true; 34 35 static 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 138 static 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 167 static 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 190 static struct notifier_block device_nb = { 191 .notifier_call = xen_pci_notifier, 192 }; 193 194 static 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 202 arch_initcall(register_xen_pci_notifier); 203 204 #ifdef CONFIG_PCI_MMCONFIG 205 static 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 */ 247 subsys_initcall_sync(xen_mcfg_late); 248 #endif 249