diff mbox

[2/5] virtio-console: Add support for multiple ports for generic guest-host communication

Message ID 1252684353-25067-3-git-send-email-amit.shah@redhat.com
State Superseded
Headers show

Commit Message

Amit Shah Sept. 11, 2009, 3:52 p.m. UTC
This interface extends the virtio-console device to handle
multiple ports that present a char device from which bits can
be sent and read.

Sample uses for such a device can be obtaining info from the
guest like the file systems used, apps installed, etc. for
offline usage and logged-in users, clipboard copy-paste, etc.
for online usage.

Each port is to be assigned a unique function, for example, the
first 4 ports may be reserved for libvirt usage, the next 4 for
generic streaming data and so on. This port-function mapping
isn't finalised yet.

For requirements, use-cases and some history see

    http://www.linux-kvm.org/page/VMchannel_Requirements

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 hw/pc.c             |   16 +-
 hw/virtio-console.c |  691 +++++++++++++++++++++++++++++++++++++++++++++++----
 hw/virtio-console.h |   55 ++++
 monitor.c           |    7 +
 qemu-monitor.hx     |   10 +
 qemu-options.hx     |    2 +-
 sysemu.h            |   10 +-
 vl.c                |   41 ++--
 8 files changed, 754 insertions(+), 78 deletions(-)

Comments

Anthony Liguori Sept. 11, 2009, 4:17 p.m. UTC | #1
Amit Shah wrote:
> This interface extends the virtio-console device to handle
> multiple ports that present a char device from which bits can
> be sent and read.
>
> Sample uses for such a device can be obtaining info from the
> guest like the file systems used, apps installed, etc. for
> offline usage and logged-in users, clipboard copy-paste, etc.
> for online usage.
>
> Each port is to be assigned a unique function, for example, the
> first 4 ports may be reserved for libvirt usage, the next 4 for
> generic streaming data and so on. This port-function mapping
> isn't finalised yet.
>
>   

I thought the consensus was that we wanted strings to identify ports 
such that a scheme like reverse fqdn could be used?

> diff --git a/hw/pc.c b/hw/pc.c
> index d96d756..d5d2542 100644
> --- a/hw/pc.c
> +++ b/hw/pc.c
> @@ -1422,11 +1422,17 @@ static void pc_init1(ram_addr_t ram_size,
>      }
>  
>      /* Add virtio console devices */
> -    if (pci_enabled) {
> -        for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
> -            if (virtcon_hds[i]) {
> -                pci_create_simple(pci_bus, -1, "virtio-console-pci");
> -            }
> +    if (pci_enabled && virtcon_nr_ports) {
> +        void *dev;
> +
> +        dev = pci_create_simple(pci_bus, -1, "virtio-console-pci");
> +        if (!dev) {
> +            fprintf(stderr, "qemu: could not create virtio console pci device\n");
> +            exit(1);
> +        }
> +
> +        for (i = 0; i < virtcon_nr_ports; i++) {
> +                virtio_console_new_port(dev, virtcon_idx[i]);
>          }
>      }
>  }
>   

If a user does:

qemu -M pc-0.11.0 -virtiocon vc -virtiocon vc

This patch will break that guest.  I think the best solution to this is 
to properly integrate with qdev and the new chardev infrastructure.  
virtio-console-pci-0.11 should have the old semantics, 
virtio-console-pci-0.12 should be a bus.

>  #include "hw.h"
> +#include "monitor.h"
> +#include "pci.h"
>   

We don't want to add PCI dependency to virtio console.  It isn't always 
used on platforms with PCI.

> +static VirtIOConsole *virtio_console;
> +static struct virtio_console_config virtcon_config;
> +static VirtIOConsolePort virtcon_ports[MAX_VIRTIO_CONSOLE_PORTS]
>   

Introducing statics like this indicates something isn't right.  Again, I 
think a proper qdev bus conversion would take care of that.

