@@ -1440,3 +1440,17 @@ int kvm_assign_set_msix_entry(kvm_context_t kvm,
return ret;
}
#endif
+
+int kvm_vbus_assign_gsi(kvm_context_t kvm, int queue, int gsi)
+{
+ int r;
+ struct kvm_vbus_gsi data = {
+ .queue = queue,
+ .gsi = gsi,
+ };
+
+ r = ioctl(kvm->vm_fd, KVM_VBUS_ASSIGN_GSI, &data);
+ if (r == -1)
+ r = -errno;
+ return r;
+}
@@ -856,6 +856,8 @@ int kvm_commit_irq_routes(kvm_context_t kvm);
*/
int kvm_get_irq_route_gsi(kvm_context_t kvm);
+int kvm_vbus_assign_gsi(kvm_context_t kvm, int queue, int gsi);
+
#ifdef KVM_CAP_DEVICE_MSIX
int kvm_assign_set_msix_nr(kvm_context_t kvm,
struct kvm_assigned_msix_nr *msix_nr);
@@ -547,6 +547,7 @@ OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o dma-helpers.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
OBJS+=virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o
+OBJS+=vbus.o
OBJS+=fw_cfg.o
ifdef CONFIG_KVM
OBJS+=kvm.o kvm-all.o
@@ -38,6 +38,7 @@
#include "virtio-console.h"
#include "hpet_emul.h"
#include "device-assignment.h"
+#include "vbus.h"
#include "qemu-kvm.h"
@@ -1076,6 +1077,10 @@ vga_bios_error:
}
}
+ if (pci_enabled) {
+ pci_vbus_init(pci_bus);
+ }
+
for(i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
@@ -71,6 +71,9 @@ extern target_phys_addr_t pci_mem_base;
#define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002
#define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003
+#define PCI_VENDOR_ID_NOVELL 0x11da
+#define PCI_DEVICE_ID_VIRTUAL_BUS 0x2000
+
typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
uint32_t address, uint32_t data, int len);
typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
new file mode 100644
@@ -0,0 +1,156 @@
+/*
+ * Add in-kernel "vbus" device support by surfacing a PCI->OTHER bridge
+ *
+ * Copyright (c) 2009, Novell Inc, Gregory Haskins <ghaskins@novell.com>
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "qemu-kvm.h"
+#include "hw.h"
+#include "pci.h"
+
+#include "vbus.h"
+
+#define PCI_CAP_ID_MSI 0x05
+
+#define PCI_MSI_FLAGS 2 /* Various flags */
+#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */
+#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */
+#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */
+#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */
+#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */
+#define PCI_MSI_RFU 3 /* Rest of capability flags */
+#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
+#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
+#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
+#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
+#define PCI_MSI_MASK_BIT 16 /* Mask bits register */
+
+struct VirtualBus {
+ PCIDevice dev;
+ struct kvm_irq_routing_entry irq[8];
+ int enabled:1;
+};
+
+static struct VirtualBus *to_bus(PCIDevice *dev)
+{
+ return (struct VirtualBus*)dev;
+}
+
+static int
+vbus_pci_cap_init(PCIDevice *dev)
+{
+ int offset = dev->cap.start;
+
+ dev->cap.length = 0;
+
+ memset(&dev->config[offset], 0, PCI_CAPABILITY_CONFIG_MSI_LENGTH);
+ dev->config[offset] = PCI_CAP_ID_MSI;
+ dev->config[offset+PCI_MSI_FLAGS] = 0x06; /* request 8 vectors */
+ dev->cap.length += PCI_CAPABILITY_CONFIG_MSI_LENGTH;
+
+ return 0;
+}
+
+static void
+vbus_pci_cap_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
+{
+ struct VirtualBus *bus = to_bus(dev);
+ unsigned int pos = dev->cap.start;
+ unsigned int ctrl = pos + PCI_MSI_FLAGS;
+
+ pci_default_cap_write_config(dev, addr, val, len);
+
+ /* Check if this is not a write to the control register. */
+ if (!(addr <= ctrl && (addr + len) > ctrl))
+ return;
+
+ /*
+ * We only get here if this is a write to the control register,
+ * but we only emulate the PIO side-effects if this is the first
+ * time we have seen an MSI_ENABLE operation. I.e. all MSI_DISABLE
+ * and subsequent MSI_ENABLE operations are ignored
+ */
+ if (!bus->enabled && val & 1) {
+ int i, total;
+ uint8_t flags = dev->config[pos+PCI_MSI_FLAGS];
+
+ total = 1 << ((flags & PCI_MSI_FLAGS_QSIZE) >> 4);
+
+ if (total > 8)
+ total = 8;
+
+ /* We need to register a GSI for each vector returned */
+ for (i = 0; i < total; i++) {
+ struct kvm_irq_routing_entry *irq = &bus->irq[i];
+ uint32_t addr;
+ uint16_t data;
+
+ addr = *(uint32_t *)&dev->config[pos + PCI_MSI_ADDRESS_LO];
+
+ data = *(uint16_t *)&dev->config[pos + PCI_MSI_DATA_32];
+ data += i;
+
+ irq->u.msi.address_lo = addr;
+ irq->u.msi.address_hi = 0;
+ irq->u.msi.data = data;
+
+ irq->type = KVM_IRQ_ROUTING_MSI;
+
+ irq->gsi = kvm_get_irq_route_gsi(kvm_context);
+ if (irq->gsi < 0) {
+ perror("vbus: kvm_get_irq_route_gsi");
+ return;
+ }
+
+ kvm_add_routing_entry(kvm_context, irq);
+ if (kvm_commit_irq_routes(kvm_context) < 0) {
+ perror("vbus: kvm_commit_irq_routes");
+ return;
+ }
+
+ kvm_vbus_assign_gsi(kvm_context, i, irq->gsi);
+ }
+
+ bus->enabled = 1;
+ }
+}
+
+void
+pci_vbus_init(PCIBus *bus)
+{
+ struct VirtualBus *_bus;
+ PCIDevice *dev;
+ uint8_t *config;
+
+ if (!kvm_check_extension(kvm_context, KVM_CAP_VBUS))
+ return;
+
+ dev = pci_register_device(bus, "vbus", sizeof(*_bus),
+ -1, NULL, NULL);
+ if (!dev) {
+ perror("vbus present but PCI allocation failed");
+ return;
+ }
+
+ config = dev->config;
+ pci_config_set_vendor_id(config, PCI_VENDOR_ID_NOVELL);
+ pci_config_set_device_id(config, PCI_DEVICE_ID_VIRTUAL_BUS);
+ pci_config_set_class(config, PCI_CLASS_BRIDGE_OTHER);
+
+ pci_enable_capability_support(dev, 0,
+ NULL,
+ vbus_pci_cap_write_config,
+ vbus_pci_cap_init);
+
+ _bus = to_bus(dev);
+
+ memset(&_bus->irq[0], 0, sizeof(_bus->irq));
+
+ _bus->enabled = 0;
+}
new file mode 100644
@@ -0,0 +1,2 @@
+
+void pci_vbus_init(PCIBus *bus);
Add support for shiming a userspace PCI object in qemu-kvm to represent the VBUS object located in the kernel. This presents a PCI bridge to the system using standard PCI mechanisms such as MSI interrupt routing. A guest may then optionally install a PCI driver for this bridge to gain access to the virtual-devices behind it. This is the userspace support for the kernel VBUS project, release v3 which you can find here: http://lkml.org/lkml/2009/4/21/408 Note that this patch uses MSIs, which has several implications: 1) This patch has a prerequisite on: http://git.kernel.org/?p=linux/kernel/git/ghaskins/vbus/kvm-userspace.git;a=commitdiff;h=2e0a71f5b234b288bad1904e8f9e1d1e6504d4b9 At the time of this writing, this patch has been accepted upstream into Avi's tree, but has not yet synced out to kernel.org 2) We need to surface 8 IRQ vectors on the bridge to fully represent 8 levels of priority. However, we utilize multi-MSI which may have spotty support in the guests (in fact x86 linux doesnt support multi-MSI). In these cases, the guest will only see one queue and therefore only have one flat priority. We will move to surfacing these GSIs as MSI-X in a future release to work around this issue. Signed-off-by: Gregory Haskins <ghaskins@novell.com> --- libkvm/libkvm.c | 14 ++++ libkvm/libkvm.h | 2 + qemu/Makefile.target | 1 qemu/hw/pc.c | 5 ++ qemu/hw/pci.h | 3 + qemu/hw/vbus.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ qemu/hw/vbus.h | 2 + 7 files changed, 183 insertions(+), 0 deletions(-) create mode 100644 qemu/hw/vbus.c create mode 100644 qemu/hw/vbus.h -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html