@@ -2956,6 +2956,8 @@ F: hw/pci/proxy.c
F: include/hw/pci/proxy.h
F: hw/pci/memory-sync.c
F: include/hw/pci/memory-sync.h
+F: hw/remote/iohub.c
+F: include/hw/remote/iohub.h
Build and test automation
-------------------------
@@ -40,6 +40,7 @@ devices-dirs-$(CONFIG_MEM_DEVICE) += mem/
devices-dirs-$(CONFIG_NUBUS) += nubus/
devices-dirs-y += semihosting/
devices-dirs-y += smbios/
+devices-dirs-y += remote/
endif
common-obj-y += $(devices-dirs-y)
@@ -10,6 +10,7 @@
#include "hw/pci/pci.h"
#include "exec/memattrs.h"
#include "hw/i386/remote-memory.h"
+#include "hw/remote/iohub.h"
static void process_connect_dev_msg(MPQemuMsg *msg, QIOChannel *com,
Error **errp);
@@ -67,6 +68,9 @@ gboolean mpqemu_process_msg(QIOChannel *ioc, GIOCondition cond,
case SYNC_SYSMEM:
remote_sysmem_reconfig(&msg, &local_err);
break;
+ case SET_IRQFD:
+ process_set_irqfd_msg(pci_dev, &msg);
+ break;
default:
error_setg(&local_err, "Unknown command (%d) received from proxy \
in remote process pid=%d", msg.cmd, getpid());
@@ -17,12 +17,16 @@
#include "qapi/error.h"
#include "io/channel-util.h"
#include "io/channel.h"
+#include "hw/pci/pci_host.h"
+#include "hw/remote/iohub.h"
static void remote_machine_init(MachineState *machine)
{
MemoryRegion *system_memory, *system_io, *pci_memory;
RemMachineState *s = REMOTE_MACHINE(machine);
RemotePCIHost *rem_host;
+ PCIHostState *pci_host;
+ PCIDevice *pci_dev;
system_memory = get_system_memory();
system_io = get_system_io();
@@ -42,6 +46,17 @@ static void remote_machine_init(MachineState *machine)
memory_region_add_subregion_overlap(system_memory, 0x0, pci_memory, -1);
qdev_realize(DEVICE(rem_host), sysbus_get_default(), &error_fatal);
+
+ 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 void remote_set_socket(Object *obj, const char *str, Error **errp)
@@ -19,6 +19,9 @@
#include "qemu/error-report.h"
#include "hw/pci/memory-sync.h"
#include "qom/object.h"
+#include "qemu/event_notifier.h"
+#include "sysemu/kvm.h"
+#include "util/event_notifier-posix.c"
static void proxy_set_socket(PCIProxyDev *pdev, int fd, Error **errp)
{
@@ -57,6 +60,53 @@ static Property proxy_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+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);
+ MPQemuMsg msg;
+
+ event_notifier_init(&dev->intr, 0);
+ event_notifier_init(&dev->resample, 0);
+
+ memset(&msg, 0, sizeof(MPQemuMsg));
+ 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;
+ msg.size = sizeof(msg.data1);
+
+ mpqemu_msg_send(&msg, dev->dev);
+
+ 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 pci_proxy_dev_realize(PCIDevice *device, Error **errp)
{
PCIProxyDev *dev = PCI_PROXY_DEV(device);
@@ -72,6 +122,8 @@ static void pci_proxy_dev_realize(PCIDevice *device, Error **errp)
}
configure_memory_sync(&dev->sync, dev->com);
+
+ setup_irqfd(dev);
}
static int config_op_send(PCIProxyDev *pdev, uint32_t addr, uint32_t *val,
new file mode 100644
@@ -0,0 +1 @@
+common-obj-$(CONFIG_MPQEMU) += iohub.o
new file mode 100644
@@ -0,0 +1,153 @@
+/*
+ * Remote IO Hub
+ *
+ * Copyright © 2018, 2020 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_bus.h"
+#include "hw/remote/iohub.h"
+#include "qemu/thread.h"
+#include "hw/boards.h"
+#include "hw/i386/remote.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;
+ event_notifier_init_fd(&iohub->irqfds[pirq], -1);
+ event_notifier_init_fd(&iohub->resamplefds[pirq], -1);
+ }
+}
+
+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;
+ int pirq, s;
+
+ pirq = token->pirq;
+
+ s = event_notifier_test_and_clear(&iohub->resamplefds[pirq]);
+
+ 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, MPQemuMsg *msg)
+{
+ RemMachineState *machine = REMOTE_MACHINE(current_machine);
+ RemoteIOHubState *iohub = machine->iohub;
+ int pirq;
+
+ g_assert(msg->data1.set_irqfd.intx < 4);
+ g_assert(msg->num_fds == 2);
+
+ pirq = remote_iohub_map_irq(pci_dev, msg->data1.set_irqfd.intx);
+
+ if (event_notifier_get_fd(&iohub->irqfds[pirq]) != -1) {
+ event_notifier_cleanup(&iohub->irqfds[pirq]);
+ event_notifier_cleanup(&iohub->resamplefds[pirq]);
+ memset(&iohub->token[pirq], 0, sizeof(ResampleToken));
+ }
+
+ event_notifier_init_fd(&iohub->irqfds[pirq], msg->fds[0]);
+ event_notifier_init_fd(&iohub->resamplefds[pirq], msg->fds[1]);
+
+ iohub->token[pirq].iohub = iohub;
+ iohub->token[pirq].pirq = pirq;
+
+ qemu_set_fd_handler(msg->fds[1], intr_resample_handler, NULL,
+ &iohub->token[pirq]);
+}
@@ -18,12 +18,14 @@
#include "hw/boards.h"
#include "hw/pci-host/remote.h"
#include "io/channel.h"
+#include "hw/remote/iohub.h"
typedef struct RemMachineState {
MachineState parent_obj;
RemotePCIHost *host;
QIOChannel *ioc;
+ RemoteIOHubState *iohub;
} RemMachineState;
#define TYPE_REMOTE_MACHINE "remote-machine"
@@ -192,6 +192,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
@@ -12,9 +12,12 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
+#include <linux/kvm.h>
+
#include "hw/pci/pci.h"
#include "io/channel.h"
#include "hw/pci/memory-sync.h"
+#include "qemu/event_notifier.h"
#define TYPE_PCI_PROXY_DEV "pci-proxy-dev"
@@ -45,6 +48,11 @@ struct PCIProxyDev {
RemoteMemSync sync;
+ struct kvm_irqfd irqfd;
+
+ EventNotifier intr;
+ EventNotifier resample;
+
ProxyMemoryRegion region[PCI_NUM_REGIONS];
};
new file mode 100644
@@ -0,0 +1,50 @@
+/*
+ * IO Hub for remote device
+ *
+ * Copyright © 2018, 2020 Oracle and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef REMOTE_IOHUB_H
+#define REMOTE_IOHUB_H
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+
+#include "hw/pci/pci.h"
+#include "qemu/event_notifier.h"
+#include "qemu/thread-posix.h"
+#include "io/mpqemu-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 ResampleToken {
+ void *iohub;
+ int pirq;
+} ResampleToken;
+
+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];
+ ResampleToken token[REMOTE_IOHUB_NB_PIRQS];
+ QemuMutex irq_level_lock[REMOTE_IOHUB_NB_PIRQS];
+} RemoteIOHubState;
+
+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, MPQemuMsg *msg);
+
+#endif
@@ -42,6 +42,7 @@ typedef enum {
PCI_CONFIG_READ,
BAR_WRITE,
BAR_READ,
+ SET_IRQFD,
MAX = INT_MAX,
} MPQemuCmd;
@@ -64,6 +65,10 @@ typedef struct {
bool memory;
} BarAccessMsg;
+typedef struct {
+ int intx;
+} SetIrqFdMsg;
+
/**
* Maximum size of data2 field in the message to be transmitted.
*/
@@ -92,6 +97,7 @@ typedef struct {
uint64_t u64;
SyncSysmemMsg sync_sysmem;
BarAccessMsg bar_access;
+ SetIrqFdMsg set_irqfd;
} data1;
int fds[REMOTE_MAX_FDS];
@@ -258,6 +258,7 @@ bool mpqemu_msg_valid(MPQemuMsg *msg)
break;
case BAR_WRITE:
case BAR_READ:
+ case SET_IRQFD:
if (msg->size != sizeof(msg->data1)) {
return false;
}