> +/* This function gets called from vl.c during initial options
> + * parsing as well as from the monitor to parse the options.
> + * So it's a good idea to not print out anything and just
> + * return values which can become meaningful.
> + */
> +int init_virtio_console_port(int port, const char *opts)
> +{
> +    char dev[256];
> +    const char *prot;
> +    const char *idx;
> +    uint32_t port_nr;
> +    int j, k;
> +
> +    memset(dev, 0, sizeof(dev));
> +    prot = strstr(opts, ",protocol=");
> +    idx  = strstr(opts, ",port=");
>   

Need to integrate with QemuOpts.

> +
> +    register_savevm("virtio-console", -1, 2, virtio_console_save, virtio_console_load, s);
>   

Should integrate with VMState.

Regards,

Anthony Liguori
Amit Shah Sept. 11, 2009, 4:34 p.m. UTC | #2
On (Fri) Sep 11 2009 [11:17:23], Anthony Liguori wrote:
> Amit Shah wrote:
>> This interface extends the virtio-console device to handle
>> multiple ports that present a char device from which bits can
>> be sent and read.
>>
>> Sample uses for such a device can be obtaining info from the
>> guest like the file systems used, apps installed, etc. for
>> offline usage and logged-in users, clipboard copy-paste, etc.
>> for online usage.
>>
>> Each port is to be assigned a unique function, for example, the
>> first 4 ports may be reserved for libvirt usage, the next 4 for
>> generic streaming data and so on. This port-function mapping
>> isn't finalised yet.
>>
>>   
>
> I thought the consensus was that we wanted strings to identify ports  
> such that a scheme like reverse fqdn could be used?

There wasn't any consensus; the discussion just ended abruptly.

That said, I'll have to revisit the discussion to see what the fqdn idea
was about.

That, or some other alternative will have to be used.

>> diff --git a/hw/pc.c b/hw/pc.c
>> index d96d756..d5d2542 100644
>> --- a/hw/pc.c
>> +++ b/hw/pc.c
>> @@ -1422,11 +1422,17 @@ static void pc_init1(ram_addr_t ram_size,
>>      }
>>       /* Add virtio console devices */
>> -    if (pci_enabled) {
>> -        for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
>> -            if (virtcon_hds[i]) {
>> -                pci_create_simple(pci_bus, -1, "virtio-console-pci");
>> -            }
>> +    if (pci_enabled && virtcon_nr_ports) {
>> +        void *dev;
>> +
>> +        dev = pci_create_simple(pci_bus, -1, "virtio-console-pci");
>> +        if (!dev) {
>> +            fprintf(stderr, "qemu: could not create virtio console pci device\n");
>> +            exit(1);
>> +        }
>> +
>> +        for (i = 0; i < virtcon_nr_ports; i++) {
>> +                virtio_console_new_port(dev, virtcon_idx[i]);
>>          }
>>      }
>>  }
>>   
>
> If a user does:
>
> qemu -M pc-0.11.0 -virtiocon vc -virtiocon vc
>
> This patch will break that guest.  I think the best solution to this is  

If there are multiple virtiocon devices specified, the first one will
default to port #0. The second one will error out saying port 0 is
taken. Isn't different from the current behaviour though.

> to properly integrate with qdev and the new chardev infrastructure.   
> virtio-console-pci-0.11 should have the old semantics,  
> virtio-console-pci-0.12 should be a bus.
>
>>  #include "hw.h"
>> +#include "monitor.h"
>> +#include "pci.h"
>>   
>
> We don't want to add PCI dependency to virtio console.  It isn't always  
> used on platforms with PCI.

OK; there's a place where I need the qdev pointer from the PCIDevice.
Any other way of obtaining that?

>> +static VirtIOConsole *virtio_console;
>> +static struct virtio_console_config virtcon_config;
>> +static VirtIOConsolePort virtcon_ports[MAX_VIRTIO_CONSOLE_PORTS]
>>   
>
> Introducing statics like this indicates something isn't right.  Again, I  
> think a proper qdev bus conversion would take care of that.
>
>> +/* This function gets called from vl.c during initial options
>> + * parsing as well as from the monitor to parse the options.
>> + * So it's a good idea to not print out anything and just
>> + * return values which can become meaningful.
>> + */
>> +int init_virtio_console_port(int port, const char *opts)
>> +{
>> +    char dev[256];
>> +    const char *prot;
>> +    const char *idx;
>> +    uint32_t port_nr;
>> +    int j, k;
>> +
>> +    memset(dev, 0, sizeof(dev));
>> +    prot = strstr(opts, ",protocol=");
>> +    idx  = strstr(opts, ",port=");
>>   
>
> Need to integrate with QemuOpts.

Wondering if it already entered master.. I'll sync up with kraxel if
not.

>> +
>> +    register_savevm("virtio-console", -1, 2, virtio_console_save, virtio_console_load, s);
>>   
>
> Should integrate with VMState.

I don't think virtio devices have been converted yet.

		Amit
Anthony Liguori Sept. 11, 2009, 4:38 p.m. UTC | #3
Amit Shah wrote:
> There wasn't any consensus; the discussion just ended abruptly.
>   

At least Gerd and I were in violent agreement.  I don't think anyone 
disagreed.

>> If a user does:
>>
>> qemu -M pc-0.11.0 -virtiocon vc -virtiocon vc
>>
>> This patch will break that guest.  I think the best solution to this is  
>>     
>
> If there are multiple virtiocon devices specified, the first one will
> default to port #0. The second one will error out saying port 0 is
> taken. Isn't different from the current behaviour though.
>   

But that's a problem for -M pc-0.11.0.  We need to have a way to specify 
a command line syntax with -M pc-0.11.0 such that the machine created 
matches what would have been created with qemu-0.11.0.  Otherwise, we 
have no hope of being able to do live migration or save/restore between 
versions.

  

>> We don't want to add PCI dependency to virtio console.  It isn't always  
>> used on platforms with PCI.
>>     
>
> OK; there's a place where I need the qdev pointer from the PCIDevice.
> Any other way of obtaining that?
>   

Why do you need the qdev pointer?


>> Need to integrate with QemuOpts.
>>     
>
> Wondering if it already entered master.. I'll sync up with kraxel if
> not.
>
>   

Yup, it's been there for a while.

>>> +
>>> +    register_savevm("virtio-console", -1, 2, virtio_console_save, virtio_console_load, s);
>>>   
>>>       
>> Should integrate with VMState.
>>     
>
> I don't think virtio devices have been converted yet.
>   

I think that's next on Juan's list.

Regards,

Anthony Liguori
Amit Shah Sept. 11, 2009, 5:30 p.m. UTC | #4
On (Fri) Sep 11 2009 [11:38:36], Anthony Liguori wrote:
> Amit Shah wrote:
>> There wasn't any consensus; the discussion just ended abruptly.
>>   
>
> At least Gerd and I were in violent agreement.  I don't think anyone  
> disagreed.

So how's suggestion about udev making symlinks based on the ports ids?

>>> If a user does:
>>>
>>> qemu -M pc-0.11.0 -virtiocon vc -virtiocon vc
>>>
>>> This patch will break that guest.  I think the best solution to this 
>>> is      
>>
>> If there are multiple virtiocon devices specified, the first one will
>> default to port #0. The second one will error out saying port 0 is
>> taken. Isn't different from the current behaviour though.
>>   
>
> But that's a problem for -M pc-0.11.0.  We need to have a way to specify  
> a command line syntax with -M pc-0.11.0 such that the machine created  
> matches what would have been created with qemu-0.11.0.  Otherwise, we  
> have no hope of being able to do live migration or save/restore between  
> versions.

I don't actually know if I understand this completely. However, with the
current behaviour, qemu just exits saying something like too many virtio
consoles since the current MAX_VIRTIO_CONSOLES is defined to 1.

>>> We don't want to add PCI dependency to virtio console.  It isn't 
>>> always  used on platforms with PCI.
>>>     
>>
>> OK; there's a place where I need the qdev pointer from the PCIDevice.
>> Any other way of obtaining that?
>>   
>
> Why do you need the qdev pointer?

To init a chardev in the port hotplug case, qdev_init_chardev() needs
the qdev pointer.

>>> Need to integrate with QemuOpts.
>>
>> Wondering if it already entered master.. I'll sync up with kraxel if
>> not.
>
> Yup, it's been there for a while.

I remembered seeing it in your queue. Anyway it's simple enough and I'll
do it.

>>>> +    register_savevm("virtio-console", -1, 2, virtio_console_save, virtio_console_load, s);
>>>>         
>>> Should integrate with VMState.
>>>     
>>
>> I don't think virtio devices have been converted yet.
>>   
>
> I think that's next on Juan's list.

Right; so we'll talk about that.

		Amit
Anthony Liguori Sept. 11, 2009, 5:34 p.m. UTC | #5
Amit Shah wrote:
> On (Fri) Sep 11 2009 [11:38:36], Anthony Liguori wrote:
>   
>> Amit Shah wrote:
>>     
>>> There wasn't any consensus; the discussion just ended abruptly.
>>>   
>>>       
>> At least Gerd and I were in violent agreement.  I don't think anyone  
>> disagreed.
>>     
>
> So how's suggestion about udev making symlinks based on the ports ids?
>   

That doesn't solve the problem that fqdn solves.  fqdn provides a means 
to avoid having to do centralized port number allocation.

>>  that's a problem for -M pc-0.11.0.  We need to have a way to specify  
>> a command line syntax with -M pc-0.11.0 such that the machine created  
>> matches what would have been created with qemu-0.11.0.  Otherwise, we  
>> have no hope of being able to do live migration or save/restore between  
>> versions.
>>     
>
> I don't actually know if I understand this completely. However, with the
> current behaviour, qemu just exits saying something like too many virtio
> consoles since the current MAX_VIRTIO_CONSOLES is defined to 1.
>   

Oh, that's goofy.  Sorry, I didn't realize that.

>> Why do you need the qdev pointer?
>>     
>
> To init a chardev in the port hotplug case, qdev_init_chardev() needs
> the qdev pointer.
>   

If you converted to a bus, this problem would go away.

>> I think that's next on Juan's list.
>>     
>
> Right; so we'll talk about that.
>
> 		Amit
>   

Regards,

Anthony Liguori
Amit Shah Sept. 14, 2009, 7:48 a.m. UTC | #6
Hey,

Just a couple of points here:


On (Fri) Sep 11 2009 [11:38:36], Anthony Liguori wrote:
> Amit Shah wrote:
>> There wasn't any consensus; the discussion just ended abruptly.
>>   
>
> At least Gerd and I were in violent agreement.  I don't think anyone  
> disagreed.

I think Gerd was ok with the idea of a symlink and Rusty and I kept
saying the port number idea isn't as bad. That does count as a
disagreement :-)

>>> Need to integrate with QemuOpts.
>>>     
>>
>> Wondering if it already entered master.. I'll sync up with kraxel if
>> not.
>>
>>   
>
> Yup, it's been there for a while.

Just checked; it's in your queue but not in master.

		Amit
Amit Shah Sept. 14, 2009, 7:55 a.m. UTC | #7
On (Mon) Sep 14 2009 [13:18:59], Amit Shah wrote:
> Hey,
> 
> Just a couple of points here:
> 
> 
> On (Fri) Sep 11 2009 [11:38:36], Anthony Liguori wrote:
> > Amit Shah wrote:
> >> There wasn't any consensus; the discussion just ended abruptly.
> >>   
> >
> > At least Gerd and I were in violent agreement.  I don't think anyone  
> > disagreed.
> 
> I think Gerd was ok with the idea of a symlink and Rusty and I kept
> saying the port number idea isn't as bad. That does count as a
> disagreement :-)
> 
> >>> Need to integrate with QemuOpts.
> >>>     
> >>
> >> Wondering if it already entered master.. I'll sync up with kraxel if
> >> not.
> >>
> >>   
> >
> > Yup, it's been there for a while.
> 
> Just checked; it's in your queue but not in master.

