diff options
author | Bharat Bhushan <Bharat.Bhushan@nxp.com> | 2017-05-11 07:04:03 (GMT) |
---|---|---|
committer | Xie Xiaobo <xiaobo.xie@nxp.com> | 2017-09-25 07:25:34 (GMT) |
commit | 5637efc7dcfaaf48781a1c0131f8f8ff6bf0a38a (patch) | |
tree | 724841ddc3e85a5cf9c26b27be6dde214229b1de /drivers/vfio | |
parent | 7a66c154a8e16622f8e8f873220de85f6035c078 (diff) | |
download | linux-5637efc7dcfaaf48781a1c0131f8f8ff6bf0a38a.tar.xz |
vfio fsl-mc: Return fsl-mc device MMIO region info
Add support for VFIO_DEVICE_GET_REGION_INFO ioctl call.
This allows usespace to know device mmap-able region details.
MC device (DPIO) have a region which is cacheable and
non-shareable. Describe these regions as cacheable
so that during mmap() they will be mapped accordingly.
Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com>
Diffstat (limited to 'drivers/vfio')
-rw-r--r-- | drivers/vfio/fsl-mc/vfio_fsl_mc.c | 94 | ||||
-rw-r--r-- | drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 20 |
2 files changed, 113 insertions, 1 deletions
diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c b/drivers/vfio/fsl-mc/vfio_fsl_mc.c index b01e9c0..e5a6779 100644 --- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c @@ -28,16 +28,90 @@ #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 = 0; + } + + vdev->num_regions = mc_dev->obj_desc.region_count; + return 0; +} + +static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev) +{ + 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; + } + + vdev->refcnt++; + mutex_unlock(&driver_lock); return 0; + +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; + + mutex_lock(&driver_lock); + + if (!(--vdev->refcnt)) + vfio_fsl_mc_regions_cleanup(vdev); + + mutex_unlock(&driver_lock); + module_put(THIS_MODULE); } @@ -72,7 +146,25 @@ static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, } case VFIO_DEVICE_GET_REGION_INFO: { - return -EINVAL; + 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: { diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h index e680f8e..e9d7f3b 100644 --- a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h @@ -13,8 +13,28 @@ #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_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; +}; + struct vfio_fsl_mc_device { struct fsl_mc_device *mc_dev; + int refcnt; + u32 num_regions; + struct vfio_fsl_mc_region *regions; }; #endif /* VFIO_PCI_PRIVATE_H */ |