xen_console.c 8.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 *  Copyright (C) International Business Machines  Corp., 2005
 *  Author(s): Anthony Liguori <aliguori@us.ibm.com>
 *
 *  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
19
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
20 21
 */

P
Peter Maydell 已提交
22
#include "qemu/osdep.h"
23 24 25
#include <sys/select.h>
#include <termios.h>

26
#include "hw/hw.h"
27
#include "sysemu/char.h"
P
Paolo Bonzini 已提交
28
#include "hw/xen/xen_backend.h"
29

30 31
#include <xen/io/console.h>

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
struct buffer {
    uint8_t *data;
    size_t consumed;
    size_t size;
    size_t capacity;
    size_t max_capacity;
};

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 XenConsole *con)
{
    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);
66
	buffer->data = g_realloc(buffer->data, buffer->capacity);
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    }

    while (cons != prod)
	buffer->data[buffer->size++] = intf->out[
	    MASK_XENCONS_IDX(cons++, intf->out)];

    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. */

	size_t over = buffer->size - buffer->max_capacity;
	uint8_t *maxpos = buffer->data + buffer->max_capacity;

	memmove(maxpos - over, maxpos, over);
85
	buffer->data = g_realloc(buffer->data, buffer->max_capacity);
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
	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;
    }
}

static int ring_free_bytes(struct XenConsole *con)
{
    struct xencons_interface *intf = con->sring;
    XENCONS_RING_IDX cons, prod, space;

    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 */

    return (sizeof(intf->in) - space);
}

static int xencons_can_receive(void *opaque)
{
    struct XenConsole *con = opaque;
    return ring_free_bytes(con);
}

static void xencons_receive(void *opaque, const uint8_t *buf, int len)
{
    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 XenConsole *con)
{
    ssize_t len, size;

    size = con->buffer.size - con->buffer.consumed;
    if (con->chr)
152
        len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed,
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
                             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 int con_init(struct XenDevice *xendev)
{
    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
175
    char *type, *dom, label[32];
176
    int ret = 0;
177
    const char *output;
178 179 180

    /* setup */
    dom = xs_get_domain_path(xenstore, con->xendev.dom);
181 182 183 184 185
    if (!xendev->dev) {
        snprintf(con->console, sizeof(con->console), "%s/console", dom);
    } else {
        snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev);
    }
186 187 188
    free(dom);

    type = xenstore_read_str(con->console, "type");
189
    if (!type || strcmp(type, "ioemu") != 0) {
190
	xen_be_printf(xendev, 1, "not for me (type=%s)\n", type);
191 192
        ret = -1;
        goto out;
193 194
    }

195
    output = xenstore_read_str(con->console, "output");
196 197

    /* no Xen override, use qemu output device */
198
    if (output == NULL) {
199 200 201
        con->chr = serial_hds[con->xendev.dev];
    } else {
        snprintf(label, sizeof(label), "xencons%d", con->xendev.dev);
M
Marc-André Lureau 已提交
202
        con->chr = qemu_chr_new(label, output);
203
    }
204

205
    xenstore_store_pv_console_info(con->xendev.dev, con->chr);
206

207
out:
208
    g_free(type);
209
    return ret;
210 211
}

212
static int con_initialise(struct XenDevice *xendev)
213 214 215 216 217 218 219 220 221 222 223
{
    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);
    int limit;

    if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1)
	return -1;
    if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1)
	return -1;
    if (xenstore_read_int(con->console, "limit", &limit) == 0)
	con->buffer.max_capacity = limit;

224
    if (!xendev->dev) {
225
        xen_pfn_t mfn = con->ring_ref;
226 227 228
        con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom,
                                          PROT_READ|PROT_WRITE,
                                          1, &mfn, NULL);
229
    } else {
230
        con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom,
231 232 233
                                             con->ring_ref,
                                             PROT_READ|PROT_WRITE);
    }
234 235 236 237
    if (!con->sring)
	return -1;

    xen_be_bind_evtchn(&con->xendev);
238 239 240 241 242 243 244 245 246 247 248
    if (con->chr) {
        if (qemu_chr_fe_claim(con->chr) == 0) {
            qemu_chr_add_handlers(con->chr, xencons_can_receive,
                                  xencons_receive, NULL, con);
        } else {
            xen_be_printf(xendev, 0,
                          "xen_console_init error chardev %s already used\n",
                          con->chr->label);
            con->chr = NULL;
        }
    }
249 250 251 252 253 254 255 256 257 258 259 260 261

    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 XenConsole *con = container_of(xendev, struct XenConsole, xendev);

262
    if (con->chr) {
263
        qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL);
264 265
        qemu_chr_fe_release(con->chr);
    }
266 267 268
    xen_be_unbind_evtchn(&con->xendev);

    if (con->sring) {
269
        if (!xendev->dev) {
270
            xenforeignmemory_unmap(xen_fmem, con->sring, 1);
271
        } else {
272
            xengnttab_unmap(xendev->gnttabdev, con->sring, 1);
273
        }
274
        con->sring = NULL;
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
    }
}

static void con_event(struct XenDevice *xendev)
{
    struct XenConsole *con = container_of(xendev, struct XenConsole, xendev);

    buffer_append(con);
    if (con->buffer.size - con->buffer.consumed)
	xencons_send(con);
}

/* -------------------------------------------------------------------- */

struct XenDevOps xen_console_ops = {
    .size       = sizeof(struct XenConsole),
291
    .flags      = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV,
292
    .init       = con_init,
293
    .initialise = con_initialise,
294 295 296
    .event      = con_event,
    .disconnect = con_disconnect,
};