ia64/xen-unstable
changeset 16235:b34ba3bcab0b
pv-qemu 8/10: Add pv console to QEMU paravirt machine
This patch adds a paravirt console driver to qemu-dm. This is used
when the QEMU machine type is 'xenpv', connecting to the ring buffer
provided by the guest kernel. The '-serial' command line flag controls
how the guest console is exposed.
For parity with xenconsoled the '-serial pty' arg can be used. For
guests which are running a qemu-dm device model, the xenconsoled
daemon is no longer needed for guest consoles. The code for the
xen_console.c is based on the original code in
tools/console/daemon/io.c, but simplified; since its only dealing with
a single guest there's no state tracking to worry about.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This patch adds a paravirt console driver to qemu-dm. This is used
when the QEMU machine type is 'xenpv', connecting to the ring buffer
provided by the guest kernel. The '-serial' command line flag controls
how the guest console is exposed.
For parity with xenconsoled the '-serial pty' arg can be used. For
guests which are running a qemu-dm device model, the xenconsoled
daemon is no longer needed for guest consoles. The code for the
xen_console.c is based on the original code in
tools/console/daemon/io.c, but simplified; since its only dealing with
a single guest there's no state tracking to worry about.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author | Keir Fraser <keir@xensource.com> |
---|---|
date | Thu Oct 25 14:42:40 2007 +0100 (2007-10-25) |
parents | 695871933840 |
children | e8905caa47b5 |
files | tools/ioemu/Makefile.target tools/ioemu/hw/xen_console.c tools/ioemu/hw/xen_console.h tools/ioemu/hw/xen_machine_pv.c tools/ioemu/xenstore.c tools/python/xen/xend/XendDomainInfo.py |
line diff
1.1 --- a/tools/ioemu/Makefile.target Thu Oct 25 14:41:35 2007 +0100 1.2 +++ b/tools/ioemu/Makefile.target Thu Oct 25 14:42:40 2007 +0100 1.3 @@ -412,6 +412,7 @@ VL_OBJS+= xen_platform.o 1.4 VL_OBJS+= xen_machine_fv.o 1.5 VL_OBJS+= xen_machine_pv.o 1.6 VL_OBJS+= xenfb.o 1.7 +VL_OBJS+= xen_console.o 1.8 VL_OBJS+= tpm_tis.o 1.9 CPPFLAGS += -DHAS_AUDIO 1.10 endif
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/tools/ioemu/hw/xen_console.c Thu Oct 25 14:42:40 2007 +0100 2.3 @@ -0,0 +1,432 @@ 2.4 +/* 2.5 + * Copyright (C) International Business Machines Corp., 2005 2.6 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> 2.7 + * 2.8 + * Copyright (C) Red Hat 2007 2.9 + * 2.10 + * Xen Console 2.11 + * 2.12 + * This program is free software; you can redistribute it and/or modify 2.13 + * it under the terms of the GNU General Public License as published by 2.14 + * the Free Software Foundation; under version 2 of the License. 2.15 + * 2.16 + * This program is distributed in the hope that it will be useful, 2.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 2.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2.19 + * GNU General Public License for more details. 2.20 + * 2.21 + * You should have received a copy of the GNU General Public License 2.22 + * along with this program; if not, write to the Free Software 2.23 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2.24 + */ 2.25 + 2.26 +#include <malloc.h> 2.27 +#include <stdlib.h> 2.28 +#include <errno.h> 2.29 +#include <string.h> 2.30 +#include <sys/select.h> 2.31 +#include <fcntl.h> 2.32 +#include <unistd.h> 2.33 +#include <termios.h> 2.34 +#include <stdarg.h> 2.35 +#include <sys/mman.h> 2.36 +#include <xs.h> 2.37 +#include <xen/io/console.h> 2.38 +#include <xenctrl.h> 2.39 + 2.40 +#include "vl.h" 2.41 + 2.42 +#include "xen_console.h" 2.43 + 2.44 +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) 2.45 + 2.46 +struct buffer 2.47 +{ 2.48 + uint8_t *data; 2.49 + size_t consumed; 2.50 + size_t size; 2.51 + size_t capacity; 2.52 + size_t max_capacity; 2.53 +}; 2.54 + 2.55 +struct domain 2.56 +{ 2.57 + int domid; 2.58 + struct buffer buffer; 2.59 + 2.60 + char *conspath; 2.61 + char *serialpath; 2.62 + int use_consolepath; 2.63 + int ring_ref; 2.64 + evtchn_port_t local_port; 2.65 + evtchn_port_t remote_port; 2.66 + int xce_handle; 2.67 + struct xs_handle *xsh; 2.68 + struct xencons_interface *interface; 2.69 + CharDriverState *chr; 2.70 +}; 2.71 + 2.72 + 2.73 +static void buffer_append(struct domain *dom) 2.74 +{ 2.75 + struct buffer *buffer = &dom->buffer; 2.76 + XENCONS_RING_IDX cons, prod, size; 2.77 + struct xencons_interface *intf = dom->interface; 2.78 + 2.79 + cons = intf->out_cons; 2.80 + prod = intf->out_prod; 2.81 + mb(); 2.82 + 2.83 + size = prod - cons; 2.84 + if ((size == 0) || (size > sizeof(intf->out))) 2.85 + return; 2.86 + 2.87 + if ((buffer->capacity - buffer->size) < size) { 2.88 + buffer->capacity += (size + 1024); 2.89 + buffer->data = realloc(buffer->data, buffer->capacity); 2.90 + if (buffer->data == NULL) { 2.91 + dolog(LOG_ERR, "Memory allocation failed"); 2.92 + exit(ENOMEM); 2.93 + } 2.94 + } 2.95 + 2.96 + while (cons != prod) 2.97 + buffer->data[buffer->size++] = intf->out[ 2.98 + MASK_XENCONS_IDX(cons++, intf->out)]; 2.99 + 2.100 + mb(); 2.101 + intf->out_cons = cons; 2.102 + xc_evtchn_notify(dom->xce_handle, dom->local_port); 2.103 + 2.104 + if (buffer->max_capacity && 2.105 + buffer->size > buffer->max_capacity) { 2.106 + /* Discard the middle of the data. */ 2.107 + 2.108 + size_t over = buffer->size - buffer->max_capacity; 2.109 + uint8_t *maxpos = buffer->data + buffer->max_capacity; 2.110 + 2.111 + memmove(maxpos - over, maxpos, over); 2.112 + buffer->data = realloc(buffer->data, buffer->max_capacity); 2.113 + buffer->size = buffer->capacity = buffer->max_capacity; 2.114 + 2.115 + if (buffer->consumed > buffer->max_capacity - over) 2.116 + buffer->consumed = buffer->max_capacity - over; 2.117 + } 2.118 +} 2.119 + 2.120 +static void buffer_advance(struct buffer *buffer, size_t len) 2.121 +{ 2.122 + buffer->consumed += len; 2.123 + if (buffer->consumed == buffer->size) { 2.124 + buffer->consumed = 0; 2.125 + buffer->size = 0; 2.126 + } 2.127 +} 2.128 + 2.129 +/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ 2.130 +int xs_gather(struct xs_handle *xs, const char *dir, ...) 2.131 +{ 2.132 + va_list ap; 2.133 + const char *name; 2.134 + char *path; 2.135 + int ret = 0; 2.136 + 2.137 + va_start(ap, dir); 2.138 + while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { 2.139 + const char *fmt = va_arg(ap, char *); 2.140 + void *result = va_arg(ap, void *); 2.141 + char *p; 2.142 + 2.143 + if (asprintf(&path, "%s/%s", dir, name) == -1) { 2.144 + ret = ENOMEM; 2.145 + break; 2.146 + } 2.147 + p = xs_read(xs, XBT_NULL, path, NULL); 2.148 + free(path); 2.149 + if (p == NULL) { 2.150 + ret = ENOENT; 2.151 + break; 2.152 + } 2.153 + if (fmt) { 2.154 + if (sscanf(p, fmt, result) == 0) 2.155 + ret = EINVAL; 2.156 + free(p); 2.157 + } else 2.158 + *(char **)result = p; 2.159 + } 2.160 + va_end(ap); 2.161 + return ret; 2.162 +} 2.163 + 2.164 +static int domain_create_ring(struct domain *dom) 2.165 +{ 2.166 + int err, remote_port, ring_ref, rc; 2.167 + 2.168 + err = xs_gather(dom->xsh, dom->serialpath, 2.169 + "ring-ref", "%u", &ring_ref, 2.170 + "port", "%i", &remote_port, 2.171 + NULL); 2.172 + if (err) { 2.173 + err = xs_gather(dom->xsh, dom->conspath, 2.174 + "ring-ref", "%u", &ring_ref, 2.175 + "port", "%i", &remote_port, 2.176 + NULL); 2.177 + if (err) { 2.178 + fprintf(stderr, "Console: failed to find ring-ref/port yet\n"); 2.179 + goto out; 2.180 + } 2.181 + dom->use_consolepath = 1; 2.182 + } else 2.183 + dom->use_consolepath = 0; 2.184 + fprintf(stderr, "Console: got ring-ref %d port %d\n", ring_ref, remote_port); 2.185 + 2.186 + if ((ring_ref == dom->ring_ref) && (remote_port == dom->remote_port)) 2.187 + goto out; 2.188 + 2.189 + if (ring_ref != dom->ring_ref) { 2.190 + if (dom->interface != NULL) 2.191 + munmap(dom->interface, getpagesize()); 2.192 + dom->interface = xc_map_foreign_range( 2.193 + xc_handle, dom->domid, getpagesize(), 2.194 + PROT_READ|PROT_WRITE, 2.195 + (unsigned long)ring_ref); 2.196 + if (dom->interface == NULL) { 2.197 + err = errno; 2.198 + goto out; 2.199 + } 2.200 + dom->ring_ref = ring_ref; 2.201 + } 2.202 + 2.203 + dom->local_port = -1; 2.204 + dom->remote_port = -1; 2.205 + 2.206 + dom->xce_handle = xc_evtchn_open(); 2.207 + if (dom->xce_handle == -1) { 2.208 + err = errno; 2.209 + goto out; 2.210 + } 2.211 + 2.212 + rc = xc_evtchn_bind_interdomain(dom->xce_handle, 2.213 + dom->domid, remote_port); 2.214 + 2.215 + if (rc == -1) { 2.216 + err = errno; 2.217 + xc_evtchn_close(dom->xce_handle); 2.218 + dom->xce_handle = -1; 2.219 + goto out; 2.220 + } 2.221 + dom->local_port = rc; 2.222 + dom->remote_port = remote_port; 2.223 + 2.224 + out: 2.225 + return err; 2.226 +} 2.227 + 2.228 + 2.229 +static struct domain *create_domain(int domid, CharDriverState *chr) 2.230 +{ 2.231 + struct domain *dom; 2.232 + char *s; 2.233 + 2.234 + dom = (struct domain *)malloc(sizeof(struct domain)); 2.235 + if (dom == NULL) { 2.236 + dolog(LOG_ERR, "Out of memory %s:%s():L%d", 2.237 + __FILE__, __FUNCTION__, __LINE__); 2.238 + exit(ENOMEM); 2.239 + } 2.240 + 2.241 + dom->domid = domid; 2.242 + dom->chr = chr; 2.243 + 2.244 + dom->xsh = xs_daemon_open(); 2.245 + if (dom->xsh == NULL) { 2.246 + fprintf(logfile, "Could not contact xenstore for console watch\n"); 2.247 + goto out; 2.248 + } 2.249 + 2.250 + dom->serialpath = xs_get_domain_path(dom->xsh, dom->domid); 2.251 + s = realloc(dom->serialpath, strlen(dom->serialpath) + 2.252 + strlen("/serial/0") + 1); 2.253 + if (s == NULL) 2.254 + goto out; 2.255 + dom->serialpath = s; 2.256 + strcat(dom->serialpath, "/serial/0"); 2.257 + 2.258 + dom->conspath = xs_get_domain_path(dom->xsh, dom->domid); 2.259 + s = realloc(dom->conspath, strlen(dom->conspath) + 2.260 + strlen("/console") + 1); 2.261 + if (s == NULL) 2.262 + goto out; 2.263 + dom->conspath = s; 2.264 + strcat(dom->conspath, "/console"); 2.265 + 2.266 + dom->buffer.data = 0; 2.267 + dom->buffer.consumed = 0; 2.268 + dom->buffer.size = 0; 2.269 + dom->buffer.capacity = 0; 2.270 + dom->buffer.max_capacity = 0; 2.271 + 2.272 + dom->ring_ref = -1; 2.273 + dom->local_port = -1; 2.274 + dom->remote_port = -1; 2.275 + dom->interface = NULL; 2.276 + dom->xce_handle = -1; 2.277 + 2.278 + 2.279 + return dom; 2.280 + out: 2.281 + free(dom->serialpath); 2.282 + free(dom->conspath); 2.283 + free(dom); 2.284 + return NULL; 2.285 +} 2.286 + 2.287 + 2.288 +static int ring_free_bytes(struct domain *dom) 2.289 +{ 2.290 + struct xencons_interface *intf = dom->interface; 2.291 + XENCONS_RING_IDX cons, prod, space; 2.292 + 2.293 + cons = intf->in_cons; 2.294 + prod = intf->in_prod; 2.295 + mb(); 2.296 + 2.297 + space = prod - cons; 2.298 + if (space > sizeof(intf->in)) 2.299 + return 0; /* ring is screwed: ignore it */ 2.300 + 2.301 + return (sizeof(intf->in) - space); 2.302 +} 2.303 + 2.304 +static int xencons_can_receive(void *opaque) 2.305 +{ 2.306 + struct domain *dom = (struct domain *)opaque; 2.307 + 2.308 + return ring_free_bytes(dom); 2.309 +} 2.310 + 2.311 +static void xencons_receive(void *opaque, const uint8_t *buf, int len) 2.312 +{ 2.313 + struct domain *dom = (struct domain *)opaque; 2.314 + int i, max; 2.315 + struct xencons_interface *intf = dom->interface; 2.316 + XENCONS_RING_IDX prod; 2.317 + 2.318 + max = ring_free_bytes(dom); 2.319 + /* The can_receive() func limits this, but check again anyway */ 2.320 + if (max < len) 2.321 + len = max; 2.322 + 2.323 + prod = intf->in_prod; 2.324 + for (i = 0; i < len; i++) { 2.325 + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = 2.326 + buf[i]; 2.327 + } 2.328 + wmb(); 2.329 + intf->in_prod = prod; 2.330 + xc_evtchn_notify(dom->xce_handle, dom->local_port); 2.331 +} 2.332 + 2.333 +static void xencons_send(struct domain *dom) 2.334 +{ 2.335 + ssize_t len; 2.336 + len = qemu_chr_write(dom->chr, dom->buffer.data + dom->buffer.consumed, 2.337 + dom->buffer.size - dom->buffer.consumed); 2.338 + if (len < 1) { 2.339 + /* 2.340 + * Disable log because if we're redirecting to /dev/pts/N we 2.341 + * don't want to flood logs when no client has the PTY open 2.342 + */ 2.343 + /* 2.344 + dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n", 2.345 + dom->domid, len, errno); 2.346 + */ 2.347 + } else { 2.348 + buffer_advance(&dom->buffer, len); 2.349 + } 2.350 +} 2.351 + 2.352 +static void xencons_ring_read(void *opaque) 2.353 +{ 2.354 + evtchn_port_t port; 2.355 + struct domain *dom = (struct domain *)opaque; 2.356 + 2.357 + if ((port = xc_evtchn_pending(dom->xce_handle)) == -1) 2.358 + return; 2.359 + 2.360 + buffer_append(dom); 2.361 + 2.362 + (void)xc_evtchn_unmask(dom->xce_handle, port); 2.363 + 2.364 + if (dom->buffer.size - dom->buffer.consumed) 2.365 + xencons_send(dom); 2.366 +} 2.367 + 2.368 +static void xencons_startup(void *opaque) 2.369 +{ 2.370 + struct domain *dom = (struct domain *)opaque; 2.371 + unsigned dummy; 2.372 + char **vec; 2.373 + int err; 2.374 + vec = xs_read_watch(dom->xsh, &dummy); 2.375 + if (vec) 2.376 + free(vec); 2.377 + fprintf(stderr, "Console: got watch\n"); 2.378 + err = domain_create_ring(dom); 2.379 + if (err) 2.380 + return; 2.381 + 2.382 + xs_unwatch(dom->xsh, dom->conspath, ""); 2.383 + xs_unwatch(dom->xsh, dom->serialpath, ""); 2.384 + qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, NULL, NULL, NULL); 2.385 + 2.386 + fprintf(stderr, "Console: connected to guest frontend\n"); 2.387 + if (qemu_set_fd_handler2(dom->xce_handle, NULL, xencons_ring_read, NULL, dom) < 0) 2.388 + return; 2.389 + 2.390 + qemu_chr_add_handlers(dom->chr, xencons_can_receive, xencons_receive, 2.391 + NULL, dom); 2.392 +} 2.393 + 2.394 + 2.395 +int xencons_init(int domid, CharDriverState *chr) 2.396 +{ 2.397 + struct domain *dom = create_domain(domid, chr); 2.398 + 2.399 + if (!dom) 2.400 + return -1; 2.401 + 2.402 + /* Setup watches so we asynchronously connect to serial console */ 2.403 + if (!(xs_watch(dom->xsh, dom->conspath, ""))) { 2.404 + fprintf(stderr, "Unable to watch console %s\n", dom->conspath); 2.405 + goto fail; 2.406 + } 2.407 + if (!(xs_watch(dom->xsh, dom->serialpath, ""))) { 2.408 + fprintf(stderr, "Unable to watch console %s\n", dom->conspath); 2.409 + xs_unwatch(dom->xsh, dom->conspath, ""); 2.410 + goto fail; 2.411 + } 2.412 + qemu_set_fd_handler2(xs_fileno(dom->xsh), NULL, xencons_startup, NULL, dom); 2.413 + fprintf(stderr, "Console: prepared domain, waiting for ringref at %s or %s\n", 2.414 + dom->conspath, dom->serialpath); 2.415 + 2.416 + return 0; 2.417 + 2.418 +fail: 2.419 + xs_daemon_close(dom->xsh); 2.420 + free(dom->serialpath); 2.421 + free(dom->conspath); 2.422 + free(dom); 2.423 + return -1; 2.424 +} 2.425 + 2.426 + 2.427 +/* 2.428 + * Local variables: 2.429 + * c-file-style: "linux" 2.430 + * indent-tabs-mode: t 2.431 + * c-indent-level: 8 2.432 + * c-basic-offset: 8 2.433 + * tab-width: 8 2.434 + * End: 2.435 + */
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/tools/ioemu/hw/xen_console.h Thu Oct 25 14:42:40 2007 +0100 3.3 @@ -0,0 +1,25 @@ 3.4 +/* 3.5 + * Copyright (C) International Business Machines Corp., 2005 3.6 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> 3.7 + * 3.8 + * Copyright (C) Red Hat 2007 3.9 + * 3.10 + * Xen Console 3.11 + * 3.12 + * This program is free software; you can redistribute it and/or modify 3.13 + * it under the terms of the GNU General Public License as published by 3.14 + * the Free Software Foundation; under version 2 of the License. 3.15 + * 3.16 + * This program is distributed in the hope that it will be useful, 3.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 3.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3.19 + * GNU General Public License for more details. 3.20 + * 3.21 + * You should have received a copy of the GNU General Public License 3.22 + * along with this program; if not, write to the Free Software 3.23 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 3.24 + */ 3.25 + 3.26 +#include "vl.h" 3.27 + 3.28 +extern int xencons_init(int domid, CharDriverState *chr);
4.1 --- a/tools/ioemu/hw/xen_machine_pv.c Thu Oct 25 14:41:35 2007 +0100 4.2 +++ b/tools/ioemu/hw/xen_machine_pv.c Thu Oct 25 14:42:40 2007 +0100 4.3 @@ -23,6 +23,7 @@ 4.4 */ 4.5 4.6 #include "vl.h" 4.7 +#include "xen_console.h" 4.8 #include "xenfb.h" 4.9 4.10 /* The Xen PV machine currently provides 4.11 @@ -39,6 +40,14 @@ static void xen_init_pv(uint64_t ram_siz 4.12 struct xenfb *xenfb; 4.13 extern int domid; 4.14 4.15 + /* Connect to text console */ 4.16 + if (serial_hds[0]) { 4.17 + if (xencons_init(domid, serial_hds[0]) < 0) { 4.18 + fprintf(stderr, "Could not connect to domain console\n"); 4.19 + exit(1); 4.20 + } 4.21 + } 4.22 + 4.23 /* Prepare PVFB state */ 4.24 xenfb = xenfb_new(domid, ds); 4.25 if (xenfb == NULL) {
5.1 --- a/tools/ioemu/xenstore.c Thu Oct 25 14:41:35 2007 +0100 5.2 +++ b/tools/ioemu/xenstore.c Thu Oct 25 14:42:40 2007 +0100 5.3 @@ -17,7 +17,7 @@ 5.4 #include <sys/stat.h> 5.5 #include <fcntl.h> 5.6 5.7 -static struct xs_handle *xsh = NULL; 5.8 +struct xs_handle *xsh = NULL; 5.9 static char *media_filename[MAX_DISKS + MAX_SCSI_DISKS]; 5.10 static QEMUTimer *insert_timer = NULL; 5.11
6.1 --- a/tools/python/xen/xend/XendDomainInfo.py Thu Oct 25 14:41:35 2007 +0100 6.2 +++ b/tools/python/xen/xend/XendDomainInfo.py Thu Oct 25 14:42:40 2007 +0100 6.3 @@ -1761,10 +1761,9 @@ class XendDomainInfo: 6.4 self.console_mfn = console_mfn 6.5 6.6 self._introduceDomain() 6.7 - if self.info.is_hvm(): 6.8 - self.image = image.create(self, self.info) 6.9 - if self.image: 6.10 - self.image.createDeviceModel(True) 6.11 + self.image = image.create(self, self.info) 6.12 + if self.image: 6.13 + self.image.createDeviceModel(True) 6.14 self._storeDomDetails() 6.15 self._registerWatches() 6.16 self.refreshShutdown()