vfio/platform: return info for device memory mapped IO regions

This patch enables the IOCTLs VFIO_DEVICE_GET_REGION_INFO ioctl call,
which allows the user to learn about the available MMIO resources of
a device.

Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com>
Signed-off-by: Baptiste Reynal <b.reynal@virtualopensystems.com>
Reviewed-by: Eric Auger <eric.auger@linaro.org>
Tested-by: Eric Auger <eric.auger@linaro.org>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
Antonios Motakis 2015-03-16 14:08:46 -06:00 committed by Alex Williamson
parent 2e8567bbb5
commit e8909e67ca
2 changed files with 124 additions and 4 deletions

View file

@ -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;
else if (cmd == VFIO_DEVICE_GET_IRQ_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)
return -EINVAL;
else if (cmd == VFIO_DEVICE_SET_IRQS)

View file

@ -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
*/