/* * PCI Backend - Provides a Virtual PCI bus (with real devices) * to the frontend * * Author: Ryan Wilson (vpci.c) * Author: Tristan Gingold , from vpci.c */ #include #include #include #include #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 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); 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; }