[PATCH] Inter-VM shared memory PCI device

Cam Macdonell
2010-03-05T23:52:53Z

Support an inter-vm shared memory device that maps a shared-memory object
as a PCI device in the guest. This patch also supports interrupts between
guest by communicating over a unix domain socket. This patch applies to the
qemu-kvm repository.

This device now creates a qemu character device and sends 1-bytes messages to
trigger interrupts. Writes are trigger by writing to the "Doorbell" register
on the shared memory PCI device. The lower 8-bits of the value written to this
register are sent as the 1-byte message so different meanings of interrupts can
be supported.

Interrupts are supported between multiple VMs by using a shared memory server

-ivshmem <size in MB>,[unix:<path>][file]

Interrupts can also be used between host and guest as well by implementing a
listener on the host that talks to shared memory server. The shared memory
server passes file descriptors for the shared memory object and eventfds (our
interrupt mechanism) to the respective qemu instances.

Sample programs, init scripts and the shared memory server are available in a
git repo here:

www.gitorious.org/nahanni
---
Makefile.target | 3 +
hw/ivshmem.c | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pc.c | 6 +
hw/pc.h | 3 +
qemu-char.c | 6 +
qemu-char.h | 3 +
qemu-options.hx | 12 ++
sysemu.h | 8 +
vl.c | 13 ++
9 files changed, 615 insertions(+), 0 deletions(-)
create mode 100644 hw/ivshmem.c

diff --git a/Makefile.target b/Makefile.target
index 82caf20..921dc74 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -217,6 +217,9 @@ obj-y += pcnet.o
obj-y += rtl8139.o
obj-y += e1000.o

