aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch')
-rw-r--r--target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch1165
1 files changed, 0 insertions, 1165 deletions
diff --git a/target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch b/target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch
deleted file mode 100644
index f2a65ffba5..0000000000
--- a/target/linux/layerscape/patches-4.9/818-vfio-support-layerscape.patch
+++ /dev/null
@@ -1,1165 +0,0 @@
-From e6af99cc1d56322fc960d072af1a7e0e9285b90c Mon Sep 17 00:00:00 2001
-From: Yangbo Lu <yangbo.lu@nxp.com>
-Date: Thu, 5 Jul 2018 17:39:43 +0800
-Subject: [PATCH 30/32] vfio: support layerscape
-
-This is an integrated patch for layerscape vfio support.
-
-Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com>
-Signed-off-by: Eric Auger <eric.auger@redhat.com>
-Signed-off-by: Robin Murphy <robin.murphy@arm.com>
-Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
-Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
----
- drivers/vfio/Kconfig | 1 +
- drivers/vfio/Makefile | 1 +
- drivers/vfio/fsl-mc/Kconfig | 9 +
- drivers/vfio/fsl-mc/Makefile | 2 +
- drivers/vfio/fsl-mc/vfio_fsl_mc.c | 752 ++++++++++++++++++++++
- drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 199 ++++++
- drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 55 ++
- drivers/vfio/vfio_iommu_type1.c | 39 +-
- include/uapi/linux/vfio.h | 1 +
- 9 files changed, 1057 insertions(+), 2 deletions(-)
- create mode 100644 drivers/vfio/fsl-mc/Kconfig
- create mode 100644 drivers/vfio/fsl-mc/Makefile
- create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc.c
- create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
- create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
-
---- a/drivers/vfio/Kconfig
-+++ b/drivers/vfio/Kconfig
-@@ -48,4 +48,5 @@ menuconfig VFIO_NOIOMMU
-
- source "drivers/vfio/pci/Kconfig"
- source "drivers/vfio/platform/Kconfig"
-+source "drivers/vfio/fsl-mc/Kconfig"
- source "virt/lib/Kconfig"
---- a/drivers/vfio/Makefile
-+++ b/drivers/vfio/Makefile
-@@ -7,3 +7,4 @@ obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vf
- obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o
- obj-$(CONFIG_VFIO_PCI) += pci/
- obj-$(CONFIG_VFIO_PLATFORM) += platform/
-+obj-$(CONFIG_VFIO_FSL_MC) += fsl-mc/
---- /dev/null
-+++ b/drivers/vfio/fsl-mc/Kconfig
-@@ -0,0 +1,9 @@
-+config VFIO_FSL_MC
-+ tristate "VFIO support for QorIQ DPAA2 fsl-mc bus devices"
-+ depends on VFIO && FSL_MC_BUS && EVENTFD
-+ help
-+ Driver to enable support for the VFIO QorIQ DPAA2 fsl-mc
-+ (Management Complex) devices. This is required to passthrough
-+ fsl-mc bus devices using the VFIO framework.
-+
-+ If you don't know what to do here, say N.
---- /dev/null
-+++ b/drivers/vfio/fsl-mc/Makefile
-@@ -0,0 +1,2 @@
-+vfio-fsl_mc-y := vfio_fsl_mc.o
-+obj-$(CONFIG_VFIO_FSL_MC) += vfio_fsl_mc.o vfio_fsl_mc_intr.o
---- /dev/null
-+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
-@@ -0,0 +1,752 @@
-+/*
-+ * Freescale Management Complex (MC) device passthrough using VFIO
-+ *
-+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
-+ * Copyright 2016-2017 NXP
-+ * Author: Bharat Bhushan <bharat.bhushan@nxp.com>
-+ *
-+ * This file is licensed under the terms of the GNU General Public
-+ * License version 2. This program is licensed "as is" without any
-+ * warranty of any kind, whether express or implied.
-+ */
-+
-+#include <linux/device.h>
-+#include <linux/iommu.h>
-+#include <linux/module.h>
-+#include <linux/mutex.h>
-+#include <linux/slab.h>
-+#include <linux/types.h>
-+#include <linux/vfio.h>
-+#include <linux/delay.h>
-+
-+#include <linux/fsl/mc.h>
-+
-+#include "vfio_fsl_mc_private.h"
-+
-+#define DRIVER_VERSION "0.10"
-+#define DRIVER_AUTHOR "Bharat Bhushan <bharat.bhushan@nxp.com>"
-+#define DRIVER_DESC "VFIO for FSL-MC devices - User Level meta-driver"
-+
-+static DEFINE_MUTEX(driver_lock);
-+
-+/* FSl-MC device regions (address and size) are aligned to 64K.
-+ * While MC firmware reports size less than 64K for some objects (it actually
-+ * reports size which does not include reserved space beyond valid bytes).
-+ * Align the size to PAGE_SIZE for userspace to mmap.
-+ */
-+static size_t aligned_region_size(struct fsl_mc_device *mc_dev, int index)
-+{
-+ size_t size;
-+
-+ size = resource_size(&mc_dev->regions[index]);
-+ return PAGE_ALIGN(size);
-+}
-+
-+static int vfio_fsl_mc_regions_init(struct vfio_fsl_mc_device *vdev)
-+{
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ int count = mc_dev->obj_desc.region_count;
-+ int i;
-+
-+ vdev->regions = kcalloc(count, sizeof(struct vfio_fsl_mc_region),
-+ GFP_KERNEL);
-+ if (!vdev->regions)
-+ return -ENOMEM;
-+
-+ for (i = 0; i < mc_dev->obj_desc.region_count; i++) {
-+ vdev->regions[i].addr = mc_dev->regions[i].start;
-+ vdev->regions[i].size = aligned_region_size(mc_dev, i);
-+ vdev->regions[i].type = VFIO_FSL_MC_REGION_TYPE_MMIO;
-+ if (mc_dev->regions[i].flags & IORESOURCE_CACHEABLE)
-+ vdev->regions[i].type |=
-+ VFIO_FSL_MC_REGION_TYPE_CACHEABLE;
-+ vdev->regions[i].flags = VFIO_REGION_INFO_FLAG_MMAP;
-+ vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ;
-+ if (!(mc_dev->regions[i].flags & IORESOURCE_READONLY))
-+ vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE;
-+ }
-+
-+ vdev->num_regions = mc_dev->obj_desc.region_count;
-+ return 0;
-+}
-+
-+static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev)
-+{
-+ int i;
-+
-+ for (i = 0; i < vdev->num_regions; i++)
-+ iounmap(vdev->regions[i].ioaddr);
-+
-+ vdev->num_regions = 0;
-+ kfree(vdev->regions);
-+}
-+
-+static int vfio_fsl_mc_open(void *device_data)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ int ret;
-+
-+ if (!try_module_get(THIS_MODULE))
-+ return -ENODEV;
-+
-+ mutex_lock(&driver_lock);
-+ if (!vdev->refcnt) {
-+ ret = vfio_fsl_mc_regions_init(vdev);
-+ if (ret)
-+ goto error_region_init;
-+
-+ ret = vfio_fsl_mc_irqs_init(vdev);
-+ if (ret)
-+ goto error_irq_init;
-+ }
-+
-+ vdev->refcnt++;
-+ mutex_unlock(&driver_lock);
-+ return 0;
-+
-+error_irq_init:
-+ vfio_fsl_mc_regions_cleanup(vdev);
-+error_region_init:
-+ mutex_unlock(&driver_lock);
-+ if (ret)
-+ module_put(THIS_MODULE);
-+
-+ return ret;
-+}
-+
-+static void vfio_fsl_mc_release(void *device_data)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+
-+ mutex_lock(&driver_lock);
-+
-+ if (!(--vdev->refcnt)) {
-+ vfio_fsl_mc_regions_cleanup(vdev);
-+ vfio_fsl_mc_irqs_cleanup(vdev);
-+ }
-+
-+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
-+ dprc_reset_container(mc_dev->mc_io, 0, mc_dev->mc_handle,
-+ mc_dev->obj_desc.id);
-+
-+ mutex_unlock(&driver_lock);
-+
-+ module_put(THIS_MODULE);
-+}
-+
-+static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd,
-+ unsigned long arg)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ unsigned long minsz;
-+
-+ if (WARN_ON(!mc_dev))
-+ return -ENODEV;
-+
-+ switch (cmd) {
-+ case VFIO_DEVICE_GET_INFO:
-+ {
-+ struct vfio_device_info info;
-+
-+ minsz = offsetofend(struct vfio_device_info, num_irqs);
-+
-+ if (copy_from_user(&info, (void __user *)arg, minsz))
-+ return -EFAULT;
-+
-+ if (info.argsz < minsz)
-+ return -EINVAL;
-+
-+ info.flags = VFIO_DEVICE_FLAGS_FSL_MC;
-+ info.num_regions = mc_dev->obj_desc.region_count;
-+ info.num_irqs = mc_dev->obj_desc.irq_count;
-+
-+ return copy_to_user((void __user *)arg, &info, minsz);
-+ }
-+ case VFIO_DEVICE_GET_REGION_INFO:
-+ {
-+ struct vfio_region_info info;
-+
-+ minsz = offsetofend(struct vfio_region_info, offset);
-+
-+ if (copy_from_user(&info, (void __user *)arg, minsz))
-+ return -EFAULT;
-+
-+ if (info.argsz < minsz)
-+ return -EINVAL;
-+
-+ if (info.index >= vdev->num_regions)
-+ return -EINVAL;
-+
-+ /* map offset to the physical address */
-+ info.offset = VFIO_FSL_MC_INDEX_TO_OFFSET(info.index);
-+ info.size = vdev->regions[info.index].size;
-+ info.flags = vdev->regions[info.index].flags;
-+
-+ return copy_to_user((void __user *)arg, &info, minsz);
-+ }
-+ case VFIO_DEVICE_GET_IRQ_INFO:
-+ {
-+ struct vfio_irq_info info;
-+
-+ minsz = offsetofend(struct vfio_irq_info, count);
-+ if (copy_from_user(&info, (void __user *)arg, minsz))
-+ return -EFAULT;
-+
-+ if (info.argsz < minsz)
-+ return -EINVAL;
-+
-+ if (info.index >= mc_dev->obj_desc.irq_count)
-+ return -EINVAL;
-+
-+ if (vdev->mc_irqs != NULL) {
-+ info.flags = vdev->mc_irqs[info.index].flags;
-+ info.count = vdev->mc_irqs[info.index].count;
-+ } else {
-+ /*
-+ * If IRQs are not initialized then these can not
-+ * be configuted and used by user-space/
-+ */
-+ info.flags = 0;
-+ info.count = 0;
-+ }
-+
-+ return copy_to_user((void __user *)arg, &info, minsz);
-+ }
-+ case VFIO_DEVICE_SET_IRQS:
-+ {
-+ struct vfio_irq_set hdr;
-+ u8 *data = NULL;
-+ int ret = 0;
-+
-+ minsz = offsetofend(struct vfio_irq_set, count);
-+
-+ if (copy_from_user(&hdr, (void __user *)arg, minsz))
-+ return -EFAULT;
-+
-+ if (hdr.argsz < minsz)
-+ return -EINVAL;
-+
-+ if (hdr.index >= mc_dev->obj_desc.irq_count)
-+ return -EINVAL;
-+
-+ if (hdr.start != 0 || hdr.count > 1)
-+ return -EINVAL;
-+
-+ if (hdr.count == 0 &&
-+ (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE) ||
-+ !(hdr.flags & VFIO_IRQ_SET_ACTION_TRIGGER)))
-+ return -EINVAL;
-+
-+ if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
-+ VFIO_IRQ_SET_ACTION_TYPE_MASK))
-+ return -EINVAL;
-+
-+ if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
-+ size_t size;
-+
-+ if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
-+ size = sizeof(uint8_t);
-+ else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
-+ size = sizeof(int32_t);
-+ else
-+ return -EINVAL;
-+
-+ if (hdr.argsz - minsz < hdr.count * size)
-+ return -EINVAL;
-+
-+ data = memdup_user((void __user *)(arg + minsz),
-+ hdr.count * size);
-+ if (IS_ERR(data))
-+ return PTR_ERR(data);
-+ }
-+
-+ ret = vfio_fsl_mc_set_irqs_ioctl(vdev, hdr.flags,
-+ hdr.index, hdr.start,
-+ hdr.count, data);
-+ return ret;
-+ }
-+ case VFIO_DEVICE_RESET:
-+ {
-+ return -EINVAL;
-+ }
-+ default:
-+ return -EINVAL;
-+ }
-+}
-+
-+static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos);
-+ loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK;
-+ struct vfio_fsl_mc_region *region;
-+ uint64_t data[8];
-+ int i;
-+
-+ /* Read ioctl supported only for DPRC and DPMCP device */
-+ if (strcmp(vdev->mc_dev->obj_desc.type, "dprc") &&
-+ strcmp(vdev->mc_dev->obj_desc.type, "dpmcp"))
-+ return -EINVAL;
-+
-+ if (index >= vdev->num_regions)
-+ return -EINVAL;
-+
-+ region = &vdev->regions[index];
-+
-+ if (!(region->flags & VFIO_REGION_INFO_FLAG_READ))
-+ return -EINVAL;
-+
-+ if (!region->type & VFIO_FSL_MC_REGION_TYPE_MMIO)
-+ return -EINVAL;
-+
-+ if (!region->ioaddr) {
-+ region->ioaddr = ioremap_nocache(region->addr, region->size);
-+ if (!region->ioaddr)
-+ return -ENOMEM;
-+ }
-+
-+ if (count != 64 || off != 0)
-+ return -EINVAL;
-+
-+ for (i = 7; i >= 0; i--)
-+ data[i] = readq(region->ioaddr + i * sizeof(uint64_t));
-+
-+ if (copy_to_user(buf, data, 64))
-+ return -EFAULT;
-+
-+ return count;
-+}
-+
-+#define MC_CMD_COMPLETION_TIMEOUT_MS 5000
-+#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500
-+
-+static int vfio_fsl_mc_dprc_wait_for_response(void __iomem *ioaddr)
-+{
-+ enum mc_cmd_status status;
-+ unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000;
-+
-+ for (;;) {
-+ u64 header;
-+ struct mc_cmd_header *resp_hdr;
-+
-+ __iormb();
-+ header = readq(ioaddr);
-+ __iormb();
-+
-+ resp_hdr = (struct mc_cmd_header *)&header;
-+ status = (enum mc_cmd_status)resp_hdr->status;
-+ if (status != MC_CMD_STATUS_READY)
-+ break;
-+
-+ udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS);
-+ timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS;
-+ if (timeout_usecs == 0)
-+ return -ETIMEDOUT;
-+ }
-+
-+ return 0;
-+}
-+
-+static int vfio_fsl_mc_send_command(void __iomem *ioaddr, uint64_t *cmd_data)
-+{
-+ int i;
-+
-+ /* Write at command header in the end */
-+ for (i = 7; i >= 0; i--)
-+ writeq(cmd_data[i], ioaddr + i * sizeof(uint64_t));
-+
-+ /* Wait for response before returning to user-space
-+ * This can be optimized in future to even prepare response
-+ * before returning to user-space and avoid read ioctl.
-+ */
-+ return vfio_fsl_mc_dprc_wait_for_response(ioaddr);
-+}
-+
-+static int vfio_handle_dprc_commands(void __iomem *ioaddr, uint64_t *cmd_data)
-+{
-+ uint64_t cmd_hdr = cmd_data[0];
-+ int cmd = (cmd_hdr >> 52) & 0xfff;
-+
-+ switch (cmd) {
-+ case DPRC_CMDID_OPEN:
-+ default:
-+ return vfio_fsl_mc_send_command(ioaddr, cmd_data);
-+ }
-+
-+ return 0;
-+}
-+
-+static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf,
-+ size_t count, loff_t *ppos)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos);
-+ loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK;
-+ struct vfio_fsl_mc_region *region;
-+ uint64_t data[8];
-+ int ret;
-+
-+ /* Write ioctl supported only for DPRC and DPMCP device */
-+ if (strcmp(vdev->mc_dev->obj_desc.type, "dprc") &&
-+ strcmp(vdev->mc_dev->obj_desc.type, "dpmcp"))
-+ return -EINVAL;
-+
-+ if (index >= vdev->num_regions)
-+ return -EINVAL;
-+
-+ region = &vdev->regions[index];
-+
-+ if (!(region->flags & VFIO_REGION_INFO_FLAG_WRITE))
-+ return -EINVAL;
-+
-+ if (!region->type & VFIO_FSL_MC_REGION_TYPE_MMIO)
-+ return -EINVAL;
-+
-+ if (!region->ioaddr) {
-+ region->ioaddr = ioremap_nocache(region->addr, region->size);
-+ if (!region->ioaddr)
-+ return -ENOMEM;
-+ }
-+
-+ if (count != 64 || off != 0)
-+ return -EINVAL;
-+
-+ if (copy_from_user(&data, buf, 64))
-+ return -EFAULT;
-+
-+ ret = vfio_handle_dprc_commands(region->ioaddr, data);
-+ if (ret)
-+ return ret;
-+
-+ return count;
-+}
-+
-+static int vfio_fsl_mc_mmap_mmio(struct vfio_fsl_mc_region region,
-+ struct vm_area_struct *vma)
-+{
-+ u64 size = vma->vm_end - vma->vm_start;
-+ u64 pgoff, base;
-+
-+ pgoff = vma->vm_pgoff &
-+ ((1U << (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
-+ base = pgoff << PAGE_SHIFT;
-+
-+ if (region.size < PAGE_SIZE || base + size > region.size)
-+ return -EINVAL;
-+ /*
-+ * Set the REGION_TYPE_CACHEABLE (QBman CENA regs) to be the
-+ * cache inhibited area of the portal to avoid coherency issues
-+ * if a user migrates to another core.
-+ */
-+ if (region.type & VFIO_FSL_MC_REGION_TYPE_CACHEABLE)
-+ vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot);
-+ else
-+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-+
-+ vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff;
-+
-+ return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
-+ size, vma->vm_page_prot);
-+}
-+
-+/* Allows mmaping fsl_mc device regions in assigned DPRC */
-+static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma)
-+{
-+ struct vfio_fsl_mc_device *vdev = device_data;
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ unsigned long size, addr;
-+ int index;
-+
-+ index = vma->vm_pgoff >> (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT);
-+
-+ if (vma->vm_end < vma->vm_start)
-+ return -EINVAL;
-+ if (vma->vm_start & ~PAGE_MASK)
-+ return -EINVAL;
-+ if (vma->vm_end & ~PAGE_MASK)
-+ return -EINVAL;
-+ if (!(vma->vm_flags & VM_SHARED))
-+ return -EINVAL;
-+ if (index >= vdev->num_regions)
-+ return -EINVAL;
-+
-+ if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP))
-+ return -EINVAL;
-+
-+ if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)
-+ && (vma->vm_flags & VM_READ))
-+ return -EINVAL;
-+
-+ if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)
-+ && (vma->vm_flags & VM_WRITE))
-+ return -EINVAL;
-+
-+ addr = vdev->regions[index].addr;
-+ size = vdev->regions[index].size;
-+
-+ vma->vm_private_data = mc_dev;
-+
-+ if (vdev->regions[index].type & VFIO_FSL_MC_REGION_TYPE_MMIO)
-+ return vfio_fsl_mc_mmap_mmio(vdev->regions[index], vma);
-+
-+ return -EFAULT;
-+}
-+
-+static const struct vfio_device_ops vfio_fsl_mc_ops = {
-+ .name = "vfio-fsl-mc",
-+ .open = vfio_fsl_mc_open,
-+ .release = vfio_fsl_mc_release,
-+ .ioctl = vfio_fsl_mc_ioctl,
-+ .read = vfio_fsl_mc_read,
-+ .write = vfio_fsl_mc_write,
-+ .mmap = vfio_fsl_mc_mmap,
-+};
-+
-+static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev)
-+{
-+ struct device *root_dprc_dev;
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ struct device *dev = &mc_dev->dev;
-+ struct fsl_mc_bus *mc_bus;
-+ struct irq_domain *mc_msi_domain;
-+ unsigned int irq_count;
-+ int ret;
-+
-+ /* device must be DPRC */
-+ if (strcmp(mc_dev->obj_desc.type, "dprc"))
-+ return -EINVAL;
-+
-+ /* mc_io must be un-initialized */
-+ WARN_ON(mc_dev->mc_io);
-+
-+ /* allocate a portal from the root DPRC for vfio use */
-+ fsl_mc_get_root_dprc(dev, &root_dprc_dev);
-+ if (WARN_ON(!root_dprc_dev))
-+ return -EINVAL;
-+
-+ ret = fsl_mc_portal_allocate(to_fsl_mc_device(root_dprc_dev),
-+ FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
-+ &mc_dev->mc_io);
-+ if (ret < 0)
-+ goto clean_msi_domain;
-+
-+ /* Reset MCP before move on */
-+ ret = fsl_mc_portal_reset(mc_dev->mc_io);
-+ if (ret < 0) {
-+ dev_err(dev, "dprc portal reset failed: error = %d\n", ret);
-+ goto free_mc_portal;
-+ }
-+
-+ /* MSI domain set up */
-+ ret = fsl_mc_find_msi_domain(root_dprc_dev->parent, &mc_msi_domain);
-+ if (ret < 0)
-+ goto free_mc_portal;
-+
-+ dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
-+
-+ ret = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
-+ &mc_dev->mc_handle);
-+ if (ret) {
-+ dev_err(dev, "dprc_open() failed: error = %d\n", ret);
-+ goto free_mc_portal;
-+ }
-+
-+ /* Initialize resource pool */
-+ fsl_mc_init_all_resource_pools(mc_dev);
-+
-+ mc_bus = to_fsl_mc_bus(mc_dev);
-+
-+ if (!mc_bus->irq_resources) {
-+ irq_count = FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS;
-+ ret = fsl_mc_populate_irq_pool(mc_bus, irq_count);
-+ if (ret < 0) {
-+ dev_err(dev, "%s: Failed to init irq-pool\n", __func__);
-+ goto clean_resource_pool;
-+ }
-+ }
-+
-+ mutex_init(&mc_bus->scan_mutex);
-+
-+ mutex_lock(&mc_bus->scan_mutex);
-+ ret = dprc_scan_objects(mc_dev, mc_dev->driver_override,
-+ &irq_count);
-+ mutex_unlock(&mc_bus->scan_mutex);
-+ if (ret) {
-+ dev_err(dev, "dprc_scan_objects() fails (%d)\n", ret);
-+ goto clean_irq_pool;
-+ }
-+
-+ if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
-+ dev_warn(&mc_dev->dev,
-+ "IRQs needed (%u) exceed IRQs preallocated (%u)\n",
-+ irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
-+ }
-+
-+ return 0;
-+
-+clean_irq_pool:
-+ fsl_mc_cleanup_irq_pool(mc_bus);
-+
-+clean_resource_pool:
-+ fsl_mc_cleanup_all_resource_pools(mc_dev);
-+ dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
-+
-+free_mc_portal:
-+ fsl_mc_portal_free(mc_dev->mc_io);
-+
-+clean_msi_domain:
-+ dev_set_msi_domain(&mc_dev->dev, NULL);
-+
-+ return ret;
-+}
-+
-+static int vfio_fsl_mc_device_remove(struct device *dev, void *data)
-+{
-+ struct fsl_mc_device *mc_dev;
-+
-+ WARN_ON(dev == NULL);
-+
-+ mc_dev = to_fsl_mc_device(dev);
-+ if (WARN_ON(mc_dev == NULL))
-+ return -ENODEV;
-+
-+ fsl_mc_device_remove(mc_dev);
-+ return 0;
-+}
-+
-+static void vfio_fsl_mc_cleanup_dprc(struct vfio_fsl_mc_device *vdev)
-+{
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ struct fsl_mc_bus *mc_bus;
-+
-+ /* device must be DPRC */
-+ if (strcmp(mc_dev->obj_desc.type, "dprc"))
-+ return;
-+
-+ device_for_each_child(&mc_dev->dev, NULL, vfio_fsl_mc_device_remove);
-+
-+ mc_bus = to_fsl_mc_bus(mc_dev);
-+ if (dev_get_msi_domain(&mc_dev->dev))
-+ fsl_mc_cleanup_irq_pool(mc_bus);
-+
-+ dev_set_msi_domain(&mc_dev->dev, NULL);
-+
-+ fsl_mc_cleanup_all_resource_pools(mc_dev);
-+ dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
-+ fsl_mc_portal_free(mc_dev->mc_io);
-+}
-+
-+static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev)
-+{
-+ struct iommu_group *group;
-+ struct vfio_fsl_mc_device *vdev;
-+ struct device *dev = &mc_dev->dev;
-+ int ret;
-+
-+ group = vfio_iommu_group_get(dev);
-+ if (!group) {
-+ dev_err(dev, "%s: VFIO: No IOMMU group\n", __func__);
-+ return -EINVAL;
-+ }
-+
-+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
-+ if (!vdev) {
-+ vfio_iommu_group_put(group, dev);
-+ return -ENOMEM;
-+ }
-+
-+ vdev->mc_dev = mc_dev;
-+
-+ ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev);
-+ if (ret) {
-+ dev_err(dev, "%s: Failed to add to vfio group\n", __func__);
-+ goto free_vfio_device;
-+ }
-+
-+ /* DPRC container scanned and it's chilren bound with vfio driver */
-+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) {
-+ ret = vfio_fsl_mc_initialize_dprc(vdev);
-+ if (ret) {
-+ vfio_del_group_dev(dev);
-+ goto free_vfio_device;
-+ }
-+ } else {
-+ struct fsl_mc_device *mc_bus_dev;
-+
-+ /* Non-dprc devices share mc_io from the parent dprc */
-+ mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
-+ if (mc_bus_dev == NULL) {
-+ vfio_del_group_dev(dev);
-+ goto free_vfio_device;
-+ }
-+
-+ mc_dev->mc_io = mc_bus_dev->mc_io;
-+
-+ /* Inherit parent MSI domain */
-+ dev_set_msi_domain(&mc_dev->dev,
-+ dev_get_msi_domain(mc_dev->dev.parent));
-+ }
-+ return 0;
-+
-+free_vfio_device:
-+ kfree(vdev);
-+ vfio_iommu_group_put(group, dev);
-+ return ret;
-+}
-+
-+static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev)
-+{
-+ struct vfio_fsl_mc_device *vdev;
-+ struct device *dev = &mc_dev->dev;
-+
-+ vdev = vfio_del_group_dev(dev);
-+ if (!vdev)
-+ return -EINVAL;
-+
-+ if (strcmp(mc_dev->obj_desc.type, "dprc") == 0)
-+ vfio_fsl_mc_cleanup_dprc(vdev);
-+ else
-+ dev_set_msi_domain(&mc_dev->dev, NULL);
-+
-+ mc_dev->mc_io = NULL;
-+
-+ vfio_iommu_group_put(mc_dev->dev.iommu_group, dev);
-+ kfree(vdev);
-+
-+ return 0;
-+}
-+
-+/*
-+ * vfio-fsl_mc is a meta-driver, so use driver_override interface to
-+ * bind a fsl_mc container with this driver and match_id_table is NULL.
-+ */
-+static struct fsl_mc_driver vfio_fsl_mc_driver = {
-+ .probe = vfio_fsl_mc_probe,
-+ .remove = vfio_fsl_mc_remove,
-+ .match_id_table = NULL,
-+ .driver = {
-+ .name = "vfio-fsl-mc",
-+ .owner = THIS_MODULE,
-+ },
-+};
-+
-+static int __init vfio_fsl_mc_driver_init(void)
-+{
-+ return fsl_mc_driver_register(&vfio_fsl_mc_driver);
-+}
-+
-+static void __exit vfio_fsl_mc_driver_exit(void)
-+{
-+ fsl_mc_driver_unregister(&vfio_fsl_mc_driver);
-+}
-+
-+module_init(vfio_fsl_mc_driver_init);
-+module_exit(vfio_fsl_mc_driver_exit);
-+
-+MODULE_VERSION(DRIVER_VERSION);
-+MODULE_LICENSE("GPL v2");
-+MODULE_AUTHOR(DRIVER_AUTHOR);
-+MODULE_DESCRIPTION(DRIVER_DESC);
---- /dev/null
-+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
-@@ -0,0 +1,199 @@
-+/*
-+ * Freescale Management Complex (MC) device passthrough using VFIO
-+ *
-+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
-+ * Author: Bharat Bhushan <bharat.bhushan@nxp.com>
-+ *
-+ * This file is licensed under the terms of the GNU General Public
-+ * License version 2. This program is licensed "as is" without any
-+ * warranty of any kind, whether express or implied.
-+ */
-+
-+#include <linux/vfio.h>
-+#include <linux/slab.h>
-+#include <linux/types.h>
-+#include <linux/eventfd.h>
-+#include <linux/msi.h>
-+
-+#include "linux/fsl/mc.h"
-+#include "vfio_fsl_mc_private.h"
-+
-+static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg)
-+{
-+ struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg;
-+
-+ eventfd_signal(mc_irq->trigger, 1);
-+ return IRQ_HANDLED;
-+}
-+
-+static int vfio_fsl_mc_irq_mask(struct vfio_fsl_mc_device *vdev,
-+ unsigned int index, unsigned int start,
-+ unsigned int count, uint32_t flags,
-+ void *data)
-+{
-+ return -EINVAL;
-+}
-+
-+static int vfio_fsl_mc_irq_unmask(struct vfio_fsl_mc_device *vdev,
-+ unsigned int index, unsigned int start,
-+ unsigned int count, uint32_t flags,
-+ void *data)
-+{
-+ return -EINVAL;
-+}
-+
-+static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev,
-+ int index, int fd)
-+{
-+ struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index];
-+ struct eventfd_ctx *trigger;
-+ int hwirq;
-+ int ret;
-+
-+ hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
-+ if (irq->trigger) {
-+ free_irq(hwirq, irq);
-+ kfree(irq->name);
-+ eventfd_ctx_put(irq->trigger);
-+ irq->trigger = NULL;
-+ }
-+
-+ if (fd < 0) /* Disable only */
-+ return 0;
-+
-+ irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
-+ hwirq, dev_name(&vdev->mc_dev->dev));
-+ if (!irq->name)
-+ return -ENOMEM;
-+
-+ trigger = eventfd_ctx_fdget(fd);
-+ if (IS_ERR(trigger)) {
-+ kfree(irq->name);
-+ return PTR_ERR(trigger);
-+ }
-+
-+ irq->trigger = trigger;
-+
-+ ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0,
-+ irq->name, irq);
-+ if (ret) {
-+ kfree(irq->name);
-+ eventfd_ctx_put(trigger);
-+ irq->trigger = NULL;
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev)
-+{
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ struct vfio_fsl_mc_irq *mc_irq;
-+ int irq_count;
-+ int ret, i;
-+
-+ /* Device does not support any interrupt */
-+ if (mc_dev->obj_desc.irq_count == 0)
-+ return 0;
-+
-+ irq_count = mc_dev->obj_desc.irq_count;
-+
-+ mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL);
-+ if (mc_irq == NULL)
-+ return -ENOMEM;
-+
-+ /* Allocate IRQs */
-+ ret = fsl_mc_allocate_irqs(mc_dev);
-+ if (ret) {
-+ kfree(mc_irq);
-+ return ret;
-+ }
-+
-+ for (i = 0; i < irq_count; i++) {
-+ mc_irq[i].count = 1;
-+ mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD;
-+ }
-+
-+ vdev->mc_irqs = mc_irq;
-+
-+ return 0;
-+}
-+
-+/* Free All IRQs for the given MC object */
-+void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev)
-+{
-+ struct fsl_mc_device *mc_dev = vdev->mc_dev;
-+ int irq_count = mc_dev->obj_desc.irq_count;
-+ int i;
-+
-+ /* Device does not support any interrupt */
-+ if (mc_dev->obj_desc.irq_count == 0)
-+ return;
-+
-+ for (i = 0; i < irq_count; i++)
-+ vfio_set_trigger(vdev, i, -1);
-+
-+ fsl_mc_free_irqs(mc_dev);
-+ kfree(vdev->mc_irqs);
-+}
-+
-+static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev,
-+ unsigned int index, unsigned int start,
-+ unsigned int count, uint32_t flags,
-+ void *data)
-+{
-+ struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index];
-+ int hwirq;
-+
-+ if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
-+ return vfio_set_trigger(vdev, index, -1);
-+
-+ if (start != 0 || count != 1)
-+ return -EINVAL;
-+
-+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
-+ int32_t fd = *(int32_t *)data;
-+
-+ return vfio_set_trigger(vdev, index, fd);
-+ }
-+
-+ hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
-+
-+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
-+ vfio_fsl_mc_irq_handler(hwirq, irq);
-+
-+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
-+ uint8_t trigger = *(uint8_t *)data;
-+
-+ if (trigger)
-+ vfio_fsl_mc_irq_handler(hwirq, irq);
-+ }
-+
-+ return 0;
-+}
-+
-+int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
-+ uint32_t flags, unsigned int index,
-+ unsigned int start, unsigned int count,
-+ void *data)
-+{
-+ int ret = -ENOTTY;
-+
-+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
-+ case VFIO_IRQ_SET_ACTION_MASK:
-+ ret = vfio_fsl_mc_irq_mask(vdev, index, start, count,
-+ flags, data);
-+ break;
-+ case VFIO_IRQ_SET_ACTION_UNMASK:
-+ ret = vfio_fsl_mc_irq_unmask(vdev, index, start, count,
-+ flags, data);
-+ break;
-+ case VFIO_IRQ_SET_ACTION_TRIGGER:
-+ ret = vfio_fsl_mc_set_irq_trigger(vdev, index, start,
-+ count, flags, data);
-+ break;
-+ }
-+
-+ return ret;
-+}
---- /dev/null
-+++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
-@@ -0,0 +1,55 @@
-+/*
-+ * Freescale Management Complex VFIO private declarations
-+ *
-+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
-+ * Copyright 2016 NXP
-+ * Author: Bharat Bhushan <bharat.bhushan@nxp.com>
-+ *
-+ * This file is licensed under the terms of the GNU General Public
-+ * License version 2. This program is licensed "as is" without any
-+ * warranty of any kind, whether express or implied.
-+ */
-+
-+#ifndef VFIO_FSL_MC_PRIVATE_H
-+#define VFIO_FSL_MC_PRIVATE_H
-+
-+#define VFIO_FSL_MC_OFFSET_SHIFT 40
-+#define VFIO_FSL_MC_OFFSET_MASK (((u64)(1) << VFIO_FSL_MC_OFFSET_SHIFT) - 1)
-+
-+#define VFIO_FSL_MC_OFFSET_TO_INDEX(off) (off >> VFIO_FSL_MC_OFFSET_SHIFT)
-+
-+#define VFIO_FSL_MC_INDEX_TO_OFFSET(index) \
-+ ((u64)(index) << VFIO_FSL_MC_OFFSET_SHIFT)
-+
-+struct vfio_fsl_mc_irq {
-+ u32 flags;
-+ u32 count;
-+ struct eventfd_ctx *trigger;
-+ char *name;
-+};
-+
-+struct vfio_fsl_mc_region {
-+ u32 flags;
-+#define VFIO_FSL_MC_REGION_TYPE_MMIO 1
-+#define VFIO_FSL_MC_REGION_TYPE_CACHEABLE 2
-+ u32 type;
-+ u64 addr;
-+ resource_size_t size;
-+ void __iomem *ioaddr;
-+};
-+
-+struct vfio_fsl_mc_device {
-+ struct fsl_mc_device *mc_dev;
-+ int refcnt;
-+ u32 num_regions;
-+ struct vfio_fsl_mc_region *regions;
-+ struct vfio_fsl_mc_irq *mc_irqs;
-+};
-+
-+int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev);
-+void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev);
-+int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
-+ uint32_t flags, unsigned int index,
-+ unsigned int start, unsigned int count,
-+ void *data);
-+#endif /* VFIO_PCI_PRIVATE_H */
---- a/drivers/vfio/vfio_iommu_type1.c
-+++ b/drivers/vfio/vfio_iommu_type1.c
-@@ -36,6 +36,8 @@
- #include <linux/uaccess.h>
- #include <linux/vfio.h>
- #include <linux/workqueue.h>
-+#include <linux/dma-iommu.h>
-+#include <linux/irqdomain.h>
-
- #define DRIVER_VERSION "0.2"
- #define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
-@@ -720,6 +722,27 @@ static void vfio_test_domain_fgsp(struct
- __free_pages(pages, order);
- }
-
-+static bool vfio_iommu_has_sw_msi(struct iommu_group *group, phys_addr_t *base)
-+{
-+ struct list_head group_resv_regions;
-+ struct iommu_resv_region *region, *next;
-+ bool ret = false;
-+
-+ INIT_LIST_HEAD(&group_resv_regions);
-+ iommu_get_group_resv_regions(group, &group_resv_regions);
-+ list_for_each_entry(region, &group_resv_regions, list) {
-+ if (region->type == IOMMU_RESV_SW_MSI) {
-+ *base = region->start;
-+ ret = true;
-+ goto out;
-+ }
-+ }
-+out:
-+ list_for_each_entry_safe(region, next, &group_resv_regions, list)
-+ kfree(region);
-+ return ret;
-+}
-+
- static int vfio_iommu_type1_attach_group(void *iommu_data,
- struct iommu_group *iommu_group)
- {
-@@ -728,6 +751,8 @@ static int vfio_iommu_type1_attach_group
- struct vfio_domain *domain, *d;
- struct bus_type *bus = NULL;
- int ret;
-+ bool resv_msi, msi_remap;
-+ phys_addr_t resv_msi_base;
-
- mutex_lock(&iommu->lock);
-
-@@ -774,11 +799,15 @@ static int vfio_iommu_type1_attach_group
- if (ret)
- goto out_domain;
-
-+ resv_msi = vfio_iommu_has_sw_msi(iommu_group, &resv_msi_base);
-+
- INIT_LIST_HEAD(&domain->group_list);
- list_add(&group->next, &domain->group_list);
-
-- if (!allow_unsafe_interrupts &&
-- !iommu_capable(bus, IOMMU_CAP_INTR_REMAP)) {
-+ msi_remap = resv_msi ? irq_domain_check_msi_remap() :
-+ iommu_capable(bus, IOMMU_CAP_INTR_REMAP);
-+
-+ if (!allow_unsafe_interrupts && !msi_remap) {
- pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
- __func__);
- ret = -EPERM;
-@@ -820,6 +849,12 @@ static int vfio_iommu_type1_attach_group
- if (ret)
- goto out_detach;
-
-+ if (resv_msi) {
-+ ret = iommu_get_msi_cookie(domain->domain, resv_msi_base);
-+ if (ret)
-+ goto out_detach;
-+ }
-+
- list_add(&domain->next, &iommu->domain_list);
-
- mutex_unlock(&iommu->lock);
---- a/include/uapi/linux/vfio.h
-+++ b/include/uapi/linux/vfio.h
-@@ -198,6 +198,7 @@ struct vfio_device_info {
- #define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */
- #define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */
- #define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */
-+#define VFIO_DEVICE_FLAGS_FSL_MC (1 << 5) /* vfio-fsl-mc device */
- __u32 num_regions; /* Max region index + 1 */
- __u32 num_irqs; /* Max IRQ index + 1 */
- };