Well it shows up in master now. Commit logs show it got pushed on
Friday.

		Amit
Anthony Liguori Sept. 14, 2009, 12:55 p.m. UTC | #8
Amit Shah wrote:
>
> Well it shows up in master now. Commit logs show it got pushed on
> Friday.
>   
commit e27c88fe9eb26648e4fb282cb3761c41f06ff18a
Author: Gerd Hoffmann <kraxel@redhat.com>
Date:   Wed Jul 22 16:43:03 2009 +0200

    QemuOpts: framework for storing and parsing options.

Regards,

Anthony Liguori
Amit Shah Sept. 14, 2009, 12:58 p.m. UTC | #9
On (Mon) Sep 14 2009 [07:55:40], Anthony Liguori wrote:
> Amit Shah wrote:
>>
>> Well it shows up in master now. Commit logs show it got pushed on
>> Friday.
>>   
> commit e27c88fe9eb26648e4fb282cb3761c41f06ff18a
> Author: Gerd Hoffmann <kraxel@redhat.com>
> Date:   Wed Jul 22 16:43:03 2009 +0200
>
>    QemuOpts: framework for storing and parsing options.

Oh I was talking about the chardev conversion; most of this depends on
that ;-)

		Amit
diff mbox

Patch

diff --git a/hw/pc.c b/hw/pc.c
index d96d756..d5d2542 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1422,11 +1422,17 @@  static void pc_init1(ram_addr_t ram_size,
     }
 
     /* Add virtio console devices */
-    if (pci_enabled) {
-        for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
-            if (virtcon_hds[i]) {
-                pci_create_simple(pci_bus, -1, "virtio-console-pci");
-            }
+    if (pci_enabled && virtcon_nr_ports) {
+        void *dev;
+
+        dev = pci_create_simple(pci_bus, -1, "virtio-console-pci");
+        if (!dev) {
+            fprintf(stderr, "qemu: could not create virtio console pci device\n");
+            exit(1);
+        }
+
+        for (i = 0; i < virtcon_nr_ports; i++) {
+                virtio_console_new_port(dev, virtcon_idx[i]);
         }
     }
 }
diff --git a/hw/virtio-console.c b/hw/virtio-console.c
index 57f8f89..9fa35ec 100644
--- a/hw/virtio-console.c
+++ b/hw/virtio-console.c
@@ -2,9 +2,11 @@ 
  * Virtio Console Device
  *
  * Copyright IBM, Corp. 2008
+ * Copyright Red Hat, Inc. 2009
  *
  * Authors:
  *  Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *  Amit Shah <amit.shah@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -12,39 +14,262 @@ 
  */
 
 #include "hw.h"
+#include "monitor.h"
+#include "pci.h"
+#include "sys-queue.h"
 #include "qemu-char.h"
 #include "virtio.h"
 #include "virtio-console.h"
 
-
 typedef struct VirtIOConsole
 {
     VirtIODevice vdev;
     VirtQueue *ivq, *ovq;
-    CharDriverState *chr;
+    struct VirtIOConsolePort *ports;
+    struct virtio_console_config *config;
+    uint32_t guest_features;
 } VirtIOConsole;
 
