162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * PCI Stub Driver - Grabs devices in backend to be exported later
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Ryan Wilson <hap9@epoch.ncsc.mil>
562306a36Sopenharmony_ci * Chris Bookholt <hap10@epoch.ncsc.mil>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci#define dev_fmt pr_fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/rwsem.h>
1462306a36Sopenharmony_ci#include <linux/list.h>
1562306a36Sopenharmony_ci#include <linux/spinlock.h>
1662306a36Sopenharmony_ci#include <linux/kref.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci#include <linux/wait.h>
1962306a36Sopenharmony_ci#include <linux/sched.h>
2062306a36Sopenharmony_ci#include <linux/atomic.h>
2162306a36Sopenharmony_ci#include <xen/events.h>
2262306a36Sopenharmony_ci#include <xen/pci.h>
2362306a36Sopenharmony_ci#include <xen/xen.h>
2462306a36Sopenharmony_ci#include <asm/xen/hypervisor.h>
2562306a36Sopenharmony_ci#include <xen/interface/physdev.h>
2662306a36Sopenharmony_ci#include "pciback.h"
2762306a36Sopenharmony_ci#include "conf_space.h"
2862306a36Sopenharmony_ci#include "conf_space_quirks.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define PCISTUB_DRIVER_NAME "pciback"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic char *pci_devs_to_hide;
3362306a36Sopenharmony_ciwait_queue_head_t xen_pcibk_aer_wait_queue;
3462306a36Sopenharmony_ci/*Add sem for sync AER handling and xen_pcibk remove/reconfigue ops,
3562306a36Sopenharmony_ci* We want to avoid in middle of AER ops, xen_pcibk devices is being removed
3662306a36Sopenharmony_ci*/
3762306a36Sopenharmony_cistatic DECLARE_RWSEM(pcistub_sem);
3862306a36Sopenharmony_cimodule_param_named(hide, pci_devs_to_hide, charp, 0444);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct pcistub_device_id {
4162306a36Sopenharmony_ci	struct list_head slot_list;
4262306a36Sopenharmony_ci	int domain;
4362306a36Sopenharmony_ci	unsigned char bus;
4462306a36Sopenharmony_ci	unsigned int devfn;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_cistatic LIST_HEAD(pcistub_device_ids);
4762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(device_ids_lock);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct pcistub_device {
5062306a36Sopenharmony_ci	struct kref kref;
5162306a36Sopenharmony_ci	struct list_head dev_list;
5262306a36Sopenharmony_ci	spinlock_t lock;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	struct pci_dev *dev;
5562306a36Sopenharmony_ci	struct xen_pcibk_device *pdev;/* non-NULL if struct pci_dev is in use */
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Access to pcistub_devices & seized_devices lists and the initialize_devices
5962306a36Sopenharmony_ci * flag must be locked with pcistub_devices_lock
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pcistub_devices_lock);
6262306a36Sopenharmony_cistatic LIST_HEAD(pcistub_devices);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* wait for device_initcall before initializing our devices
6562306a36Sopenharmony_ci * (see pcistub_init_devices_late)
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic int initialize_devices;
6862306a36Sopenharmony_cistatic LIST_HEAD(seized_devices);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct pcistub_device *psdev;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	dev_dbg(&dev->dev, "pcistub_device_alloc\n");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	psdev = kzalloc(sizeof(*psdev), GFP_KERNEL);
7762306a36Sopenharmony_ci	if (!psdev)
7862306a36Sopenharmony_ci		return NULL;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	psdev->dev = pci_dev_get(dev);
8162306a36Sopenharmony_ci	if (!psdev->dev) {
8262306a36Sopenharmony_ci		kfree(psdev);
8362306a36Sopenharmony_ci		return NULL;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	kref_init(&psdev->kref);
8762306a36Sopenharmony_ci	spin_lock_init(&psdev->lock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return psdev;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* Don't call this directly as it's called by pcistub_device_put */
9362306a36Sopenharmony_cistatic void pcistub_device_release(struct kref *kref)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct pcistub_device *psdev;
9662306a36Sopenharmony_ci	struct pci_dev *dev;
9762306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	psdev = container_of(kref, struct pcistub_device, kref);
10062306a36Sopenharmony_ci	dev = psdev->dev;
10162306a36Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	dev_dbg(&dev->dev, "pcistub_device_release\n");
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	xen_unregister_device_domain_owner(dev);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Call the reset function which does not take lock as this
10862306a36Sopenharmony_ci	 * is called from "unbind" which takes a device_lock mutex.
10962306a36Sopenharmony_ci	 */
11062306a36Sopenharmony_ci	__pci_reset_function_locked(dev);
11162306a36Sopenharmony_ci	if (dev_data &&
11262306a36Sopenharmony_ci	    pci_load_and_free_saved_state(dev, &dev_data->pci_saved_state))
11362306a36Sopenharmony_ci		dev_info(&dev->dev, "Could not reload PCI state\n");
11462306a36Sopenharmony_ci	else
11562306a36Sopenharmony_ci		pci_restore_state(dev);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (dev->msix_cap) {
11862306a36Sopenharmony_ci		struct physdev_pci_device ppdev = {
11962306a36Sopenharmony_ci			.seg = pci_domain_nr(dev->bus),
12062306a36Sopenharmony_ci			.bus = dev->bus->number,
12162306a36Sopenharmony_ci			.devfn = dev->devfn
12262306a36Sopenharmony_ci		};
12362306a36Sopenharmony_ci		int err = HYPERVISOR_physdev_op(PHYSDEVOP_release_msix,
12462306a36Sopenharmony_ci						&ppdev);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		if (err && err != -ENOSYS)
12762306a36Sopenharmony_ci			dev_warn(&dev->dev, "MSI-X release failed (%d)\n",
12862306a36Sopenharmony_ci				 err);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Disable the device */
13262306a36Sopenharmony_ci	xen_pcibk_reset_device(dev);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	kfree(dev_data);
13562306a36Sopenharmony_ci	pci_set_drvdata(dev, NULL);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Clean-up the device */
13862306a36Sopenharmony_ci	xen_pcibk_config_free_dyn_fields(dev);
13962306a36Sopenharmony_ci	xen_pcibk_config_free_dev(dev);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	pci_clear_dev_assigned(dev);
14262306a36Sopenharmony_ci	pci_dev_put(dev);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	kfree(psdev);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic inline void pcistub_device_get(struct pcistub_device *psdev)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	kref_get(&psdev->kref);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic inline void pcistub_device_put(struct pcistub_device *psdev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	kref_put(&psdev->kref, pcistub_device_release);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct pcistub_device *pcistub_device_find_locked(int domain, int bus,
15862306a36Sopenharmony_ci							 int slot, int func)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct pcistub_device *psdev;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
16362306a36Sopenharmony_ci		if (psdev->dev != NULL
16462306a36Sopenharmony_ci		    && domain == pci_domain_nr(psdev->dev->bus)
16562306a36Sopenharmony_ci		    && bus == psdev->dev->bus->number
16662306a36Sopenharmony_ci		    && slot == PCI_SLOT(psdev->dev->devfn)
16762306a36Sopenharmony_ci		    && func == PCI_FUNC(psdev->dev->devfn)) {
16862306a36Sopenharmony_ci			return psdev;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	return NULL;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic struct pcistub_device *pcistub_device_find(int domain, int bus,
17662306a36Sopenharmony_ci						  int slot, int func)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct pcistub_device *psdev;
17962306a36Sopenharmony_ci	unsigned long flags;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	psdev = pcistub_device_find_locked(domain, bus, slot, func);
18462306a36Sopenharmony_ci	if (psdev)
18562306a36Sopenharmony_ci		pcistub_device_get(psdev);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
18862306a36Sopenharmony_ci	return psdev;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic struct pci_dev *pcistub_device_get_pci_dev(struct xen_pcibk_device *pdev,
19262306a36Sopenharmony_ci						  struct pcistub_device *psdev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct pci_dev *pci_dev = NULL;
19562306a36Sopenharmony_ci	unsigned long flags;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	spin_lock_irqsave(&psdev->lock, flags);
19862306a36Sopenharmony_ci	if (!psdev->pdev) {
19962306a36Sopenharmony_ci		psdev->pdev = pdev;
20062306a36Sopenharmony_ci		pci_dev = psdev->dev;
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	spin_unlock_irqrestore(&psdev->lock, flags);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (pci_dev)
20562306a36Sopenharmony_ci		pcistub_device_get(psdev);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return pci_dev;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistruct pci_dev *pcistub_get_pci_dev_by_slot(struct xen_pcibk_device *pdev,
21162306a36Sopenharmony_ci					    int domain, int bus,
21262306a36Sopenharmony_ci					    int slot, int func)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct pcistub_device *psdev;
21562306a36Sopenharmony_ci	struct pci_dev *found_dev = NULL;
21662306a36Sopenharmony_ci	unsigned long flags;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	psdev = pcistub_device_find_locked(domain, bus, slot, func);
22162306a36Sopenharmony_ci	if (psdev)
22262306a36Sopenharmony_ci		found_dev = pcistub_device_get_pci_dev(pdev, psdev);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
22562306a36Sopenharmony_ci	return found_dev;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistruct pci_dev *pcistub_get_pci_dev(struct xen_pcibk_device *pdev,
22962306a36Sopenharmony_ci				    struct pci_dev *dev)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct pcistub_device *psdev;
23262306a36Sopenharmony_ci	struct pci_dev *found_dev = NULL;
23362306a36Sopenharmony_ci	unsigned long flags;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
23862306a36Sopenharmony_ci		if (psdev->dev == dev) {
23962306a36Sopenharmony_ci			found_dev = pcistub_device_get_pci_dev(pdev, psdev);
24062306a36Sopenharmony_ci			break;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
24562306a36Sopenharmony_ci	return found_dev;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/*
24962306a36Sopenharmony_ci * Called when:
25062306a36Sopenharmony_ci *  - XenBus state has been reconfigure (pci unplug). See xen_pcibk_remove_device
25162306a36Sopenharmony_ci *  - XenBus state has been disconnected (guest shutdown). See xen_pcibk_xenbus_remove
25262306a36Sopenharmony_ci *  - 'echo BDF > unbind' on pciback module with no guest attached. See pcistub_remove
25362306a36Sopenharmony_ci *  - 'echo BDF > unbind' with a guest still using it. See pcistub_remove
25462306a36Sopenharmony_ci *
25562306a36Sopenharmony_ci *  As such we have to be careful.
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci *  To make this easier, the caller has to hold the device lock.
25862306a36Sopenharmony_ci */
25962306a36Sopenharmony_civoid pcistub_put_pci_dev(struct pci_dev *dev)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct pcistub_device *psdev, *found_psdev = NULL;
26262306a36Sopenharmony_ci	unsigned long flags;
26362306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
26462306a36Sopenharmony_ci	int ret;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
26962306a36Sopenharmony_ci		if (psdev->dev == dev) {
27062306a36Sopenharmony_ci			found_psdev = psdev;
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
27662306a36Sopenharmony_ci	if (WARN_ON(!found_psdev))
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*hold this lock for avoiding breaking link between
28062306a36Sopenharmony_ci	* pcistub and xen_pcibk when AER is in processing
28162306a36Sopenharmony_ci	*/
28262306a36Sopenharmony_ci	down_write(&pcistub_sem);
28362306a36Sopenharmony_ci	/* Cleanup our device
28462306a36Sopenharmony_ci	 * (so it's ready for the next domain)
28562306a36Sopenharmony_ci	 */
28662306a36Sopenharmony_ci	device_lock_assert(&dev->dev);
28762306a36Sopenharmony_ci	__pci_reset_function_locked(dev);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	dev_data = pci_get_drvdata(dev);
29062306a36Sopenharmony_ci	ret = pci_load_saved_state(dev, dev_data->pci_saved_state);
29162306a36Sopenharmony_ci	if (!ret) {
29262306a36Sopenharmony_ci		/*
29362306a36Sopenharmony_ci		 * The usual sequence is pci_save_state & pci_restore_state
29462306a36Sopenharmony_ci		 * but the guest might have messed the configuration space up.
29562306a36Sopenharmony_ci		 * Use the initial version (when device was bound to us).
29662306a36Sopenharmony_ci		 */
29762306a36Sopenharmony_ci		pci_restore_state(dev);
29862306a36Sopenharmony_ci	} else
29962306a36Sopenharmony_ci		dev_info(&dev->dev, "Could not reload PCI state\n");
30062306a36Sopenharmony_ci	/* This disables the device. */
30162306a36Sopenharmony_ci	xen_pcibk_reset_device(dev);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	/* And cleanup up our emulated fields. */
30462306a36Sopenharmony_ci	xen_pcibk_config_reset_dev(dev);
30562306a36Sopenharmony_ci	xen_pcibk_config_free_dyn_fields(dev);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	dev_data->allow_interrupt_control = 0;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	xen_unregister_device_domain_owner(dev);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	spin_lock_irqsave(&found_psdev->lock, flags);
31262306a36Sopenharmony_ci	found_psdev->pdev = NULL;
31362306a36Sopenharmony_ci	spin_unlock_irqrestore(&found_psdev->lock, flags);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	pcistub_device_put(found_psdev);
31662306a36Sopenharmony_ci	up_write(&pcistub_sem);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int pcistub_match_one(struct pci_dev *dev,
32062306a36Sopenharmony_ci			     struct pcistub_device_id *pdev_id)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	/* Match the specified device by domain, bus, slot, func and also if
32362306a36Sopenharmony_ci	 * any of the device's parent bridges match.
32462306a36Sopenharmony_ci	 */
32562306a36Sopenharmony_ci	for (; dev != NULL; dev = dev->bus->self) {
32662306a36Sopenharmony_ci		if (pci_domain_nr(dev->bus) == pdev_id->domain
32762306a36Sopenharmony_ci		    && dev->bus->number == pdev_id->bus
32862306a36Sopenharmony_ci		    && dev->devfn == pdev_id->devfn)
32962306a36Sopenharmony_ci			return 1;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		/* Sometimes topmost bridge links to itself. */
33262306a36Sopenharmony_ci		if (dev == dev->bus->self)
33362306a36Sopenharmony_ci			break;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return 0;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic int pcistub_match(struct pci_dev *dev)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct pcistub_device_id *pdev_id;
34262306a36Sopenharmony_ci	unsigned long flags;
34362306a36Sopenharmony_ci	int found = 0;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
34662306a36Sopenharmony_ci	list_for_each_entry(pdev_id, &pcistub_device_ids, slot_list) {
34762306a36Sopenharmony_ci		if (pcistub_match_one(dev, pdev_id)) {
34862306a36Sopenharmony_ci			found = 1;
34962306a36Sopenharmony_ci			break;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return found;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int pcistub_init_device(struct pci_dev *dev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
36062306a36Sopenharmony_ci	int err = 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	dev_dbg(&dev->dev, "initializing...\n");
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* The PCI backend is not intended to be a module (or to work with
36562306a36Sopenharmony_ci	 * removable PCI devices (yet). If it were, xen_pcibk_config_free()
36662306a36Sopenharmony_ci	 * would need to be called somewhere to free the memory allocated
36762306a36Sopenharmony_ci	 * here and then to call kfree(pci_get_drvdata(psdev->dev)).
36862306a36Sopenharmony_ci	 */
36962306a36Sopenharmony_ci	dev_data = kzalloc(sizeof(*dev_data) +  strlen(DRV_NAME "[]")
37062306a36Sopenharmony_ci				+ strlen(pci_name(dev)) + 1, GFP_KERNEL);
37162306a36Sopenharmony_ci	if (!dev_data) {
37262306a36Sopenharmony_ci		err = -ENOMEM;
37362306a36Sopenharmony_ci		goto out;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	pci_set_drvdata(dev, dev_data);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/*
37862306a36Sopenharmony_ci	 * Setup name for fake IRQ handler. It will only be enabled
37962306a36Sopenharmony_ci	 * once the device is turned on by the guest.
38062306a36Sopenharmony_ci	 */
38162306a36Sopenharmony_ci	sprintf(dev_data->irq_name, DRV_NAME "[%s]", pci_name(dev));
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	dev_dbg(&dev->dev, "initializing config\n");
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	init_waitqueue_head(&xen_pcibk_aer_wait_queue);
38662306a36Sopenharmony_ci	err = xen_pcibk_config_init_dev(dev);
38762306a36Sopenharmony_ci	if (err)
38862306a36Sopenharmony_ci		goto out;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* HACK: Force device (& ACPI) to determine what IRQ it's on - we
39162306a36Sopenharmony_ci	 * must do this here because pcibios_enable_device may specify
39262306a36Sopenharmony_ci	 * the pci device's true irq (and possibly its other resources)
39362306a36Sopenharmony_ci	 * if they differ from what's in the configuration space.
39462306a36Sopenharmony_ci	 * This makes the assumption that the device's resources won't
39562306a36Sopenharmony_ci	 * change after this point (otherwise this code may break!)
39662306a36Sopenharmony_ci	 */
39762306a36Sopenharmony_ci	dev_dbg(&dev->dev, "enabling device\n");
39862306a36Sopenharmony_ci	err = pci_enable_device(dev);
39962306a36Sopenharmony_ci	if (err)
40062306a36Sopenharmony_ci		goto config_release;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (dev->msix_cap) {
40362306a36Sopenharmony_ci		struct physdev_pci_device ppdev = {
40462306a36Sopenharmony_ci			.seg = pci_domain_nr(dev->bus),
40562306a36Sopenharmony_ci			.bus = dev->bus->number,
40662306a36Sopenharmony_ci			.devfn = dev->devfn
40762306a36Sopenharmony_ci		};
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		err = HYPERVISOR_physdev_op(PHYSDEVOP_prepare_msix, &ppdev);
41062306a36Sopenharmony_ci		if (err && err != -ENOSYS)
41162306a36Sopenharmony_ci			dev_err(&dev->dev, "MSI-X preparation failed (%d)\n",
41262306a36Sopenharmony_ci				err);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* We need the device active to save the state. */
41662306a36Sopenharmony_ci	dev_dbg(&dev->dev, "save state of device\n");
41762306a36Sopenharmony_ci	pci_save_state(dev);
41862306a36Sopenharmony_ci	dev_data->pci_saved_state = pci_store_saved_state(dev);
41962306a36Sopenharmony_ci	if (!dev_data->pci_saved_state)
42062306a36Sopenharmony_ci		dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
42162306a36Sopenharmony_ci	else {
42262306a36Sopenharmony_ci		dev_dbg(&dev->dev, "resetting (FLR, D3, etc) the device\n");
42362306a36Sopenharmony_ci		__pci_reset_function_locked(dev);
42462306a36Sopenharmony_ci		pci_restore_state(dev);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	/* Now disable the device (this also ensures some private device
42762306a36Sopenharmony_ci	 * data is setup before we export)
42862306a36Sopenharmony_ci	 */
42962306a36Sopenharmony_ci	dev_dbg(&dev->dev, "reset device\n");
43062306a36Sopenharmony_ci	xen_pcibk_reset_device(dev);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	pci_set_dev_assigned(dev);
43362306a36Sopenharmony_ci	return 0;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ciconfig_release:
43662306a36Sopenharmony_ci	xen_pcibk_config_free_dev(dev);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciout:
43962306a36Sopenharmony_ci	pci_set_drvdata(dev, NULL);
44062306a36Sopenharmony_ci	kfree(dev_data);
44162306a36Sopenharmony_ci	return err;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/*
44562306a36Sopenharmony_ci * Because some initialization still happens on
44662306a36Sopenharmony_ci * devices during fs_initcall, we need to defer
44762306a36Sopenharmony_ci * full initialization of our devices until
44862306a36Sopenharmony_ci * device_initcall.
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_cistatic int __init pcistub_init_devices_late(void)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct pcistub_device *psdev;
45362306a36Sopenharmony_ci	unsigned long flags;
45462306a36Sopenharmony_ci	int err = 0;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	while (!list_empty(&seized_devices)) {
45962306a36Sopenharmony_ci		psdev = container_of(seized_devices.next,
46062306a36Sopenharmony_ci				     struct pcistub_device, dev_list);
46162306a36Sopenharmony_ci		list_del(&psdev->dev_list);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		err = pcistub_init_device(psdev->dev);
46662306a36Sopenharmony_ci		if (err) {
46762306a36Sopenharmony_ci			dev_err(&psdev->dev->dev,
46862306a36Sopenharmony_ci				"error %d initializing device\n", err);
46962306a36Sopenharmony_ci			kfree(psdev);
47062306a36Sopenharmony_ci			psdev = NULL;
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		if (psdev)
47662306a36Sopenharmony_ci			list_add_tail(&psdev->dev_list, &pcistub_devices);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	initialize_devices = 1;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return 0;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void pcistub_device_id_add_list(struct pcistub_device_id *new,
48762306a36Sopenharmony_ci				       int domain, int bus, unsigned int devfn)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
49062306a36Sopenharmony_ci	unsigned long flags;
49162306a36Sopenharmony_ci	int found = 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	list_for_each_entry(pci_dev_id, &pcistub_device_ids, slot_list) {
49662306a36Sopenharmony_ci		if (pci_dev_id->domain == domain && pci_dev_id->bus == bus &&
49762306a36Sopenharmony_ci		    pci_dev_id->devfn == devfn) {
49862306a36Sopenharmony_ci			found = 1;
49962306a36Sopenharmony_ci			break;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!found) {
50462306a36Sopenharmony_ci		new->domain = domain;
50562306a36Sopenharmony_ci		new->bus = bus;
50662306a36Sopenharmony_ci		new->devfn = devfn;
50762306a36Sopenharmony_ci		list_add_tail(&new->slot_list, &pcistub_device_ids);
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (found)
51362306a36Sopenharmony_ci		kfree(new);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int pcistub_seize(struct pci_dev *dev,
51762306a36Sopenharmony_ci			 struct pcistub_device_id *pci_dev_id)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct pcistub_device *psdev;
52062306a36Sopenharmony_ci	unsigned long flags;
52162306a36Sopenharmony_ci	int err = 0;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	psdev = pcistub_device_alloc(dev);
52462306a36Sopenharmony_ci	if (!psdev) {
52562306a36Sopenharmony_ci		kfree(pci_dev_id);
52662306a36Sopenharmony_ci		return -ENOMEM;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (initialize_devices) {
53262306a36Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		/* don't want irqs disabled when calling pcistub_init_device */
53562306a36Sopenharmony_ci		err = pcistub_init_device(psdev->dev);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		if (!err)
54062306a36Sopenharmony_ci			list_add(&psdev->dev_list, &pcistub_devices);
54162306a36Sopenharmony_ci	} else {
54262306a36Sopenharmony_ci		dev_dbg(&dev->dev, "deferring initialization\n");
54362306a36Sopenharmony_ci		list_add(&psdev->dev_list, &seized_devices);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (err) {
54962306a36Sopenharmony_ci		kfree(pci_dev_id);
55062306a36Sopenharmony_ci		pcistub_device_put(psdev);
55162306a36Sopenharmony_ci	} else if (pci_dev_id)
55262306a36Sopenharmony_ci		pcistub_device_id_add_list(pci_dev_id, pci_domain_nr(dev->bus),
55362306a36Sopenharmony_ci					   dev->bus->number, dev->devfn);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return err;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/* Called when 'bind'. This means we must _NOT_ call pci_reset_function or
55962306a36Sopenharmony_ci * other functions that take the sysfs lock. */
56062306a36Sopenharmony_cistatic int pcistub_probe(struct pci_dev *dev, const struct pci_device_id *id)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	int err = 0, match;
56362306a36Sopenharmony_ci	struct pcistub_device_id *pci_dev_id = NULL;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	dev_dbg(&dev->dev, "probing...\n");
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	match = pcistub_match(dev);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if ((dev->driver_override &&
57062306a36Sopenharmony_ci	     !strcmp(dev->driver_override, PCISTUB_DRIVER_NAME)) ||
57162306a36Sopenharmony_ci	    match) {
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL
57462306a36Sopenharmony_ci		    && dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
57562306a36Sopenharmony_ci			dev_err(&dev->dev, "can't export pci devices that "
57662306a36Sopenharmony_ci				"don't have a normal (0) or bridge (1) "
57762306a36Sopenharmony_ci				"header type!\n");
57862306a36Sopenharmony_ci			err = -ENODEV;
57962306a36Sopenharmony_ci			goto out;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		if (!match) {
58362306a36Sopenharmony_ci			pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
58462306a36Sopenharmony_ci			if (!pci_dev_id) {
58562306a36Sopenharmony_ci				err = -ENOMEM;
58662306a36Sopenharmony_ci				goto out;
58762306a36Sopenharmony_ci			}
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		dev_info(&dev->dev, "seizing device\n");
59162306a36Sopenharmony_ci		err = pcistub_seize(dev, pci_dev_id);
59262306a36Sopenharmony_ci	} else
59362306a36Sopenharmony_ci		/* Didn't find the device */
59462306a36Sopenharmony_ci		err = -ENODEV;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ciout:
59762306a36Sopenharmony_ci	return err;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/* Called when 'unbind'. This means we must _NOT_ call pci_reset_function or
60162306a36Sopenharmony_ci * other functions that take the sysfs lock. */
60262306a36Sopenharmony_cistatic void pcistub_remove(struct pci_dev *dev)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct pcistub_device *psdev, *found_psdev = NULL;
60562306a36Sopenharmony_ci	unsigned long flags;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	dev_dbg(&dev->dev, "removing\n");
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	xen_pcibk_config_quirk_release(dev);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
61462306a36Sopenharmony_ci		if (psdev->dev == dev) {
61562306a36Sopenharmony_ci			found_psdev = psdev;
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (found_psdev) {
62362306a36Sopenharmony_ci		dev_dbg(&dev->dev, "found device to remove %s\n",
62462306a36Sopenharmony_ci			found_psdev->pdev ? "- in-use" : "");
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		if (found_psdev->pdev) {
62762306a36Sopenharmony_ci			int domid = xen_find_device_domain_owner(dev);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci			dev_warn(&dev->dev, "****** removing device %s while still in-use by domain %d! ******\n",
63062306a36Sopenharmony_ci			       pci_name(found_psdev->dev), domid);
63162306a36Sopenharmony_ci			dev_warn(&dev->dev, "****** driver domain may still access this device's i/o resources!\n");
63262306a36Sopenharmony_ci			dev_warn(&dev->dev, "****** shutdown driver domain before binding device\n");
63362306a36Sopenharmony_ci			dev_warn(&dev->dev, "****** to other drivers or domains\n");
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci			/* N.B. This ends up calling pcistub_put_pci_dev which ends up
63662306a36Sopenharmony_ci			 * doing the FLR. */
63762306a36Sopenharmony_ci			xen_pcibk_release_pci_dev(found_psdev->pdev,
63862306a36Sopenharmony_ci						found_psdev->dev,
63962306a36Sopenharmony_ci						false /* caller holds the lock. */);
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		spin_lock_irqsave(&pcistub_devices_lock, flags);
64362306a36Sopenharmony_ci		list_del(&found_psdev->dev_list);
64462306a36Sopenharmony_ci		spin_unlock_irqrestore(&pcistub_devices_lock, flags);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		/* the final put for releasing from the list */
64762306a36Sopenharmony_ci		pcistub_device_put(found_psdev);
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic const struct pci_device_id pcistub_ids[] = {
65262306a36Sopenharmony_ci	{
65362306a36Sopenharmony_ci	 .vendor = PCI_ANY_ID,
65462306a36Sopenharmony_ci	 .device = PCI_ANY_ID,
65562306a36Sopenharmony_ci	 .subvendor = PCI_ANY_ID,
65662306a36Sopenharmony_ci	 .subdevice = PCI_ANY_ID,
65762306a36Sopenharmony_ci	 },
65862306a36Sopenharmony_ci	{0,},
65962306a36Sopenharmony_ci};
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci#define PCI_NODENAME_MAX 40
66262306a36Sopenharmony_cistatic void kill_domain_by_device(struct pcistub_device *psdev)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct xenbus_transaction xbt;
66562306a36Sopenharmony_ci	int err;
66662306a36Sopenharmony_ci	char nodename[PCI_NODENAME_MAX];
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	BUG_ON(!psdev);
66962306a36Sopenharmony_ci	snprintf(nodename, PCI_NODENAME_MAX, "/local/domain/0/backend/pci/%d/0",
67062306a36Sopenharmony_ci		psdev->pdev->xdev->otherend_id);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ciagain:
67362306a36Sopenharmony_ci	err = xenbus_transaction_start(&xbt);
67462306a36Sopenharmony_ci	if (err) {
67562306a36Sopenharmony_ci		dev_err(&psdev->dev->dev,
67662306a36Sopenharmony_ci			"error %d when start xenbus transaction\n", err);
67762306a36Sopenharmony_ci		return;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	/*PV AER handlers will set this flag*/
68062306a36Sopenharmony_ci	xenbus_printf(xbt, nodename, "aerState" , "aerfail");
68162306a36Sopenharmony_ci	err = xenbus_transaction_end(xbt, 0);
68262306a36Sopenharmony_ci	if (err) {
68362306a36Sopenharmony_ci		if (err == -EAGAIN)
68462306a36Sopenharmony_ci			goto again;
68562306a36Sopenharmony_ci		dev_err(&psdev->dev->dev,
68662306a36Sopenharmony_ci			"error %d when end xenbus transaction\n", err);
68762306a36Sopenharmony_ci		return;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci/* For each aer recovery step error_detected, mmio_enabled, etc, front_end and
69262306a36Sopenharmony_ci * backend need to have cooperation. In xen_pcibk, those steps will do similar
69362306a36Sopenharmony_ci * jobs: send service request and waiting for front_end response.
69462306a36Sopenharmony_ci*/
69562306a36Sopenharmony_cistatic pci_ers_result_t common_process(struct pcistub_device *psdev,
69662306a36Sopenharmony_ci				       pci_channel_state_t state, int aer_cmd,
69762306a36Sopenharmony_ci				       pci_ers_result_t result)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	pci_ers_result_t res = result;
70062306a36Sopenharmony_ci	struct xen_pcie_aer_op *aer_op;
70162306a36Sopenharmony_ci	struct xen_pcibk_device *pdev = psdev->pdev;
70262306a36Sopenharmony_ci	struct xen_pci_sharedinfo *sh_info = pdev->sh_info;
70362306a36Sopenharmony_ci	int ret;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	/*with PV AER drivers*/
70662306a36Sopenharmony_ci	aer_op = &(sh_info->aer_op);
70762306a36Sopenharmony_ci	aer_op->cmd = aer_cmd ;
70862306a36Sopenharmony_ci	/*useful for error_detected callback*/
70962306a36Sopenharmony_ci	aer_op->err = state;
71062306a36Sopenharmony_ci	/*pcifront_end BDF*/
71162306a36Sopenharmony_ci	ret = xen_pcibk_get_pcifront_dev(psdev->dev, psdev->pdev,
71262306a36Sopenharmony_ci		&aer_op->domain, &aer_op->bus, &aer_op->devfn);
71362306a36Sopenharmony_ci	if (!ret) {
71462306a36Sopenharmony_ci		dev_err(&psdev->dev->dev, "failed to get pcifront device\n");
71562306a36Sopenharmony_ci		return PCI_ERS_RESULT_NONE;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	wmb();
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	dev_dbg(&psdev->dev->dev, "aer_op %x dom %x bus %x devfn %x\n",
72062306a36Sopenharmony_ci			aer_cmd, aer_op->domain, aer_op->bus, aer_op->devfn);
72162306a36Sopenharmony_ci	/*local flag to mark there's aer request, xen_pcibk callback will use
72262306a36Sopenharmony_ci	* this flag to judge whether we need to check pci-front give aer
72362306a36Sopenharmony_ci	* service ack signal
72462306a36Sopenharmony_ci	*/
72562306a36Sopenharmony_ci	set_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	/*It is possible that a pcifront conf_read_write ops request invokes
72862306a36Sopenharmony_ci	* the callback which cause the spurious execution of wake_up.
72962306a36Sopenharmony_ci	* Yet it is harmless and better than a spinlock here
73062306a36Sopenharmony_ci	*/
73162306a36Sopenharmony_ci	set_bit(_XEN_PCIB_active,
73262306a36Sopenharmony_ci		(unsigned long *)&sh_info->flags);
73362306a36Sopenharmony_ci	wmb();
73462306a36Sopenharmony_ci	notify_remote_via_irq(pdev->evtchn_irq);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* Enable IRQ to signal "request done". */
73762306a36Sopenharmony_ci	xen_pcibk_lateeoi(pdev, 0);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	ret = wait_event_timeout(xen_pcibk_aer_wait_queue,
74062306a36Sopenharmony_ci				 !(test_bit(_XEN_PCIB_active, (unsigned long *)
74162306a36Sopenharmony_ci				 &sh_info->flags)), 300*HZ);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	/* Enable IRQ for pcifront request if not already active. */
74462306a36Sopenharmony_ci	if (!test_bit(_PDEVF_op_active, &pdev->flags))
74562306a36Sopenharmony_ci		xen_pcibk_lateeoi(pdev, 0);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (!ret) {
74862306a36Sopenharmony_ci		if (test_bit(_XEN_PCIB_active,
74962306a36Sopenharmony_ci			(unsigned long *)&sh_info->flags)) {
75062306a36Sopenharmony_ci			dev_err(&psdev->dev->dev,
75162306a36Sopenharmony_ci				"pcifront aer process not responding!\n");
75262306a36Sopenharmony_ci			clear_bit(_XEN_PCIB_active,
75362306a36Sopenharmony_ci			  (unsigned long *)&sh_info->flags);
75462306a36Sopenharmony_ci			aer_op->err = PCI_ERS_RESULT_NONE;
75562306a36Sopenharmony_ci			return res;
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci	}
75862306a36Sopenharmony_ci	clear_bit(_PCIB_op_pending, (unsigned long *)&pdev->flags);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	res = (pci_ers_result_t)aer_op->err;
76162306a36Sopenharmony_ci	return res;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci/*
76562306a36Sopenharmony_ci* xen_pcibk_slot_reset: it will send the slot_reset request to  pcifront in case
76662306a36Sopenharmony_ci* of the device driver could provide this service, and then wait for pcifront
76762306a36Sopenharmony_ci* ack.
76862306a36Sopenharmony_ci* @dev: pointer to PCI devices
76962306a36Sopenharmony_ci* return value is used by aer_core do_recovery policy
77062306a36Sopenharmony_ci*/
77162306a36Sopenharmony_cistatic pci_ers_result_t xen_pcibk_slot_reset(struct pci_dev *dev)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct pcistub_device *psdev;
77462306a36Sopenharmony_ci	pci_ers_result_t result;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	result = PCI_ERS_RESULT_RECOVERED;
77762306a36Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_slot_reset(bus:%x,devfn:%x)\n",
77862306a36Sopenharmony_ci		dev->bus->number, dev->devfn);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	down_write(&pcistub_sem);
78162306a36Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
78262306a36Sopenharmony_ci				dev->bus->number,
78362306a36Sopenharmony_ci				PCI_SLOT(dev->devfn),
78462306a36Sopenharmony_ci				PCI_FUNC(dev->devfn));
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (!psdev || !psdev->pdev) {
78762306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
78862306a36Sopenharmony_ci		goto end;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (!psdev->pdev->sh_info) {
79262306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
79362306a36Sopenharmony_ci			" by HVM, kill it\n");
79462306a36Sopenharmony_ci		kill_domain_by_device(psdev);
79562306a36Sopenharmony_ci		goto end;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
79962306a36Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
80062306a36Sopenharmony_ci		dev_err(&dev->dev,
80162306a36Sopenharmony_ci			"guest with no AER driver should have been killed\n");
80262306a36Sopenharmony_ci		goto end;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci	result = common_process(psdev, pci_channel_io_normal, XEN_PCI_OP_aer_slotreset, result);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
80762306a36Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
80862306a36Sopenharmony_ci		dev_dbg(&dev->dev,
80962306a36Sopenharmony_ci			"No AER slot_reset service or disconnected!\n");
81062306a36Sopenharmony_ci		kill_domain_by_device(psdev);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ciend:
81362306a36Sopenharmony_ci	if (psdev)
81462306a36Sopenharmony_ci		pcistub_device_put(psdev);
81562306a36Sopenharmony_ci	up_write(&pcistub_sem);
81662306a36Sopenharmony_ci	return result;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/*xen_pcibk_mmio_enabled: it will send the mmio_enabled request to  pcifront
82262306a36Sopenharmony_ci* in case of the device driver could provide this service, and then wait
82362306a36Sopenharmony_ci* for pcifront ack
82462306a36Sopenharmony_ci* @dev: pointer to PCI devices
82562306a36Sopenharmony_ci* return value is used by aer_core do_recovery policy
82662306a36Sopenharmony_ci*/
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cistatic pci_ers_result_t xen_pcibk_mmio_enabled(struct pci_dev *dev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct pcistub_device *psdev;
83162306a36Sopenharmony_ci	pci_ers_result_t result;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	result = PCI_ERS_RESULT_RECOVERED;
83462306a36Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_mmio_enabled(bus:%x,devfn:%x)\n",
83562306a36Sopenharmony_ci		dev->bus->number, dev->devfn);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	down_write(&pcistub_sem);
83862306a36Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
83962306a36Sopenharmony_ci				dev->bus->number,
84062306a36Sopenharmony_ci				PCI_SLOT(dev->devfn),
84162306a36Sopenharmony_ci				PCI_FUNC(dev->devfn));
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (!psdev || !psdev->pdev) {
84462306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
84562306a36Sopenharmony_ci		goto end;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (!psdev->pdev->sh_info) {
84962306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
85062306a36Sopenharmony_ci			" by HVM, kill it\n");
85162306a36Sopenharmony_ci		kill_domain_by_device(psdev);
85262306a36Sopenharmony_ci		goto end;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
85662306a36Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
85762306a36Sopenharmony_ci		dev_err(&dev->dev,
85862306a36Sopenharmony_ci			"guest with no AER driver should have been killed\n");
85962306a36Sopenharmony_ci		goto end;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci	result = common_process(psdev, pci_channel_io_normal, XEN_PCI_OP_aer_mmio, result);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
86462306a36Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
86562306a36Sopenharmony_ci		dev_dbg(&dev->dev,
86662306a36Sopenharmony_ci			"No AER mmio_enabled service or disconnected!\n");
86762306a36Sopenharmony_ci		kill_domain_by_device(psdev);
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ciend:
87062306a36Sopenharmony_ci	if (psdev)
87162306a36Sopenharmony_ci		pcistub_device_put(psdev);
87262306a36Sopenharmony_ci	up_write(&pcistub_sem);
87362306a36Sopenharmony_ci	return result;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/*xen_pcibk_error_detected: it will send the error_detected request to  pcifront
87762306a36Sopenharmony_ci* in case of the device driver could provide this service, and then wait
87862306a36Sopenharmony_ci* for pcifront ack.
87962306a36Sopenharmony_ci* @dev: pointer to PCI devices
88062306a36Sopenharmony_ci* @error: the current PCI connection state
88162306a36Sopenharmony_ci* return value is used by aer_core do_recovery policy
88262306a36Sopenharmony_ci*/
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic pci_ers_result_t xen_pcibk_error_detected(struct pci_dev *dev,
88562306a36Sopenharmony_ci	pci_channel_state_t error)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct pcistub_device *psdev;
88862306a36Sopenharmony_ci	pci_ers_result_t result;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	result = PCI_ERS_RESULT_CAN_RECOVER;
89162306a36Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_error_detected(bus:%x,devfn:%x)\n",
89262306a36Sopenharmony_ci		dev->bus->number, dev->devfn);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	down_write(&pcistub_sem);
89562306a36Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
89662306a36Sopenharmony_ci				dev->bus->number,
89762306a36Sopenharmony_ci				PCI_SLOT(dev->devfn),
89862306a36Sopenharmony_ci				PCI_FUNC(dev->devfn));
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	if (!psdev || !psdev->pdev) {
90162306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
90262306a36Sopenharmony_ci		goto end;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (!psdev->pdev->sh_info) {
90662306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
90762306a36Sopenharmony_ci			" by HVM, kill it\n");
90862306a36Sopenharmony_ci		kill_domain_by_device(psdev);
90962306a36Sopenharmony_ci		goto end;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	/*Guest owns the device yet no aer handler regiested, kill guest*/
91362306a36Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
91462306a36Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
91562306a36Sopenharmony_ci		dev_dbg(&dev->dev, "guest may have no aer driver, kill it\n");
91662306a36Sopenharmony_ci		kill_domain_by_device(psdev);
91762306a36Sopenharmony_ci		goto end;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	result = common_process(psdev, error, XEN_PCI_OP_aer_detected, result);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (result == PCI_ERS_RESULT_NONE ||
92262306a36Sopenharmony_ci		result == PCI_ERS_RESULT_DISCONNECT) {
92362306a36Sopenharmony_ci		dev_dbg(&dev->dev,
92462306a36Sopenharmony_ci			"No AER error_detected service or disconnected!\n");
92562306a36Sopenharmony_ci		kill_domain_by_device(psdev);
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ciend:
92862306a36Sopenharmony_ci	if (psdev)
92962306a36Sopenharmony_ci		pcistub_device_put(psdev);
93062306a36Sopenharmony_ci	up_write(&pcistub_sem);
93162306a36Sopenharmony_ci	return result;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/*xen_pcibk_error_resume: it will send the error_resume request to  pcifront
93562306a36Sopenharmony_ci* in case of the device driver could provide this service, and then wait
93662306a36Sopenharmony_ci* for pcifront ack.
93762306a36Sopenharmony_ci* @dev: pointer to PCI devices
93862306a36Sopenharmony_ci*/
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic void xen_pcibk_error_resume(struct pci_dev *dev)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct pcistub_device *psdev;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	dev_dbg(&dev->dev, "xen_pcibk_error_resume(bus:%x,devfn:%x)\n",
94562306a36Sopenharmony_ci		dev->bus->number, dev->devfn);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	down_write(&pcistub_sem);
94862306a36Sopenharmony_ci	psdev = pcistub_device_find(pci_domain_nr(dev->bus),
94962306a36Sopenharmony_ci				dev->bus->number,
95062306a36Sopenharmony_ci				PCI_SLOT(dev->devfn),
95162306a36Sopenharmony_ci				PCI_FUNC(dev->devfn));
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	if (!psdev || !psdev->pdev) {
95462306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not found/assigned\n");
95562306a36Sopenharmony_ci		goto end;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (!psdev->pdev->sh_info) {
95962306a36Sopenharmony_ci		dev_err(&dev->dev, "device is not connected or owned"
96062306a36Sopenharmony_ci			" by HVM, kill it\n");
96162306a36Sopenharmony_ci		kill_domain_by_device(psdev);
96262306a36Sopenharmony_ci		goto end;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (!test_bit(_XEN_PCIB_AERHANDLER,
96662306a36Sopenharmony_ci		(unsigned long *)&psdev->pdev->sh_info->flags)) {
96762306a36Sopenharmony_ci		dev_err(&dev->dev,
96862306a36Sopenharmony_ci			"guest with no AER driver should have been killed\n");
96962306a36Sopenharmony_ci		kill_domain_by_device(psdev);
97062306a36Sopenharmony_ci		goto end;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci	common_process(psdev, pci_channel_io_normal, XEN_PCI_OP_aer_resume,
97362306a36Sopenharmony_ci		       PCI_ERS_RESULT_RECOVERED);
97462306a36Sopenharmony_ciend:
97562306a36Sopenharmony_ci	if (psdev)
97662306a36Sopenharmony_ci		pcistub_device_put(psdev);
97762306a36Sopenharmony_ci	up_write(&pcistub_sem);
97862306a36Sopenharmony_ci	return;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci/*add xen_pcibk AER handling*/
98262306a36Sopenharmony_cistatic const struct pci_error_handlers xen_pcibk_error_handler = {
98362306a36Sopenharmony_ci	.error_detected = xen_pcibk_error_detected,
98462306a36Sopenharmony_ci	.mmio_enabled = xen_pcibk_mmio_enabled,
98562306a36Sopenharmony_ci	.slot_reset = xen_pcibk_slot_reset,
98662306a36Sopenharmony_ci	.resume = xen_pcibk_error_resume,
98762306a36Sopenharmony_ci};
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci/*
99062306a36Sopenharmony_ci * Note: There is no MODULE_DEVICE_TABLE entry here because this isn't
99162306a36Sopenharmony_ci * for a normal device. I don't want it to be loaded automatically.
99262306a36Sopenharmony_ci */
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic struct pci_driver xen_pcibk_pci_driver = {
99562306a36Sopenharmony_ci	/* The name should be xen_pciback, but until the tools are updated
99662306a36Sopenharmony_ci	 * we will keep it as pciback. */
99762306a36Sopenharmony_ci	.name = PCISTUB_DRIVER_NAME,
99862306a36Sopenharmony_ci	.id_table = pcistub_ids,
99962306a36Sopenharmony_ci	.probe = pcistub_probe,
100062306a36Sopenharmony_ci	.remove = pcistub_remove,
100162306a36Sopenharmony_ci	.err_handler = &xen_pcibk_error_handler,
100262306a36Sopenharmony_ci};
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic inline int str_to_slot(const char *buf, int *domain, int *bus,
100562306a36Sopenharmony_ci			      int *slot, int *func)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	int parsed = 0;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	switch (sscanf(buf, " %x:%x:%x.%x %n", domain, bus, slot, func,
101062306a36Sopenharmony_ci		       &parsed)) {
101162306a36Sopenharmony_ci	case 3:
101262306a36Sopenharmony_ci		*func = -1;
101362306a36Sopenharmony_ci		sscanf(buf, " %x:%x:%x.* %n", domain, bus, slot, &parsed);
101462306a36Sopenharmony_ci		break;
101562306a36Sopenharmony_ci	case 2:
101662306a36Sopenharmony_ci		*slot = *func = -1;
101762306a36Sopenharmony_ci		sscanf(buf, " %x:%x:*.* %n", domain, bus, &parsed);
101862306a36Sopenharmony_ci		break;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci	if (parsed && !buf[parsed])
102162306a36Sopenharmony_ci		return 0;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* try again without domain */
102462306a36Sopenharmony_ci	*domain = 0;
102562306a36Sopenharmony_ci	switch (sscanf(buf, " %x:%x.%x %n", bus, slot, func, &parsed)) {
102662306a36Sopenharmony_ci	case 2:
102762306a36Sopenharmony_ci		*func = -1;
102862306a36Sopenharmony_ci		sscanf(buf, " %x:%x.* %n", bus, slot, &parsed);
102962306a36Sopenharmony_ci		break;
103062306a36Sopenharmony_ci	case 1:
103162306a36Sopenharmony_ci		*slot = *func = -1;
103262306a36Sopenharmony_ci		sscanf(buf, " %x:*.* %n", bus, &parsed);
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	if (parsed && !buf[parsed])
103662306a36Sopenharmony_ci		return 0;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return -EINVAL;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic inline int str_to_quirk(const char *buf, int *domain, int *bus, int
104262306a36Sopenharmony_ci			       *slot, int *func, int *reg, int *size, int *mask)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	int parsed = 0;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	sscanf(buf, " %x:%x:%x.%x-%x:%x:%x %n", domain, bus, slot, func,
104762306a36Sopenharmony_ci	       reg, size, mask, &parsed);
104862306a36Sopenharmony_ci	if (parsed && !buf[parsed])
104962306a36Sopenharmony_ci		return 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* try again without domain */
105262306a36Sopenharmony_ci	*domain = 0;
105362306a36Sopenharmony_ci	sscanf(buf, " %x:%x.%x-%x:%x:%x %n", bus, slot, func, reg, size,
105462306a36Sopenharmony_ci	       mask, &parsed);
105562306a36Sopenharmony_ci	if (parsed && !buf[parsed])
105662306a36Sopenharmony_ci		return 0;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	return -EINVAL;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic int pcistub_device_id_add(int domain, int bus, int slot, int func)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
106462306a36Sopenharmony_ci	int rc = 0, devfn = PCI_DEVFN(slot, func);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (slot < 0) {
106762306a36Sopenharmony_ci		for (slot = 0; !rc && slot < 32; ++slot)
106862306a36Sopenharmony_ci			rc = pcistub_device_id_add(domain, bus, slot, func);
106962306a36Sopenharmony_ci		return rc;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (func < 0) {
107362306a36Sopenharmony_ci		for (func = 0; !rc && func < 8; ++func)
107462306a36Sopenharmony_ci			rc = pcistub_device_id_add(domain, bus, slot, func);
107562306a36Sopenharmony_ci		return rc;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if ((
107962306a36Sopenharmony_ci#if !defined(MODULE) /* pci_domains_supported is not being exported */ \
108062306a36Sopenharmony_ci    || !defined(CONFIG_PCI_DOMAINS)
108162306a36Sopenharmony_ci	     !pci_domains_supported ? domain :
108262306a36Sopenharmony_ci#endif
108362306a36Sopenharmony_ci	     domain < 0 || domain > 0xffff)
108462306a36Sopenharmony_ci	    || bus < 0 || bus > 0xff
108562306a36Sopenharmony_ci	    || PCI_SLOT(devfn) != slot
108662306a36Sopenharmony_ci	    || PCI_FUNC(devfn) != func)
108762306a36Sopenharmony_ci		return -EINVAL;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL);
109062306a36Sopenharmony_ci	if (!pci_dev_id)
109162306a36Sopenharmony_ci		return -ENOMEM;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	pr_debug("wants to seize %04x:%02x:%02x.%d\n",
109462306a36Sopenharmony_ci		 domain, bus, slot, func);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	pcistub_device_id_add_list(pci_dev_id, domain, bus, devfn);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic int pcistub_device_id_remove(int domain, int bus, int slot, int func)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct pcistub_device_id *pci_dev_id, *t;
110462306a36Sopenharmony_ci	int err = -ENOENT;
110562306a36Sopenharmony_ci	unsigned long flags;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
110862306a36Sopenharmony_ci	list_for_each_entry_safe(pci_dev_id, t, &pcistub_device_ids,
110962306a36Sopenharmony_ci				 slot_list) {
111062306a36Sopenharmony_ci		if (pci_dev_id->domain == domain && pci_dev_id->bus == bus
111162306a36Sopenharmony_ci		    && (slot < 0 || PCI_SLOT(pci_dev_id->devfn) == slot)
111262306a36Sopenharmony_ci		    && (func < 0 || PCI_FUNC(pci_dev_id->devfn) == func)) {
111362306a36Sopenharmony_ci			/* Don't break; here because it's possible the same
111462306a36Sopenharmony_ci			 * slot could be in the list more than once
111562306a36Sopenharmony_ci			 */
111662306a36Sopenharmony_ci			list_del(&pci_dev_id->slot_list);
111762306a36Sopenharmony_ci			kfree(pci_dev_id);
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci			err = 0;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci			pr_debug("removed %04x:%02x:%02x.%d from seize list\n",
112262306a36Sopenharmony_ci				 domain, bus, slot, func);
112362306a36Sopenharmony_ci		}
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return err;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int pcistub_reg_add(int domain, int bus, int slot, int func,
113162306a36Sopenharmony_ci			   unsigned int reg, unsigned int size,
113262306a36Sopenharmony_ci			   unsigned int mask)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	int err = 0;
113562306a36Sopenharmony_ci	struct pcistub_device *psdev;
113662306a36Sopenharmony_ci	struct pci_dev *dev;
113762306a36Sopenharmony_ci	struct config_field *field;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (reg > 0xfff || (size < 4 && (mask >> (size * 8))))
114062306a36Sopenharmony_ci		return -EINVAL;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
114362306a36Sopenharmony_ci	if (!psdev) {
114462306a36Sopenharmony_ci		err = -ENODEV;
114562306a36Sopenharmony_ci		goto out;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	dev = psdev->dev;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	field = kzalloc(sizeof(*field), GFP_KERNEL);
115062306a36Sopenharmony_ci	if (!field) {
115162306a36Sopenharmony_ci		err = -ENOMEM;
115262306a36Sopenharmony_ci		goto out;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	field->offset = reg;
115662306a36Sopenharmony_ci	field->size = size;
115762306a36Sopenharmony_ci	field->mask = mask;
115862306a36Sopenharmony_ci	field->init = NULL;
115962306a36Sopenharmony_ci	field->reset = NULL;
116062306a36Sopenharmony_ci	field->release = NULL;
116162306a36Sopenharmony_ci	field->clean = xen_pcibk_config_field_free;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	err = xen_pcibk_config_quirks_add_field(dev, field);
116462306a36Sopenharmony_ci	if (err)
116562306a36Sopenharmony_ci		kfree(field);
116662306a36Sopenharmony_ciout:
116762306a36Sopenharmony_ci	if (psdev)
116862306a36Sopenharmony_ci		pcistub_device_put(psdev);
116962306a36Sopenharmony_ci	return err;
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic ssize_t new_slot_store(struct device_driver *drv, const char *buf,
117362306a36Sopenharmony_ci			      size_t count)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	int domain, bus, slot, func;
117662306a36Sopenharmony_ci	int err;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
117962306a36Sopenharmony_ci	if (err)
118062306a36Sopenharmony_ci		goto out;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	err = pcistub_device_id_add(domain, bus, slot, func);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ciout:
118562306a36Sopenharmony_ci	if (!err)
118662306a36Sopenharmony_ci		err = count;
118762306a36Sopenharmony_ci	return err;
118862306a36Sopenharmony_ci}
118962306a36Sopenharmony_cistatic DRIVER_ATTR_WO(new_slot);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic ssize_t remove_slot_store(struct device_driver *drv, const char *buf,
119262306a36Sopenharmony_ci				 size_t count)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	int domain, bus, slot, func;
119562306a36Sopenharmony_ci	int err;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
119862306a36Sopenharmony_ci	if (err)
119962306a36Sopenharmony_ci		goto out;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	err = pcistub_device_id_remove(domain, bus, slot, func);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ciout:
120462306a36Sopenharmony_ci	if (!err)
120562306a36Sopenharmony_ci		err = count;
120662306a36Sopenharmony_ci	return err;
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_cistatic DRIVER_ATTR_WO(remove_slot);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic ssize_t slots_show(struct device_driver *drv, char *buf)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct pcistub_device_id *pci_dev_id;
121362306a36Sopenharmony_ci	size_t count = 0;
121462306a36Sopenharmony_ci	unsigned long flags;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
121762306a36Sopenharmony_ci	list_for_each_entry(pci_dev_id, &pcistub_device_ids, slot_list) {
121862306a36Sopenharmony_ci		if (count >= PAGE_SIZE)
121962306a36Sopenharmony_ci			break;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
122262306a36Sopenharmony_ci				   "%04x:%02x:%02x.%d\n",
122362306a36Sopenharmony_ci				   pci_dev_id->domain, pci_dev_id->bus,
122462306a36Sopenharmony_ci				   PCI_SLOT(pci_dev_id->devfn),
122562306a36Sopenharmony_ci				   PCI_FUNC(pci_dev_id->devfn));
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	return count;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_cistatic DRIVER_ATTR_RO(slots);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic ssize_t irq_handlers_show(struct device_driver *drv, char *buf)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	struct pcistub_device *psdev;
123662306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
123762306a36Sopenharmony_ci	size_t count = 0;
123862306a36Sopenharmony_ci	unsigned long flags;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
124162306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
124262306a36Sopenharmony_ci		if (count >= PAGE_SIZE)
124362306a36Sopenharmony_ci			break;
124462306a36Sopenharmony_ci		if (!psdev->dev)
124562306a36Sopenharmony_ci			continue;
124662306a36Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
124762306a36Sopenharmony_ci		if (!dev_data)
124862306a36Sopenharmony_ci			continue;
124962306a36Sopenharmony_ci		count +=
125062306a36Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count,
125162306a36Sopenharmony_ci			      "%s:%s:%sing:%ld\n",
125262306a36Sopenharmony_ci			      pci_name(psdev->dev),
125362306a36Sopenharmony_ci			      dev_data->isr_on ? "on" : "off",
125462306a36Sopenharmony_ci			      dev_data->ack_intr ? "ack" : "not ack",
125562306a36Sopenharmony_ci			      dev_data->handled);
125662306a36Sopenharmony_ci	}
125762306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
125862306a36Sopenharmony_ci	return count;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_cistatic DRIVER_ATTR_RO(irq_handlers);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic ssize_t irq_handler_state_store(struct device_driver *drv,
126362306a36Sopenharmony_ci				       const char *buf, size_t count)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct pcistub_device *psdev;
126662306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
126762306a36Sopenharmony_ci	int domain, bus, slot, func;
126862306a36Sopenharmony_ci	int err;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
127162306a36Sopenharmony_ci	if (err)
127262306a36Sopenharmony_ci		return err;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
127562306a36Sopenharmony_ci	if (!psdev) {
127662306a36Sopenharmony_ci		err = -ENOENT;
127762306a36Sopenharmony_ci		goto out;
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
128162306a36Sopenharmony_ci	if (!dev_data) {
128262306a36Sopenharmony_ci		err = -ENOENT;
128362306a36Sopenharmony_ci		goto out;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	dev_dbg(&psdev->dev->dev, "%s fake irq handler: %d->%d\n",
128762306a36Sopenharmony_ci		dev_data->irq_name, dev_data->isr_on,
128862306a36Sopenharmony_ci		!dev_data->isr_on);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	dev_data->isr_on = !(dev_data->isr_on);
129162306a36Sopenharmony_ci	if (dev_data->isr_on)
129262306a36Sopenharmony_ci		dev_data->ack_intr = 1;
129362306a36Sopenharmony_ciout:
129462306a36Sopenharmony_ci	if (psdev)
129562306a36Sopenharmony_ci		pcistub_device_put(psdev);
129662306a36Sopenharmony_ci	if (!err)
129762306a36Sopenharmony_ci		err = count;
129862306a36Sopenharmony_ci	return err;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_cistatic DRIVER_ATTR_WO(irq_handler_state);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic ssize_t quirks_store(struct device_driver *drv, const char *buf,
130362306a36Sopenharmony_ci			    size_t count)
130462306a36Sopenharmony_ci{
130562306a36Sopenharmony_ci	int domain, bus, slot, func, reg, size, mask;
130662306a36Sopenharmony_ci	int err;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	err = str_to_quirk(buf, &domain, &bus, &slot, &func, &reg, &size,
130962306a36Sopenharmony_ci			   &mask);
131062306a36Sopenharmony_ci	if (err)
131162306a36Sopenharmony_ci		goto out;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	err = pcistub_reg_add(domain, bus, slot, func, reg, size, mask);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ciout:
131662306a36Sopenharmony_ci	if (!err)
131762306a36Sopenharmony_ci		err = count;
131862306a36Sopenharmony_ci	return err;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic ssize_t quirks_show(struct device_driver *drv, char *buf)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	int count = 0;
132462306a36Sopenharmony_ci	unsigned long flags;
132562306a36Sopenharmony_ci	struct xen_pcibk_config_quirk *quirk;
132662306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
132762306a36Sopenharmony_ci	const struct config_field *field;
132862306a36Sopenharmony_ci	const struct config_field_entry *cfg_entry;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	spin_lock_irqsave(&device_ids_lock, flags);
133162306a36Sopenharmony_ci	list_for_each_entry(quirk, &xen_pcibk_quirks, quirks_list) {
133262306a36Sopenharmony_ci		if (count >= PAGE_SIZE)
133362306a36Sopenharmony_ci			goto out;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci		count += scnprintf(buf + count, PAGE_SIZE - count,
133662306a36Sopenharmony_ci				   "%02x:%02x.%01x\n\t%04x:%04x:%04x:%04x\n",
133762306a36Sopenharmony_ci				   quirk->pdev->bus->number,
133862306a36Sopenharmony_ci				   PCI_SLOT(quirk->pdev->devfn),
133962306a36Sopenharmony_ci				   PCI_FUNC(quirk->pdev->devfn),
134062306a36Sopenharmony_ci				   quirk->devid.vendor, quirk->devid.device,
134162306a36Sopenharmony_ci				   quirk->devid.subvendor,
134262306a36Sopenharmony_ci				   quirk->devid.subdevice);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		dev_data = pci_get_drvdata(quirk->pdev);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci		list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
134762306a36Sopenharmony_ci			field = cfg_entry->field;
134862306a36Sopenharmony_ci			if (count >= PAGE_SIZE)
134962306a36Sopenharmony_ci				goto out;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci			count += scnprintf(buf + count, PAGE_SIZE - count,
135262306a36Sopenharmony_ci					   "\t\t%08x:%01x:%08x\n",
135362306a36Sopenharmony_ci					   cfg_entry->base_offset +
135462306a36Sopenharmony_ci					   field->offset, field->size,
135562306a36Sopenharmony_ci					   field->mask);
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci	}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ciout:
136062306a36Sopenharmony_ci	spin_unlock_irqrestore(&device_ids_lock, flags);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	return count;
136362306a36Sopenharmony_ci}
136462306a36Sopenharmony_cistatic DRIVER_ATTR_RW(quirks);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic ssize_t permissive_store(struct device_driver *drv, const char *buf,
136762306a36Sopenharmony_ci				size_t count)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	int domain, bus, slot, func;
137062306a36Sopenharmony_ci	int err;
137162306a36Sopenharmony_ci	struct pcistub_device *psdev;
137262306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
137562306a36Sopenharmony_ci	if (err)
137662306a36Sopenharmony_ci		goto out;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
137962306a36Sopenharmony_ci	if (!psdev) {
138062306a36Sopenharmony_ci		err = -ENODEV;
138162306a36Sopenharmony_ci		goto out;
138262306a36Sopenharmony_ci	}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
138562306a36Sopenharmony_ci	/* the driver data for a device should never be null at this point */
138662306a36Sopenharmony_ci	if (!dev_data) {
138762306a36Sopenharmony_ci		err = -ENXIO;
138862306a36Sopenharmony_ci		goto release;
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci	if (!dev_data->permissive) {
139162306a36Sopenharmony_ci		dev_data->permissive = 1;
139262306a36Sopenharmony_ci		/* Let user know that what they're doing could be unsafe */
139362306a36Sopenharmony_ci		dev_warn(&psdev->dev->dev, "enabling permissive mode "
139462306a36Sopenharmony_ci			 "configuration space accesses!\n");
139562306a36Sopenharmony_ci		dev_warn(&psdev->dev->dev,
139662306a36Sopenharmony_ci			 "permissive mode is potentially unsafe!\n");
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_cirelease:
139962306a36Sopenharmony_ci	pcistub_device_put(psdev);
140062306a36Sopenharmony_ciout:
140162306a36Sopenharmony_ci	if (!err)
140262306a36Sopenharmony_ci		err = count;
140362306a36Sopenharmony_ci	return err;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic ssize_t permissive_show(struct device_driver *drv, char *buf)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct pcistub_device *psdev;
140962306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
141062306a36Sopenharmony_ci	size_t count = 0;
141162306a36Sopenharmony_ci	unsigned long flags;
141262306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
141362306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
141462306a36Sopenharmony_ci		if (count >= PAGE_SIZE)
141562306a36Sopenharmony_ci			break;
141662306a36Sopenharmony_ci		if (!psdev->dev)
141762306a36Sopenharmony_ci			continue;
141862306a36Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
141962306a36Sopenharmony_ci		if (!dev_data || !dev_data->permissive)
142062306a36Sopenharmony_ci			continue;
142162306a36Sopenharmony_ci		count +=
142262306a36Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count, "%s\n",
142362306a36Sopenharmony_ci			      pci_name(psdev->dev));
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
142662306a36Sopenharmony_ci	return count;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_cistatic DRIVER_ATTR_RW(permissive);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic ssize_t allow_interrupt_control_store(struct device_driver *drv,
143162306a36Sopenharmony_ci					     const char *buf, size_t count)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	int domain, bus, slot, func;
143462306a36Sopenharmony_ci	int err;
143562306a36Sopenharmony_ci	struct pcistub_device *psdev;
143662306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	err = str_to_slot(buf, &domain, &bus, &slot, &func);
143962306a36Sopenharmony_ci	if (err)
144062306a36Sopenharmony_ci		goto out;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	psdev = pcistub_device_find(domain, bus, slot, func);
144362306a36Sopenharmony_ci	if (!psdev) {
144462306a36Sopenharmony_ci		err = -ENODEV;
144562306a36Sopenharmony_ci		goto out;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	dev_data = pci_get_drvdata(psdev->dev);
144962306a36Sopenharmony_ci	/* the driver data for a device should never be null at this point */
145062306a36Sopenharmony_ci	if (!dev_data) {
145162306a36Sopenharmony_ci		err = -ENXIO;
145262306a36Sopenharmony_ci		goto release;
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci	dev_data->allow_interrupt_control = 1;
145562306a36Sopenharmony_cirelease:
145662306a36Sopenharmony_ci	pcistub_device_put(psdev);
145762306a36Sopenharmony_ciout:
145862306a36Sopenharmony_ci	if (!err)
145962306a36Sopenharmony_ci		err = count;
146062306a36Sopenharmony_ci	return err;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_cistatic ssize_t allow_interrupt_control_show(struct device_driver *drv,
146462306a36Sopenharmony_ci					    char *buf)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	struct pcistub_device *psdev;
146762306a36Sopenharmony_ci	struct xen_pcibk_dev_data *dev_data;
146862306a36Sopenharmony_ci	size_t count = 0;
146962306a36Sopenharmony_ci	unsigned long flags;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
147262306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
147362306a36Sopenharmony_ci		if (count >= PAGE_SIZE)
147462306a36Sopenharmony_ci			break;
147562306a36Sopenharmony_ci		if (!psdev->dev)
147662306a36Sopenharmony_ci			continue;
147762306a36Sopenharmony_ci		dev_data = pci_get_drvdata(psdev->dev);
147862306a36Sopenharmony_ci		if (!dev_data || !dev_data->allow_interrupt_control)
147962306a36Sopenharmony_ci			continue;
148062306a36Sopenharmony_ci		count +=
148162306a36Sopenharmony_ci		    scnprintf(buf + count, PAGE_SIZE - count, "%s\n",
148262306a36Sopenharmony_ci			      pci_name(psdev->dev));
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
148562306a36Sopenharmony_ci	return count;
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_cistatic DRIVER_ATTR_RW(allow_interrupt_control);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic void pcistub_exit(void)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_new_slot);
149262306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
149362306a36Sopenharmony_ci			   &driver_attr_remove_slot);
149462306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_slots);
149562306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver, &driver_attr_quirks);
149662306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
149762306a36Sopenharmony_ci			   &driver_attr_permissive);
149862306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
149962306a36Sopenharmony_ci			   &driver_attr_allow_interrupt_control);
150062306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
150162306a36Sopenharmony_ci			   &driver_attr_irq_handlers);
150262306a36Sopenharmony_ci	driver_remove_file(&xen_pcibk_pci_driver.driver,
150362306a36Sopenharmony_ci			   &driver_attr_irq_handler_state);
150462306a36Sopenharmony_ci	pci_unregister_driver(&xen_pcibk_pci_driver);
150562306a36Sopenharmony_ci}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic int __init pcistub_init(void)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	int pos = 0;
151062306a36Sopenharmony_ci	int err = 0;
151162306a36Sopenharmony_ci	int domain, bus, slot, func;
151262306a36Sopenharmony_ci	int parsed;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (pci_devs_to_hide && *pci_devs_to_hide) {
151562306a36Sopenharmony_ci		do {
151662306a36Sopenharmony_ci			parsed = 0;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci			err = sscanf(pci_devs_to_hide + pos,
151962306a36Sopenharmony_ci				     " (%x:%x:%x.%x) %n",
152062306a36Sopenharmony_ci				     &domain, &bus, &slot, &func, &parsed);
152162306a36Sopenharmony_ci			switch (err) {
152262306a36Sopenharmony_ci			case 3:
152362306a36Sopenharmony_ci				func = -1;
152462306a36Sopenharmony_ci				sscanf(pci_devs_to_hide + pos,
152562306a36Sopenharmony_ci				       " (%x:%x:%x.*) %n",
152662306a36Sopenharmony_ci				       &domain, &bus, &slot, &parsed);
152762306a36Sopenharmony_ci				break;
152862306a36Sopenharmony_ci			case 2:
152962306a36Sopenharmony_ci				slot = func = -1;
153062306a36Sopenharmony_ci				sscanf(pci_devs_to_hide + pos,
153162306a36Sopenharmony_ci				       " (%x:%x:*.*) %n",
153262306a36Sopenharmony_ci				       &domain, &bus, &parsed);
153362306a36Sopenharmony_ci				break;
153462306a36Sopenharmony_ci			}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci			if (!parsed) {
153762306a36Sopenharmony_ci				domain = 0;
153862306a36Sopenharmony_ci				err = sscanf(pci_devs_to_hide + pos,
153962306a36Sopenharmony_ci					     " (%x:%x.%x) %n",
154062306a36Sopenharmony_ci					     &bus, &slot, &func, &parsed);
154162306a36Sopenharmony_ci				switch (err) {
154262306a36Sopenharmony_ci				case 2:
154362306a36Sopenharmony_ci					func = -1;
154462306a36Sopenharmony_ci					sscanf(pci_devs_to_hide + pos,
154562306a36Sopenharmony_ci					       " (%x:%x.*) %n",
154662306a36Sopenharmony_ci					       &bus, &slot, &parsed);
154762306a36Sopenharmony_ci					break;
154862306a36Sopenharmony_ci				case 1:
154962306a36Sopenharmony_ci					slot = func = -1;
155062306a36Sopenharmony_ci					sscanf(pci_devs_to_hide + pos,
155162306a36Sopenharmony_ci					       " (%x:*.*) %n",
155262306a36Sopenharmony_ci					       &bus, &parsed);
155362306a36Sopenharmony_ci					break;
155462306a36Sopenharmony_ci				}
155562306a36Sopenharmony_ci			}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci			if (parsed <= 0)
155862306a36Sopenharmony_ci				goto parse_error;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci			err = pcistub_device_id_add(domain, bus, slot, func);
156162306a36Sopenharmony_ci			if (err)
156262306a36Sopenharmony_ci				goto out;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci			pos += parsed;
156562306a36Sopenharmony_ci		} while (pci_devs_to_hide[pos]);
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/* If we're the first PCI Device Driver to register, we're the
156962306a36Sopenharmony_ci	 * first one to get offered PCI devices as they become
157062306a36Sopenharmony_ci	 * available (and thus we can be the first to grab them)
157162306a36Sopenharmony_ci	 */
157262306a36Sopenharmony_ci	err = pci_register_driver(&xen_pcibk_pci_driver);
157362306a36Sopenharmony_ci	if (err < 0)
157462306a36Sopenharmony_ci		goto out;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	err = driver_create_file(&xen_pcibk_pci_driver.driver,
157762306a36Sopenharmony_ci				 &driver_attr_new_slot);
157862306a36Sopenharmony_ci	if (!err)
157962306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
158062306a36Sopenharmony_ci					 &driver_attr_remove_slot);
158162306a36Sopenharmony_ci	if (!err)
158262306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
158362306a36Sopenharmony_ci					 &driver_attr_slots);
158462306a36Sopenharmony_ci	if (!err)
158562306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
158662306a36Sopenharmony_ci					 &driver_attr_quirks);
158762306a36Sopenharmony_ci	if (!err)
158862306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
158962306a36Sopenharmony_ci					 &driver_attr_permissive);
159062306a36Sopenharmony_ci	if (!err)
159162306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
159262306a36Sopenharmony_ci					 &driver_attr_allow_interrupt_control);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	if (!err)
159562306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
159662306a36Sopenharmony_ci					 &driver_attr_irq_handlers);
159762306a36Sopenharmony_ci	if (!err)
159862306a36Sopenharmony_ci		err = driver_create_file(&xen_pcibk_pci_driver.driver,
159962306a36Sopenharmony_ci					&driver_attr_irq_handler_state);
160062306a36Sopenharmony_ci	if (err)
160162306a36Sopenharmony_ci		pcistub_exit();
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ciout:
160462306a36Sopenharmony_ci	return err;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ciparse_error:
160762306a36Sopenharmony_ci	pr_err("Error parsing pci_devs_to_hide at \"%s\"\n",
160862306a36Sopenharmony_ci	       pci_devs_to_hide + pos);
160962306a36Sopenharmony_ci	return -EINVAL;
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci#ifndef MODULE
161362306a36Sopenharmony_ci/*
161462306a36Sopenharmony_ci * fs_initcall happens before device_initcall
161562306a36Sopenharmony_ci * so xen_pcibk *should* get called first (b/c we
161662306a36Sopenharmony_ci * want to suck up any device before other drivers
161762306a36Sopenharmony_ci * get a chance by being the first pci device
161862306a36Sopenharmony_ci * driver to register)
161962306a36Sopenharmony_ci */
162062306a36Sopenharmony_cifs_initcall(pcistub_init);
162162306a36Sopenharmony_ci#endif
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
162462306a36Sopenharmony_cistatic struct pcistub_device *find_vfs(const struct pci_dev *pdev)
162562306a36Sopenharmony_ci{
162662306a36Sopenharmony_ci	struct pcistub_device *psdev = NULL;
162762306a36Sopenharmony_ci	unsigned long flags;
162862306a36Sopenharmony_ci	bool found = false;
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	spin_lock_irqsave(&pcistub_devices_lock, flags);
163162306a36Sopenharmony_ci	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
163262306a36Sopenharmony_ci		if (!psdev->pdev && psdev->dev != pdev
163362306a36Sopenharmony_ci		    && pci_physfn(psdev->dev) == pdev) {
163462306a36Sopenharmony_ci			found = true;
163562306a36Sopenharmony_ci			break;
163662306a36Sopenharmony_ci		}
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
163962306a36Sopenharmony_ci	if (found)
164062306a36Sopenharmony_ci		return psdev;
164162306a36Sopenharmony_ci	return NULL;
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic int pci_stub_notifier(struct notifier_block *nb,
164562306a36Sopenharmony_ci			     unsigned long action, void *data)
164662306a36Sopenharmony_ci{
164762306a36Sopenharmony_ci	struct device *dev = data;
164862306a36Sopenharmony_ci	const struct pci_dev *pdev = to_pci_dev(dev);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	if (action != BUS_NOTIFY_UNBIND_DRIVER)
165162306a36Sopenharmony_ci		return NOTIFY_DONE;
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	if (!pdev->is_physfn)
165462306a36Sopenharmony_ci		return NOTIFY_DONE;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	for (;;) {
165762306a36Sopenharmony_ci		struct pcistub_device *psdev = find_vfs(pdev);
165862306a36Sopenharmony_ci		if (!psdev)
165962306a36Sopenharmony_ci			break;
166062306a36Sopenharmony_ci		device_release_driver(&psdev->dev->dev);
166162306a36Sopenharmony_ci	}
166262306a36Sopenharmony_ci	return NOTIFY_DONE;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic struct notifier_block pci_stub_nb = {
166662306a36Sopenharmony_ci	.notifier_call = pci_stub_notifier,
166762306a36Sopenharmony_ci};
166862306a36Sopenharmony_ci#endif
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_cistatic int __init xen_pcibk_init(void)
167162306a36Sopenharmony_ci{
167262306a36Sopenharmony_ci	int err;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	if (!xen_initial_domain())
167562306a36Sopenharmony_ci		return -ENODEV;
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	err = xen_pcibk_config_init();
167862306a36Sopenharmony_ci	if (err)
167962306a36Sopenharmony_ci		return err;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci#ifdef MODULE
168262306a36Sopenharmony_ci	err = pcistub_init();
168362306a36Sopenharmony_ci	if (err < 0)
168462306a36Sopenharmony_ci		return err;
168562306a36Sopenharmony_ci#endif
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	pcistub_init_devices_late();
168862306a36Sopenharmony_ci	err = xen_pcibk_xenbus_register();
168962306a36Sopenharmony_ci	if (err)
169062306a36Sopenharmony_ci		pcistub_exit();
169162306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
169262306a36Sopenharmony_ci	else
169362306a36Sopenharmony_ci		bus_register_notifier(&pci_bus_type, &pci_stub_nb);
169462306a36Sopenharmony_ci#endif
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	return err;
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_cistatic void __exit xen_pcibk_cleanup(void)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV
170262306a36Sopenharmony_ci	bus_unregister_notifier(&pci_bus_type, &pci_stub_nb);
170362306a36Sopenharmony_ci#endif
170462306a36Sopenharmony_ci	xen_pcibk_xenbus_unregister();
170562306a36Sopenharmony_ci	pcistub_exit();
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cimodule_init(xen_pcibk_init);
170962306a36Sopenharmony_cimodule_exit(xen_pcibk_cleanup);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
171262306a36Sopenharmony_ciMODULE_ALIAS("xen-backend:pci");
1713