123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- * PCI Backend - Provides a Virtual PCI bus (with real devices)
- * to the frontend
- *
- * Author: Ryan Wilson <hap9@epoch.ncsc.mil> (vpci.c)
- * Author: Tristan Gingold <tristan.gingold@bull.net>, from vpci.c
- */
- #include <linux/list.h>
- #include <linux/slab.h>
- #include <linux/pci.h>
- #include <linux/spinlock.h>
- #include "pciback.h"
- /* There are at most 32 slots in a pci bus. */
- #define PCI_SLOT_MAX 32
- #define PCI_BUS_NBR 2
- struct slot_dev_data {
- /* Access to dev_list must be protected by lock */
- struct pci_dev *slots[PCI_BUS_NBR][PCI_SLOT_MAX];
- spinlock_t lock;
- };
- struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev,
- unsigned int domain, unsigned int bus,
- unsigned int devfn)
- {
- struct pci_dev *dev = NULL;
- struct slot_dev_data *slot_dev = pdev->pci_dev_data;
- unsigned long flags;
- if (domain != 0 || PCI_FUNC(devfn) != 0)
- return NULL;
- if (PCI_SLOT(devfn) >= PCI_SLOT_MAX || bus >= PCI_BUS_NBR)
- return NULL;
- spin_lock_irqsave(&slot_dev->lock, flags);
- dev = slot_dev->slots[bus][PCI_SLOT(devfn)];
- spin_unlock_irqrestore(&slot_dev->lock, flags);
- return dev;
- }
- int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev,
- int devid, publish_pci_dev_cb publish_cb)
- {
- int err = 0, slot, bus;
- struct slot_dev_data *slot_dev = pdev->pci_dev_data;
- unsigned long flags;
- if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) {
- err = -EFAULT;
- xenbus_dev_fatal(pdev->xdev, err,
- "Can't export bridges on the virtual PCI bus");
- goto out;
- }
- spin_lock_irqsave(&slot_dev->lock, flags);
- /* Assign to a new slot on the virtual PCI bus */
- for (bus = 0; bus < PCI_BUS_NBR; bus++)
- for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- if (slot_dev->slots[bus][slot] == NULL) {
- printk(KERN_INFO
- "pciback: slot: %s: assign to virtual "
- "slot %d, bus %d\n",
- pci_name(dev), slot, bus);
- slot_dev->slots[bus][slot] = dev;
- goto unlock;
- }
- }
- err = -ENOMEM;
- xenbus_dev_fatal(pdev->xdev, err,
- "No more space on root virtual PCI bus");
- unlock:
- spin_unlock_irqrestore(&slot_dev->lock, flags);
- /* Publish this device. */
- if (!err)
- err = publish_cb(pdev, 0, 0, PCI_DEVFN(slot, 0), devid);
- out:
- return err;
- }
- void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev)
- {
- int slot, bus;
- struct slot_dev_data *slot_dev = pdev->pci_dev_data;
- struct pci_dev *found_dev = NULL;
- unsigned long flags;
- spin_lock_irqsave(&slot_dev->lock, flags);
- for (bus = 0; bus < PCI_BUS_NBR; bus++)
- for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- if (slot_dev->slots[bus][slot] == dev) {
- slot_dev->slots[bus][slot] = NULL;
- found_dev = dev;
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&slot_dev->lock, flags);
- if (found_dev)
- pcistub_put_pci_dev(found_dev);
- }
- int pciback_init_devices(struct pciback_device *pdev)
- {
- int slot, bus;
- struct slot_dev_data *slot_dev;
- slot_dev = kmalloc(sizeof(*slot_dev), GFP_KERNEL);
- if (!slot_dev)
- return -ENOMEM;
- spin_lock_init(&slot_dev->lock);
- for (bus = 0; bus < PCI_BUS_NBR; bus++)
- for (slot = 0; slot < PCI_SLOT_MAX; slot++)
- slot_dev->slots[bus][slot] = NULL;
- pdev->pci_dev_data = slot_dev;
- return 0;
- }
- int pciback_publish_pci_roots(struct pciback_device *pdev,
- publish_pci_root_cb publish_cb)
- {
- /* The Virtual PCI bus has only one root */
- return publish_cb(pdev, 0, 0);
- }
- void pciback_release_devices(struct pciback_device *pdev)
- {
- int slot, bus;
- struct slot_dev_data *slot_dev = pdev->pci_dev_data;
- struct pci_dev *dev;
- for (bus = 0; bus < PCI_BUS_NBR; bus++)
- for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- dev = slot_dev->slots[bus][slot];
- if (dev != NULL)
- pcistub_put_pci_dev(dev);
- }
- kfree(slot_dev);
- pdev->pci_dev_data = NULL;
- }
- int pciback_get_pcifront_dev(struct pci_dev *pcidev,
- struct pciback_device *pdev,
- unsigned int *domain, unsigned int *bus,
- unsigned int *devfn)
- {
- int slot, busnr;
- struct slot_dev_data *slot_dev = pdev->pci_dev_data;
- struct pci_dev *dev;
- int found = 0;
- unsigned long flags;
- spin_lock_irqsave(&slot_dev->lock, flags);
- for (busnr = 0; busnr < PCI_BUS_NBR; bus++)
- for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
- dev = slot_dev->slots[busnr][slot];
- if (dev && dev->bus->number == pcidev->bus->number
- && dev->devfn == pcidev->devfn
- && pci_domain_nr(dev->bus) ==
- pci_domain_nr(pcidev->bus)) {
- found = 1;
- *domain = 0;
- *bus = busnr;
- *devfn = PCI_DEVFN(slot, 0);
- goto out;
- }
- }
- out:
- spin_unlock_irqrestore(&slot_dev->lock, flags);
- return found;
- }
|