aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/vfio-user.rst6
-rw-r--r--lib/irq.c293
-rw-r--r--lib/libvfio-user.c9
-rw-r--r--lib/private.h14
-rw-r--r--test/unit-tests.c168
5 files changed, 309 insertions, 181 deletions
diff --git a/docs/vfio-user.rst b/docs/vfio-user.rst
index f4562ef..93d61cd 100644
--- a/docs/vfio-user.rst
+++ b/docs/vfio-user.rst
@@ -1276,6 +1276,8 @@ VFIO IRQ set format
was sent in the message meta-data. These descriptors will be signalled when
the action defined by the action flags occurs. In AF_UNIX sockets, the
descriptors are sent as SCM_RIGHTS type ancillary data.
+ If no file descriptors are provided, this de-assigns the specified
+ previously configured interrupts.
* *VFIO_IRQ_SET_ACTION_MASK* indicates a masking event. It can be used with
VFIO_IRQ_SET_DATA_BOOL or VFIO_IRQ_SET_DATA_NONE to mask an interrupt, or
with VFIO_IRQ_SET_DATA_EVENTFD to generate an event when the guest masks
@@ -1292,8 +1294,8 @@ VFIO IRQ set format
* *index* is the index of IRQ type being setup.
* *start* is the start of the sub-index being set.
* *count* describes the number of sub-indexes being set. As a special case, a
- count of 0 with data flags of VFIO_IRQ_SET_DATA_NONE disables all interrupts
- of the index.
+ count (and start) of 0, with data flags of VFIO_IRQ_SET_DATA_NONE disables
+ all interrupts of the index.
* *data* is an optional field included when the
VFIO_IRQ_SET_DATA_BOOL flag is present. It contains an array of booleans
that specify whether the action is to be performed on the corresponding
diff --git a/lib/irq.c b/lib/irq.c
index 050453e..7ea0349 100644
--- a/lib/irq.c
+++ b/lib/irq.c
@@ -54,52 +54,87 @@ vfio_irq_idx_to_str(int index)
}
static long
-irqs_disable(vfu_ctx_t *vfu_ctx, uint32_t index)
+dev_get_irqinfo(vfu_ctx_t *vfu_ctx, struct vfio_irq_info *irq_info_in,
+ struct vfio_irq_info *irq_info_out)
+{
+ assert(vfu_ctx != NULL);
+ assert(irq_info_in != NULL);
+ assert(irq_info_out != NULL);
+
+ // Ensure provided argsz is sufficiently big and index is within bounds.
+ if ((irq_info_in->argsz < sizeof(struct vfio_irq_info)) ||
+ (irq_info_in->index >= VFU_DEV_NUM_IRQS)) {
+ vfu_log(vfu_ctx, LOG_DEBUG, "bad irq_info (size=%d index=%d)\n",
+ irq_info_in->argsz, irq_info_in->index);
+ return -EINVAL;
+ }
+
+ irq_info_out->count = vfu_ctx->irq_count[irq_info_in->index];
+ irq_info_out->flags = VFIO_IRQ_INFO_EVENTFD;
+
+ return 0;
+}
+
+int
+handle_device_get_irq_info(vfu_ctx_t *vfu_ctx, uint32_t size,
+ struct vfio_irq_info *irq_info_in,
+ struct vfio_irq_info *irq_info_out)
+{
+ assert(vfu_ctx != NULL);
+ assert(irq_info_in != NULL);
+ assert(irq_info_out != NULL);
+
+ if (size != sizeof(*irq_info_in) || size != irq_info_in->argsz) {
+ vfu_log(vfu_ctx, LOG_WARNING, "IRQ info size %d", size);
+ return -EINVAL;
+ }
+
+ return dev_get_irqinfo(vfu_ctx, irq_info_in, irq_info_out);
+}
+
+static long
+irqs_disable(vfu_ctx_t *vfu_ctx, uint32_t index, uint32_t start, uint32_t count)
{
- int *irq_efd = NULL;
- uint32_t i;
+ size_t i;
+ int *efds;
assert(vfu_ctx != NULL);
assert(index < VFU_DEV_NUM_IRQS);
+ assert(start + count <= vfu_ctx->irq_count[index]);
+
+ vfu_log(vfu_ctx, LOG_DEBUG, "disabling IRQ type %s range [%u-%u)",
+ vfio_irq_idx_to_str(index), start, start + count);
+
+ if (count == 0) {
+ count = vfu_ctx->irq_count[index];
+ }
switch (index) {
case VFIO_PCI_INTX_IRQ_INDEX:
case VFIO_PCI_MSI_IRQ_INDEX:
case VFIO_PCI_MSIX_IRQ_INDEX:
- vfu_log(vfu_ctx, LOG_DEBUG, "disabling IRQ %s",
- vfio_irq_idx_to_str(index));
- vfu_ctx->irqs->type = IRQ_NONE;
- for (i = 0; i < vfu_ctx->irqs->max_ivs; i++) {
- if (vfu_ctx->irqs->efds[i] >= 0) {
- if (close(vfu_ctx->irqs->efds[i]) == -1) {
- vfu_log(vfu_ctx, LOG_DEBUG, "failed to close IRQ fd %d: %m",
- vfu_ctx->irqs->efds[i]);
- }
- vfu_ctx->irqs->efds[i] = -1;
- }
- }
- return 0;
+ efds = vfu_ctx->irqs[index].efds;
+ break;
case VFIO_PCI_ERR_IRQ_INDEX:
- irq_efd = &vfu_ctx->irqs->err_efd;
+ efds = &vfu_ctx->irqs->err_efd;
break;
case VFIO_PCI_REQ_IRQ_INDEX:
- irq_efd = &vfu_ctx->irqs->req_efd;
+ efds = &vfu_ctx->irqs->req_efd;
break;
}
- if (irq_efd != NULL) {
- if (*irq_efd != -1) {
- if (close(*irq_efd) == -1) {
+ for (i = start; i < count; i++) {
+ if (efds[i] >= 0) {
+ if (close(efds[i]) == -1) {
vfu_log(vfu_ctx, LOG_DEBUG, "failed to close IRQ fd %d: %m",
- *irq_efd);
+ efds[i]);
}
- *irq_efd = -1;
+
+ efds[i] = -1;
}
- return 0;
}
- vfu_log(vfu_ctx, LOG_DEBUG, "failed to disable IRQs");
- return -EINVAL;
+ return 0;
}
static int
@@ -185,39 +220,11 @@ irqs_set_data_eventfd(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set,
}
static long
-irqs_trigger(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, void *data)
-{
- int err = 0;
-
- assert(vfu_ctx != NULL);
- assert(irq_set != NULL);
-
- if (irq_set->count == 0) {
- return irqs_disable(vfu_ctx, irq_set->index);
- }
-
- vfu_log(vfu_ctx, LOG_DEBUG, "setting IRQ %s flags=%#x",
- vfio_irq_idx_to_str(irq_set->index), irq_set->flags);
-
- switch (irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
- case VFIO_IRQ_SET_DATA_NONE:
- err = irqs_set_data_none(vfu_ctx, irq_set);
- break;
- case VFIO_IRQ_SET_DATA_BOOL:
- err = irqs_set_data_bool(vfu_ctx, irq_set, data);
- break;
- case VFIO_IRQ_SET_DATA_EVENTFD:
- err = irqs_set_data_eventfd(vfu_ctx, irq_set, data);
- break;
- }
-
- return err;
-}
-
-static long
-dev_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set)
+device_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set,
+ size_t nr_fds)
{
uint32_t a_type, d_type;
+ int line;
assert(vfu_ctx != NULL);
assert(irq_set != NULL);
@@ -228,162 +235,122 @@ dev_set_irqs_validate(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set)
// Ensure index is within bounds.
if (irq_set->index >= VFU_DEV_NUM_IRQS) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ index %d\n", irq_set->index);
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
}
- /* TODO make each condition a function */
-
// Only one of MASK/UNMASK/TRIGGER is valid.
if ((a_type != VFIO_IRQ_SET_ACTION_MASK) &&
(a_type != VFIO_IRQ_SET_ACTION_UNMASK) &&
(a_type != VFIO_IRQ_SET_ACTION_TRIGGER)) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ action mask %d\n", a_type);
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
}
// Only one of NONE/BOOL/EVENTFD is valid.
if ((d_type != VFIO_IRQ_SET_DATA_NONE) &&
(d_type != VFIO_IRQ_SET_DATA_BOOL) &&
(d_type != VFIO_IRQ_SET_DATA_EVENTFD)) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ data %d\n", d_type);
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
}
// Ensure irq_set's start and count are within bounds.
if ((irq_set->start >= vfu_ctx->irq_count[irq_set->index]) ||
(irq_set->start + irq_set->count > vfu_ctx->irq_count[irq_set->index])) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ start/count\n");
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
}
// Only TRIGGER is valid for ERR/REQ.
if (((irq_set->index == VFIO_PCI_ERR_IRQ_INDEX) ||
(irq_set->index == VFIO_PCI_REQ_IRQ_INDEX)) &&
(a_type != VFIO_IRQ_SET_ACTION_TRIGGER)) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ trigger w/o ERR/REQ\n");
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
+ }
+ // if count == 0, start must be 0 too
+ if ((irq_set->count == 0) && (irq_set->start != 0)) {
+ line = __LINE__;
+ goto invalid;
}
// count == 0 is only valid with ACTION_TRIGGER and DATA_NONE.
if ((irq_set->count == 0) && ((a_type != VFIO_IRQ_SET_ACTION_TRIGGER) ||
(d_type != VFIO_IRQ_SET_DATA_NONE))) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ count %d\n", irq_set->count);
- return -EINVAL;
+ line = __LINE__;
+ goto invalid;
}
- // If IRQs are set, ensure index matches what's enabled for the device.
- if ((irq_set->count != 0) && (vfu_ctx->irqs->type != IRQ_NONE) &&
- (irq_set->index != LM2VFIO_IRQT(vfu_ctx->irqs->type))) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad IRQ index\n");
- return -EINVAL;
+ // If fd's are provided, ensure it's only for VFIO_IRQ_SET_DATA_EVENTFD
+ if (nr_fds != 0 && d_type != VFIO_IRQ_SET_DATA_EVENTFD) {
+ line = __LINE__;
+ goto invalid;
+ }
+ // If fd's are provided, ensure they match ->count
+ if (nr_fds != 0 && nr_fds != irq_set->count) {
+ line = __LINE__;
+ goto invalid;
}
return 0;
+
+invalid:
+ vfu_log(vfu_ctx, LOG_DEBUG, "invalid SET_IRQS (%d): action=%u data_type=%u "
+ "index=%u start=%u count=%u nr_fds=%u", line, a_type, d_type,
+ irq_set->index, irq_set->start, irq_set->count, nr_fds);
+ return -EINVAL;
}
-static long
-dev_set_irqs(vfu_ctx_t *vfu_ctx, struct vfio_irq_set *irq_set, void *data)
+int
+handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size,
+ int *fds, size_t nr_fds, struct vfio_irq_set *irq_set)
{
- long ret;
+ uint32_t data_type;
+ int ret;
assert(vfu_ctx != NULL);
assert(irq_set != NULL);
- // Ensure irq_set is valid.
- ret = dev_set_irqs_validate(vfu_ctx, irq_set);
+ if (size < sizeof(*irq_set) || size != irq_set->argsz) {
+ vfu_log(vfu_ctx, LOG_ERR, "%s: bad size %u", __func__, size);
+ return -EINVAL;
+ }
+
+ ret = device_set_irqs_validate(vfu_ctx, irq_set, nr_fds);
if (ret != 0) {
return ret;
}
switch (irq_set->flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
- case VFIO_IRQ_SET_ACTION_MASK: // fallthrough
+ case VFIO_IRQ_SET_ACTION_MASK:
case VFIO_IRQ_SET_ACTION_UNMASK:
// We're always edge-triggered without un/mask support.
+ // FIXME: return an error? We don't report MASKABLE
return 0;
+ case VFIO_IRQ_SET_ACTION_TRIGGER:
+ break;
}
- return irqs_trigger(vfu_ctx, irq_set, data);
-}
-
-static long
-dev_get_irqinfo(vfu_ctx_t *vfu_ctx, struct vfio_irq_info *irq_info_in,
- struct vfio_irq_info *irq_info_out)
-{
- assert(vfu_ctx != NULL);
- assert(irq_info_in != NULL);
- assert(irq_info_out != NULL);
-
- // Ensure provided argsz is sufficiently big and index is within bounds.
- if ((irq_info_in->argsz < sizeof(struct vfio_irq_info)) ||
- (irq_info_in->index >= VFU_DEV_NUM_IRQS)) {
- vfu_log(vfu_ctx, LOG_DEBUG, "bad irq_info (size=%d index=%d)\n",
- irq_info_in->argsz, irq_info_in->index);
- return -EINVAL;
- }
-
- irq_info_out->count = vfu_ctx->irq_count[irq_info_in->index];
- irq_info_out->flags = VFIO_IRQ_INFO_EVENTFD;
-
- return 0;
-}
+ data_type = irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK;
-int
-handle_device_get_irq_info(vfu_ctx_t *vfu_ctx, uint32_t size,
- struct vfio_irq_info *irq_info_in,
- struct vfio_irq_info *irq_info_out)
-{
- assert(vfu_ctx != NULL);
- assert(irq_info_in != NULL);
- assert(irq_info_out != NULL);
-
- if (size != sizeof(*irq_info_in) || size != irq_info_in->argsz) {
- vfu_log(vfu_ctx, LOG_WARNING, "IRQ info size %d", size);
- return -EINVAL;
+ if ((data_type == VFIO_IRQ_SET_DATA_NONE && irq_set->count == 0) ||
+ (data_type == VFIO_IRQ_SET_DATA_EVENTFD && nr_fds == 0)) {
+ return irqs_disable(vfu_ctx, irq_set->index,
+ irq_set->start, irq_set->count);
}
- return dev_get_irqinfo(vfu_ctx, irq_info_in, irq_info_out);
-}
+ vfu_log(vfu_ctx, LOG_DEBUG, "setting IRQ %s flags=%#x range [%u-%u)",
+ vfio_irq_idx_to_str(irq_set->index), irq_set->flags,
+ irq_set->start, irq_set->start + irq_set->count);
-int
-handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size,
- int *fds, size_t nr_fds, struct vfio_irq_set *irq_set)
-{
- void *data = NULL;
-
- assert(vfu_ctx != NULL);
- assert(irq_set != NULL);
-
- if (size < sizeof(*irq_set) || size != irq_set->argsz) {
- vfu_log(vfu_ctx, LOG_ERR, "bad size %d", size);
- return -EINVAL;
- }
-
- switch (irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
- case VFIO_IRQ_SET_DATA_NONE:
- /*
- * FIXME The way it's handled in this case is a hack, it should be
- * properly fixed, see issue #359 for more details.
- */
- vfu_log(vfu_ctx, LOG_NOTICE,
- "ignore IRQ DATA_NONE, action=%#x, index=%ld, start=%ld, count=%ld",
- irq_set->flags & VFIO_IRQ_SET_ACTION_TYPE_MASK,
- irq_set->index, irq_set->start, irq_set->count);
- return 0;
- case VFIO_IRQ_SET_DATA_EVENTFD:
- data = fds;
- if (nr_fds != irq_set->count) {
- vfu_log(vfu_ctx, LOG_ERR,
- "bad number of FDs, expected=%u, actual=%d", nr_fds,
- irq_set->count);
- return -EINVAL;
- }
- break;
- case VFIO_IRQ_SET_DATA_BOOL:
- data = irq_set + 1;
- break;
- default:
- vfu_log(vfu_ctx, LOG_ERR, "invalid IRQ type %d",
- irq_set->flags & VFIO_IRQ_SET_DATA_TYPE_MASK);
- return -EINVAL;
+ switch (data_type) {
+ case VFIO_IRQ_SET_DATA_NONE:
+ return irqs_set_data_none(vfu_ctx, irq_set);
+ case VFIO_IRQ_SET_DATA_EVENTFD:
+ return irqs_set_data_eventfd(vfu_ctx, irq_set, fds);
+ case VFIO_IRQ_SET_DATA_BOOL:
+ return irqs_set_data_bool(vfu_ctx, irq_set, irq_set + 1);
+ break;
+ default:
+ // we already checked this
+ abort();
}
-
- return dev_set_irqs(vfu_ctx, irq_set, data);
}
static bool
diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c
index 77c9f0c..8df125f 100644
--- a/lib/libvfio-user.c
+++ b/lib/libvfio-user.c
@@ -1037,7 +1037,7 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx)
}
}
- //FIXME: assert(max_ivs > 0)?
+ // FIXME: assert(max_ivs > 0)?
size = sizeof(int) * max_ivs;
vfu_ctx->irqs = calloc(1, sizeof(vfu_irqs_t) + size);
if (vfu_ctx->irqs == NULL) {
@@ -1051,7 +1051,6 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx)
}
vfu_ctx->irqs->err_efd = -1;
vfu_ctx->irqs->req_efd = -1;
- vfu_ctx->irqs->type = IRQ_NONE;
vfu_ctx->irqs->max_ivs = max_ivs;
// Reflect on the config space whether INTX is available.
@@ -1423,10 +1422,8 @@ vfu_setup_device_nr_irqs(vfu_ctx_t *vfu_ctx, enum vfu_dev_irq_type type,
assert(vfu_ctx != NULL);
- if (type < VFU_DEV_INTX_IRQ || type > VFU_DEV_REQ_IRQ) {
- vfu_log(vfu_ctx, LOG_ERR, "Invalid IRQ index %d, should be between "
- "(%d to %d)", type, VFU_DEV_INTX_IRQ,
- VFU_DEV_REQ_IRQ);
+ if (type >= VFU_DEV_NUM_IRQS) {
+ vfu_log(vfu_ctx, LOG_ERR, "Invalid IRQ type index %u", type);
return ERROR_INT(EINVAL);
}
diff --git a/lib/private.h b/lib/private.h
index 8e068c6..60bbba9 100644
--- a/lib/private.h
+++ b/lib/private.h
@@ -77,19 +77,11 @@ struct transport_ops {
void (*fini)(vfu_ctx_t *vfu_ctx);
};
-typedef enum {
- IRQ_NONE = 0,
- IRQ_INTX,
- IRQ_MSI,
- IRQ_MSIX,
-} irq_type_t;
-
typedef struct {
- irq_type_t type; /* irq type this device is using */
int err_efd; /* eventfd for irq err */
int req_efd; /* eventfd for irq req */
uint32_t max_ivs; /* maximum number of ivs supported */
- int efds[0]; /* XXX must be last */
+ int efds[0]; /* must be last */
} vfu_irqs_t;
struct migration;
@@ -190,6 +182,10 @@ cmd_allowed_when_stopped_and_copying(uint16_t cmd);
bool
should_exec_command(vfu_ctx_t *vfu_ctx, uint16_t cmd);
+int
+handle_device_set_irqs(vfu_ctx_t *vfu_ctx, uint32_t size,
+ int *fds, size_t nr_fds, struct vfio_irq_set *irq_set);
+
#endif /* LIB_VFIO_USER_PRIVATE_H */
/* ex: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/unit-tests.c b/test/unit-tests.c
index 25cba6c..929b83a 100644
--- a/test/unit-tests.c
+++ b/test/unit-tests.c
@@ -1684,7 +1684,172 @@ test_dirty_pages_without_dma(UNUSED void **state)
assert_int_equal(0xabcd, r);
}
-int main(void)
+static void
+test_device_set_irqs(UNUSED void **state)
+{
+ vfu_irqs_t *irqs = alloca(sizeof (*irqs) + sizeof (int));
+ struct vfio_irq_set irq_set = { 0, };
+ vfu_ctx_t vfu_ctx = { 0, };
+ int fd = 0xdead;
+ int ret;
+
+ vfu_ctx.irq_count[VFU_DEV_MSIX_IRQ] = 2048;
+ vfu_ctx.irq_count[VFU_DEV_ERR_IRQ] = 1;
+ vfu_ctx.irq_count[VFU_DEV_REQ_IRQ] = 1;
+ vfu_ctx.irqs = irqs;
+
+ /* validation tests */
+
+ irq_set.argsz = sizeof (irq_set);
+
+ ret = handle_device_set_irqs(&vfu_ctx, 0, NULL, 0, &irq_set);
+ /* bad message size */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.argsz = 3;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad .argsz */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.argsz = sizeof (irq_set);
+ irq_set.index = VFU_DEV_NUM_IRQS;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad .index */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.index = VFU_DEV_MSIX_IRQ;
+ irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_ACTION_UNMASK;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad flags, MASK and UNMASK */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE |
+ VFIO_IRQ_SET_DATA_BOOL;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad flags, DATA_NONE and DATA_BOOL */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE;
+ irq_set.start = 2047;
+ irq_set.count = 2;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad start, count range */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.start = 2049;
+ irq_set.count = 1;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad start, count range */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.start = 0;
+ irq_set.count = 1;
+ irq_set.index = VFU_DEV_ERR_IRQ;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad action for err irq */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.index = VFU_DEV_REQ_IRQ;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad action for req irq */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.start = 1;
+ irq_set.count = 0;
+ irq_set.index = VFU_DEV_MSIX_IRQ;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad start for count == 0 */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_MASK | VFIO_IRQ_SET_DATA_NONE;
+ irq_set.count = 0;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad action for count == 0 */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_BOOL;
+ irq_set.count = 0;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ /* bad action and data type for count == 0 */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_BOOL;
+ irq_set.count = 1;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set);
+ /* bad fds for DATA_BOOL */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_NONE;
+ irq_set.count = 1;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set);
+ /* bad fds for DATA_NONE */
+ assert_int_equal(-EINVAL, ret);
+
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD;
+ irq_set.count = 2;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set);
+ /* bad fds for count == 2 */
+ assert_int_equal(-EINVAL, ret);
+
+ irqs->err_efd = irqs->req_efd = -1;
+
+ /* Basic disable functionality. */
+
+ irq_set.index = VFU_DEV_REQ_IRQ;
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_NONE;
+ irq_set.count = 0;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ assert_int_equal(0, ret);
+
+ irq_set.index = VFU_DEV_REQ_IRQ;
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD;
+ irq_set.count = 1;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), NULL, 0, &irq_set);
+ assert_int_equal(0, ret);
+
+ /* Basic enable. */
+
+ irq_set.index = VFU_DEV_MSIX_IRQ;
+ vfu_ctx.irq_count[VFU_DEV_MSIX_IRQ] = 1;
+ irqs->efds[0] = -1;
+ fd = 0xbeef;
+
+ irq_set.index = VFU_DEV_MSIX_IRQ;
+ irq_set.flags = VFIO_IRQ_SET_ACTION_TRIGGER | VFIO_IRQ_SET_DATA_EVENTFD;
+ irq_set.count = 1;
+ irq_set.start = 0;
+
+ ret = handle_device_set_irqs(&vfu_ctx, sizeof (irq_set), &fd, 1, &irq_set);
+ assert_int_equal(0, ret);
+ assert_int_equal(0xbeef, irqs->efds[0]);
+
+}
+
+int
+main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup(test_dma_map_without_dma, setup),
@@ -1741,6 +1906,7 @@ int main(void)
cmocka_unit_test_setup(test_should_exec_command, setup),
cmocka_unit_test_setup(test_exec_command, setup),
cmocka_unit_test_setup(test_dirty_pages_without_dma, setup),
+ cmocka_unit_test_setup(test_device_set_irqs, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);