-static VirtIOConsole *to_virtio_console(VirtIODevice *vdev)
+/* This struct holds individual buffers received for each port */
+typedef struct VirtIOConsolePortBuffer {
+    TAILQ_ENTRY(VirtIOConsolePortBuffer) next;
+
+    uint8_t *buf;
+
+    size_t len; /* length of the buffer */
+
+    /* The size of one write request as issued by the guest. The
+     * buffer could be split in this list but using the size value in
+     * the first buffer for each write we can identify complete
+     * writes
+     */
+    size_t size;
+} VirtIOConsolePortBuffer;
+
+struct VirtIOConsolePort {
+    VirtIOConsole *vcon;
+    CharDriverState *hd;
+
+    TAILQ_HEAD(, VirtIOConsolePortBuffer) unflushed_buffer_head;
+
+    bool guest_connected;
+    bool host_connected;
+};
+
+static VirtIOConsole *virtio_console;
+static struct virtio_console_config virtcon_config;
+static VirtIOConsolePort virtcon_ports[MAX_VIRTIO_CONSOLE_PORTS];
+
+static VirtIOConsolePort *get_port_from_id(uint32_t id)
+{
+    if (id > MAX_VIRTIO_CONSOLE_PORTS)
+        return NULL;
+
+    return &virtcon_ports[id];
+}
+
+static int get_id_from_port(VirtIOConsolePort *port)
+{
+    uint32_t i;
+
+    for (i = 0; i < MAX_VIRTIO_CONSOLE_PORTS; i++) {
+        if (port == &virtcon_ports[i]) {
+            return i;
+        }
+    }
+    return VIRTIO_CONSOLE_BAD_ID;
+}
+
+static bool use_multiport(void)
+{
+    return virtio_console->guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT);
+}
+
+static bool is_internal(uint32_t flags)
+{
+    return flags & VIRTIO_CONSOLE_ID_INTERNAL;
+}
+
+static bool is_console(uint32_t port_nr)
+{
+    if (port_nr == VIRTIO_CONSOLE_CONSOLE_PORT ||
+        port_nr == VIRTIO_CONSOLE_CONSOLE2_PORT)
+        return true;
+    return false;
+}
+
+void virtio_console_monitor_command(Monitor *mon,
+                                    const char *command, const char *param)
+{
+    int ret;
+
+    if(!strncmp(command, "add_port", 8)) {
+        if (!param) {
+            monitor_printf(mon, "Error: need port id to add new port\n");
+            return;
+        }
+        ret = init_virtio_console_port(virtcon_nr_ports, param);
+        if (ret < 0) {
+            monitor_printf(mon, "Error: cannot add new port: %s\n",
+                           strerror(-ret));
+            return;
+        }
+        virtio_console_new_port(NULL, virtcon_idx[virtcon_nr_ports]);
+        virtcon_nr_ports++;
+        virtio_console->config->nr_active_ports = cpu_to_le32(virtcon_nr_ports);
+        return;
+    }
+}
+
+static bool has_complete_data(VirtIOConsolePort *port)
+{
+    VirtIOConsolePortBuffer *buf;
+    size_t len, size;
+
+    len = 0;
+    size = 0;
+    TAILQ_FOREACH(buf, &port->unflushed_buffer_head, next) {
+        if (!buf->size && buf == TAILQ_FIRST(&port->unflushed_buffer_head)) {
+            /* We have a buffer that's lost its way; just flush it */
+            return true;
+        }
+        if (size && buf->size) {
+            /* Start of the next write request */
+            return true;
+        }
+        if (buf->size) {
+            size = buf->size;
+        }
+        len += buf->len;
+        if (len == size) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static size_t flush_buf(VirtIOConsolePort *port, const uint8_t *buf, size_t len)
 {
-    return (VirtIOConsole *)vdev;
+    if (!port->hd) {
+        return 0;
+    }
+    return qemu_chr_write(port->hd, buf, len);
 }
 
+static void flush_queue(VirtIOConsolePort *port)
+{
+    VirtIOConsolePortBuffer *buf, *buf2;
+    uint8_t *outbuf;
+    size_t outlen;
+
+    while (!TAILQ_EMPTY(&port->unflushed_buffer_head)) {
+        if (!has_complete_data(port)) {
+            break;
+        }
+
+        buf = TAILQ_FIRST(&port->unflushed_buffer_head);
+        if (!buf->size) {
+            /* This is a buf that didn't get consumed as part of a
+             * previous data stream. Bad thing, shouldn't
+             * happen. But let's handle it nonetheless
+             */
+            flush_buf(port, buf->buf, buf->len);
+            TAILQ_REMOVE(&port->unflushed_buffer_head, buf, next);
+            continue;
+        }
+
+        outlen = 0;
+        outbuf = qemu_mallocz(buf->size);
+        TAILQ_FOREACH_SAFE(buf, &port->unflushed_buffer_head, next, buf2) {
+            memcpy(outbuf + outlen, buf->buf, buf->len);
+            outlen += buf->len;
+            TAILQ_REMOVE(&port->unflushed_buffer_head, buf, next);
+        }
+        flush_buf(port, outbuf, outlen);
+        qemu_free(outbuf);
+    }
+}
+
+/* Guest wants to notify us of some event */
+static void handle_control_message(VirtIOConsolePort *port,
+                                   struct virtio_console_control *cpkt)
+{
+    switch(cpkt->event) {
+    case VIRTIO_CONSOLE_PORT_OPEN:
+        port->guest_connected = cpkt->value;
+        break;
+    }
+}
+
+/* Guest wrote something to some port.
+ *
+ * Flush the data in the entire chunk that we received rather than
+ * splitting it into multiple buffers. VNC clients don't consume split
+ * buffers
+ */
 static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq)
 {
-    VirtIOConsole *s = to_virtio_console(vdev);
     VirtQueueElement elem;
 
     while (virtqueue_pop(vq, &elem)) {
-        ssize_t len = 0;
-        int d;
+        VirtIOConsolePort *port;
+        VirtIOConsolePortBuffer *buf;
+        struct virtio_console_header header;
+        int header_len;
+
+        buf = qemu_mallocz(sizeof(*buf));
+
+        if (use_multiport()) {
+            header_len = sizeof(header);
+
+            memcpy(&header, elem.out_sg[0].iov_base, header_len);
+            port = get_port_from_id(header.id);
+            if (!port) {
+                qemu_free(buf);
+                goto next_buf;
+            }
+        } else {
+            header_len = 0;
+            port = get_port_from_id(0);
+        }
+
+        /* The guest always sends only one sg */
+        buf->len = elem.out_sg[0].iov_len - header_len;
+        buf->buf = qemu_mallocz(buf->len);
+        memcpy(buf->buf, elem.out_sg[0].iov_base + header_len, buf->len);
 
-        for (d = 0; d < elem.out_num; d++) {
-            len += qemu_chr_write(s->chr, (uint8_t *)elem.out_sg[d].iov_base,
-                                  elem.out_sg[d].iov_len);
+        if (use_multiport() && is_internal(header.flags)) {
+            handle_control_message(port,
+                                   (struct virtio_console_control *)buf->buf);
+            qemu_free(buf->buf);
+            qemu_free(buf);
+            goto next_buf;
+        }
+	if (!use_multiport() || is_console(get_id_from_port(port))) {
+            flush_buf(port, buf->buf, buf->len);
+            qemu_free(buf->buf);
+            qemu_free(buf);
+            goto next_buf;
+        }
+        TAILQ_INSERT_TAIL(&port->unflushed_buffer_head, buf, next);
+        if (use_multiport()) {
+            /* Only the first buffer in a stream will have this
+             * set. This will help us identify the first buffer and
+             * the remaining buffers in the stream based on length
+             */
+            buf->size = header.size;
+        } else {
+            /* We always want to flush all the buffers in this case */
+            buf->size = buf->len;
         }
-        virtqueue_push(vq, &elem, len);
-        virtio_notify(vdev, vq);
+        if (!port->host_connected) {
+            goto next_buf;
+        }
+        flush_queue(port);
+    next_buf:
+        virtqueue_push(vq, &elem, elem.out_sg[0].iov_len);
     }
+    virtio_notify(vdev, vq);
 }
 
 static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)
@@ -53,91 +278,459 @@  static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)
 
 static uint32_t virtio_console_get_features(VirtIODevice *vdev)
 {
-    return 0;
+    return 1 << VIRTIO_CONSOLE_F_MULTIPORT;
 }
 
