diff options
| -rw-r--r-- | drivers/vfio/platform/vfio_platform_common.c | 106 | ||||
| -rw-r--r-- | drivers/vfio/platform/vfio_platform_private.h | 22 | 
2 files changed, 124 insertions, 4 deletions
| diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c index c2f853a3b3dd..47f6309b4889 100644 --- a/drivers/vfio/platform/vfio_platform_common.c +++ b/drivers/vfio/platform/vfio_platform_common.c @@ -23,17 +23,97 @@  #include "vfio_platform_private.h" +static DEFINE_MUTEX(driver_lock); + +static int vfio_platform_regions_init(struct vfio_platform_device *vdev) +{ +	int cnt = 0, i; + +	while (vdev->get_resource(vdev, cnt)) +		cnt++; + +	vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region), +				GFP_KERNEL); +	if (!vdev->regions) +		return -ENOMEM; + +	for (i = 0; i < cnt;  i++) { +		struct resource *res = +			vdev->get_resource(vdev, i); + +		if (!res) +			goto err; + +		vdev->regions[i].addr = res->start; +		vdev->regions[i].size = resource_size(res); +		vdev->regions[i].flags = 0; + +		switch (resource_type(res)) { +		case IORESOURCE_MEM: +			vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO; +			break; +		case IORESOURCE_IO: +			vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO; +			break; +		default: +			goto err; +		} +	} + +	vdev->num_regions = cnt; + +	return 0; +err: +	kfree(vdev->regions); +	return -EINVAL; +} + +static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev) +{ +	vdev->num_regions = 0; +	kfree(vdev->regions); +} +  static void vfio_platform_release(void *device_data)  { +	struct vfio_platform_device *vdev = device_data; + +	mutex_lock(&driver_lock); + +	if (!(--vdev->refcnt)) { +		vfio_platform_regions_cleanup(vdev); +	} + +	mutex_unlock(&driver_lock); +  	module_put(THIS_MODULE);  }  static int vfio_platform_open(void *device_data)  { +	struct vfio_platform_device *vdev = device_data; +	int ret; +  	if (!try_module_get(THIS_MODULE))  		return -ENODEV; +	mutex_lock(&driver_lock); + +	if (!vdev->refcnt) { +		ret = vfio_platform_regions_init(vdev); +		if (ret) +			goto err_reg; +	} + +	vdev->refcnt++; + +	mutex_unlock(&driver_lock);  	return 0; + +err_reg: +	mutex_unlock(&driver_lock); +	module_put(THIS_MODULE); +	return ret;  }  static long vfio_platform_ioctl(void *device_data, @@ -54,15 +134,33 @@ static long vfio_platform_ioctl(void *device_data,  			return -EINVAL;  		info.flags = vdev->flags; -		info.num_regions = 0; +		info.num_regions = vdev->num_regions;  		info.num_irqs = 0;  		return copy_to_user((void __user *)arg, &info, minsz); -	} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) -		return -EINVAL; +	} else if (cmd == 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_PLATFORM_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); -	else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) +	} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO)  		return -EINVAL;  	else if (cmd == VFIO_DEVICE_SET_IRQS) diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h index c04698872440..3551f6d97fc3 100644 --- a/drivers/vfio/platform/vfio_platform_private.h +++ b/drivers/vfio/platform/vfio_platform_private.h @@ -18,7 +18,29 @@  #include <linux/types.h>  #include <linux/interrupt.h> +#define VFIO_PLATFORM_OFFSET_SHIFT   40 +#define VFIO_PLATFORM_OFFSET_MASK (((u64)(1) << VFIO_PLATFORM_OFFSET_SHIFT) - 1) + +#define VFIO_PLATFORM_OFFSET_TO_INDEX(off)	\ +	(off >> VFIO_PLATFORM_OFFSET_SHIFT) + +#define VFIO_PLATFORM_INDEX_TO_OFFSET(index)	\ +	((u64)(index) << VFIO_PLATFORM_OFFSET_SHIFT) + +struct vfio_platform_region { +	u64			addr; +	resource_size_t		size; +	u32			flags; +	u32			type; +#define VFIO_PLATFORM_REGION_TYPE_MMIO	1 +#define VFIO_PLATFORM_REGION_TYPE_PIO	2 +}; +  struct vfio_platform_device { +	struct vfio_platform_region	*regions; +	u32				num_regions; +	int				refcnt; +  	/*  	 * These fields should be filled by the bus specific binder  	 */ |