18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * PCI Stub Driver - Grabs devices in backend to be exported later
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Ryan Wilson <hap9@epoch.ncsc.mil>
58c2ecf20Sopenharmony_ci * Chris Bookholt <hap10@epoch.ncsc.mil>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci#define dev_fmt pr_fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/rwsem.h>
148c2ecf20Sopenharmony_ci#include <linux/list.h>
158c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
168c2ecf20Sopenharmony_ci#include <linux/kref.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/wait.h>
198c2ecf20Sopenharmony_ci#include <linux/sched.h>
208c2ecf20Sopenharmony_ci#include <linux/atomic.h>
218c2ecf20Sopenharmony_ci#include <xen/events.h>
228c2ecf20Sopenharmony_ci#include <asm/xen/pci.h>
238c2ecf20Sopenharmony_ci#include <asm/xen/hypervisor.h>
248c2ecf20Sopenharmony_ci#include <xen/interface/physdev.h>
258c2ecf20Sopenharmony_ci#include "pciback.h"
268c2ecf20Sopenharmony_ci#include "conf_space.h"
278c2ecf20Sopenharmony_ci#include "conf_space_quirks.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define PCISTUB_DRIVER_NAME "pciback"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic char *pci_devs_to_hide;
328c2ecf20Sopenharmony_ciwait_queue_head_t xen_pcibk_aer_wait_queue;
338c2ecf20Sopenharmony_ci/*Add sem for sync AER handling and xen_pcibk remove/reconfigue ops,
348c2ecf20Sopenharmony_ci* We want to avoid in middle of AER ops, xen_pcibk devices is being removed
358c2ecf20Sopenharmony_ci*/
368c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(pcistub_sem);
378c2ecf20Sopenharmony_cimodule_param_named(hide, pci_devs_to_hide, charp, 0444);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct pcistub_device_id {
408c2ecf20Sopenharmony_ci	struct list_head slot_list;
418c2ecf20Sopenharmony_ci	int domain;
428c2ecf20Sopenharmony_ci	unsigned char bus;
438c2ecf20Sopenharmony_ci	unsigned int devfn;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_cistatic LIST_HEAD(pcistub_device_ids);
468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(device_ids_lock);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct pcistub_device {
498c2ecf20Sopenharmony_ci	struct kref kref;
508c2ecf20Sopenharmony_ci	struct list_head dev_list;
518c2ecf20Sopenharmony_ci	spinlock_t lock;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	struct pci_dev *dev;
548c2ecf20Sopenharmony_ci	struct xen_pcibk_device *pdev;/* non-NULL if struct pci_dev is in use */
558c2ecf20Sopenharmony_ci};
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Access to pcistub_devices & seized_devices lists and the initialize_devices
588c2ecf20Sopenharmony_ci * flag must be locked with pcistub_devices_lock
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(pcistub_devices_lock);
618c2ecf20Sopenharmony_cistatic LIST_HEAD(pcistub_devices);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* wait for device_initcall before initializing our devices
648c2ecf20Sopenharmony_ci * (see pcistub_init_devices_late)
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic int initialize_devices;
678c2ecf20Sopenharmony_cistatic LIST_HEAD(seized_devices);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "pcistub_device_alloc\n");
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	psdev = kzalloc(sizeof(*psdev), GFP_KERNEL);
768c2ecf20Sopenharmony_ci	if (!psdev)
778c2ecf20Sopenharmony_ci		return NULL;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	psdev->dev = pci_dev_get(dev);
808c2ecf20Sopenharmony_ci	if (!psdev->dev) {
818c2ecf20Sopenharmony_ci		kfree(psdev);
828c2ecf20Sopenharmony_ci		return NULL;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	kref_init(&psdev->kref);
868c2ecf20Sopenharmony_ci	spin_lock_init(&psdev->lock);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return psdev;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Don't call this directly as it's called by pcistub_device_put */
928c2ecf20Sopenharmony_cistatic void pcistub_device_release(struct kref *kref)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
958c2ecf20Sopenharmony_ci	struct pci_dev *dev;
968c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	psdev = container_of(kref, struct pcistub_device, kref);
998c2ecf20Sopenharmony_ci	dev = psdev->dev;
1008c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "pcistub_device_release\n");
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	xen_unregister_device_domain_owner(dev);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Call the reset function which does not take lock as this
1078c2ecf20Sopenharmony_ci	 * is called from "unbind" which takes a device_lock mutex.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	__pci_reset_function_locked(dev);
1108c2ecf20Sopenharmony_ci	if (dev_data &&
1118c2ecf20Sopenharmony_ci	    pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
1128c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "Could not reload PCI state\n");
1138c2ecf20Sopenharmony_ci	else
1148c2ecf20Sopenharmony_ci		pci_restore_state(dev);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (dev->msix_cap) {
1178c2ecf20Sopenharmony_ci		struct physdev_pci_device ppdev = {
1188c2ecf20Sopenharmony_ci			.seg = pci_domain_nr(dev->bus),
1198c2ecf20Sopenharmony_ci			.bus = dev->bus->number,
1208c2ecf20Sopenharmony_ci			.devfn = dev->devfn
1218c2ecf20Sopenharmony_ci		};
1228c2ecf20Sopenharmony_ci		int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix,
1238c2ecf20Sopenharmony_ci						&ppdev);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		if (err && err != -ENOSYS)
1268c2ecf20Sopenharmony_ci			dev_warn(&dev->dev, "MSI-X release failed (%d)\n",
1278c2ecf20Sopenharmony_ci				 err);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Disable the device */
1318c2ecf20Sopenharmony_ci	xen_pcibk_reset_device(dev);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	kfree(dev_data);
1348c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, NULL);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Clean-up the device */
1378c2ecf20Sopenharmony_ci	xen_pcibk_config_free_dyn_fields(dev);
1388c2ecf20Sopenharmony_ci	xen_pcibk_config_free_dev(dev);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	pci_clear_dev_assigned(dev);
1418c2ecf20Sopenharmony_ci	pci_dev_put(dev);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	kfree(psdev);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic inline void pcistub_device_get(struct pcistub_device *psdev)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	kref_get(&psdev->kref);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic inline void pcistub_device_put(struct pcistub_device *psdev)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	kref_put(&psdev->kref, pcistub_device_release);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic struct pcistub_device *pcistub_device_find_locked(int domain, int bus,
1578c2ecf20Sopenharmony_ci							 int slot, int func)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
1628c2ecf20Sopenharmony_ci		if (psdev->dev != NULL
1638c2ecf20Sopenharmony_ci		    && domain == pci_domain_nr(psdev->dev->bus)
1648c2ecf20Sopenharmony_ci		    && bus == psdev->dev->bus->number
1658c2ecf20Sopenharmony_ci		    && slot == PCI_SLOT(psdev->dev->devfn)
1668c2ecf20Sopenharmony_ci		    && func == PCI_FUNC(psdev->dev->devfn)) {
1678c2ecf20Sopenharmony_ci			return psdev;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return NULL;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic struct pcistub_device *pcistub_device_find(int domain, int bus,
1758c2ecf20Sopenharmony_ci						  int slot, int func)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
1788c2ecf20Sopenharmony_ci	unsigned long flags;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	psdev = pcistub_device_find_locked(domain, bus, slot, func);
1838c2ecf20Sopenharmony_ci	if (psdev)
1848c2ecf20Sopenharmony_ci		pcistub_device_get(psdev);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
1878c2ecf20Sopenharmony_ci	return psdev;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic struct pci_dev *pcistub_device_get_pci_dev(struct xen_pcibk_device *pdev,
1918c2ecf20Sopenharmony_ci						  struct pcistub_device *psdev)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev = NULL;
1948c2ecf20Sopenharmony_ci	unsigned long flags;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	pcistub_device_get(psdev);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&psdev->lock, flags);
1998c2ecf20Sopenharmony_ci	if (!psdev->pdev) {
2008c2ecf20Sopenharmony_ci		psdev->pdev = pdev;
2018c2ecf20Sopenharmony_ci		pci_dev = psdev->dev;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&psdev->lock, flags);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (!pci_dev)
2068c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return pci_dev;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistruct pci_dev *pcistub_get_pci_dev_by_slot(struct xen_pcibk_device *pdev,
2128c2ecf20Sopenharmony_ci					    int domain, int bus,
2138c2ecf20Sopenharmony_ci					    int slot, int func)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
2168c2ecf20Sopenharmony_ci	struct pci_dev *found_dev = NULL;
2178c2ecf20Sopenharmony_ci	unsigned long flags;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	psdev = pcistub_device_find_locked(domain, bus, slot, func);
2228c2ecf20Sopenharmony_ci	if (psdev)
2238c2ecf20Sopenharmony_ci		found_dev = pcistub_device_get_pci_dev(pdev, psdev);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
2268c2ecf20Sopenharmony_ci	return found_dev;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistruct pci_dev *pcistub_get_pci_dev(struct xen_pcibk_device *pdev,
2308c2ecf20Sopenharmony_ci				    struct pci_dev *dev)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
2338c2ecf20Sopenharmony_ci	struct pci_dev *found_dev = NULL;
2348c2ecf20Sopenharmony_ci	unsigned long flags;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
2398c2ecf20Sopenharmony_ci		if (psdev->dev == dev) {
2408c2ecf20Sopenharmony_ci			found_dev = pcistub_device_get_pci_dev(pdev, psdev);
2418c2ecf20Sopenharmony_ci			break;
2428c2ecf20Sopenharmony_ci		}
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
2468c2ecf20Sopenharmony_ci	return found_dev;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/*
2508c2ecf20Sopenharmony_ci * Called when:
2518c2ecf20Sopenharmony_ci *  - XenBus state has been reconfigure (pci unplug). See xen_pcibk_remove_device
2528c2ecf20Sopenharmony_ci *  - XenBus state has been disconnected (guest shutdown). See xen_pcibk_xenbus_remove
2538c2ecf20Sopenharmony_ci *  - 'echo BDF > unbind' on pciback module with no guest attached. See pcistub_remove
2548c2ecf20Sopenharmony_ci *  - 'echo BDF > unbind' with a guest still using it. See pcistub_remove
2558c2ecf20Sopenharmony_ci *
2568c2ecf20Sopenharmony_ci *  As such we have to be careful.
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci *  To make this easier, the caller has to hold the device lock.
2598c2ecf20Sopenharmony_ci */
2608c2ecf20Sopenharmony_civoid pcistub_put_pci_dev(struct pci_dev *dev)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct pcistub_device *psdev, *found_psdev = NULL;
2638c2ecf20Sopenharmony_ci	unsigned long flags;
2648c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
2658c2ecf20Sopenharmony_ci	int ret;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
2708c2ecf20Sopenharmony_ci		if (psdev->dev == dev) {
2718c2ecf20Sopenharmony_ci			found_psdev = psdev;
2728c2ecf20Sopenharmony_ci			break;
2738c2ecf20Sopenharmony_ci		}
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
2778c2ecf20Sopenharmony_ci	if (WARN_ON(!found_psdev))
2788c2ecf20Sopenharmony_ci		return;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/*hold this lock for avoiding breaking link between
2818c2ecf20Sopenharmony_ci	* pcistub and xen_pcibk when AER is in processing
2828c2ecf20Sopenharmony_ci	*/
2838c2ecf20Sopenharmony_ci	down_write(&pcistub_sem);
2848c2ecf20Sopenharmony_ci	/* Cleanup our device
2858c2ecf20Sopenharmony_ci	 * (so it's ready for the next domain)
2868c2ecf20Sopenharmony_ci	 */
2878c2ecf20Sopenharmony_ci	device_lock_assert(&dev->dev);
2888c2ecf20Sopenharmony_ci	__pci_reset_function_locked(dev);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
2918c2ecf20Sopenharmony_ci	ret = pci_load_saved_state(dev, dev_data->pci_saved_state);
2928c2ecf20Sopenharmony_ci	if (!ret) {
2938c2ecf20Sopenharmony_ci		/*
2948c2ecf20Sopenharmony_ci		 * The usual sequence is pci_save_state & pci_restore_state
2958c2ecf20Sopenharmony_ci		 * but the guest might have messed the configuration space up.
2968c2ecf20Sopenharmony_ci		 * Use the initial version (when device was bound to us).
2978c2ecf20Sopenharmony_ci		 */
2988c2ecf20Sopenharmony_ci		pci_restore_state(dev);
2998c2ecf20Sopenharmony_ci	} else
3008c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "Could not reload PCI state\n");
3018c2ecf20Sopenharmony_ci	/* This disables the device. */
3028c2ecf20Sopenharmony_ci	xen_pcibk_reset_device(dev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* And cleanup up our emulated fields. */
3058c2ecf20Sopenharmony_ci	xen_pcibk_config_reset_dev(dev);
3068c2ecf20Sopenharmony_ci	xen_pcibk_config_free_dyn_fields(dev);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	dev_data->allow_interrupt_control = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	xen_unregister_device_domain_owner(dev);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&found_psdev->lock, flags);
3138c2ecf20Sopenharmony_ci	found_psdev->pdev = NULL;
3148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&found_psdev->lock, flags);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	pcistub_device_put(found_psdev);
3178c2ecf20Sopenharmony_ci	up_write(&pcistub_sem);
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic int pcistub_match_one(struct pci_dev *dev,
3218c2ecf20Sopenharmony_ci			     struct pcistub_device_id *pdev_id)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	/* Match the specified device by domain, bus, slot, func and also if
3248c2ecf20Sopenharmony_ci	 * any of the device's parent bridges match.
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	for (; dev != NULL; dev = dev->bus->self) {
3278c2ecf20Sopenharmony_ci		if (pci_domain_nr(dev->bus) == pdev_id->domain
3288c2ecf20Sopenharmony_ci		    && dev->bus->number == pdev_id->bus
3298c2ecf20Sopenharmony_ci		    && dev->devfn == pdev_id->devfn)
3308c2ecf20Sopenharmony_ci			return 1;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci		/* Sometimes topmost bridge links to itself. */
3338c2ecf20Sopenharmony_ci		if (dev == dev->bus->self)
3348c2ecf20Sopenharmony_ci			break;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	return 0;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int pcistub_match(struct pci_dev *dev)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct pcistub_device_id *pdev_id;
3438c2ecf20Sopenharmony_ci	unsigned long flags;
3448c2ecf20Sopenharmony_ci	int found = 0;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
3478c2ecf20Sopenharmony_ci	list_for_each_entry(pdev_id, &pcistub_device_ids, slot_list) {
3488c2ecf20Sopenharmony_ci		if (pcistub_match_one(dev, pdev_id)) {
3498c2ecf20Sopenharmony_ci			found = 1;
3508c2ecf20Sopenharmony_ci			break;
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return found;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int pcistub_init_device(struct pci_dev *dev)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
3618c2ecf20Sopenharmony_ci	int err = 0;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "initializing...\n");
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* The PCI backend is not intended to be a module (or to work with
3668c2ecf20Sopenharmony_ci	 * removable PCI devices (yet). If it were, xen_pcibk_config_free()
3678c2ecf20Sopenharmony_ci	 * would need to be called somewhere to free the memory allocated
3688c2ecf20Sopenharmony_ci	 * here and then to call kfree(pci_get_drvdata(psdev->dev)).
3698c2ecf20Sopenharmony_ci	 */
3708c2ecf20Sopenharmony_ci	dev_data = kzalloc(sizeof(*dev_data) +  strlen(DRV_NAME "[]")
3718c2ecf20Sopenharmony_ci				+ strlen(pci_name(dev)) + 1, GFP_KERNEL);
3728c2ecf20Sopenharmony_ci	if (!dev_data) {
3738c2ecf20Sopenharmony_ci		err = -ENOMEM;
3748c2ecf20Sopenharmony_ci		goto out;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, dev_data);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/*
3798c2ecf20Sopenharmony_ci	 * Setup name for fake IRQ handler. It will only be enabled
3808c2ecf20Sopenharmony_ci	 * once the device is turned on by the guest.
3818c2ecf20Sopenharmony_ci	 */
3828c2ecf20Sopenharmony_ci	sprintf(dev_data->irq_name, DRV_NAME "[%s]", pci_name(dev));
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "initializing config\n");
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	init_waitqueue_head(&xen_pcibk_aer_wait_queue);
3878c2ecf20Sopenharmony_ci	err = xen_pcibk_config_init_dev(dev);
3888c2ecf20Sopenharmony_ci	if (err)
3898c2ecf20Sopenharmony_ci		goto out;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* HACK: Force device (& ACPI) to determine what IRQ it's on - we
3928c2ecf20Sopenharmony_ci	 * must do this here because pcibios_enable_device may specify
3938c2ecf20Sopenharmony_ci	 * the pci device's true irq (and possibly its other resources)
3948c2ecf20Sopenharmony_ci	 * if they differ from what's in the configuration space.
3958c2ecf20Sopenharmony_ci	 * This makes the assumption that the device's resources won't
3968c2ecf20Sopenharmony_ci	 * change after this point (otherwise this code may break!)
3978c2ecf20Sopenharmony_ci	 */
3988c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "enabling device\n");
3998c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
4008c2ecf20Sopenharmony_ci	if (err)
4018c2ecf20Sopenharmony_ci		goto config_release;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (dev->msix_cap) {
4048c2ecf20Sopenharmony_ci		struct physdev_pci_device ppdev = {
4058c2ecf20Sopenharmony_ci			.seg = pci_domain_nr(dev->bus),
4068c2ecf20Sopenharmony_ci			.bus = dev->bus->number,
4078c2ecf20Sopenharmony_ci			.devfn = dev->devfn
4088c2ecf20Sopenharmony_ci		};
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev);
4118c2ecf20Sopenharmony_ci		if (err && err != -ENOSYS)
4128c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "MSI-X preparation failed (%d)\n",
4138c2ecf20Sopenharmony_ci				err);
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* We need the device active to save the state. */
4178c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "save state of device\n");
4188c2ecf20Sopenharmony_ci	pci_save_state(dev);
4198c2ecf20Sopenharmony_ci	dev_data->pci_saved_state = pci_store_saved_state(dev);
4208c2ecf20Sopenharmony_ci	if (!dev_data->pci_saved_state)
4218c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
4228c2ecf20Sopenharmony_ci	else {
4238c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "resetting (FLR, D3, etc) the device\n");
4248c2ecf20Sopenharmony_ci		__pci_reset_function_locked(dev);
4258c2ecf20Sopenharmony_ci		pci_restore_state(dev);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	/* Now disable the device (this also ensures some private device
4288c2ecf20Sopenharmony_ci	 * data is setup before we export)
4298c2ecf20Sopenharmony_ci	 */
4308c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "reset device\n");
4318c2ecf20Sopenharmony_ci	xen_pcibk_reset_device(dev);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	pci_set_dev_assigned(dev);
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ciconfig_release:
4378c2ecf20Sopenharmony_ci	xen_pcibk_config_free_dev(dev);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciout:
4408c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, NULL);
4418c2ecf20Sopenharmony_ci	kfree(dev_data);
4428c2ecf20Sopenharmony_ci	return err;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/*
4468c2ecf20Sopenharmony_ci * Because some initialization still happens on
4478c2ecf20Sopenharmony_ci * devices during fs_initcall, we need to defer
4488c2ecf20Sopenharmony_ci * full initialization of our devices until
4498c2ecf20Sopenharmony_ci * device_initcall.
4508c2ecf20Sopenharmony_ci */
4518c2ecf20Sopenharmony_cistatic int __init pcistub_init_devices_late(void)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
4548c2ecf20Sopenharmony_ci	unsigned long flags;
4558c2ecf20Sopenharmony_ci	int err = 0;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	while (!list_empty(&seized_devices)) {
4608c2ecf20Sopenharmony_ci		psdev = container_of(seized_devices.next,
4618c2ecf20Sopenharmony_ci				     struct pcistub_device, dev_list);
4628c2ecf20Sopenharmony_ci		list_del(&psdev->dev_list);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		err = pcistub_init_device(psdev->dev);
4678c2ecf20Sopenharmony_ci		if (err) {
4688c2ecf20Sopenharmony_ci			dev_err(&psdev->dev->dev,
4698c2ecf20Sopenharmony_ci				"error %d initializing device\n", err);
4708c2ecf20Sopenharmony_ci			kfree(psdev);
4718c2ecf20Sopenharmony_ci			psdev = NULL;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		if (psdev)
4778c2ecf20Sopenharmony_ci			list_add_tail(&psdev->dev_list, &pcistub_devices);
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	initialize_devices = 1;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_cistatic void pcistub_device_id_add_list(struct pcistub_device_id *new,
4888c2ecf20Sopenharmony_ci				       int domain, int bus, unsigned int devfn)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
4918c2ecf20Sopenharmony_ci	unsigned long flags;
4928c2ecf20Sopenharmony_ci	int found = 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	list_for_each_entry(pci_dev_id, &pcistub_device_ids, slot_list) {
4978c2ecf20Sopenharmony_ci		if (pci_dev_id->domain == domain && pci_dev_id->bus == bus &&
4988c2ecf20Sopenharmony_ci		    pci_dev_id->devfn == devfn) {
4998c2ecf20Sopenharmony_ci			found = 1;
5008c2ecf20Sopenharmony_ci			break;
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (!found) {
5058c2ecf20Sopenharmony_ci		new->domain = domain;
5068c2ecf20Sopenharmony_ci		new->bus = bus;
5078c2ecf20Sopenharmony_ci		new->devfn = devfn;
5088c2ecf20Sopenharmony_ci		list_add_tail(&new->slot_list, &pcistub_device_ids);
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (found)
5148c2ecf20Sopenharmony_ci		kfree(new);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic int pcistub_seize(struct pci_dev *dev,
5188c2ecf20Sopenharmony_ci			 struct pcistub_device_id *pci_dev_id)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
5218c2ecf20Sopenharmony_ci	unsigned long flags;
5228c2ecf20Sopenharmony_ci	int err = 0;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	psdev = pcistub_device_alloc(dev);
5258c2ecf20Sopenharmony_ci	if (!psdev) {
5268c2ecf20Sopenharmony_ci		kfree(pci_dev_id);
5278c2ecf20Sopenharmony_ci		return -ENOMEM;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (initialize_devices) {
5338c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		/* don't want irqs disabled when calling pcistub_init_device */
5368c2ecf20Sopenharmony_ci		err = pcistub_init_device(psdev->dev);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		if (!err)
5418c2ecf20Sopenharmony_ci			list_add(&psdev->dev_list, &pcistub_devices);
5428c2ecf20Sopenharmony_ci	} else {
5438c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "deferring initialization\n");
5448c2ecf20Sopenharmony_ci		list_add(&psdev->dev_list, &seized_devices);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (err) {
5508c2ecf20Sopenharmony_ci		kfree(pci_dev_id);
5518c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
5528c2ecf20Sopenharmony_ci	} else if (pci_dev_id)
5538c2ecf20Sopenharmony_ci		pcistub_device_id_add_list(pci_dev_id, pci_domain_nr(dev->bus),
5548c2ecf20Sopenharmony_ci					   dev->bus->number, dev->devfn);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	return err;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/* Called when 'bind'. This means we must _NOT_ call pci_reset_function or
5608c2ecf20Sopenharmony_ci * other functions that take the sysfs lock. */
5618c2ecf20Sopenharmony_cistatic int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	int err = 0, match;
5648c2ecf20Sopenharmony_ci	struct pcistub_device_id *pci_dev_id = NULL;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "probing...\n");
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	match = pcistub_match(dev);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if ((dev->driver_override &&
5718c2ecf20Sopenharmony_ci	     !strcmp(dev->driver_override, PCISTUB_DRIVER_NAME)) ||
5728c2ecf20Sopenharmony_ci	    match) {
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL
5758c2ecf20Sopenharmony_ci		    && dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
5768c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "can't export pci devices that "
5778c2ecf20Sopenharmony_ci				"don't have a normal (0) or bridge (1) "
5788c2ecf20Sopenharmony_ci				"header type!\n");
5798c2ecf20Sopenharmony_ci			err = -ENODEV;
5808c2ecf20Sopenharmony_ci			goto out;
5818c2ecf20Sopenharmony_ci		}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		if (!match) {
5848c2ecf20Sopenharmony_ci			pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
5858c2ecf20Sopenharmony_ci			if (!pci_dev_id) {
5868c2ecf20Sopenharmony_ci				err = -ENOMEM;
5878c2ecf20Sopenharmony_ci				goto out;
5888c2ecf20Sopenharmony_ci			}
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "seizing device\n");
5928c2ecf20Sopenharmony_ci		err = pcistub_seize(dev, pci_dev_id);
5938c2ecf20Sopenharmony_ci	} else
5948c2ecf20Sopenharmony_ci		/* Didn't find the device */
5958c2ecf20Sopenharmony_ci		err = -ENODEV;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ciout:
5988c2ecf20Sopenharmony_ci	return err;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci/* Called when 'unbind'. This means we must _NOT_ call pci_reset_function or
6028c2ecf20Sopenharmony_ci * other functions that take the sysfs lock. */
6038c2ecf20Sopenharmony_cistatic void pcistub_remove(struct pci_dev *dev)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct pcistub_device *psdev, *found_psdev = NULL;
6068c2ecf20Sopenharmony_ci	unsigned long flags;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "removing\n");
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	xen_pcibk_config_quirk_release(dev);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
6158c2ecf20Sopenharmony_ci		if (psdev->dev == dev) {
6168c2ecf20Sopenharmony_ci			found_psdev = psdev;
6178c2ecf20Sopenharmony_ci			break;
6188c2ecf20Sopenharmony_ci		}
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (found_psdev) {
6248c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "found device to remove %s\n",
6258c2ecf20Sopenharmony_ci			found_psdev->pdev ? "- in-use" : "");
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		if (found_psdev->pdev) {
6288c2ecf20Sopenharmony_ci			int domid = xen_find_device_domain_owner(dev);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci			dev_warn(&dev->dev, "****** removing device %s while still in-use by domain %d! ******\n",
6318c2ecf20Sopenharmony_ci			       pci_name(found_psdev->dev), domid);
6328c2ecf20Sopenharmony_ci			dev_warn(&dev->dev, "****** driver domain may still access this device's i/o resources!\n");
6338c2ecf20Sopenharmony_ci			dev_warn(&dev->dev, "****** shutdown driver domain before binding device\n");
6348c2ecf20Sopenharmony_ci			dev_warn(&dev->dev, "****** to other drivers or domains\n");
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci			/* N.B. This ends up calling pcistub_put_pci_dev which ends up
6378c2ecf20Sopenharmony_ci			 * doing the FLR. */
6388c2ecf20Sopenharmony_ci			xen_pcibk_release_pci_dev(found_psdev->pdev,
6398c2ecf20Sopenharmony_ci						found_psdev->dev,
6408c2ecf20Sopenharmony_ci						false /* caller holds the lock. */);
6418c2ecf20Sopenharmony_ci		}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
6448c2ecf20Sopenharmony_ci		list_del(&found_psdev->dev_list);
6458c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci		/* the final put for releasing from the list */
6488c2ecf20Sopenharmony_ci		pcistub_device_put(found_psdev);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic const struct pci_device_id pcistub_ids[] = {
6538c2ecf20Sopenharmony_ci	{
6548c2ecf20Sopenharmony_ci	 .vendor = PCI_ANY_ID,
6558c2ecf20Sopenharmony_ci	 .device = PCI_ANY_ID,
6568c2ecf20Sopenharmony_ci	 .subvendor = PCI_ANY_ID,
6578c2ecf20Sopenharmony_ci	 .subdevice = PCI_ANY_ID,
6588c2ecf20Sopenharmony_ci	 },
6598c2ecf20Sopenharmony_ci	{0,},
6608c2ecf20Sopenharmony_ci};
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci#define PCI_NODENAME_MAX 40
6638c2ecf20Sopenharmony_cistatic void kill_domain_by_device(struct pcistub_device *psdev)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct xenbus_transaction xbt;
6668c2ecf20Sopenharmony_ci	int err;
6678c2ecf20Sopenharmony_ci	char nodename[PCI_NODENAME_MAX];
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	BUG_ON(!psdev);
6708c2ecf20Sopenharmony_ci	snprintf(nodename, PCI_NODENAME_MAX, "/local/domain/0/backend/pci/%d/0",
6718c2ecf20Sopenharmony_ci		psdev->pdev->xdev->otherend_id);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ciagain:
6748c2ecf20Sopenharmony_ci	err = xenbus_transaction_start(&xbt);
6758c2ecf20Sopenharmony_ci	if (err) {
6768c2ecf20Sopenharmony_ci		dev_err(&psdev->dev->dev,
6778c2ecf20Sopenharmony_ci			"error %d when start xenbus transaction\n", err);
6788c2ecf20Sopenharmony_ci		return;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	/*PV AER handlers will set this flag*/
6818c2ecf20Sopenharmony_ci	xenbus_printf(xbt, nodename, "aerState" , "aerfail");
6828c2ecf20Sopenharmony_ci	err = xenbus_transaction_end(xbt, 0);
6838c2ecf20Sopenharmony_ci	if (err) {
6848c2ecf20Sopenharmony_ci		if (err == -EAGAIN)
6858c2ecf20Sopenharmony_ci			goto again;
6868c2ecf20Sopenharmony_ci		dev_err(&psdev->dev->dev,
6878c2ecf20Sopenharmony_ci			"error %d when end xenbus transaction\n", err);
6888c2ecf20Sopenharmony_ci		return;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci/* For each aer recovery step error_detected, mmio_enabled, etc, front_end and
6938c2ecf20Sopenharmony_ci * backend need to have cooperation. In xen_pcibk, those steps will do similar
6948c2ecf20Sopenharmony_ci * jobs: send service request and waiting for front_end response.
6958c2ecf20Sopenharmony_ci*/
6968c2ecf20Sopenharmony_cistatic pci_ers_result_t common_process(struct pcistub_device *psdev,
6978c2ecf20Sopenharmony_ci				       pci_channel_state_t state, int aer_cmd,
6988c2ecf20Sopenharmony_ci				       pci_ers_result_t result)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	pci_ers_result_t res = result;
7018c2ecf20Sopenharmony_ci	struct xen_pcie_aer_op *aer_op;
7028c2ecf20Sopenharmony_ci	struct xen_pcibk_device *pdev = psdev->pdev;
7038c2ecf20Sopenharmony_ci	struct xen_pci_sharedinfo *sh_info = pdev->sh_info;
7048c2ecf20Sopenharmony_ci	int ret;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/*with PV AER drivers*/
7078c2ecf20Sopenharmony_ci	aer_op = &(sh_info->aer_op);
7088c2ecf20Sopenharmony_ci	aer_op->cmd = aer_cmd ;
7098c2ecf20Sopenharmony_ci	/*useful for error_detected callback*/
7108c2ecf20Sopenharmony_ci	aer_op->err = state;
7118c2ecf20Sopenharmony_ci	/*pcifront_end BDF*/
7128c2ecf20Sopenharmony_ci	ret = xen_pcibk_get_pcifront_dev(psdev->dev, psdev->pdev,
7138c2ecf20Sopenharmony_ci		&aer_op->domain, &aer_op->bus, &aer_op->devfn);
7148c2ecf20Sopenharmony_ci	if (!ret) {
7158c2ecf20Sopenharmony_ci		dev_err(&psdev->dev->dev, "failed to get pcifront device\n");
7168c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_NONE;
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci	wmb();
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	dev_dbg(&psdev->dev->dev, "aer_op %x dom %x bus %x devfn %x\n",
7218c2ecf20Sopenharmony_ci			aer_cmd, aer_op->domain, aer_op->bus, aer_op->devfn);
7228c2ecf20Sopenharmony_ci	/*local flag to mark there's aer request, xen_pcibk callback will use
7238c2ecf20Sopenharmony_ci	* this flag to judge whether we need to check pci-front give aer
7248c2ecf20Sopenharmony_ci	* service ack signal
7258c2ecf20Sopenharmony_ci	*/
7268c2ecf20Sopenharmony_ci	set_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/*It is possible that a pcifront conf_read_write ops request invokes
7298c2ecf20Sopenharmony_ci	* the callback which cause the spurious execution of wake_up.
7308c2ecf20Sopenharmony_ci	* Yet it is harmless and better than a spinlock here
7318c2ecf20Sopenharmony_ci	*/
7328c2ecf20Sopenharmony_ci	set_bit(_XEN_PCIB_active,
7338c2ecf20Sopenharmony_ci		(unsigned long *)&sh_info->flags);
7348c2ecf20Sopenharmony_ci	wmb();
7358c2ecf20Sopenharmony_ci	notify_remote_via_irq(pdev->evtchn_irq);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	/* Enable IRQ to signal "request done". */
7388c2ecf20Sopenharmony_ci	xen_pcibk_lateeoi(pdev, 0);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ret = wait_event_timeout(xen_pcibk_aer_wait_queue,
7418c2ecf20Sopenharmony_ci				 !(test_bit(_XEN_PCIB_active, (unsigned long *)
7428c2ecf20Sopenharmony_ci				 &sh_info->flags)), 300*HZ);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* Enable IRQ for pcifront request if not already active. */
7458c2ecf20Sopenharmony_ci	if (!test_bit(_PDEVF_op_active, &pdev->flags))
7468c2ecf20Sopenharmony_ci		xen_pcibk_lateeoi(pdev, 0);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (!ret) {
7498c2ecf20Sopenharmony_ci		if (test_bit(_XEN_PCIB_active,
7508c2ecf20Sopenharmony_ci			(unsigned long *)&sh_info->flags)) {
7518c2ecf20Sopenharmony_ci			dev_err(&psdev->dev->dev,
7528c2ecf20Sopenharmony_ci				"pcifront aer process not responding!\n");
7538c2ecf20Sopenharmony_ci			clear_bit(_XEN_PCIB_active,
7548c2ecf20Sopenharmony_ci			  (unsigned long *)&sh_info->flags);
7558c2ecf20Sopenharmony_ci			aer_op->err = PCI_ERS_RESULT_NONE;
7568c2ecf20Sopenharmony_ci			return res;
7578c2ecf20Sopenharmony_ci		}
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci	clear_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	res = (pci_ers_result_t)aer_op->err;
7628c2ecf20Sopenharmony_ci	return res;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci/*
7668c2ecf20Sopenharmony_ci* xen_pcibk_slot_reset: it will send the slot_reset request to  pcifront in case
7678c2ecf20Sopenharmony_ci* of the device driver could provide this service, and then wait for pcifront
7688c2ecf20Sopenharmony_ci* ack.
7698c2ecf20Sopenharmony_ci* @dev: pointer to PCI devices
7708c2ecf20Sopenharmony_ci* return value is used by aer_core do_recovery policy
7718c2ecf20Sopenharmony_ci*/
7728c2ecf20Sopenharmony_cistatic pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
7758c2ecf20Sopenharmony_ci	pci_ers_result_t result;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	result = PCI_ERS_RESULT_RECOVERED;
7788c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_slot_reset(bus:%x,devfn:%x)\n",
7798c2ecf20Sopenharmony_ci		dev->bus->number, dev->devfn);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	down_write(&pcistub_sem);
7828c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
7838c2ecf20Sopenharmony_ci				dev->bus->number,
7848c2ecf20Sopenharmony_ci				PCI_SLOT(dev->devfn),
7858c2ecf20Sopenharmony_ci				PCI_FUNC(dev->devfn));
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (!psdev || !psdev->pdev) {
7888c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
7898c2ecf20Sopenharmony_ci		goto end;
7908c2ecf20Sopenharmony_ci	}
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (!psdev->pdev->sh_info) {
7938c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
7948c2ecf20Sopenharmony_ci			" by HVM, kill it\n");
7958c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
7968c2ecf20Sopenharmony_ci		goto end;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
8008c2ecf20Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
8018c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
8028c2ecf20Sopenharmony_ci			"guest with no AER driver should have been killed\n");
8038c2ecf20Sopenharmony_ci		goto end;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci	result = common_process(psdev, 1, XEN_PCI_OP_aer_slotreset, result);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
8088c2ecf20Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
8098c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev,
8108c2ecf20Sopenharmony_ci			"No AER slot_reset service or disconnected!\n");
8118c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
8128c2ecf20Sopenharmony_ci	}
8138c2ecf20Sopenharmony_ciend:
8148c2ecf20Sopenharmony_ci	if (psdev)
8158c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
8168c2ecf20Sopenharmony_ci	up_write(&pcistub_sem);
8178c2ecf20Sopenharmony_ci	return result;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci/*xen_pcibk_mmio_enabled: it will send the mmio_enabled request to  pcifront
8238c2ecf20Sopenharmony_ci* in case of the device driver could provide this service, and then wait
8248c2ecf20Sopenharmony_ci* for pcifront ack
8258c2ecf20Sopenharmony_ci* @dev: pointer to PCI devices
8268c2ecf20Sopenharmony_ci* return value is used by aer_core do_recovery policy
8278c2ecf20Sopenharmony_ci*/
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
8328c2ecf20Sopenharmony_ci	pci_ers_result_t result;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	result = PCI_ERS_RESULT_RECOVERED;
8358c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_mmio_enabled(bus:%x,devfn:%x)\n",
8368c2ecf20Sopenharmony_ci		dev->bus->number, dev->devfn);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	down_write(&pcistub_sem);
8398c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
8408c2ecf20Sopenharmony_ci				dev->bus->number,
8418c2ecf20Sopenharmony_ci				PCI_SLOT(dev->devfn),
8428c2ecf20Sopenharmony_ci				PCI_FUNC(dev->devfn));
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (!psdev || !psdev->pdev) {
8458c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
8468c2ecf20Sopenharmony_ci		goto end;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (!psdev->pdev->sh_info) {
8508c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
8518c2ecf20Sopenharmony_ci			" by HVM, kill it\n");
8528c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
8538c2ecf20Sopenharmony_ci		goto end;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
8578c2ecf20Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
8588c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
8598c2ecf20Sopenharmony_ci			"guest with no AER driver should have been killed\n");
8608c2ecf20Sopenharmony_ci		goto end;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci	result = common_process(psdev, 1, XEN_PCI_OP_aer_mmio, result);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
8658c2ecf20Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
8668c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev,
8678c2ecf20Sopenharmony_ci			"No AER mmio_enabled service or disconnected!\n");
8688c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
8698c2ecf20Sopenharmony_ci	}
8708c2ecf20Sopenharmony_ciend:
8718c2ecf20Sopenharmony_ci	if (psdev)
8728c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
8738c2ecf20Sopenharmony_ci	up_write(&pcistub_sem);
8748c2ecf20Sopenharmony_ci	return result;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci/*xen_pcibk_error_detected: it will send the error_detected request to  pcifront
8788c2ecf20Sopenharmony_ci* in case of the device driver could provide this service, and then wait
8798c2ecf20Sopenharmony_ci* for pcifront ack.
8808c2ecf20Sopenharmony_ci* @dev: pointer to PCI devices
8818c2ecf20Sopenharmony_ci* @error: the current PCI connection state
8828c2ecf20Sopenharmony_ci* return value is used by aer_core do_recovery policy
8838c2ecf20Sopenharmony_ci*/
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
8868c2ecf20Sopenharmony_ci	pci_channel_state_t error)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
8898c2ecf20Sopenharmony_ci	pci_ers_result_t result;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	result = PCI_ERS_RESULT_CAN_RECOVER;
8928c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_error_detected(bus:%x,devfn:%x)\n",
8938c2ecf20Sopenharmony_ci		dev->bus->number, dev->devfn);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	down_write(&pcistub_sem);
8968c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
8978c2ecf20Sopenharmony_ci				dev->bus->number,
8988c2ecf20Sopenharmony_ci				PCI_SLOT(dev->devfn),
8998c2ecf20Sopenharmony_ci				PCI_FUNC(dev->devfn));
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (!psdev || !psdev->pdev) {
9028c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
9038c2ecf20Sopenharmony_ci		goto end;
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if (!psdev->pdev->sh_info) {
9078c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
9088c2ecf20Sopenharmony_ci			" by HVM, kill it\n");
9098c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
9108c2ecf20Sopenharmony_ci		goto end;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	/*Guest owns the device yet no aer handler regiested, kill guest*/
9148c2ecf20Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
9158c2ecf20Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
9168c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev, "guest may have no aer driver, kill it\n");
9178c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
9188c2ecf20Sopenharmony_ci		goto end;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci	result = common_process(psdev, error, XEN_PCI_OP_aer_detected, result);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
9238c2ecf20Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
9248c2ecf20Sopenharmony_ci		dev_dbg(&dev->dev,
9258c2ecf20Sopenharmony_ci			"No AER error_detected service or disconnected!\n");
9268c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ciend:
9298c2ecf20Sopenharmony_ci	if (psdev)
9308c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
9318c2ecf20Sopenharmony_ci	up_write(&pcistub_sem);
9328c2ecf20Sopenharmony_ci	return result;
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci/*xen_pcibk_error_resume: it will send the error_resume request to  pcifront
9368c2ecf20Sopenharmony_ci* in case of the device driver could provide this service, and then wait
9378c2ecf20Sopenharmony_ci* for pcifront ack.
9388c2ecf20Sopenharmony_ci* @dev: pointer to PCI devices
9398c2ecf20Sopenharmony_ci*/
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic void xen_pcibk_error_resume(struct pci_dev *dev)
9428c2ecf20Sopenharmony_ci{
9438c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_error_resume(bus:%x,devfn:%x)\n",
9468c2ecf20Sopenharmony_ci		dev->bus->number, dev->devfn);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	down_write(&pcistub_sem);
9498c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
9508c2ecf20Sopenharmony_ci				dev->bus->number,
9518c2ecf20Sopenharmony_ci				PCI_SLOT(dev->devfn),
9528c2ecf20Sopenharmony_ci				PCI_FUNC(dev->devfn));
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (!psdev || !psdev->pdev) {
9558c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
9568c2ecf20Sopenharmony_ci		goto end;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (!psdev->pdev->sh_info) {
9608c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
9618c2ecf20Sopenharmony_ci			" by HVM, kill it\n");
9628c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
9638c2ecf20Sopenharmony_ci		goto end;
9648c2ecf20Sopenharmony_ci	}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
9678c2ecf20Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
9688c2ecf20Sopenharmony_ci		dev_err(&dev->dev,
9698c2ecf20Sopenharmony_ci			"guest with no AER driver should have been killed\n");
9708c2ecf20Sopenharmony_ci		kill_domain_by_device(psdev);
9718c2ecf20Sopenharmony_ci		goto end;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci	common_process(psdev, 1, XEN_PCI_OP_aer_resume,
9748c2ecf20Sopenharmony_ci		       PCI_ERS_RESULT_RECOVERED);
9758c2ecf20Sopenharmony_ciend:
9768c2ecf20Sopenharmony_ci	if (psdev)
9778c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
9788c2ecf20Sopenharmony_ci	up_write(&pcistub_sem);
9798c2ecf20Sopenharmony_ci	return;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci/*add xen_pcibk AER handling*/
9838c2ecf20Sopenharmony_cistatic const struct pci_error_handlers xen_pcibk_error_handler = {
9848c2ecf20Sopenharmony_ci	.error_detected = xen_pcibk_error_detected,
9858c2ecf20Sopenharmony_ci	.mmio_enabled = xen_pcibk_mmio_enabled,
9868c2ecf20Sopenharmony_ci	.slot_reset = xen_pcibk_slot_reset,
9878c2ecf20Sopenharmony_ci	.resume = xen_pcibk_error_resume,
9888c2ecf20Sopenharmony_ci};
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci/*
9918c2ecf20Sopenharmony_ci * Note: There is no MODULE_DEVICE_TABLE entry here because this isn't
9928c2ecf20Sopenharmony_ci * for a normal device. I don't want it to be loaded automatically.
9938c2ecf20Sopenharmony_ci */
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cistatic struct pci_driver xen_pcibk_pci_driver = {
9968c2ecf20Sopenharmony_ci	/* The name should be xen_pciback, but until the tools are updated
9978c2ecf20Sopenharmony_ci	 * we will keep it as pciback. */
9988c2ecf20Sopenharmony_ci	.name = PCISTUB_DRIVER_NAME,
9998c2ecf20Sopenharmony_ci	.id_table = pcistub_ids,
10008c2ecf20Sopenharmony_ci	.probe = pcistub_probe,
10018c2ecf20Sopenharmony_ci	.remove = pcistub_remove,
10028c2ecf20Sopenharmony_ci	.err_handler = &xen_pcibk_error_handler,
10038c2ecf20Sopenharmony_ci};
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic inline int str_to_slot(const char *buf, int *domain, int *bus,
10068c2ecf20Sopenharmony_ci			      int *slot, int *func)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	int parsed = 0;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	switch (sscanf(buf, " %x:%x:%x.%x %n", domain, bus, slot, func,
10118c2ecf20Sopenharmony_ci		       &parsed)) {
10128c2ecf20Sopenharmony_ci	case 3:
10138c2ecf20Sopenharmony_ci		*func = -1;
10148c2ecf20Sopenharmony_ci		sscanf(buf, " %x:%x:%x.* %n", domain, bus, slot, &parsed);
10158c2ecf20Sopenharmony_ci		break;
10168c2ecf20Sopenharmony_ci	case 2:
10178c2ecf20Sopenharmony_ci		*slot = *func = -1;
10188c2ecf20Sopenharmony_ci		sscanf(buf, " %x:%x:*.* %n", domain, bus, &parsed);
10198c2ecf20Sopenharmony_ci		break;
10208c2ecf20Sopenharmony_ci	}
10218c2ecf20Sopenharmony_ci	if (parsed && !buf[parsed])
10228c2ecf20Sopenharmony_ci		return 0;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* try again without domain */
10258c2ecf20Sopenharmony_ci	*domain = 0;
10268c2ecf20Sopenharmony_ci	switch (sscanf(buf, " %x:%x.%x %n", bus, slot, func, &parsed)) {
10278c2ecf20Sopenharmony_ci	case 2:
10288c2ecf20Sopenharmony_ci		*func = -1;
10298c2ecf20Sopenharmony_ci		sscanf(buf, " %x:%x.* %n", bus, slot, &parsed);
10308c2ecf20Sopenharmony_ci		break;
10318c2ecf20Sopenharmony_ci	case 1:
10328c2ecf20Sopenharmony_ci		*slot = *func = -1;
10338c2ecf20Sopenharmony_ci		sscanf(buf, " %x:*.* %n", bus, &parsed);
10348c2ecf20Sopenharmony_ci		break;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci	if (parsed && !buf[parsed])
10378c2ecf20Sopenharmony_ci		return 0;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	return -EINVAL;
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_cistatic inline int str_to_quirk(const char *buf, int *domain, int *bus, int
10438c2ecf20Sopenharmony_ci			       *slot, int *func, int *reg, int *size, int *mask)
10448c2ecf20Sopenharmony_ci{
10458c2ecf20Sopenharmony_ci	int parsed = 0;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	sscanf(buf, " %x:%x:%x.%x-%x:%x:%x %n", domain, bus, slot, func,
10488c2ecf20Sopenharmony_ci	       reg, size, mask, &parsed);
10498c2ecf20Sopenharmony_ci	if (parsed && !buf[parsed])
10508c2ecf20Sopenharmony_ci		return 0;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/* try again without domain */
10538c2ecf20Sopenharmony_ci	*domain = 0;
10548c2ecf20Sopenharmony_ci	sscanf(buf, " %x:%x.%x-%x:%x:%x %n", bus, slot, func, reg, size,
10558c2ecf20Sopenharmony_ci	       mask, &parsed);
10568c2ecf20Sopenharmony_ci	if (parsed && !buf[parsed])
10578c2ecf20Sopenharmony_ci		return 0;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	return -EINVAL;
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic int pcistub_device_id_add(int domain, int bus, int slot, int func)
10638c2ecf20Sopenharmony_ci{
10648c2ecf20Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
10658c2ecf20Sopenharmony_ci	int rc = 0, devfn = PCI_DEVFN(slot, func);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if (slot < 0) {
10688c2ecf20Sopenharmony_ci		for (slot = 0; !rc && slot < 32; ++slot)
10698c2ecf20Sopenharmony_ci			rc = pcistub_device_id_add(domain, bus, slot, func);
10708c2ecf20Sopenharmony_ci		return rc;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	if (func < 0) {
10748c2ecf20Sopenharmony_ci		for (func = 0; !rc && func < 8; ++func)
10758c2ecf20Sopenharmony_ci			rc = pcistub_device_id_add(domain, bus, slot, func);
10768c2ecf20Sopenharmony_ci		return rc;
10778c2ecf20Sopenharmony_ci	}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if ((
10808c2ecf20Sopenharmony_ci#if !defined(MODULE) /* pci_domains_supported is not being exported */ \
10818c2ecf20Sopenharmony_ci    || !defined(CONFIG_PCI_DOMAINS)
10828c2ecf20Sopenharmony_ci	     !pci_domains_supported ? domain :
10838c2ecf20Sopenharmony_ci#endif
10848c2ecf20Sopenharmony_ci	     domain < 0 || domain > 0xffff)
10858c2ecf20Sopenharmony_ci	    || bus < 0 || bus > 0xff
10868c2ecf20Sopenharmony_ci	    || PCI_SLOT(devfn) != slot
10878c2ecf20Sopenharmony_ci	    || PCI_FUNC(devfn) != func)
10888c2ecf20Sopenharmony_ci		return -EINVAL;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
10918c2ecf20Sopenharmony_ci	if (!pci_dev_id)
10928c2ecf20Sopenharmony_ci		return -ENOMEM;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	pr_debug("wants to seize %04x:%02x:%02x.%d\n",
10958c2ecf20Sopenharmony_ci		 domain, bus, slot, func);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	pcistub_device_id_add_list(pci_dev_id, domain, bus, devfn);
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	return 0;
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_cistatic int pcistub_device_id_remove(int domain, int bus, int slot, int func)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	struct pcistub_device_id *pci_dev_id, *t;
11058c2ecf20Sopenharmony_ci	int err = -ENOENT;
11068c2ecf20Sopenharmony_ci	unsigned long flags;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
11098c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pci_dev_id, t, &pcistub_device_ids,
11108c2ecf20Sopenharmony_ci				 slot_list) {
11118c2ecf20Sopenharmony_ci		if (pci_dev_id->domain == domain && pci_dev_id->bus == bus
11128c2ecf20Sopenharmony_ci		    && (slot < 0 || PCI_SLOT(pci_dev_id->devfn) == slot)
11138c2ecf20Sopenharmony_ci		    && (func < 0 || PCI_FUNC(pci_dev_id->devfn) == func)) {
11148c2ecf20Sopenharmony_ci			/* Don't break; here because it's possible the same
11158c2ecf20Sopenharmony_ci			 * slot could be in the list more than once
11168c2ecf20Sopenharmony_ci			 */
11178c2ecf20Sopenharmony_ci			list_del(&pci_dev_id->slot_list);
11188c2ecf20Sopenharmony_ci			kfree(pci_dev_id);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci			err = 0;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci			pr_debug("removed %04x:%02x:%02x.%d from seize list\n",
11238c2ecf20Sopenharmony_ci				 domain, bus, slot, func);
11248c2ecf20Sopenharmony_ci		}
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	return err;
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic int pcistub_reg_add(int domain, int bus, int slot, int func,
11328c2ecf20Sopenharmony_ci			   unsigned int reg, unsigned int size,
11338c2ecf20Sopenharmony_ci			   unsigned int mask)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	int err = 0;
11368c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
11378c2ecf20Sopenharmony_ci	struct pci_dev *dev;
11388c2ecf20Sopenharmony_ci	struct config_field *field;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	if (reg > 0xfff || (size < 4 && (mask >> (size * 8))))
11418c2ecf20Sopenharmony_ci		return -EINVAL;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
11448c2ecf20Sopenharmony_ci	if (!psdev) {
11458c2ecf20Sopenharmony_ci		err = -ENODEV;
11468c2ecf20Sopenharmony_ci		goto out;
11478c2ecf20Sopenharmony_ci	}
11488c2ecf20Sopenharmony_ci	dev = psdev->dev;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	field = kzalloc(sizeof(*field), GFP_KERNEL);
11518c2ecf20Sopenharmony_ci	if (!field) {
11528c2ecf20Sopenharmony_ci		err = -ENOMEM;
11538c2ecf20Sopenharmony_ci		goto out;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	field->offset = reg;
11578c2ecf20Sopenharmony_ci	field->size = size;
11588c2ecf20Sopenharmony_ci	field->mask = mask;
11598c2ecf20Sopenharmony_ci	field->init = NULL;
11608c2ecf20Sopenharmony_ci	field->reset = NULL;
11618c2ecf20Sopenharmony_ci	field->release = NULL;
11628c2ecf20Sopenharmony_ci	field->clean = xen_pcibk_config_field_free;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	err = xen_pcibk_config_quirks_add_field(dev, field);
11658c2ecf20Sopenharmony_ci	if (err)
11668c2ecf20Sopenharmony_ci		kfree(field);
11678c2ecf20Sopenharmony_ciout:
11688c2ecf20Sopenharmony_ci	if (psdev)
11698c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
11708c2ecf20Sopenharmony_ci	return err;
11718c2ecf20Sopenharmony_ci}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_cistatic ssize_t new_slot_store(struct device_driver *drv, const char *buf,
11748c2ecf20Sopenharmony_ci			      size_t count)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
11778c2ecf20Sopenharmony_ci	int err;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
11808c2ecf20Sopenharmony_ci	if (err)
11818c2ecf20Sopenharmony_ci		goto out;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	err = pcistub_device_id_add(domain, bus, slot, func);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ciout:
11868c2ecf20Sopenharmony_ci	if (!err)
11878c2ecf20Sopenharmony_ci		err = count;
11888c2ecf20Sopenharmony_ci	return err;
11898c2ecf20Sopenharmony_ci}
11908c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(new_slot);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic ssize_t remove_slot_store(struct device_driver *drv, const char *buf,
11938c2ecf20Sopenharmony_ci				 size_t count)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
11968c2ecf20Sopenharmony_ci	int err;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
11998c2ecf20Sopenharmony_ci	if (err)
12008c2ecf20Sopenharmony_ci		goto out;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	err = pcistub_device_id_remove(domain, bus, slot, func);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ciout:
12058c2ecf20Sopenharmony_ci	if (!err)
12068c2ecf20Sopenharmony_ci		err = count;
12078c2ecf20Sopenharmony_ci	return err;
12088c2ecf20Sopenharmony_ci}
12098c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(remove_slot);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic ssize_t slots_show(struct device_driver *drv, char *buf)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
12148c2ecf20Sopenharmony_ci	size_t count = 0;
12158c2ecf20Sopenharmony_ci	unsigned long flags;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
12188c2ecf20Sopenharmony_ci	list_for_each_entry(pci_dev_id, &pcistub_device_ids, slot_list) {
12198c2ecf20Sopenharmony_ci		if (count >= PAGE_SIZE)
12208c2ecf20Sopenharmony_ci			break;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
12238c2ecf20Sopenharmony_ci				   "%04x:%02x:%02x.%d\n",
12248c2ecf20Sopenharmony_ci				   pci_dev_id->domain, pci_dev_id->bus,
12258c2ecf20Sopenharmony_ci				   PCI_SLOT(pci_dev_id->devfn),
12268c2ecf20Sopenharmony_ci				   PCI_FUNC(pci_dev_id->devfn));
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	return count;
12318c2ecf20Sopenharmony_ci}
12328c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(slots);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_cistatic ssize_t irq_handlers_show(struct device_driver *drv, char *buf)
12358c2ecf20Sopenharmony_ci{
12368c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
12378c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
12388c2ecf20Sopenharmony_ci	size_t count = 0;
12398c2ecf20Sopenharmony_ci	unsigned long flags;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
12428c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
12438c2ecf20Sopenharmony_ci		if (count >= PAGE_SIZE)
12448c2ecf20Sopenharmony_ci			break;
12458c2ecf20Sopenharmony_ci		if (!psdev->dev)
12468c2ecf20Sopenharmony_ci			continue;
12478c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
12488c2ecf20Sopenharmony_ci		if (!dev_data)
12498c2ecf20Sopenharmony_ci			continue;
12508c2ecf20Sopenharmony_ci		count +=
12518c2ecf20Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count,
12528c2ecf20Sopenharmony_ci			      "%s:%s:%sing:%ld\n",
12538c2ecf20Sopenharmony_ci			      pci_name(psdev->dev),
12548c2ecf20Sopenharmony_ci			      dev_data->isr_on ? "on" : "off",
12558c2ecf20Sopenharmony_ci			      dev_data->ack_intr ? "ack" : "not ack",
12568c2ecf20Sopenharmony_ci			      dev_data->handled);
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
12598c2ecf20Sopenharmony_ci	return count;
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RO(irq_handlers);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic ssize_t irq_handler_state_store(struct device_driver *drv,
12648c2ecf20Sopenharmony_ci				       const char *buf, size_t count)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
12678c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
12688c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
12698c2ecf20Sopenharmony_ci	int err;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
12728c2ecf20Sopenharmony_ci	if (err)
12738c2ecf20Sopenharmony_ci		return err;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
12768c2ecf20Sopenharmony_ci	if (!psdev) {
12778c2ecf20Sopenharmony_ci		err = -ENOENT;
12788c2ecf20Sopenharmony_ci		goto out;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
12828c2ecf20Sopenharmony_ci	if (!dev_data) {
12838c2ecf20Sopenharmony_ci		err = -ENOENT;
12848c2ecf20Sopenharmony_ci		goto out;
12858c2ecf20Sopenharmony_ci	}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n",
12888c2ecf20Sopenharmony_ci		dev_data->irq_name, dev_data->isr_on,
12898c2ecf20Sopenharmony_ci		!dev_data->isr_on);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	dev_data->isr_on = !(dev_data->isr_on);
12928c2ecf20Sopenharmony_ci	if (dev_data->isr_on)
12938c2ecf20Sopenharmony_ci		dev_data->ack_intr = 1;
12948c2ecf20Sopenharmony_ciout:
12958c2ecf20Sopenharmony_ci	if (psdev)
12968c2ecf20Sopenharmony_ci		pcistub_device_put(psdev);
12978c2ecf20Sopenharmony_ci	if (!err)
12988c2ecf20Sopenharmony_ci		err = count;
12998c2ecf20Sopenharmony_ci	return err;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(irq_handler_state);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic ssize_t quirks_store(struct device_driver *drv, const char *buf,
13048c2ecf20Sopenharmony_ci			    size_t count)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	int domain, bus, slot, func, reg, size, mask;
13078c2ecf20Sopenharmony_ci	int err;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	err = str_to_quirk(buf, &domain, &bus, &slot, &func, &reg, &size,
13108c2ecf20Sopenharmony_ci			   &mask);
13118c2ecf20Sopenharmony_ci	if (err)
13128c2ecf20Sopenharmony_ci		goto out;
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	err = pcistub_reg_add(domain, bus, slot, func, reg, size, mask);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ciout:
13178c2ecf20Sopenharmony_ci	if (!err)
13188c2ecf20Sopenharmony_ci		err = count;
13198c2ecf20Sopenharmony_ci	return err;
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cistatic ssize_t quirks_show(struct device_driver *drv, char *buf)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	int count = 0;
13258c2ecf20Sopenharmony_ci	unsigned long flags;
13268c2ecf20Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
13278c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
13288c2ecf20Sopenharmony_ci	const struct config_field *field;
13298c2ecf20Sopenharmony_ci	const struct config_field_entry *cfg_entry;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
13328c2ecf20Sopenharmony_ci	list_for_each_entry(quirk, &xen_pcibk_quirks, quirks_list) {
13338c2ecf20Sopenharmony_ci		if (count >= PAGE_SIZE)
13348c2ecf20Sopenharmony_ci			goto out;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
13378c2ecf20Sopenharmony_ci				   "%02x:%02x.%01x\n\t%04x:%04x:%04x:%04x\n",
13388c2ecf20Sopenharmony_ci				   quirk->pdev->bus->number,
13398c2ecf20Sopenharmony_ci				   PCI_SLOT(quirk->pdev->devfn),
13408c2ecf20Sopenharmony_ci				   PCI_FUNC(quirk->pdev->devfn),
13418c2ecf20Sopenharmony_ci				   quirk->devid.vendor, quirk->devid.device,
13428c2ecf20Sopenharmony_ci				   quirk->devid.subvendor,
13438c2ecf20Sopenharmony_ci				   quirk->devid.subdevice);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(quirk->pdev);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci		list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
13488c2ecf20Sopenharmony_ci			field = cfg_entry->field;
13498c2ecf20Sopenharmony_ci			if (count >= PAGE_SIZE)
13508c2ecf20Sopenharmony_ci				goto out;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci			count += scnprintf(buf + count, PAGE_SIZE - count,
13538c2ecf20Sopenharmony_ci					   "\t\t%08x:%01x:%08x\n",
13548c2ecf20Sopenharmony_ci					   cfg_entry->base_offset +
13558c2ecf20Sopenharmony_ci					   field->offset, field->size,
13568c2ecf20Sopenharmony_ci					   field->mask);
13578c2ecf20Sopenharmony_ci		}
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ciout:
13618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return count;
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(quirks);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_cistatic ssize_t permissive_store(struct device_driver *drv, const char *buf,
13688c2ecf20Sopenharmony_ci				size_t count)
13698c2ecf20Sopenharmony_ci{
13708c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
13718c2ecf20Sopenharmony_ci	int err;
13728c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
13738c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
13768c2ecf20Sopenharmony_ci	if (err)
13778c2ecf20Sopenharmony_ci		goto out;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
13808c2ecf20Sopenharmony_ci	if (!psdev) {
13818c2ecf20Sopenharmony_ci		err = -ENODEV;
13828c2ecf20Sopenharmony_ci		goto out;
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
13868c2ecf20Sopenharmony_ci	/* the driver data for a device should never be null at this point */
13878c2ecf20Sopenharmony_ci	if (!dev_data) {
13888c2ecf20Sopenharmony_ci		err = -ENXIO;
13898c2ecf20Sopenharmony_ci		goto release;
13908c2ecf20Sopenharmony_ci	}
13918c2ecf20Sopenharmony_ci	if (!dev_data->permissive) {
13928c2ecf20Sopenharmony_ci		dev_data->permissive = 1;
13938c2ecf20Sopenharmony_ci		/* Let user know that what they're doing could be unsafe */
13948c2ecf20Sopenharmony_ci		dev_warn(&psdev->dev->dev, "enabling permissive mode "
13958c2ecf20Sopenharmony_ci			 "configuration space accesses!\n");
13968c2ecf20Sopenharmony_ci		dev_warn(&psdev->dev->dev,
13978c2ecf20Sopenharmony_ci			 "permissive mode is potentially unsafe!\n");
13988c2ecf20Sopenharmony_ci	}
13998c2ecf20Sopenharmony_cirelease:
14008c2ecf20Sopenharmony_ci	pcistub_device_put(psdev);
14018c2ecf20Sopenharmony_ciout:
14028c2ecf20Sopenharmony_ci	if (!err)
14038c2ecf20Sopenharmony_ci		err = count;
14048c2ecf20Sopenharmony_ci	return err;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cistatic ssize_t permissive_show(struct device_driver *drv, char *buf)
14088c2ecf20Sopenharmony_ci{
14098c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
14108c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
14118c2ecf20Sopenharmony_ci	size_t count = 0;
14128c2ecf20Sopenharmony_ci	unsigned long flags;
14138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
14148c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
14158c2ecf20Sopenharmony_ci		if (count >= PAGE_SIZE)
14168c2ecf20Sopenharmony_ci			break;
14178c2ecf20Sopenharmony_ci		if (!psdev->dev)
14188c2ecf20Sopenharmony_ci			continue;
14198c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
14208c2ecf20Sopenharmony_ci		if (!dev_data || !dev_data->permissive)
14218c2ecf20Sopenharmony_ci			continue;
14228c2ecf20Sopenharmony_ci		count +=
14238c2ecf20Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count, "%s\n",
14248c2ecf20Sopenharmony_ci			      pci_name(psdev->dev));
14258c2ecf20Sopenharmony_ci	}
14268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
14278c2ecf20Sopenharmony_ci	return count;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(permissive);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_cistatic ssize_t allow_interrupt_control_store(struct device_driver *drv,
14328c2ecf20Sopenharmony_ci					     const char *buf, size_t count)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
14358c2ecf20Sopenharmony_ci	int err;
14368c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
14378c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
14408c2ecf20Sopenharmony_ci	if (err)
14418c2ecf20Sopenharmony_ci		goto out;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
14448c2ecf20Sopenharmony_ci	if (!psdev) {
14458c2ecf20Sopenharmony_ci		err = -ENODEV;
14468c2ecf20Sopenharmony_ci		goto out;
14478c2ecf20Sopenharmony_ci	}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
14508c2ecf20Sopenharmony_ci	/* the driver data for a device should never be null at this point */
14518c2ecf20Sopenharmony_ci	if (!dev_data) {
14528c2ecf20Sopenharmony_ci		err = -ENXIO;
14538c2ecf20Sopenharmony_ci		goto release;
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci	dev_data->allow_interrupt_control = 1;
14568c2ecf20Sopenharmony_cirelease:
14578c2ecf20Sopenharmony_ci	pcistub_device_put(psdev);
14588c2ecf20Sopenharmony_ciout:
14598c2ecf20Sopenharmony_ci	if (!err)
14608c2ecf20Sopenharmony_ci		err = count;
14618c2ecf20Sopenharmony_ci	return err;
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic ssize_t allow_interrupt_control_show(struct device_driver *drv,
14658c2ecf20Sopenharmony_ci					    char *buf)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	struct pcistub_device *psdev;
14688c2ecf20Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
14698c2ecf20Sopenharmony_ci	size_t count = 0;
14708c2ecf20Sopenharmony_ci	unsigned long flags;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
14738c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
14748c2ecf20Sopenharmony_ci		if (count >= PAGE_SIZE)
14758c2ecf20Sopenharmony_ci			break;
14768c2ecf20Sopenharmony_ci		if (!psdev->dev)
14778c2ecf20Sopenharmony_ci			continue;
14788c2ecf20Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
14798c2ecf20Sopenharmony_ci		if (!dev_data || !dev_data->allow_interrupt_control)
14808c2ecf20Sopenharmony_ci			continue;
14818c2ecf20Sopenharmony_ci		count +=
14828c2ecf20Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count, "%s\n",
14838c2ecf20Sopenharmony_ci			      pci_name(psdev->dev));
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
14868c2ecf20Sopenharmony_ci	return count;
14878c2ecf20Sopenharmony_ci}
14888c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(allow_interrupt_control);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_cistatic void pcistub_exit(void)
14918c2ecf20Sopenharmony_ci{
14928c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_new_slot);
14938c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
14948c2ecf20Sopenharmony_ci			   &driver_attr_remove_slot);
14958c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_slots);
14968c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_quirks);
14978c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
14988c2ecf20Sopenharmony_ci			   &driver_attr_permissive);
14998c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
15008c2ecf20Sopenharmony_ci			   &driver_attr_allow_interrupt_control);
15018c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
15028c2ecf20Sopenharmony_ci			   &driver_attr_irq_handlers);
15038c2ecf20Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
15048c2ecf20Sopenharmony_ci			   &driver_attr_irq_handler_state);
15058c2ecf20Sopenharmony_ci	pci_unregister_driver(&xen_pcibk_pci_driver);
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_cistatic int __init pcistub_init(void)
15098c2ecf20Sopenharmony_ci{
15108c2ecf20Sopenharmony_ci	int pos = 0;
15118c2ecf20Sopenharmony_ci	int err = 0;
15128c2ecf20Sopenharmony_ci	int domain, bus, slot, func;
15138c2ecf20Sopenharmony_ci	int parsed;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (pci_devs_to_hide && *pci_devs_to_hide) {
15168c2ecf20Sopenharmony_ci		do {
15178c2ecf20Sopenharmony_ci			parsed = 0;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci			err = sscanf(pci_devs_to_hide + pos,
15208c2ecf20Sopenharmony_ci				     " (%x:%x:%x.%x) %n",
15218c2ecf20Sopenharmony_ci				     &domain, &bus, &slot, &func, &parsed);
15228c2ecf20Sopenharmony_ci			switch (err) {
15238c2ecf20Sopenharmony_ci			case 3:
15248c2ecf20Sopenharmony_ci				func = -1;
15258c2ecf20Sopenharmony_ci				sscanf(pci_devs_to_hide + pos,
15268c2ecf20Sopenharmony_ci				       " (%x:%x:%x.*) %n",
15278c2ecf20Sopenharmony_ci				       &domain, &bus, &slot, &parsed);
15288c2ecf20Sopenharmony_ci				break;
15298c2ecf20Sopenharmony_ci			case 2:
15308c2ecf20Sopenharmony_ci				slot = func = -1;
15318c2ecf20Sopenharmony_ci				sscanf(pci_devs_to_hide + pos,
15328c2ecf20Sopenharmony_ci				       " (%x:%x:*.*) %n",
15338c2ecf20Sopenharmony_ci				       &domain, &bus, &parsed);
15348c2ecf20Sopenharmony_ci				break;
15358c2ecf20Sopenharmony_ci			}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci			if (!parsed) {
15388c2ecf20Sopenharmony_ci				domain = 0;
15398c2ecf20Sopenharmony_ci				err = sscanf(pci_devs_to_hide + pos,
15408c2ecf20Sopenharmony_ci					     " (%x:%x.%x) %n",
15418c2ecf20Sopenharmony_ci					     &bus, &slot, &func, &parsed);
15428c2ecf20Sopenharmony_ci				switch (err) {
15438c2ecf20Sopenharmony_ci				case 2:
15448c2ecf20Sopenharmony_ci					func = -1;
15458c2ecf20Sopenharmony_ci					sscanf(pci_devs_to_hide + pos,
15468c2ecf20Sopenharmony_ci					       " (%x:%x.*) %n",
15478c2ecf20Sopenharmony_ci					       &bus, &slot, &parsed);
15488c2ecf20Sopenharmony_ci					break;
15498c2ecf20Sopenharmony_ci				case 1:
15508c2ecf20Sopenharmony_ci					slot = func = -1;
15518c2ecf20Sopenharmony_ci					sscanf(pci_devs_to_hide + pos,
15528c2ecf20Sopenharmony_ci					       " (%x:*.*) %n",
15538c2ecf20Sopenharmony_ci					       &bus, &parsed);
15548c2ecf20Sopenharmony_ci					break;
15558c2ecf20Sopenharmony_ci				}
15568c2ecf20Sopenharmony_ci			}
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci			if (parsed <= 0)
15598c2ecf20Sopenharmony_ci				goto parse_error;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci			err = pcistub_device_id_add(domain, bus, slot, func);
15628c2ecf20Sopenharmony_ci			if (err)
15638c2ecf20Sopenharmony_ci				goto out;
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci			pos += parsed;
15668c2ecf20Sopenharmony_ci		} while (pci_devs_to_hide[pos]);
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	/* If we're the first PCI Device Driver to register, we're the
15708c2ecf20Sopenharmony_ci	 * first one to get offered PCI devices as they become
15718c2ecf20Sopenharmony_ci	 * available (and thus we can be the first to grab them)
15728c2ecf20Sopenharmony_ci	 */
15738c2ecf20Sopenharmony_ci	err = pci_register_driver(&xen_pcibk_pci_driver);
15748c2ecf20Sopenharmony_ci	if (err < 0)
15758c2ecf20Sopenharmony_ci		goto out;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	err = driver_create_file(&xen_pcibk_pci_driver.driver,
15788c2ecf20Sopenharmony_ci				 &driver_attr_new_slot);
15798c2ecf20Sopenharmony_ci	if (!err)
15808c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15818c2ecf20Sopenharmony_ci					 &driver_attr_remove_slot);
15828c2ecf20Sopenharmony_ci	if (!err)
15838c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15848c2ecf20Sopenharmony_ci					 &driver_attr_slots);
15858c2ecf20Sopenharmony_ci	if (!err)
15868c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15878c2ecf20Sopenharmony_ci					 &driver_attr_quirks);
15888c2ecf20Sopenharmony_ci	if (!err)
15898c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15908c2ecf20Sopenharmony_ci					 &driver_attr_permissive);
15918c2ecf20Sopenharmony_ci	if (!err)
15928c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15938c2ecf20Sopenharmony_ci					 &driver_attr_allow_interrupt_control);
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	if (!err)
15968c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
15978c2ecf20Sopenharmony_ci					 &driver_attr_irq_handlers);
15988c2ecf20Sopenharmony_ci	if (!err)
15998c2ecf20Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
16008c2ecf20Sopenharmony_ci					&driver_attr_irq_handler_state);
16018c2ecf20Sopenharmony_ci	if (err)
16028c2ecf20Sopenharmony_ci		pcistub_exit();
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ciout:
16058c2ecf20Sopenharmony_ci	return err;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ciparse_error:
16088c2ecf20Sopenharmony_ci	pr_err("Error parsing pci_devs_to_hide at \"%s\"\n",
16098c2ecf20Sopenharmony_ci	       pci_devs_to_hide + pos);
16108c2ecf20Sopenharmony_ci	return -EINVAL;
16118c2ecf20Sopenharmony_ci}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci#ifndef MODULE
16148c2ecf20Sopenharmony_ci/*
16158c2ecf20Sopenharmony_ci * fs_initcall happens before device_initcall
16168c2ecf20Sopenharmony_ci * so xen_pcibk *should* get called first (b/c we
16178c2ecf20Sopenharmony_ci * want to suck up any device before other drivers
16188c2ecf20Sopenharmony_ci * get a chance by being the first pci device
16198c2ecf20Sopenharmony_ci * driver to register)
16208c2ecf20Sopenharmony_ci */
16218c2ecf20Sopenharmony_cifs_initcall(pcistub_init);
16228c2ecf20Sopenharmony_ci#endif
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV
16258c2ecf20Sopenharmony_cistatic struct pcistub_device *find_vfs(const struct pci_dev *pdev)
16268c2ecf20Sopenharmony_ci{
16278c2ecf20Sopenharmony_ci	struct pcistub_device *psdev = NULL;
16288c2ecf20Sopenharmony_ci	unsigned long flags;
16298c2ecf20Sopenharmony_ci	bool found = false;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
16328c2ecf20Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
16338c2ecf20Sopenharmony_ci		if (!psdev->pdev && psdev->dev != pdev
16348c2ecf20Sopenharmony_ci		    && pci_physfn(psdev->dev) == pdev) {
16358c2ecf20Sopenharmony_ci			found = true;
16368c2ecf20Sopenharmony_ci			break;
16378c2ecf20Sopenharmony_ci		}
16388c2ecf20Sopenharmony_ci	}
16398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
16408c2ecf20Sopenharmony_ci	if (found)
16418c2ecf20Sopenharmony_ci		return psdev;
16428c2ecf20Sopenharmony_ci	return NULL;
16438c2ecf20Sopenharmony_ci}
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_cistatic int pci_stub_notifier(struct notifier_block *nb,
16468c2ecf20Sopenharmony_ci			     unsigned long action, void *data)
16478c2ecf20Sopenharmony_ci{
16488c2ecf20Sopenharmony_ci	struct device *dev = data;
16498c2ecf20Sopenharmony_ci	const struct pci_dev *pdev = to_pci_dev(dev);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	if (action != BUS_NOTIFY_UNBIND_DRIVER)
16528c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
16538c2ecf20Sopenharmony_ci
16548c2ecf20Sopenharmony_ci	if (!pdev->is_physfn)
16558c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	for (;;) {
16588c2ecf20Sopenharmony_ci		struct pcistub_device *psdev = find_vfs(pdev);
16598c2ecf20Sopenharmony_ci		if (!psdev)
16608c2ecf20Sopenharmony_ci			break;
16618c2ecf20Sopenharmony_ci		device_release_driver(&psdev->dev->dev);
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_cistatic struct notifier_block pci_stub_nb = {
16678c2ecf20Sopenharmony_ci	.notifier_call = pci_stub_notifier,
16688c2ecf20Sopenharmony_ci};
16698c2ecf20Sopenharmony_ci#endif
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_cistatic int __init xen_pcibk_init(void)
16728c2ecf20Sopenharmony_ci{
16738c2ecf20Sopenharmony_ci	int err;
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci	if (!xen_initial_domain())
16768c2ecf20Sopenharmony_ci		return -ENODEV;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	err = xen_pcibk_config_init();
16798c2ecf20Sopenharmony_ci	if (err)
16808c2ecf20Sopenharmony_ci		return err;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci#ifdef MODULE
16838c2ecf20Sopenharmony_ci	err = pcistub_init();
16848c2ecf20Sopenharmony_ci	if (err < 0)
16858c2ecf20Sopenharmony_ci		return err;
16868c2ecf20Sopenharmony_ci#endif
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	pcistub_init_devices_late();
16898c2ecf20Sopenharmony_ci	err = xen_pcibk_xenbus_register();
16908c2ecf20Sopenharmony_ci	if (err)
16918c2ecf20Sopenharmony_ci		pcistub_exit();
16928c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV
16938c2ecf20Sopenharmony_ci	else
16948c2ecf20Sopenharmony_ci		bus_register_notifier(&pci_bus_type, &pci_stub_nb);
16958c2ecf20Sopenharmony_ci#endif
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	return err;
16988c2ecf20Sopenharmony_ci}
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_cistatic void __exit xen_pcibk_cleanup(void)
17018c2ecf20Sopenharmony_ci{
17028c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV
17038c2ecf20Sopenharmony_ci	bus_unregister_notifier(&pci_bus_type, &pci_stub_nb);
17048c2ecf20Sopenharmony_ci#endif
17058c2ecf20Sopenharmony_ci	xen_pcibk_xenbus_unregister();
17068c2ecf20Sopenharmony_ci	pcistub_exit();
17078c2ecf20Sopenharmony_ci}
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_cimodule_init(xen_pcibk_init);
17108c2ecf20Sopenharmony_cimodule_exit(xen_pcibk_cleanup);
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
17138c2ecf20Sopenharmony_ciMODULE_ALIAS("xen-backend:pci");
1714