From 39e9e3cb205368931887a585520c687fca31e7c2 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 16 Jan 2009 16:02:18 +0000 Subject: [PATCH] Replacement PV console backend (Gerd Hoffman) Description from Gerd's message: xen: add console backend driver. This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Note that this driver is not used at all in the default configurations of upstream Xen. Signed-off-by: Gerd Hoffmann (Applied as-is.) --- hw/xen_backend.h | 3 + hw/xen_console.c | 535 +++++++++++++++----------------------------- hw/xen_console.h | 26 --- hw/xen_machine_pv.c | 10 +- 4 files changed, 190 insertions(+), 384 deletions(-) delete mode 100644 hw/xen_console.h diff --git a/hw/xen_backend.h b/hw/xen_backend.h index 70daebd17..6ae9e0766 100644 --- a/hw/xen_backend.h +++ b/hw/xen_backend.h @@ -84,4 +84,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +extern struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_HW_XEN_BACKEND_H */ diff --git a/hw/xen_console.c b/hw/xen_console.c index 9ec08e8d9..c172cf2bc 100644 --- a/hw/xen_console.c +++ b/hw/xen_console.c @@ -9,23 +9,17 @@ * 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 "hw.h" -#include "sysemu.h" -#include "exec-all.h" -#include "qemu-char.h" - -#include #include #include #include @@ -39,401 +33,244 @@ #include #include -#include "xen_console.h" +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen_backend.h" #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 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; +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[XEN_BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; }; - -static void buffer_append(struct domain *dom) +static void buffer_append(struct XenConsole *con) { - 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; - xen_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); - } + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = qemu_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)]; + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; - xen_mb(); - intf->out_cons = cons; - xc_evtchn_notify(dom->xce_handle, dom->local_port); + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); - if (buffer->max_capacity && - buffer->size > buffer->max_capacity) { - /* Discard the middle of the data. */ + 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; + 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; + memmove(maxpos - over, maxpos, over); + buffer->data = qemu_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; - } + 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, limit, rc; - - err = xs_gather(dom->xsh, dom->serialpath, - "ring-ref", "%u", &ring_ref, - "port", "%i", &remote_port, - "limit", "%i", &limit, - NULL); - if (err) { - err = xs_gather(dom->xsh, dom->conspath, - "ring-ref", "%u", &ring_ref, - "port", "%i", &remote_port, - "limit", "%i", &limit, - 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; - dom->buffer.max_capacity = limit; - fprintf(stderr, "Console: got ring-ref %d port %d limit %d\n", - ring_ref, remote_port, limit); - - 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; + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } } - -static int ring_free_bytes(struct domain *dom) +static int ring_free_bytes(struct XenConsole *con) { - struct xencons_interface *intf = dom->interface; - XENCONS_RING_IDX cons, prod, space; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; - cons = intf->in_cons; - prod = intf->in_prod; - xen_mb(); + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); - space = prod - cons; - if (space > sizeof(intf->in)) - return 0; /* ring is screwed: ignore it */ + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ - return (sizeof(intf->in) - space); + return (sizeof(intf->in) - space); } static int xencons_can_receive(void *opaque) { - struct domain *dom = (struct domain *)opaque; - - return ring_free_bytes(dom); + struct XenConsole *con = opaque; + return ring_free_bytes(con); } 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]; - } - xen_wmb(); - intf->in_prod = prod; - xc_evtchn_notify(dom->xce_handle, dom->local_port); + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* 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]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); } -static void xencons_send(struct domain *dom) +static void xencons_send(struct XenConsole *con) { - 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); + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + if (con->chr) + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + else + len = size; + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } } -static void xencons_ring_read(void *opaque) +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) { - evtchn_port_t port; - struct domain *dom = (struct domain *)opaque; + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type, *dom; - if ((port = xc_evtchn_pending(dom->xce_handle)) == -1) - return; + /* setup */ + dom = xs_get_domain_path(xenstore, con->xendev.dom); + snprintf(con->console, sizeof(con->console), "%s/console", dom); + free(dom); - buffer_append(dom); + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } - (void)xc_evtchn_unmask(dom->xce_handle, port); + if (!serial_hds[con->xendev.dev]) + xen_be_printf(xendev, 1, "WARNING: serial line %d not configured\n", + con->xendev.dev); + else + con->chr = serial_hds[con->xendev.dev]; - if (dom->buffer.size - dom->buffer.consumed) - xencons_send(dom); + return 0; } -static void xencons_startup(void *opaque) +static int con_connect(struct XenDevice *xendev) { - 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(xc_evtchn_fd(dom->xce_handle), NULL, xencons_ring_read, NULL, dom) < 0) - return; - - qemu_chr_add_handlers(dom->chr, xencons_can_receive, xencons_receive, - NULL, dom); -} + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) + return -1; + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) + return -1; + if (0 == xenstore_read_int(con->console, "limit", &limit)) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; -int xencons_init(int domid, CharDriverState *chr) + xen_be_bind_evtchn(&con->xendev); + if (con->chr) + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) { - struct domain *dom = create_domain(domid, chr); + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); - if (!dom) - return -1; + if (con->chr) + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); - /* Setup watches so we asynchronously connect to serial console */ - if (!(xs_watch(dom->xsh, dom->conspath, ""))) { - fprintf(stderr, "Unable to watch console %s\n", dom->conspath); - goto fail; - } - if (!(xs_watch(dom->xsh, dom->serialpath, ""))) { - fprintf(stderr, "Unable to watch console %s\n", dom->conspath); - xs_unwatch(dom->xsh, dom->conspath, ""); - goto fail; - } - 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); + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} - return 0; +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); -fail: - xs_daemon_close(dom->xsh); - free(dom->serialpath); - free(dom->conspath); - free(dom); - return -1; + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); } +/* -------------------------------------------------------------------- */ -/* - * Local variables: - * c-file-style: "linux" - * indent-tabs-mode: t - * c-indent-level: 8 - * c-basic-offset: 8 - * tab-width: 8 - * End: - */ +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen_console.h b/hw/xen_console.h deleted file mode 100644 index bbea61047..000000000 --- a/hw/xen_console.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) International Business Machines Corp., 2005 - * Author(s): Anthony Liguori - * - * 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 "hw.h" -#include "qemu-xen.h" - -extern int xencons_init(int domid, CharDriverState *chr); diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c index 7d5c07cbd..e4ffd46c4 100644 --- a/hw/xen_machine_pv.c +++ b/hw/xen_machine_pv.c @@ -24,7 +24,6 @@ #include "hw.h" #include "pc.h" -#include "xen_console.h" #include "xenfb.h" #include "sysemu.h" #include "boards.h" @@ -65,14 +64,7 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size, fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); exit(1); } - - /* 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); - } - } + xen_be_register("console", &xen_console_ops); /* Prepare PVFB state */ xenfb = xenfb_new(domid, ds); -- 2.39.5