aboutsummaryrefslogtreecommitdiff
path: root/hw/remote
diff options
context:
space:
mode:
Diffstat (limited to 'hw/remote')
-rw-r--r--hw/remote/machine.c14
-rw-r--r--hw/remote/trace-events1
-rw-r--r--hw/remote/vfio-user-obj.c167
3 files changed, 178 insertions, 4 deletions
diff --git a/hw/remote/machine.c b/hw/remote/machine.c
index 645b54343d..75d550daae 100644
--- a/hw/remote/machine.c
+++ b/hw/remote/machine.c
@@ -23,6 +23,8 @@
#include "hw/remote/iommu.h"
#include "hw/qdev-core.h"
#include "hw/remote/iommu.h"
+#include "hw/remote/vfio-user-obj.h"
+#include "hw/pci/msi.h"
static void remote_machine_init(MachineState *machine)
{
@@ -54,12 +56,16 @@ static void remote_machine_init(MachineState *machine)
if (s->vfio_user) {
remote_iommu_setup(pci_host->bus);
- }
- remote_iohub_init(&s->iohub);
+ msi_nonbroken = true;
+
+ vfu_object_set_bus_irq(pci_host->bus);
+ } else {
+ remote_iohub_init(&s->iohub);
- pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq,
- &s->iohub, REMOTE_IOHUB_NB_PIRQS);
+ pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq,
+ &s->iohub, REMOTE_IOHUB_NB_PIRQS);
+ }
qbus_set_hotplug_handler(BUS(pci_host->bus), OBJECT(s));
}
diff --git a/hw/remote/trace-events b/hw/remote/trace-events
index 847d50d88f..c167b3c7a5 100644
--- a/hw/remote/trace-events
+++ b/hw/remote/trace-events
@@ -12,3 +12,4 @@ vfu_dma_unregister(uint64_t gpa) "vfu: unregistering GPA 0x%"PRIx64""
vfu_bar_register(int i, uint64_t addr, uint64_t size) "vfu: BAR %d: addr 0x%"PRIx64" size 0x%"PRIx64""
vfu_bar_rw_enter(const char *op, uint64_t addr) "vfu: %s request for BAR address 0x%"PRIx64""
vfu_bar_rw_exit(const char *op, uint64_t addr) "vfu: Finished %s of BAR address 0x%"PRIx64""
+vfu_interrupt(int pirq) "vfu: sending interrupt to device - PIRQ %d"
diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c
index dd760a99e2..5ecdec06f6 100644
--- a/hw/remote/vfio-user-obj.c
+++ b/hw/remote/vfio-user-obj.c
@@ -53,6 +53,9 @@
#include "hw/pci/pci.h"
#include "qemu/timer.h"
#include "exec/memory.h"
+#include "hw/pci/msi.h"
+#include "hw/pci/msix.h"
+#include "hw/remote/vfio-user-obj.h"
#define TYPE_VFU_OBJECT "x-vfio-user-server"
OBJECT_DECLARE_TYPE(VfuObject, VfuObjectClass, VFU_OBJECT)
@@ -96,6 +99,10 @@ struct VfuObject {
Error *unplug_blocker;
int vfu_poll_fd;
+
+ MSITriggerFunc *default_msi_trigger;
+ MSIPrepareMessageFunc *default_msi_prepare_message;
+ MSIxPrepareMessageFunc *default_msix_prepare_message;
};
static void vfu_object_init_ctx(VfuObject *o, Error **errp);
@@ -520,6 +527,155 @@ static void vfu_object_register_bars(vfu_ctx_t *vfu_ctx, PCIDevice *pdev)
}
}
+static int vfu_object_map_irq(PCIDevice *pci_dev, int intx)
+{
+ int pci_bdf = PCI_BUILD_BDF(pci_bus_num(pci_get_bus(pci_dev)),
+ pci_dev->devfn);
+
+ return pci_bdf;
+}
+
+static void vfu_object_set_irq(void *opaque, int pirq, int level)
+{
+ PCIBus *pci_bus = opaque;
+ PCIDevice *pci_dev = NULL;
+ vfu_ctx_t *vfu_ctx = NULL;
+ int pci_bus_num, devfn;
+
+ if (level) {
+ pci_bus_num = PCI_BUS_NUM(pirq);
+ devfn = PCI_BDF_TO_DEVFN(pirq);
+
+ /*
+ * pci_find_device() performs at O(1) if the device is attached
+ * to the root PCI bus. Whereas, if the device is attached to a
+ * secondary PCI bus (such as when a root port is involved),
+ * finding the parent PCI bus could take O(n)
+ */
+ pci_dev = pci_find_device(pci_bus, pci_bus_num, devfn);
+
+ vfu_ctx = pci_dev->irq_opaque;
+
+ g_assert(vfu_ctx);
+
+ vfu_irq_trigger(vfu_ctx, 0);
+ }
+}
+
+static MSIMessage vfu_object_msi_prepare_msg(PCIDevice *pci_dev,
+ unsigned int vector)
+{
+ MSIMessage msg;
+
+ msg.address = 0;
+ msg.data = vector;
+
+ return msg;
+}
+
+static void vfu_object_msi_trigger(PCIDevice *pci_dev, MSIMessage msg)
+{
+ vfu_ctx_t *vfu_ctx = pci_dev->irq_opaque;
+
+ vfu_irq_trigger(vfu_ctx, msg.data);
+}
+
+static void vfu_object_setup_msi_cbs(VfuObject *o)
+{
+ o->default_msi_trigger = o->pci_dev->msi_trigger;
+ o->default_msi_prepare_message = o->pci_dev->msi_prepare_message;
+ o->default_msix_prepare_message = o->pci_dev->msix_prepare_message;
+
+ o->pci_dev->msi_trigger = vfu_object_msi_trigger;
+ o->pci_dev->msi_prepare_message = vfu_object_msi_prepare_msg;
+ o->pci_dev->msix_prepare_message = vfu_object_msi_prepare_msg;
+}
+
+static void vfu_object_restore_msi_cbs(VfuObject *o)
+{
+ o->pci_dev->msi_trigger = o->default_msi_trigger;
+ o->pci_dev->msi_prepare_message = o->default_msi_prepare_message;
+ o->pci_dev->msix_prepare_message = o->default_msix_prepare_message;
+}
+
+static void vfu_msix_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
+ uint32_t count, bool mask)
+{
+ VfuObject *o = vfu_get_private(vfu_ctx);
+ Error *err = NULL;
+ uint32_t vector;
+
+ for (vector = start; vector < count; vector++) {
+ msix_set_mask(o->pci_dev, vector, mask, &err);
+ if (err) {
+ VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
+ error_get_pretty(err));
+ error_free(err);
+ err = NULL;
+ }
+ }
+}
+
+static void vfu_msi_irq_state(vfu_ctx_t *vfu_ctx, uint32_t start,
+ uint32_t count, bool mask)
+{
+ VfuObject *o = vfu_get_private(vfu_ctx);
+ Error *err = NULL;
+ uint32_t vector;
+
+ for (vector = start; vector < count; vector++) {
+ msi_set_mask(o->pci_dev, vector, mask, &err);
+ if (err) {
+ VFU_OBJECT_ERROR(o, "vfu: %s: %s", o->device,
+ error_get_pretty(err));
+ error_free(err);
+ err = NULL;
+ }
+ }
+}
+
+static int vfu_object_setup_irqs(VfuObject *o, PCIDevice *pci_dev)
+{
+ vfu_ctx_t *vfu_ctx = o->vfu_ctx;
+ int ret;
+
+ ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (msix_nr_vectors_allocated(pci_dev)) {
+ ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSIX_IRQ,
+ msix_nr_vectors_allocated(pci_dev));
+ vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSIX_IRQ,
+ &vfu_msix_irq_state);
+ } else if (msi_nr_vectors_allocated(pci_dev)) {
+ ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_MSI_IRQ,
+ msi_nr_vectors_allocated(pci_dev));
+ vfu_setup_irq_state_callback(vfu_ctx, VFU_DEV_MSI_IRQ,
+ &vfu_msi_irq_state);
+ }
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ vfu_object_setup_msi_cbs(o);
+
+ pci_dev->irq_opaque = vfu_ctx;
+
+ return 0;
+}
+
+void vfu_object_set_bus_irq(PCIBus *pci_bus)
+{
+ int bus_num = pci_bus_num(pci_bus);
+ int max_bdf = PCI_BUILD_BDF(bus_num, PCI_DEVFN_MAX - 1);
+
+ pci_bus_irqs(pci_bus, vfu_object_set_irq, vfu_object_map_irq, pci_bus,
+ max_bdf);
+}
+
/*
* TYPE_VFU_OBJECT depends on the availability of the 'socket' and 'device'
* properties. It also depends on devices instantiated in QEMU. These
@@ -632,6 +788,13 @@ static void vfu_object_init_ctx(VfuObject *o, Error **errp)
vfu_object_register_bars(o->vfu_ctx, o->pci_dev);
+ ret = vfu_object_setup_irqs(o, o->pci_dev);
+ if (ret < 0) {
+ error_setg(errp, "vfu: Failed to setup interrupts for %s",
+ o->device);
+ goto fail;
+ }
+
ret = vfu_realize_ctx(o->vfu_ctx);
if (ret < 0) {
error_setg(errp, "vfu: Failed to realize device %s- %s",
@@ -657,6 +820,8 @@ fail:
o->unplug_blocker = NULL;
}
if (o->pci_dev) {
+ vfu_object_restore_msi_cbs(o);
+ o->pci_dev->irq_opaque = NULL;
object_unref(OBJECT(o->pci_dev));
o->pci_dev = NULL;
}
@@ -716,6 +881,8 @@ static void vfu_object_finalize(Object *obj)
}
if (o->pci_dev) {
+ vfu_object_restore_msi_cbs(o);
+ o->pci_dev->irq_opaque = NULL;
object_unref(OBJECT(o->pci_dev));
o->pci_dev = NULL;
}