-static int vcon_can_read(void *opaque)
+static void virtio_console_set_features(VirtIODevice *vdev, uint32_t features)
 {
-    VirtIOConsole *s = (VirtIOConsole *) opaque;
+    virtio_console->guest_features = features;
+}
 
-    if (!virtio_queue_ready(s->ivq) ||
-        !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
-        virtio_queue_empty(s->ivq))
-        return 0;
+/* Guest requested config info */
+static void virtio_console_get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+    memcpy(config_data, &virtcon_config, sizeof(struct virtio_console_config));
+}
 
-    /* current implementations have a page sized buffer.
-     * We fall back to a one byte per read if there is not enough room.
-     * It would be cool to have a function that returns the available byte
-     * instead of checking for a limit */
-    if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0))
-        return TARGET_PAGE_SIZE;
-    if (virtqueue_avail_bytes(s->ivq, 1, 0))
-        return 1;
-    return 0;
+static void virtio_console_set_config(VirtIODevice *vdev,
+                                      const uint8_t *config_data)
+{
+    struct virtio_console_config config;
+
+    memcpy(&config, config_data, sizeof(config));
 }
 
-static void vcon_read(void *opaque, const uint8_t *buf, int size)
+static size_t write_to_port(VirtIOConsolePort *port,
+                            const uint8_t *buf, size_t size, uint32_t flags)
 {
-    VirtIOConsole *s = (VirtIOConsole *) opaque;
+    VirtQueue *vq = port->vcon->ivq;
     VirtQueueElement elem;
-    int offset = 0;
+    size_t offset = 0;
+    size_t len = 0;
+
+    if (!virtio_queue_ready(vq)) {
+        return 0;
+    }
+
+    if (!use_multiport() && is_internal(flags)) {
+        return 0;
+    }
 
-    /* The current kernel implementation has only one outstanding input
-     * buffer of PAGE_SIZE. Nevertheless, this function is prepared to
-     * handle multiple buffers with multiple sg element for input */
     while (offset < size) {
-        int i = 0;
-        if (!virtqueue_pop(s->ivq, &elem))
-                break;
-        while (offset < size && i < elem.in_num) {
-            int len = MIN(elem.in_sg[i].iov_len, size - offset);
-            memcpy(elem.in_sg[i].iov_base, buf + offset, len);
+        struct virtio_console_header header;
+        int i, header_len;
+
+        header_len = use_multiport() ? sizeof(header) : 0;
+
+        if (!virtqueue_pop(vq, &elem)) {
+            break;
+        }
+        if (elem.in_sg[0].iov_len < header_len) {
+            /* We can't even store our port number in this buffer. Bug? */
+            fprintf(stderr, "virtio-console: size %zd less than expected\n",
+                    elem.in_sg[0].iov_len);
+            exit(1);
+        }
+        header.id = cpu_to_le32(get_id_from_port(port));
+        header.flags = flags;
+        memcpy(elem.in_sg[0].iov_base, &header, header_len);
+
+        for (i = 0; offset < size && i < elem.in_num; i++) {
+            len = MIN(elem.in_sg[i].iov_len - header_len, size - offset);
+
+            memcpy(elem.in_sg[i].iov_base + header_len, buf + offset, len);
             offset += len;
-            i++;
+            header_len = 0;
         }
-        virtqueue_push(s->ivq, &elem, size);
+        header_len = use_multiport() ? sizeof(header) : 0;
+        virtqueue_push(vq, &elem, len + header_len);
     }
-    virtio_notify(&s->vdev, s->ivq);
+    virtio_notify(&port->vcon->vdev, vq);
+    return offset;
+}
+
+static void send_control_event(VirtIOConsolePort *port,
+                               struct virtio_console_control *cpkt)
+{
+    write_to_port(port, (uint8_t *)cpkt, sizeof(*cpkt),
+                  VIRTIO_CONSOLE_ID_INTERNAL);
+}
+
+/* Readiness of the guest to accept data on a port */
+static int vcon_can_read(void *opaque)
+{
+    VirtIOConsolePort *port = opaque;
+    VirtQueue *vq = port->vcon->ivq;
+    int size, header_len;
+
+    if (use_multiport()) {
+        header_len = sizeof(struct virtio_console_header);
+    } else {
+        header_len = 0;
+    }
+
+    if (!virtio_queue_ready(vq) ||
+        !(port->vcon->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
+        virtio_queue_empty(vq)) {
+        return 0;
+    }
+    if (!port->guest_connected) {
+        return 0;
+    }
+    size = TARGET_PAGE_SIZE;
+    if (virtqueue_avail_bytes(vq, size, 0)) {
+        return size - header_len;
+    }
+    size = header_len + 1;
+    if (virtqueue_avail_bytes(vq, size, 0)) {
+        return size - header_len;
+    }
+    return 0;
+}
+
+/* Send data from a char device over to the guest */
+static void vcon_read(void *opaque, const uint8_t *buf, int size)
+{
+    VirtIOConsolePort *port = opaque;
+
+    write_to_port(port, buf, size, 0);
 }
 
 static void vcon_event(void *opaque, int event)
 {
-    /* we will ignore any event for the time being */
+    VirtIOConsolePort *port = opaque;
+    struct virtio_console_control cpkt;
+    bool update_needed;
+
+    cpkt.event = VIRTIO_CONSOLE_PORT_OPEN;
+
+    update_needed = false;
+    switch (event) {
+    case CHR_EVENT_OPENED: {
+        cpkt.value = 1;
+        update_needed = true;
+        port->host_connected = true;
+
+        /* Flush any buffers that were pending while the chardev was
+         * disconnected
+         */
+        flush_queue(port);
+        break;
+    }
+    case CHR_EVENT_CLOSED:
+        cpkt.value = 0;
+        update_needed = true;
+        port->host_connected = false;
+        break;
+    default:
+        break;
+    }
+    if (!update_needed) {
+        return;
+    }
+    send_control_event(port, &cpkt);
+}
+
+static void set_port_active(uint32_t *map, uint32_t idx)
+{
+    int i;
+
+    i = idx / 32;
+    idx %= 32;
+
+    map[i] |= 1U << idx;
+}
+
+static bool is_port_active(uint32_t *map, uint32_t idx)
+{
+    int i;
+
+    i = idx / 32;
+    idx %= 32;
+
+    return map[i] & (1U << idx);
+}
+
+/*
+ * Get the guest_connected status after a migration since it's all
+ * packed in the same way as ports_map in the config space is packed.
+ *
+ * return VIRTIO_CONSOLE_BAD_ID if nothing was found in the map, else
+ * return the port number whose guest was connected before the
+ * migration
+ */
+static uint32_t find_next_active_in_map(uint32_t *map, unsigned int i)
+{
+    uint32_t port_nr;
+
+    port_nr = ffs(*map);
+    if (!port_nr) {
+        return VIRTIO_CONSOLE_BAD_ID;
+    }
+    /* We used ffs above */
+    port_nr--;
+
+    *map &= ~(1U << port_nr);
+
+    port_nr += i * 32;
+    return port_nr;
+}
+
+/* This function gets called from vl.c during initial options
+ * parsing as well as from the monitor to parse the options.
+ * So it's a good idea to not print out anything and just
+ * return values which can become meaningful.
+ */
+int init_virtio_console_port(int port, const char *opts)
+{
+    char dev[256];
+    const char *prot;
+    const char *idx;
+    uint32_t port_nr;
+    int j, k;
+
+    memset(dev, 0, sizeof(dev));
+    prot = strstr(opts, ",protocol=");
+    idx  = strstr(opts, ",port=");
+
+    port_nr = VIRTIO_CONSOLE_CONSOLE_PORT;
+    if (idx) {
+        port_nr = atol(idx + 6); /* skip ',port=' */
+    }
+    if (port_nr >= MAX_VIRTIO_CONSOLE_PORTS) {
+        return -ENOSPC;
+    }
+    if (is_port_active(virtcon_config.ports_map, port_nr)) {
+        return -EEXIST;
+    }
+
+    /* Just to maintain compatibility with other qemu options,
+     * we have the format of
+     *
+     * -virtioconsole unix:/tmp/foo,protocol=bar,port=3
+     *
+     * so to parse the 'unix:', we have to do the following
+     */
+    j = k = 0;
+    while (opts[j] && &opts[j] != prot && &opts[j] != idx) {
+        dev[k++] = opts[j++];
+    }
+
+    if (dev[0] && strncmp(dev, "none", 4)) {
+        char label[32];
+        snprintf(label, sizeof(label), "virtcon%u", port_nr);
+        virtcon_hds[port] = qemu_chr_open(label, dev, NULL);
+        if (!virtcon_hds[port]) {
+            return -EIO;
+        }
+    }
+    virtcon_idx[port] = port_nr;
+    /* The guest never opens the 'vcon device; it instead uses hvc.
+     * To ensure reads don't block in vcons_can_read on guest_connected,
+     * set it here
+     */
+    if (is_console(port_nr)) {
+        virtcon_ports[port_nr].guest_connected = true;
+    }
+    set_port_active(virtcon_config.ports_map, port_nr);
+
+    return 0;
+}
+
+void *virtio_console_new_port(PCIDevice *dev, uint32_t idx)
+{
+    VirtIOConsolePort *port;
+
+    port = get_port_from_id(idx);
+    port->vcon = virtio_console;
+
+    /* Hot-adding ports to existing device */
+    if (!dev) {
+        dev = port->vcon->vdev.binding_opaque;
+    }
+    port->hd = qdev_init_chardev(&dev->qdev);
+    if (port->hd) {
+        qemu_chr_add_handlers(port->hd, vcon_can_read, vcon_read, vcon_event,
+                              port);
+    }
+    TAILQ_INIT(&port->unflushed_buffer_head);
+    /* Send an update to the guest about this new port added */
+    virtio_notify_config(&port->vcon->vdev);
+    return port;
 }
 
 static void virtio_console_save(QEMUFile *f, void *opaque)
 {
     VirtIOConsole *s = opaque;
+    uint32_t guest_connected_map[MAX_VIRTIO_CONSOLE_PORTS / 32];
+    unsigned int i, nr_bufs, max_ports;
 
+    /* The virtio device */
     virtio_save(&s->vdev, f);
+    /* The config space */
+    qemu_put_be16s(f, &virtcon_config.cols);
+    qemu_put_be16s(f, &virtcon_config.rows);
+    qemu_put_be32s(f, &virtcon_config.max_nr_ports);
+    qemu_put_be32s(f, &virtcon_config.nr_active_ports);
+
+    max_ports = le32_to_cpu(virtcon_config.max_nr_ports);
+
+    for (i = 0; i < max_ports / 32; i++) {
+        qemu_put_be32s(f, &virtcon_config.ports_map[i]);
+    }
+    /* Items in struct VirtIOConsole */
+    qemu_put_be32s(f, &s->guest_features);
+
+    /*
+     * Items in struct VirtIOConsolePort
+     *   guest_connected is a single bit in each port; pack all these
+     *   bits in uint32_t words and then send them across
+     */
+    for (i = 0; i < max_ports / 32; i++) {
+        guest_connected_map[i] = 0;
+    }
+    for (i = 0; i < max_ports; i++) {
+        if (virtcon_ports[i].guest_connected) {
+            set_port_active(guest_connected_map, i);
+        }
+    }
+    for (i = 0; i < max_ports / 32; i++) {
+        qemu_put_be32s(f, &guest_connected_map[i]);
+    }
+
+    /* All the pending buffers from active ports */
+    for (i = 0; i < max_ports; i++) {
+        VirtIOConsolePortBuffer *buf;
+
+        if (!is_port_active(virtcon_config.ports_map, i)) {
+            continue;
+        }
+        nr_bufs = 0;
+        TAILQ_FOREACH(buf, &virtcon_ports[i].unflushed_buffer_head, next) {
+            nr_bufs++;
+        }
+        /* First the port number, then the nr of bufs and then the bufs */
+        qemu_put_be32s(f, &i);
+        qemu_put_be32s(f, &nr_bufs);
+        if (!nr_bufs) {
+            continue;
+        }
+        TAILQ_FOREACH(buf, &virtcon_ports[i].unflushed_buffer_head, next) {
+            qemu_put_be64s(f, &buf->len);
+            qemu_put_be64s(f, &buf->size);
+            qemu_put_buffer(f, buf->buf, buf->len);
+        }
+    }
 }
 
 static int virtio_console_load(QEMUFile *f, void *opaque, int version_id)
 {
     VirtIOConsole *s = opaque;
+    uint32_t guest_connected_map[MAX_VIRTIO_CONSOLE_PORTS / 32];
+    unsigned int i, max_ports;
 
-    if (version_id != 1)
+    if (version_id > 2)
         return -EINVAL;
 
+    /* The virtio device */
     virtio_load(&s->vdev, f);
+
+    if (version_id < 2)
+        return 0;
+
+    /* The config space */
+    qemu_get_be16s(f, &virtcon_config.cols);
+    qemu_get_be16s(f, &virtcon_config.rows);
+    virtcon_config.max_nr_ports = cpu_to_le32(qemu_get_be32(f));
+    virtcon_config.nr_active_ports = cpu_to_le32(qemu_get_be32(f));
+
+    max_ports = le32_to_cpu(virtcon_config.max_nr_ports);
+
+    for (i = 0; i < max_ports / 32; i++) {
+        qemu_get_be32s(f, &virtcon_config.ports_map[i]);
+    }
+    /* Items in struct VirtIOConsole */
+    qemu_get_be32s(f, &virtio_console->guest_features);
+
+    /* Items in struct VirtIOConsolePort */
+    for (i = 0; i < max_ports / 32; i++) {
+        guest_connected_map[i] = qemu_get_be32(f);
+    }
+
+    for (i = 0; i < max_ports / 32; i++) {
+        uint32_t port_nr, map;
+
+        map = guest_connected_map[i];
+        while (1) {
+            port_nr = find_next_active_in_map(&map, i);
+            if (port_nr == VIRTIO_CONSOLE_BAD_ID) {
+                break;
+            }
+            virtcon_ports[port_nr].guest_connected = true;
+        }
+    }
+
+    /* All the pending buffers from active ports */
+    for (i = 0; i < virtcon_config.nr_active_ports; i++) {
+        VirtIOConsolePortBuffer *buf;
+        unsigned int nr, nr_bufs;
+
+        /* First the port number, then the nr of bufs and then the bufs */
+        qemu_get_be32s(f, &nr);
+        qemu_get_be32s(f, &nr_bufs);
+        if (!nr_bufs) {
+            continue;
+        }
+        for (; nr_bufs; nr_bufs--) {
+            buf = qemu_malloc(sizeof(*buf));
+
+            qemu_get_be64s(f, &buf->len);
+            qemu_get_be64s(f, &buf->size);
+            buf->buf = qemu_malloc(buf->len);
+            qemu_get_buffer(f, buf->buf, buf->len);
+            TAILQ_INSERT_TAIL(&virtcon_ports[nr].unflushed_buffer_head, buf,
+                              next);
+        }
+    }
+
     return 0;
 }
 
 VirtIODevice *virtio_console_init(DeviceState *dev)
 {
     VirtIOConsole *s;
+
+    if (MAX_VIRTIO_CONSOLE_PORTS % 32) {
+        /* We require MAX_VIRTIO_CONSOLE_PORTS be a multiple of 32:
+         * We anyway use up that much space for the bitmap and it
+         * simplifies some calculations
+         */
+        return NULL;
+    }
+
     s = (VirtIOConsole *)virtio_common_init("virtio-console",
                                             VIRTIO_ID_CONSOLE,
-                                            0, sizeof(VirtIOConsole));
+                                            sizeof(struct virtio_console_config),
+                                            sizeof(VirtIOConsole));
+
+    virtio_console = s;
     s->vdev.get_features = virtio_console_get_features;
+    s->vdev.set_features = virtio_console_set_features;
+    s->vdev.get_config = virtio_console_get_config;
+    s->vdev.set_config = virtio_console_set_config;
 
+    /* Add queue for host to guest transfers */
     s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
+    /* Add queue for guest to host transfers */
     s->ovq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
 
-    s->chr = qdev_init_chardev(dev);
-    qemu_chr_add_handlers(s->chr, vcon_can_read, vcon_read, vcon_event, s);
+    s->ports = virtcon_ports;
+    s->config = &virtcon_config;
+
+    register_savevm("virtio-console", -1, 2, virtio_console_save, virtio_console_load, s);
 
-    register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s);
+    virtcon_config.max_nr_ports = cpu_to_le32(MAX_VIRTIO_CONSOLE_PORTS);
+    virtcon_config.nr_active_ports = cpu_to_le32(virtcon_nr_ports);
 
     return &s->vdev;
 }
