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 "qemu/osdep.h" 23 #include <sys/select.h> 24 #include <termios.h> 25 26 #include "qapi/error.h" 27 #include "sysemu/sysemu.h" 28 #include "chardev/char-fe.h" 29 #include "hw/xen/xen-legacy-backend.h" 30 31 #include "hw/xen/interface/io/console.h" 32 33 struct buffer { 34 uint8_t *data; 35 size_t consumed; 36 size_t size; 37 size_t capacity; 38 size_t max_capacity; 39 }; 40 41 struct XenConsole { 42 struct XenLegacyDevice xendev; /* must be first */ 43 struct buffer buffer; 44 char console[XEN_BUFSIZE]; 45 int ring_ref; 46 void *sring; 47 CharBackend chr; 48 int backlog; 49 }; 50 51 static void buffer_append(struct XenConsole *con) 52 { 53 struct buffer *buffer = &con->buffer; 54 XENCONS_RING_IDX cons, prod, size; 55 struct xencons_interface *intf = con->sring; 56 57 cons = intf->out_cons; 58 prod = intf->out_prod; 59 xen_mb(); 60 61 size = prod - cons; 62 if ((size == 0) || (size > sizeof(intf->out))) 63 return; 64 65 if ((buffer->capacity - buffer->size) < size) { 66 buffer->capacity += (size + 1024); 67 buffer->data = g_realloc(buffer->data, buffer->capacity); 68 } 69 70 while (cons != prod) 71 buffer->data[buffer->size++] = intf->out[ 72 MASK_XENCONS_IDX(cons++, intf->out)]; 73 74 xen_mb(); 75 intf->out_cons = cons; 76 xen_pv_send_notify(&con->xendev); 77 78 if (buffer->max_capacity && 79 buffer->size > buffer->max_capacity) { 80 /* Discard the middle of the data. */ 81 82 size_t over = buffer->size - buffer->max_capacity; 83 uint8_t *maxpos = buffer->data + buffer->max_capacity; 84 85 memmove(maxpos - over, maxpos, over); 86 buffer->data = g_realloc(buffer->data, buffer->max_capacity); 87 buffer->size = buffer->capacity = buffer->max_capacity; 88 89 if (buffer->consumed > buffer->max_capacity - over) 90 buffer->consumed = buffer->max_capacity - over; 91 } 92 } 93 94 static void buffer_advance(struct buffer *buffer, size_t len) 95 { 96 buffer->consumed += len; 97 if (buffer->consumed == buffer->size) { 98 buffer->consumed = 0; 99 buffer->size = 0; 100 } 101 } 102 103 static int ring_free_bytes(struct XenConsole *con) 104 { 105 struct xencons_interface *intf = con->sring; 106 XENCONS_RING_IDX cons, prod, space; 107 108 cons = intf->in_cons; 109 prod = intf->in_prod; 110 xen_mb(); 111 112 space = prod - cons; 113 if (space > sizeof(intf->in)) 114 return 0; /* ring is screwed: ignore it */ 115 116 return (sizeof(intf->in) - space); 117 } 118 119 static int xencons_can_receive(void *opaque) 120 { 121 struct XenConsole *con = opaque; 122 return ring_free_bytes(con); 123 } 124 125 static void xencons_receive(void *opaque, const uint8_t *buf, int len) 126 { 127 struct XenConsole *con = opaque; 128 struct xencons_interface *intf = con->sring; 129 XENCONS_RING_IDX prod; 130 int i, max; 131 132 max = ring_free_bytes(con); 133 /* The can_receive() func limits this, but check again anyway */ 134 if (max < len) 135 len = max; 136 137 prod = intf->in_prod; 138 for (i = 0; i < len; i++) { 139 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = 140 buf[i]; 141 } 142 xen_wmb(); 143 intf->in_prod = prod; 144 xen_pv_send_notify(&con->xendev); 145 } 146 147 static void xencons_send(struct XenConsole *con) 148 { 149 ssize_t len, size; 150 151 size = con->buffer.size - con->buffer.consumed; 152 if (qemu_chr_fe_backend_connected(&con->chr)) { 153 len = qemu_chr_fe_write(&con->chr, 154 con->buffer.data + con->buffer.consumed, 155 size); 156 } else { 157 len = size; 158 } 159 if (len < 1) { 160 if (!con->backlog) { 161 con->backlog = 1; 162 xen_pv_printf(&con->xendev, 1, 163 "backlog piling up, nobody listening?\n"); 164 } 165 } else { 166 buffer_advance(&con->buffer, len); 167 if (con->backlog && len == size) { 168 con->backlog = 0; 169 xen_pv_printf(&con->xendev, 1, "backlog is gone\n"); 170 } 171 } 172 } 173 174 /* -------------------------------------------------------------------- */ 175 176 static int store_con_info(struct XenConsole *con) 177 { 178 Chardev *cs = qemu_chr_fe_get_driver(&con->chr); 179 char *pts = NULL; 180 char *dom_path; 181 GString *path; 182 int ret = -1; 183 184 /* Only continue if we're talking to a pty. */ 185 if (!CHARDEV_IS_PTY(cs)) { 186 return 0; 187 } 188 pts = cs->filename + 4; 189 190 dom_path = qemu_xen_xs_get_domain_path(xenstore, xen_domid); 191 if (!dom_path) { 192 return 0; 193 } 194 195 path = g_string_new(dom_path); 196 free(dom_path); 197 198 if (con->xendev.dev) { 199 g_string_append_printf(path, "/device/console/%d", con->xendev.dev); 200 } else { 201 g_string_append(path, "/console"); 202 } 203 g_string_append(path, "/tty"); 204 205 if (xenstore_write_str(con->console, path->str, pts)) { 206 fprintf(stderr, "xenstore_write_str for '%s' fail", path->str); 207 goto out; 208 } 209 ret = 0; 210 211 out: 212 g_string_free(path, true); 213 free(path); 214 215 return ret; 216 } 217 218 static int con_init(struct XenLegacyDevice *xendev) 219 { 220 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 221 char *type, *dom, label[32]; 222 int ret = 0; 223 const char *output; 224 225 /* setup */ 226 dom = qemu_xen_xs_get_domain_path(xenstore, con->xendev.dom); 227 if (!xendev->dev) { 228 snprintf(con->console, sizeof(con->console), "%s/console", dom); 229 } else { 230 snprintf(con->console, sizeof(con->console), "%s/device/console/%d", dom, xendev->dev); 231 } 232 free(dom); 233 234 type = xenstore_read_str(con->console, "type"); 235 if (!type || strcmp(type, "ioemu") != 0) { 236 xen_pv_printf(xendev, 1, "not for me (type=%s)\n", type); 237 ret = -1; 238 goto out; 239 } 240 241 output = xenstore_read_str(con->console, "output"); 242 243 /* no Xen override, use qemu output device */ 244 if (output == NULL) { 245 if (con->xendev.dev) { 246 qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev), 247 &error_abort); 248 } 249 } else { 250 snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); 251 qemu_chr_fe_init(&con->chr, 252 /* 253 * FIXME: sure we want to support implicit 254 * muxed monitors here? 255 */ 256 qemu_chr_new_mux_mon(label, output, NULL), 257 &error_abort); 258 } 259 260 store_con_info(con); 261 262 out: 263 g_free(type); 264 return ret; 265 } 266 267 static int con_initialise(struct XenLegacyDevice *xendev) 268 { 269 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 270 int limit; 271 272 if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) 273 return -1; 274 if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) 275 return -1; 276 if (xenstore_read_int(con->console, "limit", &limit) == 0) 277 con->buffer.max_capacity = limit; 278 279 if (!xendev->dev) { 280 xen_pfn_t mfn = con->ring_ref; 281 con->sring = qemu_xen_foreignmem_map(con->xendev.dom, NULL, 282 PROT_READ | PROT_WRITE, 283 1, &mfn, NULL); 284 } else { 285 con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, 286 PROT_READ | PROT_WRITE); 287 } 288 if (!con->sring) 289 return -1; 290 291 xen_be_bind_evtchn(&con->xendev); 292 qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, 293 xencons_receive, NULL, NULL, con, NULL, true); 294 295 xen_pv_printf(xendev, 1, 296 "ring mfn %d, remote port %d, local port %d, limit %zd\n", 297 con->ring_ref, 298 con->xendev.remote_port, 299 con->xendev.local_port, 300 con->buffer.max_capacity); 301 return 0; 302 } 303 304 static void con_disconnect(struct XenLegacyDevice *xendev) 305 { 306 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 307 308 qemu_chr_fe_deinit(&con->chr, false); 309 xen_pv_unbind_evtchn(&con->xendev); 310 311 if (con->sring) { 312 if (!xendev->dev) { 313 qemu_xen_foreignmem_unmap(con->sring, 1); 314 } else { 315 xen_be_unmap_grant_ref(xendev, con->sring, con->ring_ref); 316 } 317 con->sring = NULL; 318 } 319 } 320 321 static void con_event(struct XenLegacyDevice *xendev) 322 { 323 struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); 324 325 buffer_append(con); 326 if (con->buffer.size - con->buffer.consumed) 327 xencons_send(con); 328 } 329 330 /* -------------------------------------------------------------------- */ 331 332 struct XenDevOps xen_console_ops = { 333 .size = sizeof(struct XenConsole), 334 .flags = DEVOPS_FLAG_IGNORE_STATE|DEVOPS_FLAG_NEED_GNTDEV, 335 .init = con_init, 336 .initialise = con_initialise, 337 .event = con_event, 338 .disconnect = con_disconnect, 339 }; 340