162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Backend Xenbus Setup - handles setup with frontend and xend 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/vmalloc.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <xen/xenbus.h> 1662306a36Sopenharmony_ci#include <xen/events.h> 1762306a36Sopenharmony_ci#include <xen/pci.h> 1862306a36Sopenharmony_ci#include "pciback.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define INVALID_EVTCHN_IRQ (-1) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic bool __read_mostly passthrough; 2362306a36Sopenharmony_cimodule_param(passthrough, bool, S_IRUGO); 2462306a36Sopenharmony_ciMODULE_PARM_DESC(passthrough, 2562306a36Sopenharmony_ci "Option to specify how to export PCI topology to guest:\n"\ 2662306a36Sopenharmony_ci " 0 - (default) Hide the true PCI topology and makes the frontend\n"\ 2762306a36Sopenharmony_ci " there is a single PCI bus with only the exported devices on it.\n"\ 2862306a36Sopenharmony_ci " For example, a device at 03:05.0 will be re-assigned to 00:00.0\n"\ 2962306a36Sopenharmony_ci " while second device at 02:1a.1 will be re-assigned to 00:01.1.\n"\ 3062306a36Sopenharmony_ci " 1 - Passthrough provides a real view of the PCI topology to the\n"\ 3162306a36Sopenharmony_ci " frontend (for example, a device at 06:01.b will still appear at\n"\ 3262306a36Sopenharmony_ci " 06:01.b to the frontend). This is similar to how Xen 2.0.x\n"\ 3362306a36Sopenharmony_ci " exposed PCI devices to its driver domains. This may be required\n"\ 3462306a36Sopenharmony_ci " for drivers which depend on finding their hardware in certain\n"\ 3562306a36Sopenharmony_ci " bus/slot locations."); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic struct xen_pcibk_device *alloc_pdev(struct xenbus_device *xdev) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct xen_pcibk_device *pdev; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci pdev = kzalloc(sizeof(struct xen_pcibk_device), GFP_KERNEL); 4262306a36Sopenharmony_ci if (pdev == NULL) 4362306a36Sopenharmony_ci goto out; 4462306a36Sopenharmony_ci dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pdev->xdev = xdev; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci mutex_init(&pdev->dev_lock); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci pdev->sh_info = NULL; 5162306a36Sopenharmony_ci pdev->evtchn_irq = INVALID_EVTCHN_IRQ; 5262306a36Sopenharmony_ci pdev->be_watching = 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci INIT_WORK(&pdev->op_work, xen_pcibk_do_op); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (xen_pcibk_init_devices(pdev)) { 5762306a36Sopenharmony_ci kfree(pdev); 5862306a36Sopenharmony_ci pdev = NULL; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci dev_set_drvdata(&xdev->dev, pdev); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciout: 6462306a36Sopenharmony_ci return pdev; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void xen_pcibk_disconnect(struct xen_pcibk_device *pdev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci mutex_lock(&pdev->dev_lock); 7062306a36Sopenharmony_ci /* Ensure the guest can't trigger our handler before removing devices */ 7162306a36Sopenharmony_ci if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) { 7262306a36Sopenharmony_ci unbind_from_irqhandler(pdev->evtchn_irq, pdev); 7362306a36Sopenharmony_ci pdev->evtchn_irq = INVALID_EVTCHN_IRQ; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* If the driver domain started an op, make sure we complete it 7762306a36Sopenharmony_ci * before releasing the shared memory */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci flush_work(&pdev->op_work); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (pdev->sh_info != NULL) { 8262306a36Sopenharmony_ci xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_info); 8362306a36Sopenharmony_ci pdev->sh_info = NULL; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci mutex_unlock(&pdev->dev_lock); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void free_pdev(struct xen_pcibk_device *pdev) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci if (pdev->be_watching) { 9162306a36Sopenharmony_ci unregister_xenbus_watch(&pdev->be_watch); 9262306a36Sopenharmony_ci pdev->be_watching = 0; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci xen_pcibk_disconnect(pdev); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* N.B. This calls pcistub_put_pci_dev which does the FLR on all 9862306a36Sopenharmony_ci * of the PCIe devices. */ 9962306a36Sopenharmony_ci xen_pcibk_release_devices(pdev); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci dev_set_drvdata(&pdev->xdev->dev, NULL); 10262306a36Sopenharmony_ci pdev->xdev = NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci kfree(pdev); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int xen_pcibk_do_attach(struct xen_pcibk_device *pdev, int gnt_ref, 10862306a36Sopenharmony_ci evtchn_port_t remote_evtchn) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int err = 0; 11162306a36Sopenharmony_ci void *vaddr; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, 11462306a36Sopenharmony_ci "Attaching to frontend resources - gnt_ref=%d evtchn=%u\n", 11562306a36Sopenharmony_ci gnt_ref, remote_evtchn); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci err = xenbus_map_ring_valloc(pdev->xdev, &gnt_ref, 1, &vaddr); 11862306a36Sopenharmony_ci if (err < 0) { 11962306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 12062306a36Sopenharmony_ci "Error mapping other domain page in ours."); 12162306a36Sopenharmony_ci goto out; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pdev->sh_info = vaddr; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci err = bind_interdomain_evtchn_to_irqhandler_lateeoi( 12762306a36Sopenharmony_ci pdev->xdev, remote_evtchn, xen_pcibk_handle_event, 12862306a36Sopenharmony_ci 0, DRV_NAME, pdev); 12962306a36Sopenharmony_ci if (err < 0) { 13062306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 13162306a36Sopenharmony_ci "Error binding event channel to IRQ"); 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci pdev->evtchn_irq = err; 13562306a36Sopenharmony_ci err = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Attached!\n"); 13862306a36Sopenharmony_ciout: 13962306a36Sopenharmony_ci return err; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int xen_pcibk_attach(struct xen_pcibk_device *pdev) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci int err = 0; 14562306a36Sopenharmony_ci int gnt_ref; 14662306a36Sopenharmony_ci evtchn_port_t remote_evtchn; 14762306a36Sopenharmony_ci char *magic = NULL; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci mutex_lock(&pdev->dev_lock); 15162306a36Sopenharmony_ci /* Make sure we only do this setup once */ 15262306a36Sopenharmony_ci if (xenbus_read_driver_state(pdev->xdev->nodename) != 15362306a36Sopenharmony_ci XenbusStateInitialised) 15462306a36Sopenharmony_ci goto out; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Wait for frontend to state that it has published the configuration */ 15762306a36Sopenharmony_ci if (xenbus_read_driver_state(pdev->xdev->otherend) != 15862306a36Sopenharmony_ci XenbusStateInitialised) 15962306a36Sopenharmony_ci goto out; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Reading frontend config\n"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci err = xenbus_gather(XBT_NIL, pdev->xdev->otherend, 16462306a36Sopenharmony_ci "pci-op-ref", "%u", &gnt_ref, 16562306a36Sopenharmony_ci "event-channel", "%u", &remote_evtchn, 16662306a36Sopenharmony_ci "magic", NULL, &magic, NULL); 16762306a36Sopenharmony_ci if (err) { 16862306a36Sopenharmony_ci /* If configuration didn't get read correctly, wait longer */ 16962306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 17062306a36Sopenharmony_ci "Error reading configuration from frontend"); 17162306a36Sopenharmony_ci goto out; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) { 17562306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, -EFAULT, 17662306a36Sopenharmony_ci "version mismatch (%s/%s) with pcifront - " 17762306a36Sopenharmony_ci "halting " DRV_NAME, 17862306a36Sopenharmony_ci magic, XEN_PCI_MAGIC); 17962306a36Sopenharmony_ci err = -EFAULT; 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci err = xen_pcibk_do_attach(pdev, gnt_ref, remote_evtchn); 18462306a36Sopenharmony_ci if (err) 18562306a36Sopenharmony_ci goto out; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Connecting...\n"); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); 19062306a36Sopenharmony_ci if (err) 19162306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 19262306a36Sopenharmony_ci "Error switching to connected state!"); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err); 19562306a36Sopenharmony_ciout: 19662306a36Sopenharmony_ci mutex_unlock(&pdev->dev_lock); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci kfree(magic); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return err; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int xen_pcibk_publish_pci_dev(struct xen_pcibk_device *pdev, 20462306a36Sopenharmony_ci unsigned int domain, unsigned int bus, 20562306a36Sopenharmony_ci unsigned int devfn, unsigned int devid) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int err; 20862306a36Sopenharmony_ci int len; 20962306a36Sopenharmony_ci char str[64]; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci len = snprintf(str, sizeof(str), "vdev-%d", devid); 21262306a36Sopenharmony_ci if (unlikely(len >= (sizeof(str) - 1))) { 21362306a36Sopenharmony_ci err = -ENOMEM; 21462306a36Sopenharmony_ci goto out; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Note: The PV protocol uses %02x, don't change it */ 21862306a36Sopenharmony_ci err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, 21962306a36Sopenharmony_ci "%04x:%02x:%02x.%02x", domain, bus, 22062306a36Sopenharmony_ci PCI_SLOT(devfn), PCI_FUNC(devfn)); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciout: 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int xen_pcibk_export_device(struct xen_pcibk_device *pdev, 22762306a36Sopenharmony_ci int domain, int bus, int slot, int func, 22862306a36Sopenharmony_ci int devid) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct pci_dev *dev; 23162306a36Sopenharmony_ci int err = 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "exporting dom %x bus %x slot %x func %x\n", 23462306a36Sopenharmony_ci domain, bus, slot, func); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci dev = pcistub_get_pci_dev_by_slot(pdev, domain, bus, slot, func); 23762306a36Sopenharmony_ci if (!dev) { 23862306a36Sopenharmony_ci err = -EINVAL; 23962306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 24062306a36Sopenharmony_ci "Couldn't locate PCI device " 24162306a36Sopenharmony_ci "(%04x:%02x:%02x.%d)! " 24262306a36Sopenharmony_ci "perhaps already in-use?", 24362306a36Sopenharmony_ci domain, bus, slot, func); 24462306a36Sopenharmony_ci goto out; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci err = xen_pcibk_add_pci_dev(pdev, dev, devid, 24862306a36Sopenharmony_ci xen_pcibk_publish_pci_dev); 24962306a36Sopenharmony_ci if (err) 25062306a36Sopenharmony_ci goto out; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dev_info(&dev->dev, "registering for %d\n", pdev->xdev->otherend_id); 25362306a36Sopenharmony_ci if (xen_register_device_domain_owner(dev, 25462306a36Sopenharmony_ci pdev->xdev->otherend_id) != 0) { 25562306a36Sopenharmony_ci dev_err(&dev->dev, "Stealing ownership from dom%d.\n", 25662306a36Sopenharmony_ci xen_find_device_domain_owner(dev)); 25762306a36Sopenharmony_ci xen_unregister_device_domain_owner(dev); 25862306a36Sopenharmony_ci xen_register_device_domain_owner(dev, pdev->xdev->otherend_id); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* TODO: It'd be nice to export a bridge and have all of its children 26262306a36Sopenharmony_ci * get exported with it. This may be best done in xend (which will 26362306a36Sopenharmony_ci * have to calculate resource usage anyway) but we probably want to 26462306a36Sopenharmony_ci * put something in here to ensure that if a bridge gets given to a 26562306a36Sopenharmony_ci * driver domain, that all devices under that bridge are not given 26662306a36Sopenharmony_ci * to other driver domains (as he who controls the bridge can disable 26762306a36Sopenharmony_ci * it and stop the other devices from working). 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ciout: 27062306a36Sopenharmony_ci return err; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int xen_pcibk_remove_device(struct xen_pcibk_device *pdev, 27462306a36Sopenharmony_ci int domain, int bus, int slot, int func) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int err = 0; 27762306a36Sopenharmony_ci struct pci_dev *dev; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "removing dom %x bus %x slot %x func %x\n", 28062306a36Sopenharmony_ci domain, bus, slot, func); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dev = xen_pcibk_get_pci_dev(pdev, domain, bus, PCI_DEVFN(slot, func)); 28362306a36Sopenharmony_ci if (!dev) { 28462306a36Sopenharmony_ci err = -EINVAL; 28562306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device " 28662306a36Sopenharmony_ci "(%04x:%02x:%02x.%d)! not owned by this domain\n", 28762306a36Sopenharmony_ci domain, bus, slot, func); 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci dev_dbg(&dev->dev, "unregistering for %d\n", pdev->xdev->otherend_id); 29262306a36Sopenharmony_ci xen_unregister_device_domain_owner(dev); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* N.B. This ends up calling pcistub_put_pci_dev which ends up 29562306a36Sopenharmony_ci * doing the FLR. */ 29662306a36Sopenharmony_ci xen_pcibk_release_pci_dev(pdev, dev, true /* use the lock. */); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciout: 29962306a36Sopenharmony_ci return err; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int xen_pcibk_publish_pci_root(struct xen_pcibk_device *pdev, 30362306a36Sopenharmony_ci unsigned int domain, unsigned int bus) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci unsigned int d, b; 30662306a36Sopenharmony_ci int i, root_num, len, err; 30762306a36Sopenharmony_ci char str[64]; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n"); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, 31262306a36Sopenharmony_ci "root_num", "%d", &root_num); 31362306a36Sopenharmony_ci if (err == 0 || err == -ENOENT) 31462306a36Sopenharmony_ci root_num = 0; 31562306a36Sopenharmony_ci else if (err < 0) 31662306a36Sopenharmony_ci goto out; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Verify that we haven't already published this pci root */ 31962306a36Sopenharmony_ci for (i = 0; i < root_num; i++) { 32062306a36Sopenharmony_ci len = snprintf(str, sizeof(str), "root-%d", i); 32162306a36Sopenharmony_ci if (unlikely(len >= (sizeof(str) - 1))) { 32262306a36Sopenharmony_ci err = -ENOMEM; 32362306a36Sopenharmony_ci goto out; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, 32762306a36Sopenharmony_ci str, "%x:%x", &d, &b); 32862306a36Sopenharmony_ci if (err < 0) 32962306a36Sopenharmony_ci goto out; 33062306a36Sopenharmony_ci if (err != 2) { 33162306a36Sopenharmony_ci err = -EINVAL; 33262306a36Sopenharmony_ci goto out; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (d == domain && b == bus) { 33662306a36Sopenharmony_ci err = 0; 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci len = snprintf(str, sizeof(str), "root-%d", root_num); 34262306a36Sopenharmony_ci if (unlikely(len >= (sizeof(str) - 1))) { 34362306a36Sopenharmony_ci err = -ENOMEM; 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n", 34862306a36Sopenharmony_ci root_num, domain, bus); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, str, 35162306a36Sopenharmony_ci "%04x:%02x", domain, bus); 35262306a36Sopenharmony_ci if (err) 35362306a36Sopenharmony_ci goto out; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, 35662306a36Sopenharmony_ci "root_num", "%d", (root_num + 1)); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciout: 35962306a36Sopenharmony_ci return err; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int xen_pcibk_reconfigure(struct xen_pcibk_device *pdev, 36362306a36Sopenharmony_ci enum xenbus_state state) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int err = 0; 36662306a36Sopenharmony_ci int num_devs; 36762306a36Sopenharmony_ci int domain, bus, slot, func; 36862306a36Sopenharmony_ci unsigned int substate; 36962306a36Sopenharmony_ci int i, len; 37062306a36Sopenharmony_ci char state_str[64]; 37162306a36Sopenharmony_ci char dev_str[64]; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Reconfiguring device ...\n"); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci mutex_lock(&pdev->dev_lock); 37762306a36Sopenharmony_ci if (xenbus_read_driver_state(pdev->xdev->nodename) != state) 37862306a36Sopenharmony_ci goto out; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", 38162306a36Sopenharmony_ci &num_devs); 38262306a36Sopenharmony_ci if (err != 1) { 38362306a36Sopenharmony_ci if (err >= 0) 38462306a36Sopenharmony_ci err = -EINVAL; 38562306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 38662306a36Sopenharmony_ci "Error reading number of devices"); 38762306a36Sopenharmony_ci goto out; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci for (i = 0; i < num_devs; i++) { 39162306a36Sopenharmony_ci len = snprintf(state_str, sizeof(state_str), "state-%d", i); 39262306a36Sopenharmony_ci if (unlikely(len >= (sizeof(state_str) - 1))) { 39362306a36Sopenharmony_ci err = -ENOMEM; 39462306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 39562306a36Sopenharmony_ci "String overflow while reading " 39662306a36Sopenharmony_ci "configuration"); 39762306a36Sopenharmony_ci goto out; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci substate = xenbus_read_unsigned(pdev->xdev->nodename, state_str, 40062306a36Sopenharmony_ci XenbusStateUnknown); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci switch (substate) { 40362306a36Sopenharmony_ci case XenbusStateInitialising: 40462306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Attaching dev-%d ...\n", i); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci len = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); 40762306a36Sopenharmony_ci if (unlikely(len >= (sizeof(dev_str) - 1))) { 40862306a36Sopenharmony_ci err = -ENOMEM; 40962306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 41062306a36Sopenharmony_ci "String overflow while " 41162306a36Sopenharmony_ci "reading configuration"); 41262306a36Sopenharmony_ci goto out; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, 41562306a36Sopenharmony_ci dev_str, "%x:%x:%x.%x", 41662306a36Sopenharmony_ci &domain, &bus, &slot, &func); 41762306a36Sopenharmony_ci if (err < 0) { 41862306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 41962306a36Sopenharmony_ci "Error reading device " 42062306a36Sopenharmony_ci "configuration"); 42162306a36Sopenharmony_ci goto out; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci if (err != 4) { 42462306a36Sopenharmony_ci err = -EINVAL; 42562306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 42662306a36Sopenharmony_ci "Error parsing pci device " 42762306a36Sopenharmony_ci "configuration"); 42862306a36Sopenharmony_ci goto out; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci err = xen_pcibk_export_device(pdev, domain, bus, slot, 43262306a36Sopenharmony_ci func, i); 43362306a36Sopenharmony_ci if (err) 43462306a36Sopenharmony_ci goto out; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Publish pci roots. */ 43762306a36Sopenharmony_ci err = xen_pcibk_publish_pci_roots(pdev, 43862306a36Sopenharmony_ci xen_pcibk_publish_pci_root); 43962306a36Sopenharmony_ci if (err) { 44062306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 44162306a36Sopenharmony_ci "Error while publish PCI root" 44262306a36Sopenharmony_ci "buses for frontend"); 44362306a36Sopenharmony_ci goto out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, 44762306a36Sopenharmony_ci state_str, "%d", 44862306a36Sopenharmony_ci XenbusStateInitialised); 44962306a36Sopenharmony_ci if (err) { 45062306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 45162306a36Sopenharmony_ci "Error switching substate of " 45262306a36Sopenharmony_ci "dev-%d\n", i); 45362306a36Sopenharmony_ci goto out; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci case XenbusStateClosing: 45862306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "Detaching dev-%d ...\n", i); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci len = snprintf(dev_str, sizeof(dev_str), "vdev-%d", i); 46162306a36Sopenharmony_ci if (unlikely(len >= (sizeof(dev_str) - 1))) { 46262306a36Sopenharmony_ci err = -ENOMEM; 46362306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 46462306a36Sopenharmony_ci "String overflow while " 46562306a36Sopenharmony_ci "reading configuration"); 46662306a36Sopenharmony_ci goto out; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, 46962306a36Sopenharmony_ci dev_str, "%x:%x:%x.%x", 47062306a36Sopenharmony_ci &domain, &bus, &slot, &func); 47162306a36Sopenharmony_ci if (err < 0) { 47262306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 47362306a36Sopenharmony_ci "Error reading device " 47462306a36Sopenharmony_ci "configuration"); 47562306a36Sopenharmony_ci goto out; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci if (err != 4) { 47862306a36Sopenharmony_ci err = -EINVAL; 47962306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 48062306a36Sopenharmony_ci "Error parsing pci device " 48162306a36Sopenharmony_ci "configuration"); 48262306a36Sopenharmony_ci goto out; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci err = xen_pcibk_remove_device(pdev, domain, bus, slot, 48662306a36Sopenharmony_ci func); 48762306a36Sopenharmony_ci if (err) 48862306a36Sopenharmony_ci goto out; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* TODO: If at some point we implement support for pci 49162306a36Sopenharmony_ci * root hot-remove on pcifront side, we'll need to 49262306a36Sopenharmony_ci * remove unnecessary xenstore nodes of pci roots here. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci default: 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (state != XenbusStateReconfiguring) 50362306a36Sopenharmony_ci /* Make sure we only reconfigure once. */ 50462306a36Sopenharmony_ci goto out; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci err = xenbus_switch_state(pdev->xdev, XenbusStateReconfigured); 50762306a36Sopenharmony_ci if (err) { 50862306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 50962306a36Sopenharmony_ci "Error switching to reconfigured state!"); 51062306a36Sopenharmony_ci goto out; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ciout: 51462306a36Sopenharmony_ci mutex_unlock(&pdev->dev_lock); 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic void xen_pcibk_frontend_changed(struct xenbus_device *xdev, 51962306a36Sopenharmony_ci enum xenbus_state fe_state) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct xen_pcibk_device *pdev = dev_get_drvdata(&xdev->dev); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci switch (fe_state) { 52662306a36Sopenharmony_ci case XenbusStateInitialised: 52762306a36Sopenharmony_ci xen_pcibk_attach(pdev); 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci case XenbusStateReconfiguring: 53162306a36Sopenharmony_ci xen_pcibk_reconfigure(pdev, XenbusStateReconfiguring); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci case XenbusStateConnected: 53562306a36Sopenharmony_ci /* pcifront switched its state from reconfiguring to connected. 53662306a36Sopenharmony_ci * Then switch to connected state. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci xenbus_switch_state(xdev, XenbusStateConnected); 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci case XenbusStateClosing: 54262306a36Sopenharmony_ci xen_pcibk_disconnect(pdev); 54362306a36Sopenharmony_ci xenbus_switch_state(xdev, XenbusStateClosing); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci case XenbusStateClosed: 54762306a36Sopenharmony_ci xen_pcibk_disconnect(pdev); 54862306a36Sopenharmony_ci xenbus_switch_state(xdev, XenbusStateClosed); 54962306a36Sopenharmony_ci if (xenbus_dev_is_online(xdev)) 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci fallthrough; /* if not online */ 55262306a36Sopenharmony_ci case XenbusStateUnknown: 55362306a36Sopenharmony_ci dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); 55462306a36Sopenharmony_ci device_unregister(&xdev->dev); 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci default: 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int xen_pcibk_setup_backend(struct xen_pcibk_device *pdev) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci /* Get configuration from xend (if available now) */ 56562306a36Sopenharmony_ci int domain, bus, slot, func; 56662306a36Sopenharmony_ci int err = 0; 56762306a36Sopenharmony_ci int i, num_devs; 56862306a36Sopenharmony_ci char dev_str[64]; 56962306a36Sopenharmony_ci char state_str[64]; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mutex_lock(&pdev->dev_lock); 57262306a36Sopenharmony_ci /* It's possible we could get the call to setup twice, so make sure 57362306a36Sopenharmony_ci * we're not already connected. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci if (xenbus_read_driver_state(pdev->xdev->nodename) != 57662306a36Sopenharmony_ci XenbusStateInitWait) 57762306a36Sopenharmony_ci goto out; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci dev_dbg(&pdev->xdev->dev, "getting be setup\n"); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, "num_devs", "%d", 58262306a36Sopenharmony_ci &num_devs); 58362306a36Sopenharmony_ci if (err != 1) { 58462306a36Sopenharmony_ci if (err >= 0) 58562306a36Sopenharmony_ci err = -EINVAL; 58662306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 58762306a36Sopenharmony_ci "Error reading number of devices"); 58862306a36Sopenharmony_ci goto out; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for (i = 0; i < num_devs; i++) { 59262306a36Sopenharmony_ci int l = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); 59362306a36Sopenharmony_ci if (unlikely(l >= (sizeof(dev_str) - 1))) { 59462306a36Sopenharmony_ci err = -ENOMEM; 59562306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 59662306a36Sopenharmony_ci "String overflow while reading " 59762306a36Sopenharmony_ci "configuration"); 59862306a36Sopenharmony_ci goto out; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err = xenbus_scanf(XBT_NIL, pdev->xdev->nodename, dev_str, 60262306a36Sopenharmony_ci "%x:%x:%x.%x", &domain, &bus, &slot, &func); 60362306a36Sopenharmony_ci if (err < 0) { 60462306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 60562306a36Sopenharmony_ci "Error reading device configuration"); 60662306a36Sopenharmony_ci goto out; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci if (err != 4) { 60962306a36Sopenharmony_ci err = -EINVAL; 61062306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 61162306a36Sopenharmony_ci "Error parsing pci device " 61262306a36Sopenharmony_ci "configuration"); 61362306a36Sopenharmony_ci goto out; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci err = xen_pcibk_export_device(pdev, domain, bus, slot, func, i); 61762306a36Sopenharmony_ci if (err) 61862306a36Sopenharmony_ci goto out; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Switch substate of this device. */ 62162306a36Sopenharmony_ci l = snprintf(state_str, sizeof(state_str), "state-%d", i); 62262306a36Sopenharmony_ci if (unlikely(l >= (sizeof(state_str) - 1))) { 62362306a36Sopenharmony_ci err = -ENOMEM; 62462306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 62562306a36Sopenharmony_ci "String overflow while reading " 62662306a36Sopenharmony_ci "configuration"); 62762306a36Sopenharmony_ci goto out; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci err = xenbus_printf(XBT_NIL, pdev->xdev->nodename, state_str, 63062306a36Sopenharmony_ci "%d", XenbusStateInitialised); 63162306a36Sopenharmony_ci if (err) { 63262306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, "Error switching " 63362306a36Sopenharmony_ci "substate of dev-%d\n", i); 63462306a36Sopenharmony_ci goto out; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci err = xen_pcibk_publish_pci_roots(pdev, xen_pcibk_publish_pci_root); 63962306a36Sopenharmony_ci if (err) { 64062306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 64162306a36Sopenharmony_ci "Error while publish PCI root buses " 64262306a36Sopenharmony_ci "for frontend"); 64362306a36Sopenharmony_ci goto out; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci err = xenbus_switch_state(pdev->xdev, XenbusStateInitialised); 64762306a36Sopenharmony_ci if (err) 64862306a36Sopenharmony_ci xenbus_dev_fatal(pdev->xdev, err, 64962306a36Sopenharmony_ci "Error switching to initialised state!"); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciout: 65262306a36Sopenharmony_ci mutex_unlock(&pdev->dev_lock); 65362306a36Sopenharmony_ci if (!err) 65462306a36Sopenharmony_ci /* see if pcifront is already configured (if not, we'll wait) */ 65562306a36Sopenharmony_ci xen_pcibk_attach(pdev); 65662306a36Sopenharmony_ci return err; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void xen_pcibk_be_watch(struct xenbus_watch *watch, 66062306a36Sopenharmony_ci const char *path, const char *token) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct xen_pcibk_device *pdev = 66362306a36Sopenharmony_ci container_of(watch, struct xen_pcibk_device, be_watch); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci switch (xenbus_read_driver_state(pdev->xdev->nodename)) { 66662306a36Sopenharmony_ci case XenbusStateInitWait: 66762306a36Sopenharmony_ci xen_pcibk_setup_backend(pdev); 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci case XenbusStateInitialised: 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * We typically move to Initialised when the first device was 67362306a36Sopenharmony_ci * added. Hence subsequent devices getting added may need 67462306a36Sopenharmony_ci * reconfiguring. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci xen_pcibk_reconfigure(pdev, XenbusStateInitialised); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci default: 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int xen_pcibk_xenbus_probe(struct xenbus_device *dev, 68562306a36Sopenharmony_ci const struct xenbus_device_id *id) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci int err = 0; 68862306a36Sopenharmony_ci struct xen_pcibk_device *pdev = alloc_pdev(dev); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (pdev == NULL) { 69162306a36Sopenharmony_ci err = -ENOMEM; 69262306a36Sopenharmony_ci xenbus_dev_fatal(dev, err, 69362306a36Sopenharmony_ci "Error allocating xen_pcibk_device struct"); 69462306a36Sopenharmony_ci goto out; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* wait for xend to configure us */ 69862306a36Sopenharmony_ci err = xenbus_switch_state(dev, XenbusStateInitWait); 69962306a36Sopenharmony_ci if (err) 70062306a36Sopenharmony_ci goto out; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* watch the backend node for backend configuration information */ 70362306a36Sopenharmony_ci err = xenbus_watch_path(dev, dev->nodename, &pdev->be_watch, 70462306a36Sopenharmony_ci NULL, xen_pcibk_be_watch); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci goto out; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci pdev->be_watching = 1; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* We need to force a call to our callback here in case 71162306a36Sopenharmony_ci * xend already configured us! 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_ci xen_pcibk_be_watch(&pdev->be_watch, NULL, NULL); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ciout: 71662306a36Sopenharmony_ci return err; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void xen_pcibk_xenbus_remove(struct xenbus_device *dev) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct xen_pcibk_device *pdev = dev_get_drvdata(&dev->dev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (pdev != NULL) 72462306a36Sopenharmony_ci free_pdev(pdev); 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic const struct xenbus_device_id xen_pcibk_ids[] = { 72862306a36Sopenharmony_ci {"pci"}, 72962306a36Sopenharmony_ci {""}, 73062306a36Sopenharmony_ci}; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic struct xenbus_driver xen_pcibk_driver = { 73362306a36Sopenharmony_ci .name = DRV_NAME, 73462306a36Sopenharmony_ci .ids = xen_pcibk_ids, 73562306a36Sopenharmony_ci .probe = xen_pcibk_xenbus_probe, 73662306a36Sopenharmony_ci .remove = xen_pcibk_xenbus_remove, 73762306a36Sopenharmony_ci .otherend_changed = xen_pcibk_frontend_changed, 73862306a36Sopenharmony_ci}; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ciconst struct xen_pcibk_backend *__read_mostly xen_pcibk_backend; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ciint __init xen_pcibk_xenbus_register(void) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci if (!xen_pcibk_pv_support()) 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci xen_pcibk_backend = &xen_pcibk_vpci_backend; 74862306a36Sopenharmony_ci if (passthrough) 74962306a36Sopenharmony_ci xen_pcibk_backend = &xen_pcibk_passthrough_backend; 75062306a36Sopenharmony_ci pr_info("backend is %s\n", xen_pcibk_backend->name); 75162306a36Sopenharmony_ci return xenbus_register_backend(&xen_pcibk_driver); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_civoid __exit xen_pcibk_xenbus_unregister(void) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci if (xen_pcibk_pv_support()) 75762306a36Sopenharmony_ci xenbus_unregister_driver(&xen_pcibk_driver); 75862306a36Sopenharmony_ci} 759