diff --git a/hw/virtio-console.h b/hw/virtio-console.h
index 84d0717..9bf6aaf 100644
--- a/hw/virtio-console.h
+++ b/hw/virtio-console.h
@@ -2,9 +2,11 @@ 
  * Virtio Console Support
  *
  * Copyright IBM, Corp. 2008
+ * Copyright Red Hat, Inc. 2009
  *
  * Authors:
  *  Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *  Amit Shah <amit.shah@redhat.com>
  *
  * This work is licensed under the terms of the GNU GPL, version 2.  See
  * the COPYING file in the top-level directory.
@@ -13,7 +15,60 @@ 
 #ifndef _QEMU_VIRTIO_CONSOLE_H
 #define _QEMU_VIRTIO_CONSOLE_H
 
+#include "sysemu.h"
+
+/* Interface shared between the guest kernel and qemu */
+
 /* The ID for virtio console */
 #define VIRTIO_ID_CONSOLE 3
 
+/* Invalid port number */
+#define VIRTIO_CONSOLE_BAD_ID		(~(uint32_t)0)
+
+/* Port number to function mapping */
+#define VIRTIO_CONSOLE_CONSOLE_PORT	0
+#define VIRTIO_CONSOLE_CONSOLE2_PORT	1
+
+/* Features supported */
+#define VIRTIO_CONSOLE_F_MULTIPORT	1
+
+struct virtio_console_config
+{
+    /*
+     * These two fields are used by VIRTIO_CONSOLE_F_SIZE which
+     * isn't implemented here yet
+     */
+    uint16_t cols;
+    uint16_t rows;
+
+    uint32_t max_nr_ports;
+    uint32_t nr_active_ports;
+    /* MAX_VIRTIO_CONSOLE_PORTS is always a multiple of 32 */
+    uint32_t ports_map[MAX_VIRTIO_CONSOLE_PORTS / 32];
+} __attribute__((packed));
+
+struct virtio_console_control
+{
+    uint16_t event;
+    uint16_t value;
+};
+
+struct virtio_console_header {
+    uint32_t id; /* Port id */
+    uint32_t flags; /* Some message between host and guest */
+    uint32_t size; /* Size that's sent with the first buffer of each stream */
+} __attribute__((packed));
+
+/* Messages between host and guest */
+#define VIRTIO_CONSOLE_ID_INTERNAL	(1 << 0)
+
+/* Some events for the internal messages (control packets) */
+#define VIRTIO_CONSOLE_PORT_OPEN	0
+
+
+/* In-qemu interface */
+typedef struct VirtIOConsolePort VirtIOConsolePort;
+void virtio_console_monitor_command(Monitor *mon,
+                                    const char *command, const char *param);
+
 #endif
