1 /* 2 * Copyright (C) International Business Machines Corp., 2005 3 * Author(s): Anthony Liguori <aliguori@us.ibm.com> 4 * 5 * Copyright (C) Red Hat 2007 6 * 7 * Xen Console 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; under version 2 of the License. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * 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 22 #include <stdlib.h> 23 #include <errno.h> 24 #include <string.h> 25 #include <sys/select.h> 26 #include <fcntl.h> 27 #include <unistd.h> 28 #include <termios.h> 29 #include <stdarg.h> 30 #include <sys/mman.h> 31 32 #include "hw/hw.h" 33 #include "sysemu/char.h" 34 #include "hw/xen/xen_backend.h" 35 36 #include <xen/io/console.h> 37 38 struct buffer { 39 uint8_t *data; 40 size_t consumed; 41 size_t size; 42 size_t capacity; 43 size_t max_capacity; 44 }; 45 46 struct XenConsole { 47 struct XenDevice xendev; /* must be first */ 48 struct buffer buffer; 49 char console[XEN_BUFSIZE]; 50 int ring_ref; 51 void *sring; 52 CharDriverState *chr; 53 int backlog; 54 }; 55 56 static void buffer_append(struct XenConsole *con) 57 { 58 struct buffer *buffer = &con->buffer; 59 XENCONS_RING_IDX cons, prod, size; 60 struct xencons_interface *intf = con->sring; 61 62 cons = intf->out_cons; 63 prod = intf->out_prod; 64 xen_mb(); 65 66 size = prod - cons; 67 if ((size == 0) || (size > sizeof(intf->out))) 68 return; 69 70 if ((buffer->capacity - buffer->size) < size) { 71 buffer->capacity += (size + 1024); 72 buffer->data = g_realloc(buffer->data, buffer->capacity); 73 } 74 75 while (cons != prod) 76 buffer->data[buffer->size++] = intf->out[ 77 MASK_XENCONS_IDX(cons++, intf->out)]; 78 79 xen_mb(); 80 intf->out_cons = cons; 81 xen_be_send_notify(&con->xendev); 82 83 if (buffer->max_capacity && 84 buffer->size > buffer->max_capacity) { 85 /* Discard the middle of the data. */ 86 87 size_t over = buffer->size - buffer->max_capacity; 88 uint8_t *maxpos = buffer->data + buffer->max_capacity; 89 90 memmove(maxpos - over, maxpos, over); 91 buffer->data = g_realloc(buffer->data, buffer->max_capacity); 92 buffer->size = buffer->capacity = buffer->max_capacity; 93 94 if (buffer->consumed > buffer->max_capacity - over) 95 buffer->consumed = buffer->max_capacity - over; 96 } 97 } 98 99 static void buffer_advance(struct buffer *buffer, size_t len) 100 { 101 buffer->consumed += len; 102 if (buffer->consumed == buffer->size) { 103 buffer->consumed = 0; 104 buffer->size = 0; 105 } 106 } 107 108 static int ring_free_bytes(struct XenConsole *con) 109 { 110 struct xencons_interface *intf = con->sring; 111 XENCONS_RING_IDX cons, prod, space; 112 113 cons = intf->in_cons; 114 prod = intf->in_prod; 115 xen_mb(); 116 117 space = prod - cons; 118 if (space > sizeof(intf->in)) 119 return 0; /* ring is screwed: ignore it */ 120 121 return (sizeof(intf->in) - space); 122 } 123 124 static int xencons_can_receive(void *opaque) 125 { 126 struct XenConsole *con = opaque; 127 return ring_free_bytes(con); 128 } 129 130 static void xencons_receive(void *opaque, const uint8_t *buf, int len) 131 { 132 struct XenConsole *con = opaque; 133 struct xencons_interface *intf = con->sring; 134 XENCONS_RING_IDX prod; 135 int i, max; 136 137 max = ring_free_bytes(con); 138 /* The can_receive() func limits this, but check again anyway */ 139 if (max < len) 140 len = max; 141 142 prod = intf->in_prod; 143 for (i = 0; i < len; i++) { 144 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = 145 buf[i]; 146 } 147 xen_wmb(); 148 intf->in_prod = prod; 149 xen_be_send_notify(&con->xendev); 150 } 151 152 static void xencons_send(struct XenConsole *con) 153 { 154 ssize_t len, size; 155 156 size = con->buffer.size - con->buffer.consumed; 157 if (con->chr) 158 len = qemu_chr_fe_write(con->chr, con->buffer.data + con->buffer.consumed, 159 size); 160 else 161 len = size; 162 if (len < 1) { 163 if (!con->backlog) { 164 con->backlog = 1; 165 xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); 166 } 167 } else { 168 buffer_advance(&con->buffer, len); 169 if (con->backlog && len == size) { 170 con->backlog = 0; 171 xen_be_printf(&con->xendev, 1, "backlog is gone\n"); 172 } 173 } 174 } 175 176 /* -------------------------------------------------------------------- */ 177 178 static int con_init(struct XenDevice *xendev) 179 { 180 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 181 char *type, *dom, label[32]; 182 int ret = 0; 183 const char *output; 184 185 /* setup */ 186 dom = xs_get_domain_path(xenstore, con->xendev.dom); 187 if (!xendev->dev) { 188 snprintf(con->console, sizeof(con->console), "%s/console", dom); 189 } else { 190 snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev); 191 } 192 free(dom); 193 194 type = xenstore_read_str(con->console, "type"); 195 if (!type || strcmp(type, "ioemu") != 0) { 196 xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); 197 ret = -1; 198 goto out; 199 } 200 201 output = xenstore_read_str(con->console, "output"); 202 203 /* no Xen override, use qemu output device */ 204 if (output == NULL) { 205 con->chr = serial_hds[con->xendev.dev]; 206 } else { 207 snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); 208 con->chr = qemu_chr_new(label, output, NULL); 209 } 210 211 xenstore_store_pv_console_info(con->xendev.dev, con->chr); 212 213 out: 214 g_free(type); 215 return ret; 216 } 217 218 static int con_initialise(struct XenDevice *xendev) 219 { 220 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 221 int limit; 222 223 if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) 224 return -1; 225 if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) 226 return -1; 227 if (xenstore_read_int(con->console, "limit", &limit) == 0) 228 con->buffer.max_capacity = limit; 229 230 if (!xendev->dev) { 231 con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, 232 XC_PAGE_SIZE, 233 PROT_READ|PROT_WRITE, 234 con->ring_ref); 235 } else { 236 con->sring = xc_gnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, 237 con->ring_ref, 238 PROT_READ|PROT_WRITE); 239 } 240 if (!con->sring) 241 return -1; 242 243 xen_be_bind_evtchn(&con->xendev); 244 if (con->chr) { 245 if (qemu_chr_fe_claim(con->chr) == 0) { 246 qemu_chr_add_handlers(con->chr, xencons_can_receive, 247 xencons_receive, NULL, con); 248 } else { 249 xen_be_printf(xendev, 0, 250 "xen_console_init error chardev %s already used\n", 251 con->chr->label); 252 con->chr = NULL; 253 } 254 } 255 256 xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", 257 con->ring_ref, 258 con->xendev.remote_port, 259 con->xendev.local_port, 260 con->buffer.max_capacity); 261 return 0; 262 } 263 264 static void con_disconnect(struct XenDevice *xendev) 265 { 266 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 267 268 if (!xendev->dev) { 269 return; 270 } 271 if (con->chr) { 272 qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); 273 qemu_chr_fe_release(con->chr); 274 } 275 xen_be_unbind_evtchn(&con->xendev); 276 277 if (con->sring) { 278 if (!xendev->gnttabdev) { 279 munmap(con->sring, XC_PAGE_SIZE); 280 } else { 281 xc_gnttab_munmap(xendev->gnttabdev, con->sring, 1); 282 } 283 con->sring = NULL; 284 } 285 } 286 287 static void con_event(struct XenDevice *xendev) 288 { 289 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 290 291 buffer_append(con); 292 if (con->buffer.size - con->buffer.consumed) 293 xencons_send(con); 294 } 295 296 /* -------------------------------------------------------------------- */ 297 298 struct XenDevOps xen_console_ops = { 299 .size = sizeof(struct XenConsole), 300 .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV, 301 .init = con_init, 302 .initialise = con_initialise, 303 .event = con_event, 304 .disconnect = con_disconnect, 305 }; 306