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. The xenconsoled
daemon is no longer needed for guest consoles with this change applied. The
code for the xen_console.c is based on the original tools/console/daemon/io.c,
but greatly 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 04c80f1aa008 tools/ioemu/Makefile.target
--- a/tools/ioemu/Makefile.target Wed Aug 15 14:17:34 2007 -0400
+++ b/tools/ioemu/Makefile.target Wed Aug 15 15:06:54 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 04c80f1aa008 tools/ioemu/hw/xen_console.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.c Wed Aug 15 15:33:12 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 04c80f1aa008 tools/ioemu/hw/xen_console.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/ioemu/hw/xen_console.h Wed Aug 15 15:06:54 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 04c80f1aa008 tools/ioemu/hw/xen_machine_pv.c
--- a/tools/ioemu/hw/xen_machine_pv.c Wed Aug 15 14:17:34 2007 -0400
+++ b/tools/ioemu/hw/xen_machine_pv.c Wed Aug 15 15:06:54 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 04c80f1aa008 tools/ioemu/xenstore.c
--- a/tools/ioemu/xenstore.c Wed Aug 15 14:17:34 2007 -0400
+++ b/tools/ioemu/xenstore.c Wed Aug 15 15:06:54 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
|