brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 416f231 Raw
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