WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

Re: [Xen-devel] PATCH: 8/10: Add pv console to QEMU paravirt machine

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: Re: [Xen-devel] PATCH: 8/10: Add pv console to QEMU paravirt machine
From: "Daniel P. Berrange" <berrange@xxxxxxxxxx>
Date: Wed, 22 Aug 2007 23:32:43 +0100
Delivery-date: Wed, 22 Aug 2007 15:34:12 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
In-reply-to: <20070822222653.GR18061@xxxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <20070822222653.GR18061@xxxxxxxxxx>
Reply-to: "Daniel P. Berrange" <berrange@xxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.4.1i
This patch adds a paravirt console driver to qemu-dm. This is used when the QEMU
machine type is 'xenpv', connecting to the ring buffer provided by the guest
kernel. The '-serial' command line flag controls how the guest console is
exposed.

For parity with xenconsoled the '-serial pty' arg can be used. For guests which
are running a qemu-dm device model, the xenconsoled daemon is no longer needed
for guest consoles. The code for the xen_console.c is based on the original code
in tools/console/daemon/io.c, but simplified; since its only dealing with a 
single
guest there's no state tracking to worry about.

 b/tools/ioemu/hw/xen_console.c  |  424 ++++++++++++++++++++++++++++++++++++++++
 b/tools/ioemu/hw/xen_console.h  |   25 ++
 tools/ioemu/Makefile.target     |    1 
 tools/ioemu/hw/xen_machine_pv.c |    9 
 tools/ioemu/xenstore.c          |    2 
 5 files changed, 460 insertions(+), 1 deletion(-)


   Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx>

Dan.


diff -r fb720737dddb tools/ioemu/Makefile.target
--- a/tools/ioemu/Makefile.target       Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/Makefile.target       Tue Aug 21 22:16:00 2007 -0400
@@ -403,6 +403,7 @@ VL_OBJS+= xen_machine_fv.o
 VL_OBJS+= xen_machine_fv.o
 VL_OBJS+= xen_machine_pv.o
 VL_OBJS+= xenfb.o
+VL_OBJS+= xen_console.o
 VL_OBJS+= tpm_tis.o
 CPPFLAGS += -DHAS_AUDIO
 endif
