1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2009, Intel Corporation. 4 * 5 * Author: Weidong Han <weidong.han@intel.com> 6 */ 7 8 #include <linux/pci.h> 9 #include <linux/acpi.h> 10 #include <linux/pci-acpi.h> 11 #include <xen/pci.h> 12 #include <xen/xen.h> 13 #include <xen/interface/physdev.h> 14 #include <xen/interface/xen.h> 15 16 #include <asm/xen/hypervisor.h> 17 #include <asm/xen/hypercall.h> 18 #include "../pci/pci.h" 19 #ifdef CONFIG_PCI_MMCONFIG 20 #include <asm/pci_x86.h> 21 22 static int xen_mcfg_late(void); 23 #endif 24 25 static bool __read_mostly pci_seg_supported = true; 26 27 static int xen_add_device(struct device *dev) 28 { 29 int r; 30 struct pci_dev *pci_dev = to_pci_dev(dev); 31 #ifdef CONFIG_PCI_IOV 32 struct pci_dev *physfn = pci_dev->physfn; 33 #endif 34 #ifdef CONFIG_PCI_MMCONFIG 35 static bool pci_mcfg_reserved = false; 36 /* 37 * Reserve MCFG areas in Xen on first invocation due to this being 38 * potentially called from inside of acpi_init immediately after 39 * MCFG table has been finally parsed. 40 */ 41 if (!pci_mcfg_reserved) { 42 xen_mcfg_late(); 43 pci_mcfg_reserved = true; 44 } 45 #endif 46 if (pci_seg_supported) { 47 struct { 48 struct physdev_pci_device_add add; 49 uint32_t pxm; 50 } add_ext = { 51 .add.seg = pci_domain_nr(pci_dev->bus), 52 .add.bus = pci_dev->bus->number, 53 .add.devfn = pci_dev->devfn 54 }; 55 struct physdev_pci_device_add *add = &add_ext.add; 56 57 #ifdef CONFIG_ACPI 58 acpi_handle handle; 59 #endif 60 61 #ifdef CONFIG_PCI_IOV 62 if (pci_dev->is_virtfn) { 63 add->flags = XEN_PCI_DEV_VIRTFN; 64 add->physfn.bus = physfn->bus->number; 65 add->physfn.devfn = physfn->devfn; 66 } else 67 #endif 68 if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) 69 add->flags = XEN_PCI_DEV_EXTFN; 70 71 #ifdef CONFIG_ACPI 72 handle = ACPI_HANDLE(&pci_dev->dev); 73 #ifdef CONFIG_PCI_IOV 74 if (!handle && pci_dev->is_virtfn) 75 handle = ACPI_HANDLE(physfn->bus->bridge); 76 #endif 77 if (!handle) { 78 /* 79 * This device was not listed in the ACPI name space at 80 * all. Try to get acpi handle of parent pci bus. 81 */ 82 struct pci_bus *pbus; 83 for (pbus = pci_dev->bus; pbus; pbus = pbus->parent) { 84 handle = acpi_pci_get_bridge_handle(pbus); 85 if (handle) 86 break; 87 } 88 } 89 if (handle) { 90 acpi_status status; 91 92 do { 93 unsigned long long pxm; 94 95 status = acpi_evaluate_integer(handle, "_PXM", 96 NULL, &pxm); 97 if (ACPI_SUCCESS(status)) { 98 add->optarr[0] = pxm; 99 add->flags |= XEN_PCI_DEV_PXM; 100 break; 101 } 102 status = acpi_get_parent(handle, &handle); 103 } while (ACPI_SUCCESS(status)); 104 } 105 #endif /* CONFIG_ACPI */ 106 107 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, add); 108 if (r != -ENOSYS) 109 return r; 110 pci_seg_supported = false; 111 } 112 113 if (pci_domain_nr(pci_dev->bus)) 114 r = -ENOSYS; 115 #ifdef CONFIG_PCI_IOV 116 else if (pci_dev->is_virtfn) { 117 struct physdev_manage_pci_ext manage_pci_ext = { 118 .bus = pci_dev->bus->number, 119 .devfn = pci_dev->devfn, 120 .is_virtfn = 1, 121 .physfn.bus = physfn->bus->number, 122 .physfn.devfn = physfn->devfn, 123 }; 124 125 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, 126 &manage_pci_ext); 127 } 128 #endif 129 else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { 130 struct physdev_manage_pci_ext manage_pci_ext = { 131 .bus = pci_dev->bus->number, 132 .devfn = pci_dev->devfn, 133 .is_extfn = 1, 134 }; 135 136 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, 137 &manage_pci_ext); 138 } else { 139 struct physdev_manage_pci manage_pci = { 140 .bus = pci_dev->bus->number, 141 .devfn = pci_dev->devfn, 142 }; 143 144 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add, 145 &manage_pci); 146 } 147 148 return r; 149 } 150 151 static int xen_remove_device(struct device *dev) 152 { 153 int r; 154 struct pci_dev *pci_dev = to_pci_dev(dev); 155 156 if (pci_seg_supported) { 157 struct physdev_pci_device device = { 158 .seg = pci_domain_nr(pci_dev->bus), 159 .bus = pci_dev->bus->number, 160 .devfn = pci_dev->devfn 161 }; 162 163 r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove, 164 &device); 165 } else if (pci_domain_nr(pci_dev->bus)) 166 r = -ENOSYS; 167 else { 168 struct physdev_manage_pci manage_pci = { 169 .bus = pci_dev->bus->number, 170 .devfn = pci_dev->devfn 171 }; 172 173 r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, 174 &manage_pci); 175 } 176 177 return r; 178 } 179 180 static int xen_pci_notifier(struct notifier_block *nb, 181 unsigned long action, void *data) 182 { 183 struct device *dev = data; 184 int r = 0; 185 186 switch (action) { 187 case BUS_NOTIFY_ADD_DEVICE: 188 r = xen_add_device(dev); 189 break; 190 case BUS_NOTIFY_DEL_DEVICE: 191 r = xen_remove_device(dev); 192 break; 193 default: 194 return NOTIFY_DONE; 195 } 196 if (r) 197 dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n", 198 action == BUS_NOTIFY_ADD_DEVICE ? "add" : 199 (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?")); 200 return NOTIFY_OK; 201 } 202 203 static struct notifier_block device_nb = { 204 .notifier_call = xen_pci_notifier, 205 }; 206 207 static int __init register_xen_pci_notifier(void) 208 { 209 if (!xen_initial_domain()) 210 return 0; 211 212 return bus_register_notifier(&pci_bus_type, &device_nb); 213 } 214 215 arch_initcall(register_xen_pci_notifier); 216 217 #ifdef CONFIG_PCI_MMCONFIG 218 static int xen_mcfg_late(void) 219 { 220 struct pci_mmcfg_region *cfg; 221 int rc; 222 223 if (!xen_initial_domain()) 224 return 0; 225 226 if ((pci_probe & PCI_PROBE_MMCONF) == 0) 227 return 0; 228 229 if (list_empty(&pci_mmcfg_list)) 230 return 0; 231 232 /* Check whether they are in the right area. */ 233 list_for_each_entry(cfg, &pci_mmcfg_list, list) { 234 struct physdev_pci_mmcfg_reserved r; 235 236 r.address = cfg->address; 237 r.segment = cfg->segment; 238 r.start_bus = cfg->start_bus; 239 r.end_bus = cfg->end_bus; 240 r.flags = XEN_PCI_MMCFG_RESERVED; 241 242 rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r); 243 switch (rc) { 244 case 0: 245 case -ENOSYS: 246 continue; 247 248 default: 249 pr_warn("Failed to report MMCONFIG reservation" 250 " state for %s to hypervisor" 251 " (%d)\n", 252 cfg->name, rc); 253 } 254 } 255 return 0; 256 } 257 #endif 258 259 #ifdef CONFIG_XEN_DOM0 260 struct xen_device_domain_owner { 261 domid_t domain; 262 struct pci_dev *dev; 263 struct list_head list; 264 }; 265 266 static DEFINE_SPINLOCK(dev_domain_list_spinlock); 267 static LIST_HEAD(dev_domain_list); 268 269 static struct xen_device_domain_owner *find_device(struct pci_dev *dev) 270 { 271 struct xen_device_domain_owner *owner; 272 273 list_for_each_entry(owner, &dev_domain_list, list) { 274 if (owner->dev == dev) 275 return owner; 276 } 277 return NULL; 278 } 279 280 int xen_find_device_domain_owner(struct pci_dev *dev) 281 { 282 struct xen_device_domain_owner *owner; 283 int domain = -ENODEV; 284 285 spin_lock(&dev_domain_list_spinlock); 286 owner = find_device(dev); 287 if (owner) 288 domain = owner->domain; 289 spin_unlock(&dev_domain_list_spinlock); 290 return domain; 291 } 292 EXPORT_SYMBOL_GPL(xen_find_device_domain_owner); 293 294 int xen_register_device_domain_owner(struct pci_dev *dev, uint16_t domain) 295 { 296 struct xen_device_domain_owner *owner; 297 298 owner = kzalloc(sizeof(struct xen_device_domain_owner), GFP_KERNEL); 299 if (!owner) 300 return -ENODEV; 301 302 spin_lock(&dev_domain_list_spinlock); 303 if (find_device(dev)) { 304 spin_unlock(&dev_domain_list_spinlock); 305 kfree(owner); 306 return -EEXIST; 307 } 308 owner->domain = domain; 309 owner->dev = dev; 310 list_add_tail(&owner->list, &dev_domain_list); 311 spin_unlock(&dev_domain_list_spinlock); 312 return 0; 313 } 314 EXPORT_SYMBOL_GPL(xen_register_device_domain_owner); 315 316 int xen_unregister_device_domain_owner(struct pci_dev *dev) 317 { 318 struct xen_device_domain_owner *owner; 319 320 spin_lock(&dev_domain_list_spinlock); 321 owner = find_device(dev); 322 if (!owner) { 323 spin_unlock(&dev_domain_list_spinlock); 324 return -ENODEV; 325 } 326 list_del(&owner->list); 327 spin_unlock(&dev_domain_list_spinlock); 328 kfree(owner); 329 return 0; 330 } 331 EXPORT_SYMBOL_GPL(xen_unregister_device_domain_owner); 332 #endif 333