diff --git a/monitor.c b/monitor.c
index f5f4d0e..1b9e2a3 100644
--- a/monitor.c
+++ b/monitor.c
@@ -47,6 +47,7 @@ 
 #include "qint.h"
 #include "qdict.h"
 #include "qstring.h"
+#include "hw/virtio-console.h"
 
 //#define DEBUG
 //#define DEBUG_COMPLETION
@@ -1800,6 +1801,12 @@  int monitor_get_fd(Monitor *mon, const char *fdname)
     return -1;
 }
 
+static void do_virtio_console_action(Monitor *mon,
+                                     const char *command, const char *param)
+{
+    virtio_console_monitor_command(mon, command, param);
+}
+
 static const mon_cmd_t mon_cmds[] = {
 #include "qemu-monitor.h"
     { NULL, NULL, },
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 9f91873..9d75f02 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -573,6 +573,16 @@  STEXI
 Change watchdog action.
 ETEXI
 
+    { "virtio-console", "ss?", do_virtio_console_action,
+      "<command> [<parameters>]\n",
+      "virtio-serial write port=3,key=get,value=clipboard\n"
+      "virtio-serial add_port\n"
+      "virtio-serial add_port port=6,name=foo,protocol=keyvalue\n" },
+STEXI
+@item virtio-console
+Hot-add ports or send data to virtio-console port
+ETEXI
+
     { "acl_show", "aclname:s", do_acl_show, "aclname",
       "list rules in the access control list" },
 STEXI
diff --git a/qemu-options.hx b/qemu-options.hx
index ce38a3b..7608577 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1603,7 +1603,7 @@  ETEXI
 
 DEF("virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon, \
     "-virtioconsole c\n" \
-    "                set virtio console\n")
+    "                define virtio console\n")
 STEXI
 @item -virtioconsole @var{c}
 Set virtio console.
diff --git a/sysemu.h b/sysemu.h
index ac16c21..8eea3ab 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -2,6 +2,7 @@ 
 #define SYSEMU_H
 /* Misc. things related to the system emulator.  */
 
+#include <stdbool.h>
 #include "qemu-common.h"
 #include "qemu-option.h"
 #include "sys-queue.h"
@@ -230,9 +231,14 @@  extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
 
 /* virtio consoles */
 
-#define MAX_VIRTIO_CONSOLES 1
+#define MAX_VIRTIO_CONSOLE_PORTS 64 /* Should be a multiple of 32 */
+#define VIRTIO_CONSOLE_PROTO_MAX_LEN 30
 
-extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLE_PORTS];
+extern uint32_t virtcon_idx[MAX_VIRTIO_CONSOLE_PORTS];
+extern int virtcon_nr_ports;
+extern int init_virtio_console_port(int port, const char *opts);
+extern void *virtio_console_new_port(PCIDevice *dev, uint32_t idx);
 
 #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
 
