@@ -27,6 +27,9 @@
#include <unistd.h>
#include <assert.h>
#include <string.h>
+#include <linux/kvm.h>
+#include <errno.h>
+
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "io/proxy-link.h"
@@ -44,6 +47,16 @@
#include "hw/proxy/qemu-proxy.h"
#include "hw/proxy/memory-sync.h"
#include "qom/object.h"
+#include "qemu/event_notifier.h"
+#include "sysemu/kvm.h"
+#include "util/event_notifier-posix.c"
+
+/*
+ * TODO: kvm_vm_ioctl is only available for per-target objects (NEED_CPU_H).
+ * The invocation of kvm_vm_ioctl should be moved to a per-target object. Until
+ * the the following definition is necessary
+ */
+int kvm_vm_ioctl(KVMState *s, int type, ...);
static void pci_proxy_dev_realize(PCIDevice *dev, Error **errp);
@@ -204,6 +217,52 @@ static void pci_proxy_dev_register_types(void)
type_init(pci_proxy_dev_register_types)
+static void proxy_intx_update(PCIDevice *pci_dev)
+{
+ PCIProxyDev *dev = PCI_PROXY_DEV(pci_dev);
+ PCIINTxRoute route;
+ int pin = pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1;
+
+ if (dev->irqfd.fd) {
+ dev->irqfd.flags = KVM_IRQFD_FLAG_DEASSIGN;
+ (void) kvm_vm_ioctl(kvm_state, KVM_IRQFD, &dev->irqfd);
+ memset(&dev->irqfd, 0, sizeof(struct kvm_irqfd));
+ }
+
+ route = pci_device_route_intx_to_irq(pci_dev, pin);
+
+ dev->irqfd.fd = event_notifier_get_fd(&dev->intr);
+ dev->irqfd.resamplefd = event_notifier_get_fd(&dev->resample);
+ dev->irqfd.gsi = route.irq;
+ dev->irqfd.flags |= KVM_IRQFD_FLAG_RESAMPLE;
+ (void) kvm_vm_ioctl(kvm_state, KVM_IRQFD, &dev->irqfd);
+}
+
+static void setup_irqfd(PCIProxyDev *dev)
+{
+ PCIDevice *pci_dev = PCI_DEVICE(dev);
+ ProcMsg msg;
+
+ event_notifier_init(&dev->intr, 0);
+ event_notifier_init(&dev->resample, 0);
+
+ memset(&msg, 0, sizeof(ProcMsg));
+ msg.cmd = SET_IRQFD;
+ msg.num_fds = 2;
+ msg.fds[0] = event_notifier_get_fd(&dev->intr);
+ msg.fds[1] = event_notifier_get_fd(&dev->resample);
+ msg.data1.set_irqfd.intx =
+ pci_get_byte(pci_dev->config + PCI_INTERRUPT_PIN) - 1;
+
+ proxy_proc_send(dev->proxy_link, &msg);
+
+ memset(&dev->irqfd, 0, sizeof(struct kvm_irqfd));
+
+ proxy_intx_update(pci_dev);
+
+ pci_device_set_intx_routing_notifier(pci_dev, proxy_intx_update);
+}
+
static void init_proxy(PCIDevice *dev, char *command, Error **errp)
{
PCIProxyDev *pdev = PCI_PROXY_DEV(dev);
@@ -231,7 +290,8 @@ static void init_proxy(PCIDevice *dev, char *command, Error **errp)
}
proxy_link_set_sock(pdev->proxy_link, pdev->rsocket);
- configure_memory_sync(dev->sync, dev->proxy_link);
+ configure_memory_sync(pdev->sync, pdev->proxy_link);
+ setup_irqfd(pdev);
}
static void pci_proxy_dev_realize(PCIDevice *device, Error **errp)
@@ -191,6 +191,9 @@
#define PCI_DEVICE_ID_SUN_SIMBA 0x5000
#define PCI_DEVICE_ID_SUN_SABRE 0xa000
+#define PCI_VENDOR_ID_ORACLE 0x108e
+#define PCI_DEVICE_ID_REMOTE_IOHUB 0xb000
+
#define PCI_VENDOR_ID_CMD 0x1095
#define PCI_DEVICE_ID_CMD_646 0x0646
@@ -23,8 +23,11 @@
#ifndef QEMU_PROXY_H
#define QEMU_PROXY_H
+#include <linux/kvm.h>
+
#include "io/proxy-link.h"
#include "hw/proxy/memory-sync.h"
+#include "qemu/event_notifier.h"
#define TYPE_PCI_PROXY_DEV "pci-proxy-dev"
@@ -46,6 +49,8 @@ typedef struct PCIProxyDev {
ProxyLinkState *proxy_link;
RemoteMemSync *sync;
+ struct kvm_irqfd irqfd;
+
EventNotifier intr;
EventNotifier resample;
@@ -58,6 +58,8 @@ typedef struct ProxyLinkState ProxyLinkState;
* SYNC_SYSMEM Shares QEMU's RAM with remote device's RAM
* BAR_WRITE Writes to PCI BAR region
* BAR_READ Reads from PCI BAR region
+ * SET_IRQFD Sets the IRQFD to be used to raise interrupts directly
+ * from remote device
*
*/
typedef enum {
@@ -67,6 +69,7 @@ typedef enum {
SYNC_SYSMEM,
BAR_WRITE,
BAR_READ,
+ SET_IRQFD,
MAX,
} proc_cmd_t;
@@ -95,6 +98,10 @@ typedef struct {
bool memory;
} bar_access_msg_t;
+typedef struct {
+ int intx;
+} set_irqfd_msg_t;
+
typedef struct {
proc_cmd_t cmd;
int bytestream;
@@ -104,6 +111,7 @@ typedef struct {
uint64_t u64;
sync_sysmem_msg_t sync_sysmem;
bar_access_msg_t bar_access;
+ set_irqfd_msg_t set_irqfd;
} data1;
int fds[REMOTE_MAX_FDS];
new file mode 100644
@@ -0,0 +1,63 @@
+/*
+ * IO Hub for remote device
+ *
+ * Copyright 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef REMOTE_IOHUB_H
+#define REMOTE_IOHUB_H
+
+#include <sys/types.h>
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "qemu/event_notifier.h"
+#include "qemu/thread-posix.h"
+#include "io/proxy-link.h"
+
+#define REMOTE_IOHUB_NB_PIRQS 8
+
+#define REMOTE_IOHUB_DEV 31
+#define REMOTE_IOHUB_FUNC 0
+
+#define TYPE_REMOTE_IOHUB_DEVICE "remote-iohub"
+#define REMOTE_IOHUB_DEVICE(obj) \
+ OBJECT_CHECK(RemoteIOHubState, (obj), TYPE_REMOTE_IOHUB_DEVICE)
+
+typedef struct RemoteIOHubState {
+ PCIDevice d;
+ uint8_t irq_num[PCI_SLOT_MAX][PCI_NUM_PINS];
+ EventNotifier irqfds[REMOTE_IOHUB_NB_PIRQS];
+ EventNotifier resamplefds[REMOTE_IOHUB_NB_PIRQS];
+ unsigned int irq_level[REMOTE_IOHUB_NB_PIRQS];
+ QemuMutex irq_level_lock[REMOTE_IOHUB_NB_PIRQS];
+} RemoteIOHubState;
+
+typedef struct ResampleToken {
+ RemoteIOHubState *iohub;
+ int pirq;
+} ResampleToken;
+
+int remote_iohub_map_irq(PCIDevice *pci_dev, int intx);
+void remote_iohub_set_irq(void *opaque, int pirq, int level);
+void process_set_irqfd_msg(PCIDevice *pci_dev, ProcMsg *msg);
+
+#endif
@@ -30,11 +30,13 @@
#include "hw/boards.h"
#include "remote/pcihost.h"
#include "qemu/notify.h"
+#include "remote/iohub.h"
typedef struct RemMachineState {
MachineState parent_obj;
RemPCIHost *host;
+ RemoteIOHubState *iohub;
} RemMachineState;
#define TYPE_REMOTE_MACHINE "remote-machine"
@@ -1,3 +1,4 @@
remote-pci-obj-$(CONFIG_MPQEMU) += remote-main.o
remote-pci-obj-$(CONFIG_MPQEMU) += pcihost.o
remote-pci-obj-$(CONFIG_MPQEMU) += machine.o
+remote-pci-obj-$(CONFIG_MPQEMU) += iohub.o
new file mode 100644
@@ -0,0 +1,159 @@
+/*
+ * Remote IO Hub
+ *
+ * Copyright 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_bus.h"
+#include "remote/iohub.h"
+#include "qemu/thread.h"
+#include "hw/boards.h"
+#include "remote/machine.h"
+#include "qemu/main-loop.h"
+
+static void remote_iohub_initfn(Object *obj)
+{
+ RemoteIOHubState *iohub = REMOTE_IOHUB_DEVICE(obj);
+ int slot, intx, pirq;
+
+ memset(&iohub->irqfds, 0, sizeof(iohub->irqfds));
+ memset(&iohub->resamplefds, 0, sizeof(iohub->resamplefds));
+
+ for (slot = 0; slot < PCI_SLOT_MAX; slot++ ) {
+ for (intx = 0; intx < PCI_NUM_PINS; intx++) {
+ iohub->irq_num[slot][intx] = (slot + intx) % 4 + 4;
+ }
+ }
+
+ for (pirq = 0; pirq < REMOTE_IOHUB_NB_PIRQS; pirq++) {
+ qemu_mutex_init(&iohub->irq_level_lock[pirq]);
+ iohub->irq_level[pirq] = 0;
+ }
+}
+
+static void remote_iohub_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ k->vendor_id = PCI_VENDOR_ID_ORACLE;
+ k->device_id = PCI_DEVICE_ID_REMOTE_IOHUB;
+}
+
+static const TypeInfo remote_iohub_info = {
+ .name = TYPE_REMOTE_IOHUB_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(RemoteIOHubState),
+ .instance_init = remote_iohub_initfn,
+ .class_init = remote_iohub_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+static void remote_iohub_register(void)
+{
+ type_register_static(&remote_iohub_info);
+}
+
+type_init(remote_iohub_register);
+
+int remote_iohub_map_irq(PCIDevice *pci_dev, int intx)
+{
+ BusState *bus = qdev_get_parent_bus(&pci_dev->qdev);
+ PCIBus *pci_bus = PCI_BUS(bus);
+ PCIDevice *pci_iohub =
+ pci_bus->devices[PCI_DEVFN(REMOTE_IOHUB_DEV, REMOTE_IOHUB_FUNC)];
+ RemoteIOHubState *iohub = REMOTE_IOHUB_DEVICE(pci_iohub);
+
+ return iohub->irq_num[PCI_SLOT(pci_dev->devfn)][intx];
+}
+
+/*
+ * TODO: Using lock to set the interrupt level could become a
+ * performance bottleneck. Check if atomic arithmetic
+ * is possible.
+ */
+void remote_iohub_set_irq(void *opaque, int pirq, int level)
+{
+ RemoteIOHubState *iohub = opaque;
+
+ assert(pirq >= 0);
+ assert(pirq < REMOTE_IOHUB_NB_PIRQS);
+
+ qemu_mutex_lock(&iohub->irq_level_lock[pirq]);
+
+ if (level) {
+ if (++iohub->irq_level[pirq] == 1) {
+ event_notifier_set(&iohub->irqfds[pirq]);
+ }
+ } else if (iohub->irq_level[pirq] > 0) {
+ iohub->irq_level[pirq]--;
+ }
+
+ qemu_mutex_unlock(&iohub->irq_level_lock[pirq]);
+}
+
+static void intr_resample_handler(void *opaque)
+{
+ ResampleToken *token = opaque;
+ RemoteIOHubState *iohub = token->iohub;
+ uint64_t val;
+ int pirq, s;
+
+ pirq = token->pirq;
+
+ s = read(event_notifier_get_fd(&iohub->resamplefds[pirq]), &val,
+ sizeof(uint64_t));
+
+ assert(s >= 0);
+
+ qemu_mutex_lock(&iohub->irq_level_lock[pirq]);
+
+ if (iohub->irq_level[pirq]) {
+ event_notifier_set(&iohub->irqfds[pirq]);
+ }
+
+ qemu_mutex_unlock(&iohub->irq_level_lock[pirq]);
+}
+
+void process_set_irqfd_msg(PCIDevice *pci_dev, ProcMsg *msg)
+{
+ RemMachineState *machine = REMOTE_MACHINE(current_machine);
+ RemoteIOHubState *iohub = machine->iohub;
+ ResampleToken *token;
+ int pirq = remote_iohub_map_irq(pci_dev, msg->data1.set_irqfd.intx);
+
+ assert(msg->num_fds == 2);
+
+ event_notifier_init_fd(&iohub->irqfds[pirq], msg->fds[0]);
+ event_notifier_init_fd(&iohub->resamplefds[pirq], msg->fds[1]);
+
+ token = g_malloc0(sizeof(ResampleToken));
+ token->iohub = iohub;
+ token->pirq = pirq;
+
+ qemu_set_fd_handler(msg->fds[1], intr_resample_handler, NULL, token);
+}
@@ -40,6 +40,8 @@
#include "qemu-common.h"
#include "sysemu/sysemu.h"
#include "qemu/notify.h"
+#include "hw/pci/pci_host.h"
+#include "remote/iohub.h"
static NotifierList machine_init_done_notifiers =
NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers);
@@ -70,6 +72,8 @@ static void remote_machine_init(Object *obj)
RemMachineState *s = REMOTE_MACHINE(obj);
RemPCIHost *rem_host;
MemoryRegion *system_memory, *system_io, *pci_memory;
+ PCIHostState *pci_host;
+ PCIDevice *pci_dev;
Error *error_abort = NULL;
@@ -101,6 +105,17 @@ static void remote_machine_init(Object *obj)
qemu_mutex_unlock_iothread();
qdev_init_nofail(DEVICE(rem_host));
+
+ pci_host = PCI_HOST_BRIDGE(rem_host);
+ pci_dev = pci_create_simple_multifunction(pci_host->bus,
+ PCI_DEVFN(REMOTE_IOHUB_DEV,
+ REMOTE_IOHUB_FUNC),
+ true, TYPE_REMOTE_IOHUB_DEVICE);
+
+ s->iohub = REMOTE_IOHUB_DEVICE(pci_dev);
+
+ pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq,
+ s->iohub, REMOTE_IOHUB_NB_PIRQS);
}
static const TypeInfo remote_machine = {
@@ -47,6 +47,7 @@
#include "block/block.h"
#include "exec/memattrs.h"
#include "exec/address-spaces.h"
+#include "remote/iohub.h"
static ProxyLinkState *proxy_link;
PCIDevice *remote_pci_dev;
@@ -185,6 +186,9 @@ static void process_msg(GIOCondition cond)
goto finalize_loop;
}
break;
+ case SET_IRQFD:
+ process_set_irqfd_msg(remote_pci_dev, msg);
+ break;
default:
error_setg(&err, "Unknown command");
goto finalize_loop;