+# Inter-VM PCI shared memory
+obj-y += ivshmem.o
+
# Hardware support
obj-i386-y = ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o
obj-i386-y += pckbd.o $(sound-obj-y) dma.o
diff --git a/hw/ivshmem.c b/hw/ivshmem.c
new file mode 100644
index 0000000..aa88c07
--- /dev/null
+++ b/hw/ivshmem.c
@@ -0,0 +1,561 @@
+/*
+ * Inter-VM Shared Memory PCI device.
+ *
+ * Author:
+ * Cam Macdonell <cam@cs.ualberta.ca>
+ *
+ * Based On: cirrus_vga.c and rtl8139.c
+ *
+ * This code is licensed under the GNU GPL v2.
+ */
+
+#include "hw.h"
+#include "console.h"
+#include "pc.h"
+#include "pci.h"
+#include "sysemu.h"
+
+#include "qemu-common.h"
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#define PCI_COMMAND_IOACCESS 0x0001
+#define PCI_COMMAND_MEMACCESS 0x0002
+#define PCI_COMMAND_BUSMASTER 0x0004
+
+#define DEBUG_IVSHMEM
+#define MAX_EVENT_FDS 16
+
+#ifdef DEBUG_IVSHMEM
+#define IVSHMEM_DPRINTF(fmt, args...) \
+ do {printf("IVSHMEM: " fmt, ##args); } while (0)
+#else
+#define IVSHMEM_DPRINTF(fmt, args...)
+#endif
+
+#define BROADCAST_VAL ((1 << 8) - 1)
+
+typedef struct IVShmemState {
+ uint16_t intrmask;
+ uint16_t intrstatus;
+ uint16_t doorbell;
+
+ PCIDevice *pci_dev;
+ CharDriverState * chr;
+ CharDriverState * eventfd_chr;
+ int ivshmem_mmio_io_addr;
+
+ uint8_t *ivshmem_ptr;
+ unsigned long ivshmem_offset;
+ unsigned int ivshmem_size;
+ int shm_fd; /* shared memory file descriptor */
+
+ int eventfds[16]; /* for now we have a limit of 16 inter-connected guests */
+ int eventfd_posn;
+ uint16_t eventfd_bitvec;
+ int num_eventfds;
+} IVShmemState;
+
+typedef struct PCI_IVShmemState {
+ PCIDevice dev;
+ IVShmemState ivshmem_state;
+} PCI_IVShmemState;
+
+typedef struct IVShmemDesc {
+ char * chrdev;
+ int size;
+} IVShmemDesc;
+
+/* registers for the Inter-VM shared memory device */
+enum ivshmem_registers {
+ IntrMask = 0,
+ IntrStatus = 2,
+ Doorbell = 4,
+ IVPosition = 6,
+ IVLiveList = 8
+};
+
+static int num_ivshmem_devices = 0;
+static IVShmemDesc ivshmem_desc;
+
+static void ivshmem_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCI_IVShmemState *d = (PCI_IVShmemState *)pci_dev;
+ IVShmemState *s = &d->ivshmem_state;
+
+ IVSHMEM_DPRINTF("addr = %u size = %u\n", (uint32_t)addr, (uint32_t)size);
+ cpu_register_physical_memory(addr, s->ivshmem_size, s->ivshmem_offset);
+
+}
+
+void ivshmem_init(const char * optarg) {
+
+ char * temp;
+ char * ivshmem_sz;
+ int size;
+
+ num_ivshmem_devices++;
+
+ /* currently we only support 1 device */
+ if (num_ivshmem_devices > MAX_IVSHMEM_DEVICES) {
+ return;
+ }
+
+ temp = strdup(optarg);
+
+ ivshmem_sz=strsep(&temp,",");
+
+ if (ivshmem_sz != NULL) {
+ size = atol(ivshmem_sz);
+ } else {
+ size = -1;
+ }
+
+ ivshmem_desc.chrdev = strsep(&temp,"\0");
+
+ if ( size == -1) {
+ ivshmem_desc.size = TARGET_PAGE_SIZE;
+ } else {
+ ivshmem_desc.size = size*1024*1024;
+ }
+
+}
+
+int ivshmem_get_size(void) {
+ return ivshmem_desc.size;
+}
+
+static void broadcast_eventfds(int val, IVShmemState *s)
+{
+
+ int dest = val >> 4;
+ u_int64_t writelong = val & 0xff;
+
+ for (dest = 1; dest < s->num_eventfds; dest++) {
+
+ if (s->eventfds[dest] != -1) {
+ IVSHMEM_DPRINTF("Writing %ld to VM %d\n", writelong, dest);
+ if (write(s->eventfds[dest], &(writelong), 8) != 8)
+ IVSHMEM_DPRINTF("error writing to eventfd\n");
+ }
+
+ }
+
+}
+
+/* accessing registers - based on rtl8139 */
+static void ivshmem_update_irq(IVShmemState *s)
+{
+ int isr;
+ isr = (s->intrstatus & s->intrmask) & 0xffff;
+
+ /* don't print ISR resets */
+ if (isr) {
+ IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
+ isr ? 1 : 0, s->intrstatus, s->intrmask);
+ }
+
+ qemu_set_irq(s->pci_dev->irq[0], (isr != 0));
+}
+
+static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num,
+ pcibus_t addr, pcibus_t size, int type)
+{
+ PCI_IVShmemState *d = (PCI_IVShmemState *)pci_dev;
+ IVShmemState *s = &d->ivshmem_state;
+
+ cpu_register_physical_memory(addr + 0, 0x100, s->ivshmem_mmio_io_addr);
+}
+
+static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
+
+ s->intrmask = val;
+
+ ivshmem_update_irq(s);
+}
+
+static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrmask;
+
+ IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
+
+ return ret;
+}
+
+static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
+{
+ IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
+
+ s->intrstatus = val;
+
+ ivshmem_update_irq(s);
+ return;
+}
+
+static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
+{
+ uint32_t ret = s->intrstatus;
+
+ /* reading ISR clears all interrupts */
+ s->intrstatus = 0;
+
+ ivshmem_update_irq(s);
+
+ return ret;
+}
+
+static void ivshmem_io_writew(void *opaque, uint8_t addr, uint32_t val)
+{
+ IVShmemState *s = opaque;
+
+ // 32-bits are written to the address
+ int dest = val >> 8;
+ u_int64_t writelong = val & 0xff;
+
+ IVSHMEM_DPRINTF("writing 0x%x to 0x%lx\n", addr, (unsigned long) opaque);
+
+ addr &= 0xfe;
+
+ switch (addr)
+ {
+ case IntrMask:
+ ivshmem_IntrMask_write(s, val);
+ break;
+
+ case IntrStatus:
+ ivshmem_IntrStatus_write(s, val);
+ break;
+
+ case Doorbell:
+
+ if (dest == BROADCAST_VAL) {
+ broadcast_eventfds(val, s);
+ } else if (dest <= s->num_eventfds) {
+ IVSHMEM_DPRINTF("Writing %ld to VM %d\n", writelong, dest);
+ if (write(s->eventfds[dest], &(writelong), 8) != 8)
+ IVSHMEM_DPRINTF("error writing to eventfd\n");
+ } else {
+ IVSHMEM_DPRINTF("Invalid %ld to VM %d\n", writelong, dest);
+ }
+
+ break;
+ default:
+ IVSHMEM_DPRINTF("why are we writing 0x%x\n", addr);
+ }
+}
+
+static void ivshmem_io_writel(void *opaque, uint8_t addr, uint32_t val)
+{
+ IVSHMEM_DPRINTF("We shouldn't be writing longs\n");
+}
+
+static void ivshmem_io_writeb(void *opaque, uint8_t addr, uint32_t val)
+{
+ IVSHMEM_DPRINTF("We shouldn't be writing bytes\n");
+}
+
+static uint32_t ivshmem_io_readw(void *opaque, uint8_t addr)
+{
+
+ IVShmemState *s = opaque;
+ uint32_t ret;
+
+ switch (addr)
+ {
+ case IntrMask:
+ ret = ivshmem_IntrMask_read(s);
+ break;
+ case IntrStatus:
+ ret = ivshmem_IntrStatus_read(s);
+ break;
+
+ case IVPosition:
+ /* return my id in the ivshmem list */
+ ret = s->eventfd_posn;
+ break;
+ case IVLiveList:
+ /* return the list of live VMs id for ivshmem */
+ ret = s->eventfd_bitvec;
+ break;
+
+ default:
+ IVSHMEM_DPRINTF("why are we reading 0x%x\n", addr);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static uint32_t ivshmem_io_readl(void *opaque, uint8_t addr)
+{
+ IVSHMEM_DPRINTF("We shouldn't be reading longs\n");
+ return 0;
+}
+
+static uint32_t ivshmem_io_readb(void *opaque, uint8_t addr)
+{
+ IVSHMEM_DPRINTF("We shouldn't be reading bytes\n");
+
+ return 0;
+}
+
+static void ivshmem_mmio_writeb(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ ivshmem_io_writeb(opaque, addr & 0xFF, val);
+}
+
+static void ivshmem_mmio_writew(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ ivshmem_io_writew(opaque, addr & 0xFF, val);
+}
+
+static void ivshmem_mmio_writel(void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ ivshmem_io_writel(opaque, addr & 0xFF, val);
+}
+
+static uint32_t ivshmem_mmio_readb(void *opaque, target_phys_addr_t addr)
+{
+ return ivshmem_io_readb(opaque, addr & 0xFF);
+}
+
+static uint32_t ivshmem_mmio_readw(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = ivshmem_io_readw(opaque, addr & 0xFF);
+ return val;
+}
+
+static uint32_t ivshmem_mmio_readl(void *opaque, target_phys_addr_t addr)
+{
+ uint32_t val = ivshmem_io_readl(opaque, addr & 0xFF);
+ return val;
+}
+
+static CPUReadMemoryFunc *ivshmem_mmio_read[3] = {
+ ivshmem_mmio_readb,
+ ivshmem_mmio_readw,
+ ivshmem_mmio_readl,
+};
+
+static CPUWriteMemoryFunc *ivshmem_mmio_write[3] = {
+ ivshmem_mmio_writeb,
+ ivshmem_mmio_writew,
+ ivshmem_mmio_writel,
+};
+
+
+static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
+{
+ IVShmemState *s = opaque;
+
+ ivshmem_IntrStatus_write(s, *buf);
+
+ IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
+}
+
+static void ivshmem_event(void *opaque, int event)
+{
+// IVShmemState *s = opaque;
+ IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
+}
+
+static int ivshmem_can_receive(void * opaque)
+{
+ return 8;
+}
+
+static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd)
+{
+ // create a event character device based on the passed eventfd
+ IVShmemState *s = opaque;
+ CharDriverState * chr;
+
+ chr = qemu_chr_open_eventfd(eventfd);
+
+ if (chr == NULL) {
+ IVSHMEM_DPRINTF("creating eventfd for eventfd %d failed\n", eventfd);
+ exit(-1);
+ }
+
+ qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
+ ivshmem_event, s);
+
+ return chr;
+
+}
+
+static int check_shm_size(IVShmemState *s, int shmemfd) {
+ /* check that the guest isn't going to try and map more memory than the
+ * card server allocated return -1 to indicate error */
+
+ struct stat buf;
+
+ fstat(shmemfd, &buf);
+
+ if (s->ivshmem_size > buf.st_size) {
+ fprintf(stderr, "IVSHMEM ERROR: Requested memory size greater");
+ fprintf(stderr, " than shared object size (%d > %ld)\n",
+ s->ivshmem_size, buf.st_size);
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static void create_shared_memory_BAR(IVShmemState *s, int fd) {
+
+ s->shm_fd = fd;
+
+ s->ivshmem_offset = qemu_add_file_to_ram(s->shm_fd, s->ivshmem_size,
+ MAP_SHARED);
+
+ s->ivshmem_ptr = qemu_get_ram_ptr(s->ivshmem_offset);
+
+ /* region for shared memory */
+ pci_register_bar(s->pci_dev, 1, ivshmem_desc.size,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map);
+}
+
+static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
+{
+ IVShmemState *s = opaque;
+ int incoming_fd, tmp_fd;
+ long incoming_posn;
+
+ memcpy(&incoming_posn, buf, sizeof(long));
+ /* pick off s->chr->msgfd and store it, posn should accompany msg */
+ tmp_fd = qemu_chr_get_msgfd(s->chr);
+ IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
+
+ if (tmp_fd == -1) {
+ s->eventfd_posn = incoming_posn;
+ return;
+ }
+
+ /* because of the implementation of get_msgfd, we need a dup */
+ incoming_fd = dup(tmp_fd);
+
+ /* if the position is -1, then it's shared memory fd */
+ if (incoming_posn == -1) {
+
+ s->eventfd_bitvec = 0;
+ s->num_eventfds = 0;
+
+ if (check_shm_size(s, incoming_fd) == -1) {
+ exit(-1);
+ }
+
+ /* creating a BAR in qemu_chr callback may be crazy */
+ create_shared_memory_BAR(s, incoming_fd);
+
+ return;
+ }
+
+ /* this is an eventfd for a particular guest VM */
+ IVSHMEM_DPRINTF("eventfds[%ld] = %d\n", incoming_posn, incoming_fd);
+ s->eventfds[incoming_posn] = incoming_fd;
+
+ /* bitmap to keep track of live VMs */
+ s->eventfd_bitvec |= 1 << incoming_posn;
+
+ /* keep track of the maximum VM ID */
+ if (incoming_posn > s->num_eventfds) {
+ s->num_eventfds = incoming_posn;
+ }
+
+ /* initialize char device for callback on my eventfd */
+ if (incoming_posn == s->eventfd_posn) {
+ s->eventfd_chr = create_eventfd_chr_device(s, s->eventfds[s->eventfd_posn]);
+ }
+
+ return;
+}
+
+int pci_ivshmem_init(PCIBus *bus)
+{
+ PCI_IVShmemState *d;
+ IVShmemState *s;
+ uint8_t *pci_conf;
+
+ d = (PCI_IVShmemState *)pci_register_device(bus, "kvm_ivshmem",
+ sizeof(PCI_IVShmemState),
+ -1, NULL, NULL);
+ if (!d) {
+ return -1;
+ }
+
+ s = &d->ivshmem_state;
+
+ s->pci_dev = &d->dev;
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0xf4; // Qumranet vendor ID 0x5002
+ pci_conf[0x01] = 0x1a;
+ pci_conf[0x02] = 0x10;
+ pci_conf[0x03] = 0x11;
+ pci_conf[0x04] = PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS;
+ pci_conf[0x0a] = 0x00; // RAM controller
+ pci_conf[0x0b] = 0x05;
+ pci_conf[0x0e] = 0x00; // header_type
+
+ pci_conf[PCI_INTERRUPT_PIN] = 1; // we are going to support interrupts
+
+ s->ivshmem_size = ivshmem_desc.size;
+
+ if (ivshmem_desc.chrdev != NULL) {
+ if (strncmp(ivshmem_desc.chrdev, "unix:", 5) == 0) {
+
+ /* if we get a UNIX socket as the parameter we will talk
+ * to the ivshmem server*/
+ char label[32];
+
+ s->eventfd_posn = -1;
+
+ snprintf(label, 32, "ivshmem_chardev");
+ s->chr = qemu_chr_open(label, ivshmem_desc.chrdev, NULL);
+ if (s->chr == NULL) {
+ fprintf(stderr, "No server listening on %s\n",
+ ivshmem_desc.chrdev);
+ exit(-1);
+ }
+
+ qemu_chr_add_handlers(s->chr, ivshmem_can_receive, ivshmem_read,
+ ivshmem_event, s);
+
+ } else {
+ /* just map the file, we're not using a server */
+ int fd;
+
+ if ((fd = shm_open(ivshmem_desc.chrdev, O_CREAT|O_RDWR,
+ S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
+ fprintf(stderr, "kvm_shmem: could not open shared file\n");
+ exit(-1);
+ }
+
+ /* mmap onto PCI device's memory */
+ if (ftruncate(fd, ivshmem_desc.size) != 0) {
+ fprintf(stderr, "kvm_ivshmem: could not truncate shared file\n");
+ }
+
+ create_shared_memory_BAR(s, fd);
+
+ }
+ }
+
+ IVSHMEM_DPRINTF("shared memory size is = %d\n", ivshmem_desc.size);
+
+ s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read,
+ ivshmem_mmio_write, s);
+ /* region for registers*/
+ pci_register_bar(&d->dev, 0, 0x100,
+ PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map);
+
+ return 0;
+}
+
diff --git a/hw/pc.c b/hw/pc.c
index 0aebae9..073402a 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -90,6 +90,8 @@ struct e820_table {

static struct e820_table e820_table;

+extern int ivshmem_enabled;
+
typedef struct isa_irq_state {
qemu_irq *i8259;
qemu_irq *ioapic;
@@ -965,6 +967,10 @@ static void pc_init1(ram_addr_t ram_size,
}
}

+ if (pci_enabled && ivshmem_enabled) {
+ pci_ivshmem_init(pci_bus);
+ }
+
rtc_state = rtc_init(2000);

qemu_register_boot_set(pc_boot_set, rtc_state);
diff --git a/hw/pc.h b/hw/pc.h
index 88b5841..36b214c 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -167,6 +167,9 @@ void isa_ne2000_init(int base, int irq, NICInfo *nd);

void extboot_init(BlockDriverState *bs);

+/* ivshmem.c */
+int pci_ivshmem_init(PCIBus *bus);
+
int cpu_is_bsp(CPUState *env);

/* e820 types */
diff --git a/qemu-char.c b/qemu-char.c
index 4169492..bff911c 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2066,6 +2066,12 @@ static void tcp_chr_read(void *opaque)
}
}

+CharDriverState *qemu_chr_open_eventfd(int eventfd){
+
+ return qemu_chr_open_fd(eventfd, eventfd);
+
+}
+
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
diff --git a/qemu-char.h b/qemu-char.h
index bcc0766..9a0d2c0 100644
--- a/qemu-char.h
+++ b/qemu-char.h
@@ -93,6 +93,9 @@ void qemu_chr_info_print(Monitor *mon, const QObject *ret_data);
void qemu_chr_info(Monitor *mon, QObject **ret_data);
CharDriverState *qemu_chr_find(const char *name);

+/* add an eventfd to the qemu devices that are polled */
+CharDriverState *qemu_chr_open_eventfd(int eventfd);
+
extern int term_escape_char;

/* async I/O support */
diff --git a/qemu-options.hx b/qemu-options.hx
index 9683d09..6a37083 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1679,6 +1679,18 @@ STEXI
Setup monitor on chardev @var{name}.
ETEXI

+DEF("ivshmem", HAS_ARG, QEMU_OPTION_ivshmem, \
+ "-ivshmem size,unix:file connects to shared memory PCI card server \
+ listening on unix domain socket 'path' of at least 'size' (in MB) and \
+ exposes as a PCI device in the guest\n")
+STEXI
+@item -ivshmem @var{size},unix:@var{file}
+Connects to a shared memory PCI server at UDS @var{file} that shares a POSIX
+shared object of size @var{size} and creates a PCI device of the same size that
+maps the shared object (received from the server) into the device for guests to
+access. The servers also sends eventfds to the guest to support interrupts.
+ETEXI
+
DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \
"-debugcon dev redirect the debug console to char device 'dev'\n")
STEXI
diff --git a/sysemu.h b/sysemu.h
index 01405e1..ace4645 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -241,6 +241,14 @@ extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];

extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];

+/* inter-VM shared memory devices */
+
+#define MAX_IVSHMEM_DEVICES 1
+
+extern CharDriverState * ivshmem_chardev;
+void ivshmem_init(const char * optarg);
+int ivshmem_get_size(void);
+
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)

#ifdef HAS_AUDIO
diff --git a/vl.c b/vl.c
index 8847b70..01ca13b 100644
--- a/vl.c
+++ b/vl.c
@@ -194,6 +194,7 @@ int autostart;
static int rtc_utc = 1;
static int rtc_date_offset = -1; /* -1 means no change */
QEMUClock *rtc_clock;
+int ivshmem_enabled = 0;
int vga_interface_type = VGA_NONE;
#ifdef TARGET_SPARC
int graphic_width = 1024;
@@ -212,6 +213,8 @@ int no_quit = 0;
CharDriverState *serial_hds[MAX_SERIAL_PORTS];
CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+CharDriverState *ivshmem_chardev;
+const char * ivshmem_device;
#ifdef TARGET_I386
int win2k_install_hack = 0;
int rtc_td_hack = 0;
@@ -4953,6 +4956,8 @@ int main(int argc, char **argv, char **envp)
kernel_cmdline = "";
cyls = heads = secs = 0;
translation = BIOS_ATA_TRANSLATION_AUTO;
+ ivshmem_device = NULL;
+ ivshmem_chardev = NULL;

for (i = 0; i < MAX_NODES; i++) {
node_mem[i] = 0;
@@ -5439,6 +5444,10 @@ int main(int argc, char **argv, char **envp)
add_device_config(DEV_PARALLEL, optarg);
default_parallel = 0;
break;
+ case QEMU_OPTION_ivshmem:
+ ivshmem_device = optarg;
+ ivshmem_enabled = 1;
+ break;
case QEMU_OPTION_debugcon:
add_device_config(DEV_DEBUGCON, optarg);
break;
@@ -5956,6 +5965,10 @@ int main(int argc, char **argv, char **envp)
if (ram_size == 0)
ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;

+ if (ivshmem_enabled) {
+ ivshmem_init(ivshmem_device);
+ }
+
/* init the dynamic translator */
cpu_exec_init_all(tb_size * 1024 * 1024);

--
1.6.0.6