13b20eb23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e28c31a9SWeidong Han /* 3e28c31a9SWeidong Han * Copyright (c) 2009, Intel Corporation. 4e28c31a9SWeidong Han * 5e28c31a9SWeidong Han * Author: Weidong Han <weidong.han@intel.com> 6e28c31a9SWeidong Han */ 7e28c31a9SWeidong Han 8e28c31a9SWeidong Han #include <linux/pci.h> 955e901fcSJan Beulich #include <linux/acpi.h> 100b97b03dSRoss Lagerwall #include <linux/pci-acpi.h> 11a67efff2SOleksandr Andrushchenko #include <xen/pci.h> 12e28c31a9SWeidong Han #include <xen/xen.h> 13e28c31a9SWeidong Han #include <xen/interface/physdev.h> 14e28c31a9SWeidong Han #include <xen/interface/xen.h> 15e28c31a9SWeidong Han 16e28c31a9SWeidong Han #include <asm/xen/hypervisor.h> 17e28c31a9SWeidong Han #include <asm/xen/hypercall.h> 18e28c31a9SWeidong Han #include "../pci/pci.h" 19b7ef4a6dSBen Hutchings #ifdef CONFIG_PCI_MMCONFIG 208deb3eb1SKonrad Rzeszutek Wilk #include <asm/pci_x86.h> 21a4098bc6SIgor Druzhinin 22a4098bc6SIgor Druzhinin static int xen_mcfg_late(void); 23b7ef4a6dSBen Hutchings #endif 24e28c31a9SWeidong Han 2555e901fcSJan Beulich static bool __read_mostly pci_seg_supported = true; 2655e901fcSJan Beulich 27e28c31a9SWeidong Han static int xen_add_device(struct device *dev) 28e28c31a9SWeidong Han { 29e28c31a9SWeidong Han int r; 30e28c31a9SWeidong Han struct pci_dev *pci_dev = to_pci_dev(dev); 3155e901fcSJan Beulich #ifdef CONFIG_PCI_IOV 3255e901fcSJan Beulich struct pci_dev *physfn = pci_dev->physfn; 3355e901fcSJan Beulich #endif 34a4098bc6SIgor Druzhinin #ifdef CONFIG_PCI_MMCONFIG 35a4098bc6SIgor Druzhinin static bool pci_mcfg_reserved = false; 36a4098bc6SIgor Druzhinin /* 37a4098bc6SIgor Druzhinin * Reserve MCFG areas in Xen on first invocation due to this being 38a4098bc6SIgor Druzhinin * potentially called from inside of acpi_init immediately after 39a4098bc6SIgor Druzhinin * MCFG table has been finally parsed. 40a4098bc6SIgor Druzhinin */ 41a4098bc6SIgor Druzhinin if (!pci_mcfg_reserved) { 42a4098bc6SIgor Druzhinin xen_mcfg_late(); 43a4098bc6SIgor Druzhinin pci_mcfg_reserved = true; 44a4098bc6SIgor Druzhinin } 45a4098bc6SIgor Druzhinin #endif 4655e901fcSJan Beulich if (pci_seg_supported) { 47a8d0b5ebSGustavo A. R. Silva DEFINE_RAW_FLEX(struct physdev_pci_device_add, add, optarr, 1); 48a8d0b5ebSGustavo A. R. Silva 49a8d0b5ebSGustavo A. R. Silva add->seg = pci_domain_nr(pci_dev->bus); 50a8d0b5ebSGustavo A. R. Silva add->bus = pci_dev->bus->number; 51a8d0b5ebSGustavo A. R. Silva add->devfn = pci_dev->devfn; 52486edb24SBoris Ostrovsky 5355e901fcSJan Beulich #ifdef CONFIG_ACPI 5455e901fcSJan Beulich acpi_handle handle; 5555e901fcSJan Beulich #endif 56e28c31a9SWeidong Han 57e28c31a9SWeidong Han #ifdef CONFIG_PCI_IOV 58e28c31a9SWeidong Han if (pci_dev->is_virtfn) { 59486edb24SBoris Ostrovsky add->flags = XEN_PCI_DEV_VIRTFN; 60486edb24SBoris Ostrovsky add->physfn.bus = physfn->bus->number; 61486edb24SBoris Ostrovsky add->physfn.devfn = physfn->devfn; 6255e901fcSJan Beulich } else 6355e901fcSJan Beulich #endif 6455e901fcSJan Beulich if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) 65486edb24SBoris Ostrovsky add->flags = XEN_PCI_DEV_EXTFN; 6655e901fcSJan Beulich 6755e901fcSJan Beulich #ifdef CONFIG_ACPI 683a83f992SRafael J. Wysocki handle = ACPI_HANDLE(&pci_dev->dev); 6955e901fcSJan Beulich #ifdef CONFIG_PCI_IOV 7055e901fcSJan Beulich if (!handle && pci_dev->is_virtfn) 713a83f992SRafael J. Wysocki handle = ACPI_HANDLE(physfn->bus->bridge); 7255e901fcSJan Beulich #endif 730b97b03dSRoss Lagerwall if (!handle) { 740b97b03dSRoss Lagerwall /* 750b97b03dSRoss Lagerwall * This device was not listed in the ACPI name space at 760b97b03dSRoss Lagerwall * all. Try to get acpi handle of parent pci bus. 770b97b03dSRoss Lagerwall */ 780b97b03dSRoss Lagerwall struct pci_bus *pbus; 790b97b03dSRoss Lagerwall for (pbus = pci_dev->bus; pbus; pbus = pbus->parent) { 800b97b03dSRoss Lagerwall handle = acpi_pci_get_bridge_handle(pbus); 810b97b03dSRoss Lagerwall if (handle) 820b97b03dSRoss Lagerwall break; 830b97b03dSRoss Lagerwall } 840b97b03dSRoss Lagerwall } 8555e901fcSJan Beulich if (handle) { 8655e901fcSJan Beulich acpi_status status; 8755e901fcSJan Beulich 8855e901fcSJan Beulich do { 8955e901fcSJan Beulich unsigned long long pxm; 9055e901fcSJan Beulich 9155e901fcSJan Beulich status = acpi_evaluate_integer(handle, "_PXM", 9255e901fcSJan Beulich NULL, &pxm); 9355e901fcSJan Beulich if (ACPI_SUCCESS(status)) { 94486edb24SBoris Ostrovsky add->optarr[0] = pxm; 95486edb24SBoris Ostrovsky add->flags |= XEN_PCI_DEV_PXM; 9655e901fcSJan Beulich break; 9755e901fcSJan Beulich } 9855e901fcSJan Beulich status = acpi_get_parent(handle, &handle); 9955e901fcSJan Beulich } while (ACPI_SUCCESS(status)); 10055e901fcSJan Beulich } 10155e901fcSJan Beulich #endif /* CONFIG_ACPI */ 10255e901fcSJan Beulich 103486edb24SBoris Ostrovsky r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_add, add); 10455e901fcSJan Beulich if (r != -ENOSYS) 10555e901fcSJan Beulich return r; 10655e901fcSJan Beulich pci_seg_supported = false; 10755e901fcSJan Beulich } 10855e901fcSJan Beulich 10955e901fcSJan Beulich if (pci_domain_nr(pci_dev->bus)) 11055e901fcSJan Beulich r = -ENOSYS; 11155e901fcSJan Beulich #ifdef CONFIG_PCI_IOV 11255e901fcSJan Beulich else if (pci_dev->is_virtfn) { 113e28c31a9SWeidong Han struct physdev_manage_pci_ext manage_pci_ext = { 114e28c31a9SWeidong Han .bus = pci_dev->bus->number, 115e28c31a9SWeidong Han .devfn = pci_dev->devfn, 116e28c31a9SWeidong Han .is_virtfn = 1, 11755e901fcSJan Beulich .physfn.bus = physfn->bus->number, 11855e901fcSJan Beulich .physfn.devfn = physfn->devfn, 119e28c31a9SWeidong Han }; 120e28c31a9SWeidong Han 121e28c31a9SWeidong Han r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, 122e28c31a9SWeidong Han &manage_pci_ext); 12355e901fcSJan Beulich } 124e28c31a9SWeidong Han #endif 12555e901fcSJan Beulich else if (pci_ari_enabled(pci_dev->bus) && PCI_SLOT(pci_dev->devfn)) { 126e28c31a9SWeidong Han struct physdev_manage_pci_ext manage_pci_ext = { 127e28c31a9SWeidong Han .bus = pci_dev->bus->number, 128e28c31a9SWeidong Han .devfn = pci_dev->devfn, 129e28c31a9SWeidong Han .is_extfn = 1, 130e28c31a9SWeidong Han }; 131e28c31a9SWeidong Han 132e28c31a9SWeidong Han r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add_ext, 133e28c31a9SWeidong Han &manage_pci_ext); 134e28c31a9SWeidong Han } else { 135e28c31a9SWeidong Han struct physdev_manage_pci manage_pci = { 136e28c31a9SWeidong Han .bus = pci_dev->bus->number, 137e28c31a9SWeidong Han .devfn = pci_dev->devfn, 138e28c31a9SWeidong Han }; 139e28c31a9SWeidong Han 140e28c31a9SWeidong Han r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_add, 141e28c31a9SWeidong Han &manage_pci); 142e28c31a9SWeidong Han } 143e28c31a9SWeidong Han 144e28c31a9SWeidong Han return r; 145e28c31a9SWeidong Han } 146e28c31a9SWeidong Han 147e28c31a9SWeidong Han static int xen_remove_device(struct device *dev) 148e28c31a9SWeidong Han { 149e28c31a9SWeidong Han int r; 150e28c31a9SWeidong Han struct pci_dev *pci_dev = to_pci_dev(dev); 151e28c31a9SWeidong Han 15255e901fcSJan Beulich if (pci_seg_supported) { 15355e901fcSJan Beulich struct physdev_pci_device device = { 15455e901fcSJan Beulich .seg = pci_domain_nr(pci_dev->bus), 15555e901fcSJan Beulich .bus = pci_dev->bus->number, 15655e901fcSJan Beulich .devfn = pci_dev->devfn 15755e901fcSJan Beulich }; 15855e901fcSJan Beulich 15955e901fcSJan Beulich r = HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_remove, 16055e901fcSJan Beulich &device); 16155e901fcSJan Beulich } else if (pci_domain_nr(pci_dev->bus)) 16255e901fcSJan Beulich r = -ENOSYS; 16355e901fcSJan Beulich else { 16455e901fcSJan Beulich struct physdev_manage_pci manage_pci = { 16555e901fcSJan Beulich .bus = pci_dev->bus->number, 16655e901fcSJan Beulich .devfn = pci_dev->devfn 16755e901fcSJan Beulich }; 168e28c31a9SWeidong Han 169e28c31a9SWeidong Han r = HYPERVISOR_physdev_op(PHYSDEVOP_manage_pci_remove, 170e28c31a9SWeidong Han &manage_pci); 17155e901fcSJan Beulich } 172e28c31a9SWeidong Han 173e28c31a9SWeidong Han return r; 174e28c31a9SWeidong Han } 175e28c31a9SWeidong Han 176*88801d04SJiqian Chen int xen_reset_device(const struct pci_dev *dev) 177*88801d04SJiqian Chen { 178*88801d04SJiqian Chen struct pci_device_reset device = { 179*88801d04SJiqian Chen .dev.seg = pci_domain_nr(dev->bus), 180*88801d04SJiqian Chen .dev.bus = dev->bus->number, 181*88801d04SJiqian Chen .dev.devfn = dev->devfn, 182*88801d04SJiqian Chen .flags = PCI_DEVICE_RESET_FLR, 183*88801d04SJiqian Chen }; 184*88801d04SJiqian Chen 185*88801d04SJiqian Chen return HYPERVISOR_physdev_op(PHYSDEVOP_pci_device_reset, &device); 186*88801d04SJiqian Chen } 187*88801d04SJiqian Chen EXPORT_SYMBOL_GPL(xen_reset_device); 188*88801d04SJiqian Chen 189e28c31a9SWeidong Han static int xen_pci_notifier(struct notifier_block *nb, 190e28c31a9SWeidong Han unsigned long action, void *data) 191e28c31a9SWeidong Han { 192e28c31a9SWeidong Han struct device *dev = data; 193e28c31a9SWeidong Han int r = 0; 194e28c31a9SWeidong Han 195e28c31a9SWeidong Han switch (action) { 196e28c31a9SWeidong Han case BUS_NOTIFY_ADD_DEVICE: 197e28c31a9SWeidong Han r = xen_add_device(dev); 198e28c31a9SWeidong Han break; 199e28c31a9SWeidong Han case BUS_NOTIFY_DEL_DEVICE: 200e28c31a9SWeidong Han r = xen_remove_device(dev); 201e28c31a9SWeidong Han break; 202e28c31a9SWeidong Han default: 20312e13ac8SJan Beulich return NOTIFY_DONE; 20412e13ac8SJan Beulich } 20512e13ac8SJan Beulich if (r) 20612e13ac8SJan Beulich dev_err(dev, "Failed to %s - passthrough or MSI/MSI-X might fail!\n", 20712e13ac8SJan Beulich action == BUS_NOTIFY_ADD_DEVICE ? "add" : 20812e13ac8SJan Beulich (action == BUS_NOTIFY_DEL_DEVICE ? "delete" : "?")); 20912e13ac8SJan Beulich return NOTIFY_OK; 210e28c31a9SWeidong Han } 211e28c31a9SWeidong Han 21212e13ac8SJan Beulich static struct notifier_block device_nb = { 213e28c31a9SWeidong Han .notifier_call = xen_pci_notifier, 214e28c31a9SWeidong Han }; 215e28c31a9SWeidong Han 216e28c31a9SWeidong Han static int __init register_xen_pci_notifier(void) 217e28c31a9SWeidong Han { 218e28c31a9SWeidong Han if (!xen_initial_domain()) 219e28c31a9SWeidong Han return 0; 220e28c31a9SWeidong Han 221e28c31a9SWeidong Han return bus_register_notifier(&pci_bus_type, &device_nb); 222e28c31a9SWeidong Han } 223e28c31a9SWeidong Han 224e28c31a9SWeidong Han arch_initcall(register_xen_pci_notifier); 2258deb3eb1SKonrad Rzeszutek Wilk 2268deb3eb1SKonrad Rzeszutek Wilk #ifdef CONFIG_PCI_MMCONFIG 227a4098bc6SIgor Druzhinin static int xen_mcfg_late(void) 2288deb3eb1SKonrad Rzeszutek Wilk { 2298deb3eb1SKonrad Rzeszutek Wilk struct pci_mmcfg_region *cfg; 2308deb3eb1SKonrad Rzeszutek Wilk int rc; 2318deb3eb1SKonrad Rzeszutek Wilk 2328deb3eb1SKonrad Rzeszutek Wilk if (!xen_initial_domain()) 2338deb3eb1SKonrad Rzeszutek Wilk return 0; 2348deb3eb1SKonrad Rzeszutek Wilk 2358deb3eb1SKonrad Rzeszutek Wilk if ((pci_probe & PCI_PROBE_MMCONF) == 0) 2368deb3eb1SKonrad Rzeszutek Wilk return 0; 2378deb3eb1SKonrad Rzeszutek Wilk 2388deb3eb1SKonrad Rzeszutek Wilk if (list_empty(&pci_mmcfg_list)) 2398deb3eb1SKonrad Rzeszutek Wilk return 0; 2408deb3eb1SKonrad Rzeszutek Wilk 2418deb3eb1SKonrad Rzeszutek Wilk /* Check whether they are in the right area. */ 2428deb3eb1SKonrad Rzeszutek Wilk list_for_each_entry(cfg, &pci_mmcfg_list, list) { 2438deb3eb1SKonrad Rzeszutek Wilk struct physdev_pci_mmcfg_reserved r; 2448deb3eb1SKonrad Rzeszutek Wilk 2458deb3eb1SKonrad Rzeszutek Wilk r.address = cfg->address; 2468deb3eb1SKonrad Rzeszutek Wilk r.segment = cfg->segment; 2478deb3eb1SKonrad Rzeszutek Wilk r.start_bus = cfg->start_bus; 2488deb3eb1SKonrad Rzeszutek Wilk r.end_bus = cfg->end_bus; 2498deb3eb1SKonrad Rzeszutek Wilk r.flags = XEN_PCI_MMCFG_RESERVED; 2508deb3eb1SKonrad Rzeszutek Wilk 2518deb3eb1SKonrad Rzeszutek Wilk rc = HYPERVISOR_physdev_op(PHYSDEVOP_pci_mmcfg_reserved, &r); 2528deb3eb1SKonrad Rzeszutek Wilk switch (rc) { 2538deb3eb1SKonrad Rzeszutek Wilk case 0: 2548deb3eb1SKonrad Rzeszutek Wilk case -ENOSYS: 2558deb3eb1SKonrad Rzeszutek Wilk continue; 2568deb3eb1SKonrad Rzeszutek Wilk 2578deb3eb1SKonrad Rzeszutek Wilk default: 2588deb3eb1SKonrad Rzeszutek Wilk pr_warn("Failed to report MMCONFIG reservation" 2598deb3eb1SKonrad Rzeszutek Wilk " state for %s to hypervisor" 2608deb3eb1SKonrad Rzeszutek Wilk " (%d)\n", 2618deb3eb1SKonrad Rzeszutek Wilk cfg->name, rc); 2628deb3eb1SKonrad Rzeszutek Wilk } 2638deb3eb1SKonrad Rzeszutek Wilk } 2648deb3eb1SKonrad Rzeszutek Wilk return 0; 2658deb3eb1SKonrad Rzeszutek Wilk } 2668deb3eb1SKonrad Rzeszutek Wilk #endif 267a67efff2SOleksandr Andrushchenko 268a67efff2SOleksandr Andrushchenko #ifdef CONFIG_XEN_DOM0 269a67efff2SOleksandr Andrushchenko struct xen_device_domain_owner { 270a67efff2SOleksandr Andrushchenko domid_t domain; 271a67efff2SOleksandr Andrushchenko struct pci_dev *dev; 272a67efff2SOleksandr Andrushchenko struct list_head list; 273a67efff2SOleksandr Andrushchenko }; 274a67efff2SOleksandr Andrushchenko 275a67efff2SOleksandr Andrushchenko static DEFINE_SPINLOCK(dev_domain_list_spinlock); 276f66edf68SCai Huoqing static LIST_HEAD(dev_domain_list); 277a67efff2SOleksandr Andrushchenko 278a67efff2SOleksandr Andrushchenko static struct xen_device_domain_owner *find_device(struct pci_dev *dev) 279a67efff2SOleksandr Andrushchenko { 280a67efff2SOleksandr Andrushchenko struct xen_device_domain_owner *owner; 281a67efff2SOleksandr Andrushchenko 282a67efff2SOleksandr Andrushchenko list_for_each_entry(owner, &dev_domain_list, list) { 283a67efff2SOleksandr Andrushchenko if (owner->dev == dev) 284a67efff2SOleksandr Andrushchenko return owner; 285a67efff2SOleksandr Andrushchenko } 286a67efff2SOleksandr Andrushchenko return NULL; 287a67efff2SOleksandr Andrushchenko } 288a67efff2SOleksandr Andrushchenko 289a67efff2SOleksandr Andrushchenko int xen_find_device_domain_owner(struct pci_dev *dev) 290a67efff2SOleksandr Andrushchenko { 291a67efff2SOleksandr Andrushchenko struct xen_device_domain_owner *owner; 292a67efff2SOleksandr Andrushchenko int domain = -ENODEV; 293a67efff2SOleksandr Andrushchenko 294a67efff2SOleksandr Andrushchenko spin_lock(&dev_domain_list_spinlock); 295a67efff2SOleksandr Andrushchenko owner = find_device(dev); 296a67efff2SOleksandr Andrushchenko if (owner) 297a67efff2SOleksandr Andrushchenko domain = owner->domain; 298a67efff2SOleksandr Andrushchenko spin_unlock(&dev_domain_list_spinlock); 299a67efff2SOleksandr Andrushchenko return domain; 300a67efff2SOleksandr Andrushchenko } 301a67efff2SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_find_device_domain_owner); 302a67efff2SOleksandr Andrushchenko 303a67efff2SOleksandr Andrushchenko int xen_register_device_domain_owner(struct pci_dev *dev, uint16_t domain) 304a67efff2SOleksandr Andrushchenko { 305a67efff2SOleksandr Andrushchenko struct xen_device_domain_owner *owner; 306a67efff2SOleksandr Andrushchenko 307a67efff2SOleksandr Andrushchenko owner = kzalloc(sizeof(struct xen_device_domain_owner), GFP_KERNEL); 308a67efff2SOleksandr Andrushchenko if (!owner) 309a67efff2SOleksandr Andrushchenko return -ENODEV; 310a67efff2SOleksandr Andrushchenko 311a67efff2SOleksandr Andrushchenko spin_lock(&dev_domain_list_spinlock); 312a67efff2SOleksandr Andrushchenko if (find_device(dev)) { 313a67efff2SOleksandr Andrushchenko spin_unlock(&dev_domain_list_spinlock); 314a67efff2SOleksandr Andrushchenko kfree(owner); 315a67efff2SOleksandr Andrushchenko return -EEXIST; 316a67efff2SOleksandr Andrushchenko } 317a67efff2SOleksandr Andrushchenko owner->domain = domain; 318a67efff2SOleksandr Andrushchenko owner->dev = dev; 319a67efff2SOleksandr Andrushchenko list_add_tail(&owner->list, &dev_domain_list); 320a67efff2SOleksandr Andrushchenko spin_unlock(&dev_domain_list_spinlock); 321a67efff2SOleksandr Andrushchenko return 0; 322a67efff2SOleksandr Andrushchenko } 323a67efff2SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_register_device_domain_owner); 324a67efff2SOleksandr Andrushchenko 325a67efff2SOleksandr Andrushchenko int xen_unregister_device_domain_owner(struct pci_dev *dev) 326a67efff2SOleksandr Andrushchenko { 327a67efff2SOleksandr Andrushchenko struct xen_device_domain_owner *owner; 328a67efff2SOleksandr Andrushchenko 329a67efff2SOleksandr Andrushchenko spin_lock(&dev_domain_list_spinlock); 330a67efff2SOleksandr Andrushchenko owner = find_device(dev); 331a67efff2SOleksandr Andrushchenko if (!owner) { 332a67efff2SOleksandr Andrushchenko spin_unlock(&dev_domain_list_spinlock); 333a67efff2SOleksandr Andrushchenko return -ENODEV; 334a67efff2SOleksandr Andrushchenko } 335a67efff2SOleksandr Andrushchenko list_del(&owner->list); 336a67efff2SOleksandr Andrushchenko spin_unlock(&dev_domain_list_spinlock); 337a67efff2SOleksandr Andrushchenko kfree(owner); 338a67efff2SOleksandr Andrushchenko return 0; 339a67efff2SOleksandr Andrushchenko } 340a67efff2SOleksandr Andrushchenko EXPORT_SYMBOL_GPL(xen_unregister_device_domain_owner); 341a67efff2SOleksandr Andrushchenko #endif 342