The user can specify the location of the memory region (register) used by the guest driver to clear the pending interrupt; if enabled, this mechanism will overlap to the default one (timer based).
The region is provided as command line property "intclr-region" of the vfio-platform device. The property is a string "region_index;offset;size" where: region_index: is the index of the memory region where the register lives, offset: offset of the register in the region, size: size of the register. example: -device vfio-platform,...,intclr-region="0;0x2c;4" Signed-off-by: Alvise Rigo <a.r...@virtualopensystems.com> --- hw/vfio/platform.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index c4a4286..ec6a29e 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -40,6 +40,7 @@ #include "vfio-common.h" +/*#define DEBUG_VFIO 1*/ #ifdef DEBUG_VFIO #define DPRINTF(fmt, ...) \ do { fprintf(stderr, "vfio: %s: " fmt, __func__, ## __VA_ARGS__); } \ @@ -56,6 +57,11 @@ #define TYPE_VFIO_PLATFORM "vfio-platform" +struct intclr_region { + hwaddr offset; + hwaddr size; +}; + typedef struct VFIORegion { off_t fd_offset; /* offset of region within device fd */ int fd; /* device fd, allows us to pass VFIORegion as opaque data */ @@ -65,6 +71,7 @@ typedef struct VFIORegion { size_t size; uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ uint8_t nr; /* cache the region number for debug */ + struct intclr_region intclr_reg; } VFIORegion; @@ -99,6 +106,8 @@ typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; struct VFIOGroup *group; QLIST_HEAD(, VFIOINTp) intp_list; + char *intclr_region_str; /* clear interrupt region string */ + bool has_intclr_region; } VFIODevice; @@ -120,6 +129,48 @@ void vfio_get_props(SysBusDevice *s, char **pname, *psize = vdev->regions[0].size; } +static int parse_clrint_string(const char *intclr_str, uint32_t *id_reg, + hwaddr *off_reg, uint64_t *size_reg) +{ + char *str_ptr = g_strdup(intclr_str); + char *idx, *off, *size; + + if (strlen(intclr_str) < 5) { + return -1; + } + + idx = str_ptr; + str_ptr = strchr(str_ptr, ';'); + if (!str_ptr || idx == str_ptr) { + return -1; + } + *str_ptr = '\0'; + + off = ++str_ptr; + str_ptr = strchr(str_ptr, ';'); + if (!str_ptr || off == str_ptr) { + return -1; + } + *str_ptr = '\0'; + + size = ++str_ptr; + if (!*size) { + return -1; + } + + *id_reg = strtol(idx, NULL, 10); + *off_reg = strtol(off, NULL, 0); + *size_reg = strtol(size, NULL, 10); + + if (errno == EINVAL || errno == ERANGE) { + return -1; + } + + DPRINTF("intclr region - id: %u offset: 0x%"HWADDR_PRIx" size: %"PRIx64"\n", + *id_reg, *off_reg, *size_reg); + return 0; +} + static void vfio_disable_irqindex(VFIODevice *vdev, int index) { struct vfio_irq_set irq_set = { @@ -397,6 +448,8 @@ static void vfio_region_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { VFIORegion *region = opaque; + VFIODevice *vdev = NULL; + union { uint8_t byte; uint16_t word; @@ -427,13 +480,22 @@ static void vfio_region_write(void *opaque, hwaddr addr, DPRINTF("(region %d, addr=0x%"HWADDR_PRIx", data= 0x%"PRIx64", %d)\n", region->nr, addr, data, size); - vfio_irq_eoi(container_of(region, VFIODevice, regions[region->nr])); + vdev = container_of(region, VFIODevice, regions[region->nr]); + struct intclr_region *intr = ®ion->intclr_reg; + /* If an interrupt clear region has been specified we clear the pending + * intterrupts only when the memory accesse is inside the region. + * */ + if ((addr >= intr->offset && addr + size <= intr->offset + intr->size) + || !vdev->has_intclr_region) { + vfio_irq_eoi(vdev); + } } static uint64_t vfio_region_read(void *opaque, hwaddr addr, unsigned size) { VFIORegion *region = opaque; + VFIODevice *vdev = NULL; union { uint8_t byte; uint16_t word; @@ -466,7 +528,10 @@ static uint64_t vfio_region_read(void *opaque, hwaddr addr, unsigned size) DPRINTF("(region %d, addr= 0x%"HWADDR_PRIx", data=%d) = 0x%"PRIx64"\n", region->nr, addr, size, data); - vfio_irq_eoi(container_of(region, VFIODevice, regions[region->nr])); + vdev = container_of(region, VFIODevice, regions[region->nr]); + if (!vdev->has_intclr_region) { + vfio_irq_eoi(vdev); + } return data; } @@ -669,6 +734,17 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) } } + hwaddr intclr_off = 0; + uint64_t intclr_size = 0; + uint32_t id = 0; + if (vdev->intclr_region_str) { + ret = parse_clrint_string(vdev->intclr_region_str, &id, &intclr_off, + &intclr_size); + } else { + ret = -1; + } + vdev->has_intclr_region = (!ret) ? true : false; + ret = vfio_get_device(group, path, vdev); if (ret) { error_report("vfio: failed to get device %s", path); @@ -678,6 +754,16 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) for (i = 0; i < vdev->num_regions; i++) { vfio_map_region(vdev, i); + + if (id == i && vdev->has_intclr_region) { + struct intclr_region *intclr_reg = NULL; + + intclr_reg = &(vdev->regions[i].intclr_reg); + + intclr_reg->offset = intclr_off; + intclr_reg->size = intclr_size; + } + sysbus_init_mmio(sbdev, &vdev->regions[i].mem); } } @@ -729,6 +815,7 @@ typedef struct VFIOPlatformDeviceClass { static Property vfio_platform_dev_properties[] = { DEFINE_PROP_STRING("vfio_device", VFIODevice, name), DEFINE_PROP_STRING("compat", VFIODevice, compat), +DEFINE_PROP_STRING("intclr-region", VFIODevice, intclr_region_str), DEFINE_PROP_UINT32("mmap-timeout-ms", VFIODevice, mmap_timeout, 1100), DEFINE_PROP_END_OF_LIST(), }; -- 1.9.1