diff -r fb720737dddb tools/ioemu/hw/xen_console.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.c      Tue Aug 21 22:16:00 2007 -0400
@@ -0,0 +1,424 @@
+/*
+ *  Copyright (C) International Business Machines  Corp., 2005
+ *  Author(s): Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  Copyright (C) Red Hat 2007
+ *
+ *  Xen Console
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <xs.h>
+#include <xen/io/console.h>
+#include <xenctrl.h>
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include "vl.h"
+
+#include "xen_console.h"
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
+#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
+
+#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__)
+
+struct buffer
+{
+       uint8_t *data;
+       size_t consumed;
+       size_t size;
+       size_t capacity;
+       size_t max_capacity;
+};
+
+struct domain
+{
+       int domid;
+       struct buffer buffer;
+
+       char *conspath;
+       char *serialpath;
+       int use_consolepath;
+       int ring_ref;
+       evtchn_port_t local_port;
+       evtchn_port_t remote_port;
+       int xce_handle;
+       struct xs_handle *xsh;
+       struct xencons_interface *interface;
+       CharDriverState *chr;
+};
+
+
+static void buffer_append(struct domain *dom)
+{
+       struct buffer *buffer = &dom->buffer;
+       XENCONS_RING_IDX cons, prod, size;
+       struct xencons_interface *intf = dom->interface;
+
+       cons = intf->out_cons;
+       prod = intf->out_prod;
+       mb();
+
+       size = prod - cons;
+       if ((size == 0) || (size > sizeof(intf->out)))
+               return;
+
+       if ((buffer->capacity - buffer->size) < size) {
+               buffer->capacity += (size + 1024);
+               buffer->data = realloc(buffer->data, buffer->capacity);
+               if (buffer->data == NULL) {
+                       dolog(LOG_ERR, "Memory allocation failed");
+                       exit(ENOMEM);
+               }
+       }
+
+       while (cons != prod)
+               buffer->data[buffer->size++] = intf->out[
+                       MASK_XENCONS_IDX(cons++, intf->out)];
+
+       mb();
+       intf->out_cons = cons;
+       xc_evtchn_notify(dom->xce_handle, dom->local_port);
+
+       if (buffer->max_capacity &&
+           buffer->size > buffer->max_capacity) {
+               /* Discard the middle of the data. */
+
+               size_t over = buffer->size - buffer->max_capacity;
+               uint8_t *maxpos = buffer->data + buffer->max_capacity;
+
+               memmove(maxpos - over, maxpos, over);
+               buffer->data = realloc(buffer->data, buffer->max_capacity);
+               buffer->size = buffer->capacity = buffer->max_capacity;
+
+               if (buffer->consumed > buffer->max_capacity - over)
+                       buffer->consumed = buffer->max_capacity - over;
+       }
+}
+
+static void buffer_advance(struct buffer *buffer, size_t len)
+{
+       buffer->consumed += len;
+       if (buffer->consumed == buffer->size) {
+               buffer->consumed = 0;
+               buffer->size = 0;
+       }
+}
+
+/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
+int xs_gather(struct xs_handle *xs, const char *dir, ...)
+{
+       va_list ap;
+       const char *name;
+       char *path;
+       int ret = 0;
+
+       va_start(ap, dir);
+       while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
+               const char *fmt = va_arg(ap, char *);
+               void *result = va_arg(ap, void *);
+               char *p;
+
+               if (asprintf(&path, "%s/%s", dir, name) == -1) {
+                       ret = ENOMEM;
+                       break;
+               }
+               p = xs_read(xs, XBT_NULL, path, NULL);
+               free(path);
+               if (p == NULL) {
+                       ret = ENOENT;
+                       break;
+               }
+               if (fmt) {
+                       if (sscanf(p, fmt, result) == 0)
+                               ret = EINVAL;
+                       free(p);
+               } else
+                       *(char **)result = p;
+       }
+       va_end(ap);
+       return ret;
+}
+
+static int domain_create_ring(struct domain *dom)
+{
+       int err, remote_port, ring_ref, rc;
+
+       err = xs_gather(dom->xsh, dom->serialpath,
+                       "ring-ref", "%u", &ring_ref,
+                       "port", "%i", &remote_port,
+                       NULL);
+       if (err) {
+               err = xs_gather(dom->xsh, dom->conspath,
+                               "ring-ref", "%u", &ring_ref,
+                               "port", "%i", &remote_port,
+                               NULL);
+               if (err) {
+                       fprintf(stderr, "Console: failed to find ring-ref/port 
yet\n");
+                       goto out;
+               }
+               dom->use_consolepath = 1;
+       } else
+               dom->use_consolepath = 0;
+       fprintf(stderr, "Console: got ring-ref %d port %d\n", ring_ref, 
remote_port);
+
+       if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port))
+               goto out;
+
+       if (ring_ref != dom->ring_ref) {
+               if (dom->interface != NULL)
+                       munmap(dom->interface, getpagesize());
+               dom->interface = xc_map_foreign_range(
+                       xc_handle, dom->domid, getpagesize(),
+                       PROT_READ|PROT_WRITE,
+                       (unsigned long)ring_ref);
+               if (dom->interface == NULL) {
+                       err = -errno;
+                       goto out;
+               }
+               dom->ring_ref = ring_ref;
+       }
+
+       dom->local_port = -1;
+       dom->remote_port = -1;
+
+       dom->xce_handle = xc_evtchn_open();
+       if (dom->xce_handle == -1) {
+               err = -errno;
+               goto out;
+       }
+
+       rc = xc_evtchn_bind_interdomain(dom->xce_handle,
+               dom->domid, remote_port);
+
+       if (rc == -1) {
+               err = -errno;
+               xc_evtchn_close(dom->xce_handle);
+               dom->xce_handle = -1;
+               goto out;
+       }
+       dom->local_port = rc;
+       dom->remote_port = remote_port;
+
+ out:
+       return err;
+}
+
+
+static struct domain *create_domain(int domid, CharDriverState *chr)
+{
+       struct domain *dom;
+       char *s;
+
+       dom = (struct domain *)malloc(sizeof(struct domain));
+       if (dom == NULL) {
+               dolog(LOG_ERR, "Out of memory %s:%s():L%d",
+                     __FILE__, __FUNCTION__, __LINE__);
+               exit(ENOMEM);
+       }
+
+       dom->domid = domid;
+       dom->chr = chr;
+
+       dom->xsh = xs_daemon_open();
+       if (dom->xsh == NULL) {
+               fprintf(logfile, "Could not contact xenstore for console 
watch\n");
+               goto out;
+       }
+
+       dom->serialpath = xs_get_domain_path(dom->xsh, dom->domid);
+       s = realloc(dom->serialpath, strlen(dom->serialpath) +
+                   strlen("/serial/0") + 1);
+       if (s == NULL)
+               goto out;
+       dom->serialpath = s;
+       strcat(dom->serialpath, "/serial/0");
+
+       dom->conspath = xs_get_domain_path(dom->xsh, dom->domid);
+       s = realloc(dom->conspath, strlen(dom->conspath) +
+                   strlen("/console") + 1);
+       if (s == NULL)
+               goto out;
+       dom->conspath = s;
+       strcat(dom->conspath, "/console");
+
+       dom->buffer.data = 0;
+       dom->buffer.consumed = 0;
+       dom->buffer.size = 0;
+       dom->buffer.capacity = 0;
+       dom->buffer.max_capacity = 0;
+
+       dom->ring_ref = -1;
+       dom->local_port = -1;
+       dom->remote_port = -1;
+       dom->interface = NULL;
+       dom->xce_handle = -1;
+
+
+       return dom;
+ out:
+       free(dom->serialpath);
+       free(dom->conspath);
+       free(dom);
+       return NULL;
+}
+
+
+static int ring_free_bytes(struct domain *dom)
+{
+       struct xencons_interface *intf = dom->interface;
+       XENCONS_RING_IDX cons, prod, space;
+
+       cons = intf->in_cons;
+       prod = intf->in_prod;
+       mb();
+
+       space = prod - cons;
+       if (space > sizeof(intf->in))
+               return 0; /* ring is screwed: ignore it */
+
+       return (sizeof(intf->in) - space);
+}
+
+static int xencons_can_receive(void *opaque)
+{
+       struct domain *dom = (struct domain *)opaque;
+
+       return ring_free_bytes(dom);
+}
+
+static void xencons_receive(void *opaque, const uint8_t *buf, int len)
+{
+       struct domain *dom = (struct domain *)opaque;
+       int i, max;
+       struct xencons_interface *intf = dom->interface;
+       XENCONS_RING_IDX prod;
+
+       max = ring_free_bytes(dom);
+       /* The can_receive() func limits this, but check again anyway */
+       if (max < len)
+               len = max;
+
+       prod = intf->in_prod;
+       for (i = 0; i < len; i++) {
+               intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
+                       buf[i];
+       }
+       wmb();
+       intf->in_prod = prod;
+       xc_evtchn_notify(dom->xce_handle, dom->local_port);
+}
+
+static void xencons_send(struct domain *dom)
+{
+       ssize_t len;
+       len = qemu_chr_write(dom->chr, dom->buffer.data + dom->buffer.consumed,
+                            dom->buffer.size - dom->buffer.consumed);
+       if (len < 1) {
+               /*
+                * Disable log because if we're redirecting to /dev/pts/N we
+                * don't want to flood logs when no client has the PTY open
+                */
+               /*
+               dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
+                     dom->domid, len, errno);
+               */
+       } else {
+               buffer_advance(&dom->buffer, len);
+       }
+}
+
+static void xencons_ring_read(void *opaque)
+{
+       evtchn_port_t port;
+       struct domain *dom = (struct domain *)opaque;
+
+       if ((port = xc_evtchn_pending(dom->xce_handle)) == -1)
+               return;
+
+       buffer_append(dom);
+
+       (void)xc_evtchn_unmask(dom->xce_handle, port);
+
+       if (dom->buffer.size - dom->buffer.consumed)
+               xencons_send(dom);
+}
+
+static void xencons_startup(void *opaque)
+{
+       struct domain *dom = (struct domain *)opaque;
+       unsigned dummy;
+       char **vec;
+       int err;
+       vec = xs_read_watch(dom->xsh, &dummy);
+       if (vec)
+               free(vec);
+       fprintf(stderr, "Console: got watch\n");
+       err = domain_create_ring(dom);
+       if (err)
+               return;
+
+       xs_unwatch(dom->xsh, dom->conspath, "");
+       xs_unwatch(dom->xsh, dom->serialpath, "");
+       qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, NULL, NULL, NULL);
+
+       fprintf(stderr, "Console: connected to guest frontend\n");
+       if (qemu_set_fd_handler2(dom->xce_handle, NULL, xencons_ring_read, 
NULL, dom) < 0)
+               return;
+
+       qemu_chr_add_handlers(dom->chr, xencons_can_receive, xencons_receive,
+                             NULL, dom);
+}
+
+
+int xencons_init(int domid, CharDriverState *chr)
+{
+       struct domain *dom = create_domain(domid, chr);
+
+       if (!dom)
+               return -1;
+
+       /* Setup watches so we asynchronously connect to serial console */
+       xs_watch(dom->xsh, dom->conspath, "");
+       xs_watch(dom->xsh, dom->serialpath, "");
+       qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, xencons_startup, NULL, 
dom);
+       fprintf(stderr, "Console: prepared domain, waiting for ringref at %s or 
%s\n",
+               dom->conspath, dom->serialpath);
+
+       return 0;
+}
+
+
+/*
+ * Local variables:
+ *  c-file-style: "linux"
+ *  indent-tabs-mode: t
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -r fb720737dddb tools/ioemu/hw/xen_console.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.h      Tue Aug 21 22:16:00 2007 -0400
@@ -0,0 +1,25 @@
+/*
+ *  Copyright (C) International Business Machines  Corp., 2005
+ *  Author(s): Anthony Liguori <aliguori@xxxxxxxxxx>
+ *
+ *  Copyright (C) Red Hat 2007
+ *
+ *  Xen Console
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "vl.h"
+
+extern int xencons_init(int domid, CharDriverState *chr);
diff -r fb720737dddb tools/ioemu/hw/xen_machine_pv.c
--- a/tools/ioemu/hw/xen_machine_pv.c   Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/hw/xen_machine_pv.c   Tue Aug 21 22:16:00 2007 -0400
@@ -23,6 +23,7 @@
  */
 
 #include "vl.h"
+#include "xen_console.h"
 #include "xenfb.h"
 
 
@@ -39,6 +40,14 @@ static void xen_init_pv(uint64_t ram_siz
 {
     struct xenfb *xenfb;
     extern int domid;
+
+    /* Connect to text console */
+    if (serial_hds[0]) {
+        if (xencons_init(domid, serial_hds[0]) < 0) {
+            fprintf(stderr, "Could not connect to domain console\n");
+            exit(1);
+        }
+    }
 
     /* Prepare PVFB state */
     xenfb = xenfb_new(domid, ds);
diff -r fb720737dddb tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c    Tue Aug 21 22:15:30 2007 -0400
+++ b/tools/ioemu/xenstore.c    Tue Aug 21 22:16:00 2007 -0400
@@ -17,7 +17,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
-static struct xs_handle *xsh = NULL;
+struct xs_handle *xsh = NULL;
 static char *media_filename[MAX_DISKS + MAX_SCSI_DISKS];
 static QEMUTimer *insert_timer = NULL;
 

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel