* 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 <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <xen/io/console.h>
#include <xenctrl.h>
-#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,
+};