diff --git a/vl.c b/vl.c
index c6c6a6b..73e068e 100644
--- a/vl.c
+++ b/vl.c
@@ -211,7 +211,10 @@  static int no_frame = 0;
 int no_quit = 0;
 CharDriverState *serial_hds[MAX_SERIAL_PORTS];
 CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
-CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLE_PORTS];
+char virtcon_prot[MAX_VIRTIO_CONSOLE_PORTS][VIRTIO_CONSOLE_PROTO_MAX_LEN];
+uint32_t virtcon_idx[MAX_VIRTIO_CONSOLE_PORTS];
+int virtcon_nr_ports;
 #ifdef TARGET_I386
 int win2k_install_hack = 0;
 int rtc_td_hack = 0;
@@ -4782,8 +4785,7 @@  int main(int argc, char **argv, char **envp)
     int serial_device_index;
     const char *parallel_devices[MAX_PARALLEL_PORTS];
     int parallel_device_index;
-    const char *virtio_consoles[MAX_VIRTIO_CONSOLES];
-    int virtio_console_index;
+    const char *virtio_consoles[MAX_VIRTIO_CONSOLE_PORTS];
     const char *loadvm = NULL;
     QEMUMachine *machine;
     const char *cpu_model;
@@ -4857,9 +4859,9 @@  int main(int argc, char **argv, char **envp)
         parallel_devices[i] = NULL;
     parallel_device_index = 0;
 
-    for(i = 0; i < MAX_VIRTIO_CONSOLES; i++)
+    for(i = 0; i < MAX_VIRTIO_CONSOLE_PORTS; i++)
         virtio_consoles[i] = NULL;
-    virtio_console_index = 0;
+    virtcon_nr_ports = 0;
 
     monitor_devices[0] = "vc:80Cx24C";
     for (i = 1; i < MAX_MONITOR_DEVICES; i++) {
@@ -5309,12 +5311,12 @@  int main(int argc, char **argv, char **envp)
                 }
                 break;
             case QEMU_OPTION_virtiocon:
-                if (virtio_console_index >= MAX_VIRTIO_CONSOLES) {
+                if (virtcon_nr_ports >= MAX_VIRTIO_CONSOLE_PORTS) {
                     fprintf(stderr, "qemu: too many virtio consoles\n");
                     exit(1);
                 }
-                virtio_consoles[virtio_console_index] = optarg;
-                virtio_console_index++;
+                virtio_consoles[virtcon_nr_ports] = optarg;
+                virtcon_nr_ports++;
                 break;
             case QEMU_OPTION_parallel:
                 if (parallel_device_index >= MAX_PARALLEL_PORTS) {
@@ -5854,17 +5856,13 @@  int main(int argc, char **argv, char **envp)
         }
     }
 
-    for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
-        const char *devname = virtio_consoles[i];
-        if (devname && strcmp(devname, "none")) {
-            char label[32];
-            snprintf(label, sizeof(label), "virtcon%d", i);
-            virtcon_hds[i] = qemu_chr_open(label, devname, NULL);
-            if (!virtcon_hds[i]) {
-                fprintf(stderr, "qemu: could not open virtio console '%s'\n",
-                        devname);
-                exit(1);
-            }
+    for (i = 0; i < virtcon_nr_ports; i++) {
+        int ret;
+
+        ret = init_virtio_console_port(i, virtio_consoles[i]);
+        if (ret < 0) {
+            fprintf(stderr, "qemu: could not init virtio console port at \"%s\"\n", virtio_consoles[i]);
+            exit(1);
         }
     }
 
@@ -5995,11 +5993,12 @@  int main(int argc, char **argv, char **envp)
         }
     }
 
-    for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) {
+    for(i = 0; i < MAX_VIRTIO_CONSOLE_PORTS; i++) {
         const char *devname = virtio_consoles[i];
         if (virtcon_hds[i] && devname) {
             if (strstart(devname, "vc", 0))
-                qemu_chr_printf(virtcon_hds[i], "virtio console%d\r\n", i);
+                qemu_chr_printf(virtcon_hds[i], "virtio console%d\r\n",
+                                virtcon_idx[i]);
         }
     }