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