From: Ian Jackson Date: Wed, 5 Mar 2008 18:10:39 +0000 (+0000) Subject: wip xen landing from xen-unstable 17192:59b8768d0d0d X-Git-Tag: xen-3.3.0-rc1~234 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=88026221c8d41c2488011ba94d397180333c53ea;p=qemu-xen-3.4-testing.git wip xen landing from xen-unstable 17192:59b8768d0d0d --- diff --git a/.gitignore b/.gitignore index d47fcc4c..4189d92d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ qemu-img *.[oa] *~ + +i386-dm/Makefile diff --git a/hw/xen_console.c b/hw/xen_console.c new file mode 100644 index 00000000..fbd33f07 --- /dev/null +++ b/hw/xen_console.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori + * + * 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 with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vl.h" + +#include "xen_console.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 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; +}; + + +static void buffer_append(struct domain *dom) +{ + 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); + } + } + + 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); + + 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); + buffer->data = 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; + } +} + +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, rc; + + err = xs_gather(dom->xsh, dom->serialpath, + "ring-ref", "%u", &ring_ref, + "port", "%i", &remote_port, + NULL); + if (err) { + err = xs_gather(dom->xsh, dom->conspath, + "ring-ref", "%u", &ring_ref, + "port", "%i", &remote_port, + 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; + fprintf(stderr, "Console: got ring-ref %d port %d\n", ring_ref, remote_port); + + 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; +} + + +static int ring_free_bytes(struct domain *dom) +{ + struct xencons_interface *intf = dom->interface; + 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 domain *dom = (struct domain *)opaque; + + return ring_free_bytes(dom); +} + +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); +} + +static void xencons_send(struct domain *dom) +{ + 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); + } +} + +static void xencons_ring_read(void *opaque) +{ + evtchn_port_t port; + struct domain *dom = (struct domain *)opaque; + + if ((port = xc_evtchn_pending(dom->xce_handle)) == -1) + return; + + buffer_append(dom); + + (void)xc_evtchn_unmask(dom->xce_handle, port); + + if (dom->buffer.size - dom->buffer.consumed) + xencons_send(dom); +} + +static void xencons_startup(void *opaque) +{ + 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); +} + + +int xencons_init(int domid, CharDriverState *chr) +{ + struct domain *dom = create_domain(domid, chr); + + if (!dom) + return -1; + + /* 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); + + return 0; + +fail: + xs_daemon_close(dom->xsh); + free(dom->serialpath); + free(dom->conspath); + free(dom); + return -1; +} + + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/hw/xen_console.h b/hw/xen_console.h new file mode 100644 index 00000000..ece35776 --- /dev/null +++ b/hw/xen_console.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori + * + * 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 with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "vl.h" + +extern int xencons_init(int domid, CharDriverState *chr); diff --git a/hw/xen_machine_fv.c b/hw/xen_machine_fv.c new file mode 100644 index 00000000..2d02b5e1 --- /dev/null +++ b/hw/xen_machine_fv.c @@ -0,0 +1,295 @@ +/* + * QEMU Xen FV Machine + * + * Copyright (c) 2003-2007 Fabrice Bellard + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" +#ifdef CONFIG_STUBDOM +#include +#endif +#include +#include + +#if defined(MAPCACHE) + +#if defined(__i386__) +#define MAX_MCACHE_SIZE 0x40000000 /* 1GB max for x86 */ +#define MCACHE_BUCKET_SHIFT 16 +#elif defined(__x86_64__) +#define MAX_MCACHE_SIZE 0x1000000000 /* 64GB max for x86_64 */ +#define MCACHE_BUCKET_SHIFT 20 +#endif + +#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) + +#define BITS_PER_LONG (sizeof(long)*8) +#define BITS_TO_LONGS(bits) \ + (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG) +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] +#define test_bit(bit,map) \ + (!!((map)[(bit)/BITS_PER_LONG] & (1UL << ((bit)%BITS_PER_LONG)))) + +struct map_cache { + unsigned long paddr_index; + uint8_t *vaddr_base; + DECLARE_BITMAP(valid_mapping, MCACHE_BUCKET_SIZE>>XC_PAGE_SHIFT); +}; + +static struct map_cache *mapcache_entry; +static unsigned long nr_buckets; + +/* For most cases (>99.9%), the page address is the same. */ +static unsigned long last_address_index = ~0UL; +static uint8_t *last_address_vaddr; + +static int qemu_map_cache_init(void) +{ + unsigned long size; + + nr_buckets = (((MAX_MCACHE_SIZE >> XC_PAGE_SHIFT) + + (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> + (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); + + /* + * Use mmap() directly: lets us allocate a big hash table with no up-front + * cost in storage space. The OS will allocate memory only for the buckets + * that we actually use. All others will contain all zeroes. + */ + size = nr_buckets * sizeof(struct map_cache); + size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); + fprintf(logfile, "qemu_map_cache_init nr_buckets = %lx size %lu\n", nr_buckets, size); + mapcache_entry = mmap(NULL, size, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANON, -1, 0); + if (mapcache_entry == MAP_FAILED) { + errno = ENOMEM; + return -1; + } + + return 0; +} + +static void qemu_remap_bucket(struct map_cache *entry, + unsigned long address_index) +{ + uint8_t *vaddr_base; + unsigned long pfns[MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT]; + unsigned int i, j; + + if (entry->vaddr_base != NULL) { + errno = munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE); + if (errno) { + fprintf(logfile, "unmap fails %d\n", errno); + exit(-1); + } + } + + for (i = 0; i < MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT; i++) + pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; + + vaddr_base = xc_map_foreign_batch(xc_handle, domid, PROT_READ|PROT_WRITE, + pfns, MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT); + if (vaddr_base == NULL) { + fprintf(logfile, "xc_map_foreign_batch error %d\n", errno); + exit(-1); + } + + entry->vaddr_base = vaddr_base; + entry->paddr_index = address_index; + + for (i = 0; i < MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT; i += BITS_PER_LONG) { + unsigned long word = 0; + j = ((i + BITS_PER_LONG) > (MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT)) ? + (MCACHE_BUCKET_SIZE >> XC_PAGE_SHIFT) % BITS_PER_LONG : BITS_PER_LONG; + while (j > 0) + word = (word << 1) | (((pfns[i + --j] >> 28) & 0xf) != 0xf); + entry->valid_mapping[i / BITS_PER_LONG] = word; + } +} + +uint8_t *qemu_map_cache(target_phys_addr_t phys_addr) +{ + struct map_cache *entry; + unsigned long address_index = phys_addr >> MCACHE_BUCKET_SHIFT; + unsigned long address_offset = phys_addr & (MCACHE_BUCKET_SIZE-1); + + if (address_index == last_address_index) + return last_address_vaddr + address_offset; + + entry = &mapcache_entry[address_index % nr_buckets]; + + if (entry->vaddr_base == NULL || entry->paddr_index != address_index || + !test_bit(address_offset>>XC_PAGE_SHIFT, entry->valid_mapping)) + qemu_remap_bucket(entry, address_index); + + if (!test_bit(address_offset>>XC_PAGE_SHIFT, entry->valid_mapping)) + return NULL; + + last_address_index = address_index; + last_address_vaddr = entry->vaddr_base; + + return last_address_vaddr + address_offset; +} + +void qemu_invalidate_map_cache(void) +{ + unsigned long i; + + mapcache_lock(); + + for (i = 0; i < nr_buckets; i++) { + struct map_cache *entry = &mapcache_entry[i]; + + if (entry->vaddr_base == NULL) + continue; + + errno = munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE); + if (errno) { + fprintf(logfile, "unmap fails %d\n", errno); + exit(-1); + } + + entry->paddr_index = 0; + entry->vaddr_base = NULL; + } + + last_address_index = ~0UL; + last_address_vaddr = NULL; + + mapcache_unlock(); +} + +#endif /* defined(MAPCACHE) */ + + +static void xen_init_fv(uint64_t ram_size, int vga_ram_size, char *boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *direct_pci) +{ + unsigned long ioreq_pfn; + extern void *shared_page; + extern void *buffered_io_page; +#ifdef __ia64__ + unsigned long nr_pages; + xen_pfn_t *page_array; + extern void *buffered_pio_page; + int i; +#endif + +#if defined(__i386__) || defined(__x86_64__) + + if (qemu_map_cache_init()) { + fprintf(logfile, "qemu_map_cache_init returned: error %d\n", errno); + exit(-1); + } +#endif + + xc_get_hvm_param(xc_handle, domid, HVM_PARAM_IOREQ_PFN, &ioreq_pfn); + fprintf(logfile, "shared page at pfn %lx\n", ioreq_pfn); + shared_page = xc_map_foreign_range(xc_handle, domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (shared_page == NULL) { + fprintf(logfile, "map shared IO page returned error %d\n", errno); + exit(-1); + } + + xc_get_hvm_param(xc_handle, domid, HVM_PARAM_BUFIOREQ_PFN, &ioreq_pfn); + fprintf(logfile, "buffered io page at pfn %lx\n", ioreq_pfn); + buffered_io_page = xc_map_foreign_range(xc_handle, domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (buffered_io_page == NULL) { + fprintf(logfile, "map buffered IO page returned error %d\n", errno); + exit(-1); + } + +#if defined(__ia64__) + xc_get_hvm_param(xc_handle, domid, HVM_PARAM_BUFPIOREQ_PFN, &ioreq_pfn); + fprintf(logfile, "buffered pio page at pfn %lx\n", ioreq_pfn); + buffered_pio_page = xc_map_foreign_range(xc_handle, domid, XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, ioreq_pfn); + if (buffered_pio_page == NULL) { + fprintf(logfile, "map buffered PIO page returned error %d\n", errno); + exit(-1); + } + + nr_pages = ram_size / XC_PAGE_SIZE; + + page_array = (xen_pfn_t *)malloc(nr_pages * sizeof(xen_pfn_t)); + if (page_array == NULL) { + fprintf(logfile, "malloc returned error %d\n", errno); + exit(-1); + } + + for (i = 0; i < nr_pages; i++) + page_array[i] = i; + + /* VTI will not use memory between 3G~4G, so we just pass a legal pfn + to make QEMU map continuous virtual memory space */ + if (ram_size > MMIO_START) { + for (i = 0 ; i < (MEM_G >> XC_PAGE_SHIFT); i++) + page_array[(MMIO_START >> XC_PAGE_SHIFT) + i] = + (STORE_PAGE_START >> XC_PAGE_SHIFT); + } + /* skipping VGA hole, same as above */ + if (ram_size > VGA_IO_START) { + for (i = 0 ; i < (VGA_IO_SIZE >> XC_PAGE_SHIFT); i++) + page_array[(VGA_IO_START >> XC_PAGE_SHIFT) + i] = + (STORE_PAGE_START >> XC_PAGE_SHIFT); + } + + phys_ram_base = xc_map_foreign_batch(xc_handle, domid, + PROT_READ|PROT_WRITE, + page_array, nr_pages); + if (phys_ram_base == 0) { + fprintf(logfile, "xc_map_foreign_batch returned error %d\n", errno); + exit(-1); + } + free(page_array); +#endif + + timeoffset_get(); + + + pc_machine.init(ram_size, vga_ram_size, boot_device, ds, fd_filename, + snapshot, kernel_filename, kernel_cmdline, initrd_filename, + direct_pci); +} + +QEMUMachine xenfv_machine = { + "xenfv", + "Xen Fully-virtualized PC", + xen_init_fv, +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c new file mode 100644 index 00000000..41f051db --- /dev/null +++ b/hw/xen_machine_pv.c @@ -0,0 +1,74 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "vl.h" +#include "xen_console.h" +#include "xenfb.h" + +/* The Xen PV machine currently provides + * - a virtual framebuffer + * - .... + */ +static void xen_init_pv(uint64_t ram_size, int vga_ram_size, char *boot_device, + DisplayState *ds, const char **fd_filename, + int snapshot, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *direct_pci) +{ + struct xenfb *xenfb; + extern int domid; + + /* Connect to text console */ + if (serial_hds[0]) { + if (xencons_init(domid, serial_hds[0]) < 0) { + fprintf(stderr, "Could not connect to domain console\n"); + exit(1); + } + } + + /* Prepare PVFB state */ + xenfb = xenfb_new(domid, ds); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } +} + +QEMUMachine xenpv_machine = { + "xenpv", + "Xen Para-virtualized PC", + xen_init_pv, +}; + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/hw/xen_platform.c b/hw/xen_platform.c new file mode 100644 index 00000000..597e2c05 --- /dev/null +++ b/hw/xen_platform.c @@ -0,0 +1,160 @@ +/* + * XEN platform fake pci device, formerly known as the event channel device + * + * Copyright (c) 2003-2004 Intel Corp. + * Copyright (c) 2006 XenSource + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "vl.h" + +#include + +extern FILE *logfile; + +static void platform_ioport_map(PCIDevice *pci_dev, int region_num, + uint32_t addr, uint32_t size, int type) +{ + /* nothing yet */ +} + +static uint32_t platform_mmio_read(void *opaque, target_phys_addr_t addr) +{ + static int warnings = 0; + if (warnings < 5) { + fprintf(logfile, "Warning: attempted read from physical address " + "0x%"PRIx64" in xen platform mmio space\n", (uint64_t)addr); + warnings++; + } + return 0; +} + +static void platform_mmio_write(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + static int warnings = 0; + if (warnings < 5) { + fprintf(logfile, "Warning: attempted write of 0x%x to physical " + "address 0x%"PRIx64" in xen platform mmio space\n", + val, (uint64_t)addr); + warnings++; + } + return; +} + +static CPUReadMemoryFunc *platform_mmio_read_funcs[3] = { + platform_mmio_read, + platform_mmio_read, + platform_mmio_read, +}; + +static CPUWriteMemoryFunc *platform_mmio_write_funcs[3] = { + platform_mmio_write, + platform_mmio_write, + platform_mmio_write, +}; + +static void platform_mmio_map(PCIDevice *d, int region_num, + uint32_t addr, uint32_t size, int type) +{ + int mmio_io_addr; + + mmio_io_addr = cpu_register_io_memory(0, platform_mmio_read_funcs, + platform_mmio_write_funcs, NULL); + + cpu_register_physical_memory(addr, 0x1000000, mmio_io_addr); +} + +struct pci_config_header { + uint16_t vendor_id; + uint16_t device_id; + uint16_t command; + uint16_t status; + uint8_t revision; + uint8_t api; + uint8_t subclass; + uint8_t class; + uint8_t cache_line_size; /* Units of 32 bit words */ + uint8_t latency_timer; /* In units of bus cycles */ + uint8_t header_type; /* Should be 0 */ + uint8_t bist; /* Built in self test */ + uint32_t base_address_regs[6]; + uint32_t reserved1; + uint16_t subsystem_vendor_id; + uint16_t subsystem_id; + uint32_t rom_addr; + uint32_t reserved3; + uint32_t reserved4; + uint8_t interrupt_line; + uint8_t interrupt_pin; + uint8_t min_gnt; + uint8_t max_lat; +}; + +void xen_pci_save(QEMUFile *f, void *opaque) +{ + PCIDevice *d = opaque; + + pci_device_save(d, f); +} + +int xen_pci_load(QEMUFile *f, void *opaque, int version_id) +{ + PCIDevice *d = opaque; + + if (version_id != 1) + return -EINVAL; + + return pci_device_load(d, f); +} + +void pci_xen_platform_init(PCIBus *bus) +{ + PCIDevice *d; + struct pci_config_header *pch; + + printf("Register xen platform.\n"); + d = pci_register_device(bus, "xen-platform", sizeof(PCIDevice), -1, NULL, + NULL); + pch = (struct pci_config_header *)d->config; + pch->vendor_id = 0x5853; + pch->device_id = 0x0001; + pch->command = 3; /* IO and memory access */ + pch->revision = 1; + pch->api = 0; + pch->subclass = 0x80; /* Other */ + pch->class = 0xff; /* Unclassified device class */ + pch->header_type = 0; + pch->interrupt_pin = 1; + + /* Microsoft WHQL requires non-zero subsystem IDs. */ + /* http://www.pcisig.com/reflector/msg02205.html. */ + pch->subsystem_vendor_id = pch->vendor_id; /* Duplicate vendor id. */ + pch->subsystem_id = 0x0001; /* Hardcode sub-id as 1. */ + + pci_register_io_region(d, 0, 0x100, PCI_ADDRESS_SPACE_IO, + platform_ioport_map); + + /* reserve 16MB mmio address for share memory*/ + pci_register_io_region(d, 1, 0x1000000, PCI_ADDRESS_SPACE_MEM_PREFETCH, + platform_mmio_map); + + register_savevm("platform", 0, 1, xen_pci_save, xen_pci_load, d); + printf("Done register platform.\n"); +} diff --git a/hw/xenfb.c b/hw/xenfb.c new file mode 100644 index 00000000..affbcaaa --- /dev/null +++ b/hw/xenfb.c @@ -0,0 +1,1411 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenfb.h" + +#ifdef CONFIG_STUBDOM +#include +#include +#include +#endif + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from */ +#endif + +// FIXME defend against malicious frontend? + +struct xenfb; + +struct xenfb_device { + const char *devicetype; + char nodename[64]; /* backend xenstore dir */ + char otherend[64]; /* frontend xenstore dir */ + int otherend_id; /* frontend domid */ + enum xenbus_state state; /* backend state */ + void *page; /* shared page */ + evtchn_port_t port; + struct xenfb *xenfb; +}; + +struct xenfb { + DisplayState *ds; /* QEMU graphical console state */ + int evt_xch; /* event channel driver handle */ + int xc; /* hypervisor interface handle */ + struct xs_handle *xsh; /* xs daemon handle */ + struct xenfb_device fb, kbd; + void *pixels; /* guest framebuffer data */ + size_t fb_len; /* size of framebuffer */ + int row_stride; /* width of one row in framebuffer */ + int depth; /* colour depth of guest framebuffer */ + int width; /* pixel width of guest framebuffer */ + int height; /* pixel height of guest framebuffer */ + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + char protocol[64]; /* frontend protocol */ +}; + +/* Functions for frontend/backend state machine*/ +static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler); +static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler); +static void xenfb_backend_created_kbd(void *opaque); +static void xenfb_backend_created_fb(void *opaque); +static void xenfb_frontend_initialized_kbd(void *opaque); +static void xenfb_frontend_initialized_fb(void *opaque); +static void xenfb_frontend_connected_kbd(void *opaque); + +/* Helper functions for checking state of frontend/backend devices */ +static int xenfb_frontend_connected(struct xenfb_device *dev); +static int xenfb_frontend_initialized(struct xenfb_device *dev); +static int xenfb_backend_created(struct xenfb_device *dev); + +/* Functions which tie the PVFB into the QEMU device model */ +static void xenfb_key_event(void *opaque, int keycode); +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state); +static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h); +static void xenfb_update(void *opaque); +static void xenfb_invalidate(void *opaque); +static void xenfb_screen_dump(void *opaque, const char *name); +static int xenfb_register_console(struct xenfb *xenfb); + +/* + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +static const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +static const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; + +static unsigned char scancode2linux[512]; + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static void xenfb_device_init(struct xenfb_device *dev, + const char *type, + struct xenfb *xenfb) +{ + dev->devicetype = type; + dev->otherend_id = -1; + dev->port = -1; + dev->xenfb = xenfb; +} + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_device_set_domain(struct xenfb_device *dev, int domid) +{ + dev->otherend_id = domid; + + if (!xenfb_path_in_dom(dev->xenfb->xsh, + dev->otherend, sizeof(dev->otherend), + domid, "device/%s/0", dev->devicetype)) { + errno = ENOENT; + return -1; + } + if (!xenfb_path_in_dom(dev->xenfb->xsh, + dev->nodename, sizeof(dev->nodename), + 0, "backend/%s/%d/0", dev->devicetype, domid)) { + errno = ENOENT; + return -1; + } + + return 0; +} + +struct xenfb *xenfb_new(int domid, DisplayState *ds) +{ + struct xenfb *xenfb = qemu_malloc(sizeof(struct xenfb)); + int serrno; + int i; + + if (xenfb == NULL) + return NULL; + + /* Prepare scancode mapping table */ + for (i = 0; i < 128; i++) { + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + scancode2linux[i | 0x80] = + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + } + + memset(xenfb, 0, sizeof(*xenfb)); + xenfb->evt_xch = xenfb->xc = -1; + xenfb_device_init(&xenfb->fb, "vfb", xenfb); + xenfb_device_init(&xenfb->kbd, "vkbd", xenfb); + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) + goto fail; + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) + goto fail; + + xenfb->xsh = xs_daemon_open(); + if (!xenfb->xsh) + goto fail; + + xenfb->ds = ds; + xenfb_device_set_domain(&xenfb->fb, domid); + xenfb_device_set_domain(&xenfb->kbd, domid); + + fprintf(stderr, "FB: Waiting for KBD backend creation\n"); + xenfb_wait_for_backend(&xenfb->kbd, xenfb_backend_created_kbd); + + return xenfb; + + fail: + serrno = errno; + xenfb_shutdown(xenfb); + errno = serrno; + return NULL; +} + + +static enum xenbus_state xenfb_read_state(struct xs_handle *xsh, + const char *dir) +{ + int ret, state; + + ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state); + if (ret < 0) + return XenbusStateUnknown; + + if ((unsigned)state > XenbusStateClosed) + state = XenbusStateUnknown; + return state; +} + +static int xenfb_switch_state(struct xenfb_device *dev, + enum xenbus_state state) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + + if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0) + return -1; + dev->state = state; + return 0; +} + + +static int xenfb_hotplug(struct xenfb_device *dev) +{ + if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, + "hotplug-status", "connected")) + return -1; + return 0; +} + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct xenfb *xenfb, int domid) +{ + struct xenfb_page *page = xenfb->fb.page; + int n_fbmfns; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (0 == strlen(xenfb->protocol)) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don't set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (0 == ptr32[1]) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_32)) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_64)) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = malloc(sizeof(unsigned long) * n_fbdirs); + fbmfns = malloc(sizeof(unsigned long) * n_fbmfns); + if (!pgmfns || !fbmfns) + goto out; + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xenfb->xc, domid, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xenfb->xc, domid, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + + out: + if (pgmfns) + free(pgmfns); + if (fbmfns) + free(fbmfns); + return ret; +} + +static int xenfb_bind(struct xenfb_device *dev) +{ + struct xenfb *xenfb = dev->xenfb; + unsigned long mfn; + evtchn_port_t evtchn; + + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu", + &mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u", + &evtchn) < 0) + return -1; + + dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch, + dev->otherend_id, evtchn); + if (dev->port == -1) + return -1; + + dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id, + XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + if (dev->page == NULL) + return -1; + + return 0; +} + +static void xenfb_unbind(struct xenfb_device *dev) +{ + if (dev->page) { + munmap(dev->page, XC_PAGE_SIZE); + dev->page = NULL; + } + if (dev->port >= 0) { + xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port); + dev->port = -1; + } +} + + +static void xenfb_detach_dom(struct xenfb *xenfb) +{ + xenfb_unbind(&xenfb->fb); + xenfb_unbind(&xenfb->kbd); + if (xenfb->pixels) { + munmap(xenfb->pixels, xenfb->fb_len); + xenfb->pixels = NULL; + } +} + +/* Remove the backend area in xenbus since the framebuffer really is + going away. */ +void xenfb_shutdown(struct xenfb *xenfb) +{ + fprintf(stderr, "FB: Shutting down backend\n"); + xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename); + xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename); + + xenfb_detach_dom(xenfb); + if (xenfb->xc >= 0) + xc_interface_close(xenfb->xc); + if (xenfb->evt_xch >= 0) + xc_evtchn_close(xenfb->evt_xch); + if (xenfb->xsh) + xs_daemon_close(xenfb->xsh); + free(xenfb); +} + + +static void xenfb_on_fb_event(struct xenfb *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->fb.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "%s bogus update ignored\n", + xenfb->fb.nodename); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "%s bogus update clipped\n", + xenfb->fb.nodename); + break; + } + xenfb_guest_copy(xenfb, x, y, w, h); + break; + } + } + xen_mb(); /* ensure we're done with ring contents */ + page->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port); +} + +static void xenfb_on_kbd_event(struct xenfb *xenfb) +{ + struct xenkbd_page *page = xenfb->kbd.page; + + /* We don't understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +static int xenfb_on_state_change(struct xenfb_device *dev) +{ + enum xenbus_state state; + + state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); + + switch (state) { + case XenbusStateUnknown: + /* There was an error reading the frontend state. The + domain has probably gone away; in any case, there's + not much point in us continuing. */ + return -1; + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + break; + case XenbusStateClosing: + xenfb_unbind(dev); + xenfb_switch_state(dev, state); + break; + case XenbusStateClosed: + xenfb_switch_state(dev, state); + } + return 0; +} + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct xenfb *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *page = xenfb->kbd.page; + + if (xenfb->kbd.state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct xenfb *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + event.motion.rel_z = rel_z; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct xenfb *xenfb, + int abs_x, int abs_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + event.pos.rel_z = rel_z; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Process events from the frontend event channel */ +static void xenfb_dispatch_channel(void *opaque) +{ + struct xenfb *xenfb = (struct xenfb *)opaque; + evtchn_port_t port; + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) { + xenfb_shutdown(xenfb); + exit(1); + } + + if (port == xenfb->fb.port) + xenfb_on_fb_event(xenfb); + else if (port == xenfb->kbd.port) + xenfb_on_kbd_event(xenfb); + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) { + xenfb_shutdown(xenfb); + exit(1); + } +} + +/* Process ongoing events from the frontend devices */ +static void xenfb_dispatch_store(void *opaque) +{ + struct xenfb *xenfb = (struct xenfb *)opaque; + unsigned dummy; + char **vec; + int r; + + vec = xs_read_watch(xenfb->xsh, &dummy); + free(vec); + r = xenfb_on_state_change(&xenfb->fb); + if (r == 0) + r = xenfb_on_state_change(&xenfb->kbd); + if (r < 0) { + xenfb_shutdown(xenfb); + exit(1); + } +} + + +/**************************************************************** + * + * Functions for processing frontend config + * + ****************************************************************/ + + +/* Process the frontend framebuffer config */ +static int xenfb_read_frontend_fb_config(struct xenfb *xenfb) { + struct xenfb_page *fb_page; + int val; + + if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + fprintf(stderr, "feature-update not supported\n"); + errno = ENOTSUP; + return -1; + } + if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "protocol", "%63s", + xenfb->protocol) < 0) + xenfb->protocol[0] = '\0'; + xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "request-update", "1"); + + /* TODO check for permitted ranges */ + fb_page = xenfb->fb.page; + xenfb->depth = fb_page->depth; + xenfb->width = fb_page->width; + xenfb->height = fb_page->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = fb_page->mem_length; + xenfb->row_stride = fb_page->line_length; + fprintf(stderr, "Framebuffer depth %d width %d height %d line %d\n", + fb_page->depth, fb_page->width, fb_page->height, fb_page->line_length); + if (xenfb_map_fb(xenfb, xenfb->fb.otherend_id) < 0) + return -1; + + if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected)) + return -1; + if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected)) + return -1; + + return 0; +} + +/* Process the frontend keyboard config */ +static int xenfb_read_frontend_kbd_config(struct xenfb *xenfb) +{ + int val; + + if (xenfb_xs_scanf1(xenfb->xsh, xenfb->kbd.otherend, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->abs_pointer_wanted = val; + + return 0; +} + + +/**************************************************************** + * + * Functions for frontend/backend state machine + * + ****************************************************************/ + +/* Register a watch against a frontend device, and setup + * QEMU event loop to poll the xenstore FD for notification */ +static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler) +{ + fprintf(stderr, "Doing frontend watch on %s\n", dev->otherend); + if (!xs_watch(dev->xenfb->xsh, dev->otherend, "")) { + fprintf(stderr, "Watch for dev failed\n"); + return -1; + } + + if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0) + return -1; + + return 0; +} + +/* Register a watch against a backend device, and setup + * QEMU event loop to poll the xenstore FD for notification */ +static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler) +{ + fprintf(stderr, "Doing backend watch on %s\n", dev->nodename); + if (!xs_watch(dev->xenfb->xsh, dev->nodename, "")) { + fprintf(stderr, "Watch for dev failed\n"); + return -1; + } + + if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0) + return -1; + + return 0; +} + +/* Callback invoked while waiting for KBD backend to change + * to the created state */ +static void xenfb_backend_created_kbd(void *opaque) +{ + struct xenfb_device *dev = (struct xenfb_device *)opaque; + int ret = xenfb_backend_created(dev); + if (ret < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + if (ret) + return; /* Still waiting */ + + if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, "feature-abs-pointer", "1")) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + + fprintf(stderr, "FB: Waiting for FB backend creation\n"); + xenfb_wait_for_backend(&dev->xenfb->fb, xenfb_backend_created_fb); +} + +/* Callback invoked while waiting for FB backend to change + * to the created state */ +static void xenfb_backend_created_fb(void *opaque) +{ + struct xenfb_device *dev = (struct xenfb_device *)opaque; + int ret = xenfb_backend_created(dev); + if (ret < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + if (ret) + return; /* Still waiting */ + + fprintf(stderr, "FB: Waiting for KBD frontend initialization\n"); + xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_initialized_kbd); +} + +/* Callback invoked while waiting for KBD frontend to change + * to the initialized state */ +static void xenfb_frontend_initialized_kbd(void *opaque) +{ + struct xenfb_device *dev = (struct xenfb_device *)opaque; + int ret = xenfb_frontend_initialized(dev); + if (ret < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + if (ret) + return; /* Still waiting */ + + + fprintf(stderr, "FB: Waiting for FB frontend initialization\n"); + xenfb_wait_for_frontend(&dev->xenfb->fb, xenfb_frontend_initialized_fb); +} + +/* Callback invoked while waiting for FB frontend to change + * to the initialized state */ +static void xenfb_frontend_initialized_fb(void *opaque) +{ + struct xenfb_device *dev = (struct xenfb_device *)opaque; + int ret = xenfb_frontend_initialized(dev); + if (ret < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + if (ret) + return; /* Still waiting */ + + + if (xenfb_read_frontend_fb_config(dev->xenfb)) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + + fprintf(stderr, "FB: Waiting for KBD frontend connection\n"); + xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_connected_kbd); +} + +/* Callback invoked while waiting for KBD frontend to change + * to the connected state */ +static void xenfb_frontend_connected_kbd(void *opaque) +{ + struct xenfb_device *dev = (struct xenfb_device *)opaque; + int ret = xenfb_frontend_connected(dev); + if (ret < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + if (ret) + return; /* Still waiting */ + + if (xenfb_read_frontend_kbd_config(dev->xenfb) < 0) { + xenfb_shutdown(dev->xenfb); + exit(1); + } + + xenfb_register_console(dev->xenfb); +} + + +/**************************************************************** + * + * Helper functions for checking state of frontend/backend devices + * + ****************************************************************/ + +/* Helper to determine if a frontend device is in Connected state */ +static int xenfb_frontend_connected(struct xenfb_device *dev) +{ + unsigned int state; + unsigned int dummy; + char **vec; + vec = xs_read_watch(dev->xenfb->xsh, &dummy); + if (!vec) + return -1; + free(vec); + + state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); + if (!((1 <xenfb->xsh, dev->otherend, "");*/ + + switch (state) { + case XenbusStateConnected: + break; + default: + return -1; + } + return 0; +} + + +/* Helper to determine if a frontend device is in Initialized state */ +static int xenfb_frontend_initialized(struct xenfb_device *dev) +{ + unsigned int state; + unsigned int dummy; + char **vec; + vec = xs_read_watch(dev->xenfb->xsh, &dummy); + if (!vec) + return -1; + free(vec); + + state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); + + if (!((1 << state) & ((1 << XenbusStateUnknown) + | (1 << XenbusStateInitialised) +#if 1 /* TODO fudging state to permit restarting; to be removed */ + | (1 << XenbusStateConnected) +#endif + ))) { + fprintf(stderr, "FB: Carry on waiting\n"); + return 1; + } + + xs_unwatch(dev->xenfb->xsh, dev->otherend, ""); + + switch (state) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + if (xenfb_bind(dev) < 0) + return -1; + + return 0; +} + +/* Helper to determine if a backend device is in Created state */ +static int xenfb_backend_created(struct xenfb_device *dev) +{ + unsigned int state; + unsigned int dummy; + char **vec; + vec = xs_read_watch(dev->xenfb->xsh, &dummy); + if (!vec) + return -1; + free(vec); + + state = xenfb_read_state(dev->xenfb->xsh, dev->nodename); + + if (!((1 <xenfb->xsh, dev->nodename, ""); + + switch (state) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + break; + default: + fprintf(stderr, "Wrong state %d\n", state); + return -1; + } + xenfb_switch_state(dev, XenbusStateInitWait); + if (xenfb_hotplug(dev) < 0) + return -1; + + return 0; +} + + +/**************************************************************** + * + * QEMU device model integration functions + * + ****************************************************************/ + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the '0xe0' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + static int extended = 0; + int down = 1; + if (scancode == 0xe0) { + extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (extended) { + scancode |= 0x80; + extended = 0; + } + xenfb_send_key(opaque, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don't actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + int i; + struct xenfb *xenfb = opaque; + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (xenfb->ds->width - 1) / 0x7fff, + dy * (xenfb->ds->height - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(xenfb->ds->data \ + + (line * xenfb->ds->linesize) \ + + (x * xenfb->ds->depth / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + xenfb->ds->depth / 8); \ + } \ + } + + +/* This copies data from the guest framebuffer region, into QEMU's copy + * NB. QEMU's copy is stored in the pixel format of a) the local X + * server (SDL case) or b) the current VNC client pixel format. + * When shifting between colour depths we preserve the MSB. + */ +static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h) +{ + int line; + + if (!xenfb->ds->shared_buf) { + if (xenfb->depth == xenfb->ds->depth) { /* Perfect match can use fast path */ + for (line = y ; line < (y+h) ; line++) { + memcpy(xenfb->ds->data + (line * xenfb->ds->linesize) + (x * xenfb->ds->depth / 8), + xenfb->pixels + (line * xenfb->row_stride) + (x * xenfb->depth / 8), + w * xenfb->depth / 8); + } + } else { /* Mismatch requires slow pixel munging */ + /* 8 bit == r:3 g:3 b:2 */ + /* 16 bit == r:5 g:6 b:5 */ + /* 24 bit == r:8 g:8 b:8 */ + /* 32 bit == r:8 g:8 b:8 (padding:8) */ + if (xenfb->depth == 8) { + if (xenfb->ds->depth == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (xenfb->ds->depth == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } + } else if (xenfb->depth == 16) { + if (xenfb->ds->depth == 8) { + BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2); + } else if (xenfb->ds->depth == 32) { + BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8); + } + } else if (xenfb->depth == 24 || xenfb->depth == 32) { + if (xenfb->ds->depth == 8) { + BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2); + } else if (xenfb->ds->depth == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (xenfb->ds->depth == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } + } + } + } + dpy_update(xenfb->ds, x, y, w, h); +} + +/* Periodic update of display, no need for any in our case */ +static void xenfb_update(void *opaque) +{ + struct xenfb *xenfb = opaque; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct xenfb *xenfb = opaque; + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); +} + +/* Screen dump is not used in Xen, so no need to impl this....yet */ +static void xenfb_screen_dump(void *opaque, const char *name) { } + + +/* Register a QEMU graphical console, and key/mouse handler, + * connecting up their events to the frontend */ +static int xenfb_register_console(struct xenfb *xenfb) { + /* Register our keyboard & mouse handlers */ + qemu_add_kbd_event_handler(xenfb_key_event, xenfb); + qemu_add_mouse_event_handler(xenfb_mouse_event, xenfb, + xenfb->abs_pointer_wanted, + "Xen PVFB Mouse"); + + /* Tell QEMU to allocate a graphical console */ + graphic_console_init(xenfb->ds, + xenfb_update, + xenfb_invalidate, + xenfb_screen_dump, + xenfb); + dpy_colourdepth(xenfb->ds, xenfb->depth); + dpy_resize(xenfb->ds, xenfb->width, xenfb->height, xenfb->row_stride); + if (xenfb->ds->shared_buf) + dpy_setdata(xenfb->ds, xenfb->pixels); + + if (qemu_set_fd_handler2(xc_evtchn_fd(xenfb->evt_xch), NULL, xenfb_dispatch_channel, NULL, xenfb) < 0) + return -1; + if (qemu_set_fd_handler2(xs_fileno(xenfb->xsh), NULL, xenfb_dispatch_store, NULL, xenfb) < 0) + return -1; + + fprintf(stderr, "Xen Framebuffer registered\n"); + return 0; +} + +#ifdef CONFIG_STUBDOM +static struct semaphore kbd_sem = __SEMAPHORE_INITIALIZER(kbd_sem, 0); +static struct kbdfront_dev *kbd_dev; +static char *kbd_path, *fb_path; + +static unsigned char linux2scancode[KEY_MAX + 1]; + +#define WIDTH 1024 +#define HEIGHT 768 +#define DEPTH 32 +#define LINESIZE (1280 * (DEPTH / 8)) +#define MEMSIZE (LINESIZE * HEIGHT) + +int xenfb_connect_vkbd(const char *path) +{ + kbd_path = strdup(path); + return 0; +} + +int xenfb_connect_vfb(const char *path) +{ + fb_path = strdup(path); + return 0; +} + +static void xenfb_pv_update(DisplayState *s, int x, int y, int w, int h) +{ + struct fbfront_dev *fb_dev = s->opaque; + fbfront_update(fb_dev, x, y, w, h); +} + +static void xenfb_pv_resize(DisplayState *s, int w, int h, int linesize) +{ + struct fbfront_dev *fb_dev = s->opaque; + fprintf(stderr,"resize to %dx%d required\n", w, h); + s->width = w; + s->height = h; + /* TODO: send resize event if supported */ + memset(s->data, 0, MEMSIZE); + fbfront_update(fb_dev, 0, 0, WIDTH, HEIGHT); +} + +static void xenfb_pv_colourdepth(DisplayState *ds, int depth) +{ + /* TODO: send redepth event if supported */ + static int lastdepth = -1; + if (depth != lastdepth) { + fprintf(stderr,"redepth to %d required\n", depth); + lastdepth = depth; + } + /* We can't redepth for now */ + ds->depth = DEPTH; +} + +static void xenfb_kbd_handler(void *opaque) +{ +#define KBD_NUM_BATCH 64 + union xenkbd_in_event buf[KBD_NUM_BATCH]; + int n, i; + DisplayState *s = opaque; + static int buttons; + static int x, y; + + n = kbdfront_receive(kbd_dev, buf, KBD_NUM_BATCH); + for (i = 0; i < n; i++) { + switch (buf[i].type) { + + case XENKBD_TYPE_MOTION: + fprintf(stderr, "FB backend sent us relative mouse motion event!\n"); + break; + + case XENKBD_TYPE_POS: + { + int new_x = buf[i].pos.abs_x; + int new_y = buf[i].pos.abs_y; + if (new_x >= s->width) + new_x = s->width - 1; + if (new_y >= s->height) + new_y = s->height - 1; + if (kbd_mouse_is_absolute()) { + kbd_mouse_event( + new_x * 0x7FFF / (s->width - 1), + new_y * 0x7FFF / (s->height - 1), + buf[i].pos.rel_z, + buttons); + } else { + kbd_mouse_event( + new_x - x, + new_y - y, + buf[i].pos.rel_z, + buttons); + } + x = new_x; + y = new_y; + break; + } + + case XENKBD_TYPE_KEY: + { + int keycode = buf[i].key.keycode; + int button = 0; + + if (keycode == BTN_LEFT) + button = MOUSE_EVENT_LBUTTON; + else if (keycode == BTN_RIGHT) + button = MOUSE_EVENT_RBUTTON; + else if (keycode == BTN_MIDDLE) + button = MOUSE_EVENT_MBUTTON; + + if (button) { + if (buf[i].key.pressed) + buttons |= button; + else + buttons &= ~button; + if (kbd_mouse_is_absolute()) + kbd_mouse_event( + x * 0x7FFF / (s->width - 1), + y * 0x7FFF / (s->height - 1), + 0, + buttons); + else + kbd_mouse_event(0, 0, 0, buttons); + } else { + int scancode = linux2scancode[keycode]; + if (!scancode) { + fprintf(stderr, "Can't convert keycode %x to scancode\n", keycode); + break; + } + if (scancode & 0x80) { + kbd_put_keycode(0xe0); + scancode &= 0x7f; + } + if (!buf[i].key.pressed) + scancode |= 0x80; + kbd_put_keycode(scancode); + } + break; + } + } + } +} + +static void xenfb_pv_refresh(DisplayState *ds) +{ + /* always request negociation */ + ds->depth = -1; + vga_hw_update(); +} + +static void kbdfront_thread(void *p) +{ + int scancode, keycode; + kbd_dev = init_kbdfront(p, 1); + if (!kbd_dev) { + fprintf(stderr,"can't open keyboard\n"); + exit(1); + } + up(&kbd_sem); + for (scancode = 0; scancode < 128; scancode++) { + keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode]]; + linux2scancode[keycode] = scancode; + keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode] | 0x80]; + linux2scancode[keycode] = scancode | 0x80; + } +} + +int xenfb_pv_display_init(DisplayState *ds) +{ + void *data; + struct fbfront_dev *fb_dev; + int kbd_fd; + + if (!fb_path || !kbd_path) + return -1; + + create_thread("kbdfront", kbdfront_thread, (void*) kbd_path); + + data = qemu_memalign(PAGE_SIZE, VGA_RAM_SIZE); + fb_dev = init_fbfront(fb_path, data, WIDTH, HEIGHT, DEPTH, LINESIZE, MEMSIZE); + if (!fb_dev) { + fprintf(stderr,"can't open frame buffer\n"); + exit(1); + } + free(fb_path); + + down(&kbd_sem); + free(kbd_path); + + kbd_fd = kbdfront_open(kbd_dev); + qemu_set_fd_handler(kbd_fd, xenfb_kbd_handler, NULL, ds); + + ds->data = data; + ds->linesize = LINESIZE; + ds->depth = DEPTH; + ds->bgr = 0; + ds->width = WIDTH; + ds->height = HEIGHT; + ds->dpy_update = xenfb_pv_update; + ds->dpy_resize = xenfb_pv_resize; + ds->dpy_colourdepth = xenfb_pv_colourdepth; + ds->dpy_refresh = xenfb_pv_refresh; + ds->opaque = fb_dev; + return 0; +} +#endif + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/hw/xenfb.h b/hw/xenfb.h new file mode 100644 index 00000000..2f6ef2b4 --- /dev/null +++ b/hw/xenfb.h @@ -0,0 +1,13 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include "vl.h" +#include +#include + +struct xenfb; + +struct xenfb *xenfb_new(int domid, DisplayState *ds); +void xenfb_shutdown(struct xenfb *xenfb); + +#endif diff --git a/i386-dm/config.h b/i386-dm/config.h new file mode 100644 index 00000000..1b423bfe --- /dev/null +++ b/i386-dm/config.h @@ -0,0 +1,7 @@ +#include "../config-host.h" +#define CONFIG_QEMU_PREFIX "" +#define TARGET_ARCH "i386" +#define TARGET_I386 1 +#define CONFIG_SOFTMMU 1 +#define CONFIG_DM 1 +#define CONFIG_SDL 1 diff --git a/i386-dm/config.mak b/i386-dm/config.mak new file mode 100644 index 00000000..1f48b2f6 --- /dev/null +++ b/i386-dm/config.mak @@ -0,0 +1 @@ +include ../xen-config.mak diff --git a/i386-dm/cpu.h b/i386-dm/cpu.h new file mode 100644 index 00000000..6071a852 --- /dev/null +++ b/i386-dm/cpu.h @@ -0,0 +1,93 @@ +/* + * i386 virtual CPU header + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CPU_I386_H +#define CPU_I386_H + +#include "config.h" + +#ifdef TARGET_X86_64 +#define TARGET_LONG_BITS 64 +#else +/* #define TARGET_LONG_BITS 32 */ +#define TARGET_LONG_BITS 64 /* for Qemu map cache */ +#endif + +/* target supports implicit self modifying code */ +#define TARGET_HAS_SMC +/* support for self modifying code even if the modified instruction is + close to the modifying instruction */ +#define TARGET_HAS_PRECISE_SMC + +#include "cpu-defs.h" + +#ifdef CONFIG_SOFTFLOAT +#include "softfloat.h" +#endif + +#if defined(__i386__) && !defined(CONFIG_SOFTMMU) +#define USE_CODE_COPY +#endif + +#ifdef CONFIG_SOFTFLOAT +#ifdef USE_X86LDOUBLE +typedef floatx80 CPU86_LDouble; +#else +typedef float64 CPU86_LDouble; +#endif +#endif + +/* Empty for now */ +typedef struct CPUX86State { + uint32_t a20_mask; + + int interrupt_request; + + CPU_COMMON +} CPUX86State; + +CPUX86State *cpu_x86_init(void); +int cpu_x86_exec(CPUX86State *s); +void cpu_x86_close(CPUX86State *s); +int cpu_get_pic_interrupt(CPUX86State *s); +/* MSDOS compatibility mode FPU exception support */ +void cpu_set_ferr(CPUX86State *s); + +void cpu_x86_set_a20(CPUX86State *env, int a20_state); + +#ifndef IN_OP_I386 +void cpu_x86_outb(CPUX86State *env, int addr, int val); +void cpu_x86_outw(CPUX86State *env, int addr, int val); +void cpu_x86_outl(CPUX86State *env, int addr, int val); +int cpu_x86_inb(CPUX86State *env, int addr); +int cpu_x86_inw(CPUX86State *env, int addr); +int cpu_x86_inl(CPUX86State *env, int addr); +#endif + +/* helper2.c */ +int main_loop(void); + +#if defined(__i386__) || defined(__x86_64__) +#define TARGET_PAGE_BITS 12 +#elif defined(__ia64__) +#define TARGET_PAGE_BITS 14 +#endif +#include "cpu-all.h" + +#endif /* CPU_I386_H */ diff --git a/i386-dm/hooks.mak b/i386-dm/hooks.mak new file mode 100644 index 00000000..bb21544d --- /dev/null +++ b/i386-dm/hooks.mak @@ -0,0 +1,3 @@ +OBJS += $(SOUND_HW) $(AUDIODRV) mixeng.o +CPPFLAGS += -DHAS_AUDIO +QEMU_PROG=qemu-dm diff --git a/i386-stubdom/config.mak b/i386-stubdom/config.mak new file mode 100644 index 00000000..1f48b2f6 --- /dev/null +++ b/i386-stubdom/config.mak @@ -0,0 +1 @@ +include ../xen-config.mak diff --git a/i386-stubdom/hooks.mak b/i386-stubdom/hooks.mak new file mode 100644 index 00000000..33b9bce9 --- /dev/null +++ b/i386-stubdom/hooks.mak @@ -0,0 +1,11 @@ +include ../xen-hooks.mak + +OBJS += block-vbd.o +OBJS += tpm_tis.o + +QEMU_STUBDOM= libqemu.a + +PROGS=$(QEMU_STUBDOM) + +$(QEMU_STUBDOM): $(OBJS) + $(AR) rcs $@ $^ diff --git a/xen-config-host.mak b/xen-config-host.mak new file mode 100644 index 00000000..9e9b589f --- /dev/null +++ b/xen-config-host.mak @@ -0,0 +1,41 @@ +XEN_ROOT ?= ../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAKE=make +INSTALL=install -c + +prefix=$(PREFIX) +bindir=$(prefix)/bin +mandir=$(prefix)/share/man +datadir=$(prefix)/share/qemu +docdir=$(prefix)/share/doc/qemu + +CC=gcc-3.4 +HOST_CC=gcc +AR=ar +STRIP=strip -s -R .comment -R .note +OS_CFLAGS= +OS_LDFLAGS= +ARCH_CFLAGS=-m32 +ARCH_LDFLAGS=-m32 +CFLAGS= -Wall -O2 -g -fno-strict-aliasing +LDFLAGS= -g +EXESUF= +AIOLIBS=-lrt -lpthread +ARCH=i386 +CONFIG_GDBSTUB=yes +CONFIG_SLIRP=yes +CONFIG_OSS=yes +CONFIG_VNC_TLS=yes +CONFIG_VNC_TLS_CFLAGS= +CONFIG_VNC_TLS_LIBS=-lgnutls +VERSION=0.9.1 +SRC_PATH=/u/iwj/work/qemu-iwj.git + +BUILD_DOCS=yes +CONFIG_SDL=yes +SDL_LIBS=-L/usr/lib -lSDL +SDL_CFLAGS=-I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT +CONFIG_CURSES=yes +CURSES_LIBS=-lcurses +TOOLS=qemu-img$(EXESUF) diff --git a/xen-config.mak b/xen-config.mak new file mode 100644 index 00000000..6d99287f --- /dev/null +++ b/xen-config.mak @@ -0,0 +1,3 @@ +include ../config-host.mak +XEN_ROOT ?= ../../.. +include $(XEN_ROOT)/tools/Rules.mk diff --git a/xen-hooks.mak b/xen-hooks.mak new file mode 100644 index 00000000..9804a05a --- /dev/null +++ b/xen-hooks.mak @@ -0,0 +1,24 @@ +CPPFLAGS+= -I$(XEN_ROOT)/tools/libxc +CPPFLAGS+= -I$(XEN_ROOT)/tools/xenstore +CPPFLAGS+= -I$(XEN_ROOT)/tools/include + +SSE2 := $(call cc-option,$(CC),-msse2,) +ifeq ($(SSE2),-msse2) +CFLAGS += -DUSE_SSE2=1 -msse2 +endif + +QEMU_PROG=qemu-dm + +LIBS += -L../../libxc -lxenctrl -lxenguest +LIBS += -L../../xenstore -lxenstore + +LDFLAGS := $(CFLAGS) $(LDFLAGS) + +OBJS += loader.o +OBJS += piix4acpi.o +OBJS += xenstore.o +OBJS += xen_platform.o +OBJS += xen_machine_fv.o +OBJS += xen_machine_pv.o +OBJS += xenfb.o +OBJS += xen_console.o diff --git a/xen-make-target-dir b/xen-make-target-dir new file mode 100755 index 00000000..08e6a0ac --- /dev/null +++ b/xen-make-target-dir @@ -0,0 +1,12 @@ +#!/bin/sh +set -e +if [ $# != 1 ]; then echo >&2 "usage: $0 "; exit 1; fi +target=$1 + +test -d $target || mkdir $target + +perl -pe ' + BEGIN { print "# autogenerated - do not edit\n" or die $!; } + print "include hooks.mak\n" or die $! + if m/^all\s*\:/; +' Makefile.target >$target/Makefile diff --git a/xenstore.c b/xenstore.c new file mode 100644 index 00000000..e1c25322 --- /dev/null +++ b/xenstore.c @@ -0,0 +1,895 @@ +/* + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * Copyright (C) 2006 Christian Limpach + * Copyright (C) 2006 XenSource Ltd. + * + */ + +#include "vl.h" +#include "block_int.h" +#include +#ifndef CONFIG_STUBDOM +#include +#include +#endif +#include +#include +#include + +struct xs_handle *xsh = NULL; +static char *media_filename[MAX_DISKS + MAX_SCSI_DISKS]; +static QEMUTimer *insert_timer = NULL; + +#define UWAIT_MAX (30*1000000) /* thirty seconds */ +#define UWAIT (100000) /* 1/10th second */ + +static int pasprintf(char **buf, const char *fmt, ...) +{ + va_list ap; + int ret = 0; + + if (*buf) + free(*buf); + va_start(ap, fmt); + if (vasprintf(buf, fmt, ap) == -1) { + buf = NULL; + ret = -1; + } + va_end(ap); + return ret; +} + +static void insert_media(void *opaque) +{ + int i; + + for (i = 0; i < MAX_DISKS + MAX_SCSI_DISKS; i++) { + if (media_filename[i] && bs_table[i]) { + do_change(bs_table[i]->device_name, media_filename[i]); + free(media_filename[i]); + media_filename[i] = NULL; + } + } +} + +void xenstore_check_new_media_present(int timeout) +{ + + if (insert_timer == NULL) + insert_timer = qemu_new_timer(rt_clock, insert_media, NULL); + qemu_mod_timer(insert_timer, qemu_get_clock(rt_clock) + timeout); +} + +static void waitForDevice(char *fn) +{ + struct stat sbuf; + int status; + int uwait = UWAIT_MAX; + + do { + status = stat(fn, &sbuf); + if (!status) break; + usleep(UWAIT); + uwait -= UWAIT; + } while (uwait > 0); + + return; +} + +#define DIRECT_PCI_STR_LEN 160 +char direct_pci_str[DIRECT_PCI_STR_LEN]; +void xenstore_parse_domain_config(int domid) +{ + char **e = NULL; + char *buf = NULL, *path; + char *fpath = NULL, *bpath = NULL, + *dev = NULL, *params = NULL, *type = NULL, *drv = NULL; + int i, is_scsi, is_hdN = 0; + unsigned int len, num, hd_index, pci_devid = 0; + BlockDriverState *bs; + + for(i = 0; i < MAX_DISKS + MAX_SCSI_DISKS; i++) + media_filename[i] = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) { + fprintf(logfile, "Could not contact xenstore for domain config\n"); + return; + } + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(logfile, "xs_get_domain_path() error\n"); + goto out; + } + + if (pasprintf(&buf, "%s/device/vbd", path) == -1) + goto out; + + e = xs_directory(xsh, XBT_NULL, buf, &num); + if (e == NULL) + goto out; + + for (i = 0; i < num; i++) { + /* read the backend path */ + if (pasprintf(&buf, "%s/device/vbd/%s/backend", path, e[i]) == -1) + continue; + free(bpath); + bpath = xs_read(xsh, XBT_NULL, buf, &len); + if (bpath == NULL) + continue; + /* read the name of the device */ + if (pasprintf(&buf, "%s/dev", bpath) == -1) + continue; + free(dev); + dev = xs_read(xsh, XBT_NULL, buf, &len); + if (dev == NULL) + continue; + if (!strncmp(dev, "hd", 2)) { + is_hdN = 1; + break; + } + } + + for (i = 0; i < num; i++) { + /* read the backend path */ + if (pasprintf(&buf, "%s/device/vbd/%s/backend", path, e[i]) == -1) + continue; + free(bpath); + bpath = xs_read(xsh, XBT_NULL, buf, &len); + if (bpath == NULL) + continue; + /* read the name of the device */ + if (pasprintf(&buf, "%s/dev", bpath) == -1) + continue; + free(dev); + dev = xs_read(xsh, XBT_NULL, buf, &len); + if (dev == NULL) + continue; + /* Change xvdN to look like hdN */ + if (!is_hdN && !strncmp(dev, "xvd", 3)) { + fprintf(logfile, "Change xvd%c to look like hd%c\n", + dev[3], dev[3]); + memmove(dev, dev+1, strlen(dev)); + dev[0] = 'h'; + dev[1] = 'd'; + } + is_scsi = !strncmp(dev, "sd", 2); + if ((strncmp(dev, "hd", 2) && !is_scsi) || strlen(dev) != 3 ) + continue; + hd_index = dev[2] - 'a'; + if (hd_index >= (is_scsi ? MAX_SCSI_DISKS : MAX_DISKS)) + continue; + /* read the type of the device */ + if (pasprintf(&buf, "%s/device/vbd/%s/device-type", path, e[i]) == -1) + continue; + free(type); + type = xs_read(xsh, XBT_NULL, buf, &len); + if (pasprintf(&buf, "%s/params", bpath) == -1) + continue; + free(params); + params = xs_read(xsh, XBT_NULL, buf, &len); + if (params == NULL) + continue; + /* read the name of the device */ + if (pasprintf(&buf, "%s/type", bpath) == -1) + continue; + free(drv); + drv = xs_read(xsh, XBT_NULL, buf, &len); + if (drv == NULL) + continue; + /* Strip off blktap sub-type prefix aio: - QEMU can autodetect this */ + if (!strcmp(drv, "tap") && params[0]) { + char *offset = strchr(params, ':'); + if (!offset) + continue ; + memmove(params, offset+1, strlen(offset+1)+1 ); + fprintf(logfile, "Strip off blktap sub-type prefix to %s\n", params); + } + + /* + * check if device has a phantom vbd; the phantom is hooked + * to the frontend device (for ease of cleanup), so lookup + * the frontend device, and see if there is a phantom_vbd + * if there is, we will use resolution as the filename + */ + if (pasprintf(&buf, "%s/device/vbd/%s/phantom_vbd", path, e[i]) == -1) + continue; + free(fpath); + fpath = xs_read(xsh, XBT_NULL, buf, &len); + if (fpath) { + if (pasprintf(&buf, "%s/dev", fpath) == -1) + continue; + free(params); + params = xs_read(xsh, XBT_NULL, buf , &len); + if (params) { + /* + * wait for device, on timeout silently fail because we will + * fail to open below + */ + waitForDevice(params); + } + } + + bs = bs_table[hd_index + (is_scsi ? MAX_DISKS : 0)] = bdrv_new(dev); + /* check if it is a cdrom */ + if (type && !strcmp(type, "cdrom")) { + bdrv_set_type_hint(bs, BDRV_TYPE_CDROM); + if (pasprintf(&buf, "%s/params", bpath) != -1) + xs_watch(xsh, buf, dev); + } + + /* open device now if media present */ +#ifdef CONFIG_STUBDOM + if (pasprintf(&buf, "%s/device/vbd/%s", path, e[i]) == -1) + continue; + if (bdrv_open2(bs, buf, 0 /* snapshot */, &bdrv_vbd) == 0) { + pstrcpy(bs->filename, sizeof(bs->filename), params); + continue; + } +#endif + + if (params[0]) { + if (bdrv_open(bs, params, 0 /* snapshot */) < 0) + fprintf(stderr, "qemu: could not open vbd '%s' or hard disk image '%s'\n", buf, params); + } + } + +#ifdef CONFIG_STUBDOM + if (pasprintf(&buf, "%s/device/vkbd", path) == -1) + goto out; + + free(e); + e = xs_directory(xsh, XBT_NULL, buf, &num); + + if (e) { + for (i = 0; i < num; i++) { + if (pasprintf(&buf, "%s/device/vkbd/%s", path, e[i]) == -1) + continue; + xenfb_connect_vkbd(buf); + } + } + + if (pasprintf(&buf, "%s/device/vfb", path) == -1) + goto out; + + free(e); + e = xs_directory(xsh, XBT_NULL, buf, &num); + + if (e) { + for (i = 0; i < num; i++) { + if (pasprintf(&buf, "%s/device/vfb/%s", path, e[i]) == -1) + continue; + xenfb_connect_vfb(buf); + } + } +#endif + + + /* Set a watch for log-dirty requests from the migration tools */ + if (pasprintf(&buf, "/local/domain/0/device-model/%u/logdirty/next-active", + domid) != -1) { + xs_watch(xsh, buf, "logdirty"); + fprintf(logfile, "Watching %s\n", buf); + } + + /* Set a watch for suspend requests from the migration tools */ + if (pasprintf(&buf, + "/local/domain/0/device-model/%u/command", domid) != -1) { + xs_watch(xsh, buf, "dm-command"); + fprintf(logfile, "Watching %s\n", buf); + } + + /* get the pci pass-through parameter */ + if (pasprintf(&buf, "/local/domain/0/backend/pci/%u/%u/num_devs", + domid, pci_devid) == -1) + goto out; + + free(params); + params = xs_read(xsh, XBT_NULL, buf, &len); + if (params == NULL) + goto out; + num = atoi(params); + + for ( i = 0; i < num; i++ ) { + if (pasprintf(&buf, "/local/domain/0/backend/pci/%u/%u/dev-%d", + domid, pci_devid, i) != -1) { + free(dev); + dev = xs_read(xsh, XBT_NULL, buf, &len); + + if ( strlen(dev) + strlen(direct_pci_str) > DIRECT_PCI_STR_LEN ) { + fprintf(stderr, "qemu: too many pci pass-through devices\n"); + memset(direct_pci_str, 0, DIRECT_PCI_STR_LEN); + goto out; + } + + /* append to direct_pci_str */ + if ( dev ) { + strcat(direct_pci_str, dev); + strcat(direct_pci_str, "-"); + } + } + } + + + out: + free(type); + free(params); + free(dev); + free(bpath); + free(buf); + free(path); + free(e); + free(drv); + return; +} + +int xenstore_fd(void) +{ + if (xsh) + return xs_fileno(xsh); + return -1; +} + +unsigned long *logdirty_bitmap = NULL; +unsigned long logdirty_bitmap_size; +extern int vga_ram_size, bios_size; + +void xenstore_process_logdirty_event(void) +{ +#ifdef CONFIG_STUBDOM + /* XXX we just can't use shm. */ + return; +#else + char *act; + static char *active_path = NULL; + static char *next_active_path = NULL; + static char *seg = NULL; + unsigned int len; + int i; + + if (!seg) { + char *path = NULL, *key_ascii, key_terminated[17] = {0,}; + key_t key; + int shmid; + + /* Find and map the shared memory segment for log-dirty bitmaps */ + if (pasprintf(&path, + "/local/domain/0/device-model/%u/logdirty/key", + domid) == -1) { + fprintf(logfile, "Log-dirty: out of memory\n"); + exit(1); + } + + key_ascii = xs_read(xsh, XBT_NULL, path, &len); + free(path); + + if (!key_ascii) + /* No key yet: wait for the next watch */ + return; + + strncpy(key_terminated, key_ascii, 16); + free(key_ascii); + key = (key_t) strtoull(key_terminated, NULL, 16); + + /* Figure out how bit the log-dirty bitmaps are */ + logdirty_bitmap_size = xc_memory_op(xc_handle, + XENMEM_maximum_gpfn, &domid) + 1; + logdirty_bitmap_size = ((logdirty_bitmap_size + HOST_LONG_BITS - 1) + / HOST_LONG_BITS); /* longs */ + logdirty_bitmap_size *= sizeof (unsigned long); /* bytes */ + + /* Map the shared-memory segment */ + fprintf(logfile, "%s: key=%16.16llx size=%lu\n", __FUNCTION__, + (unsigned long long)key, logdirty_bitmap_size); + shmid = shmget(key, 2 * logdirty_bitmap_size, S_IRUSR|S_IWUSR); + if (shmid == -1) { + fprintf(logfile, "Log-dirty: shmget failed: segment %16.16llx " + "(%s)\n", (unsigned long long)key, strerror(errno)); + exit(1); + } + + seg = shmat(shmid, NULL, 0); + if (seg == (void *)-1) { + fprintf(logfile, "Log-dirty: shmat failed: segment %16.16llx " + "(%s)\n", (unsigned long long)key, strerror(errno)); + exit(1); + } + + fprintf(logfile, "Log-dirty: mapped segment at %p\n", seg); + + /* Double-check that the bitmaps are the size we expect */ + if (logdirty_bitmap_size != *(uint32_t *)seg) { + fprintf(logfile, "Log-dirty: got %u, calc %lu\n", + *(uint32_t *)seg, logdirty_bitmap_size); + /* Stale key: wait for next watch */ + shmdt(seg); + seg = NULL; + return; + } + + /* Remember the paths for the next-active and active entries */ + if (pasprintf(&active_path, + "/local/domain/0/device-model/%u/logdirty/active", + domid) == -1) { + fprintf(logfile, "Log-dirty: out of memory\n"); + exit(1); + } + if (pasprintf(&next_active_path, + "/local/domain/0/device-model/%u/logdirty/next-active", + domid) == -1) { + fprintf(logfile, "Log-dirty: out of memory\n"); + exit(1); + } + } + + fprintf(logfile, "Triggered log-dirty buffer switch\n"); + + /* Read the required active buffer from the store */ + act = xs_read(xsh, XBT_NULL, next_active_path, &len); + if (!act) { + fprintf(logfile, "Log-dirty: can't read next-active\n"); + exit(1); + } + + /* Switch buffers */ + i = act[0] - '0'; + if (i != 0 && i != 1) { + fprintf(logfile, "Log-dirty: bad next-active entry: %s\n", act); + exit(1); + } + logdirty_bitmap = (unsigned long *)(seg + i * logdirty_bitmap_size); + + /* Ack that we've switched */ + xs_write(xsh, XBT_NULL, active_path, act, len); + free(act); +#endif +} + + +/* Accept state change commands from the control tools */ +static void xenstore_process_dm_command_event(void) +{ + char *path = NULL, *command = NULL, *par = NULL; + unsigned int len; + extern int suspend_requested; + + if (pasprintf(&path, + "/local/domain/0/device-model/%u/command", domid) == -1) { + fprintf(logfile, "out of memory reading dm command\n"); + goto out; + } + command = xs_read(xsh, XBT_NULL, path, &len); + if (!command) + goto out; + + if (!strncmp(command, "save", len)) { + fprintf(logfile, "dm-command: pause and save state\n"); + suspend_requested = 1; + } else if (!strncmp(command, "continue", len)) { + fprintf(logfile, "dm-command: continue after state save\n"); + suspend_requested = 0; + } else if (!strncmp(command, "pci-rem", len)) { + fprintf(logfile, "dm-command: hot remove pass-through pci dev \n"); + + if (pasprintf(&path, + "/local/domain/0/device-model/%u/parameter", domid) == -1) { + fprintf(logfile, "out of memory reading dm command parameter\n"); + goto out; + } + par = xs_read(xsh, XBT_NULL, path, &len); + if (!par) + goto out; + + do_pci_del(par); + free(par); + } else if (!strncmp(command, "pci-ins", len)) { + fprintf(logfile, "dm-command: hot insert pass-through pci dev \n"); + + if (pasprintf(&path, + "/local/domain/0/device-model/%u/parameter", domid) == -1) { + fprintf(logfile, "out of memory reading dm command parameter\n"); + goto out; + } + par = xs_read(xsh, XBT_NULL, path, &len); + if (!par) + goto out; + + do_pci_add(par); + free(par); + } else { + fprintf(logfile, "dm-command: unknown command\"%*s\"\n", len, command); + } + + out: + free(path); + free(command); +} + +void xenstore_record_dm(char *subpath, char *state) +{ + char *path = NULL; + + if (pasprintf(&path, + "/local/domain/0/device-model/%u/%s", domid, subpath) == -1) { + fprintf(logfile, "out of memory recording dm \n"); + goto out; + } + if (!xs_write(xsh, XBT_NULL, path, state, strlen(state))) + fprintf(logfile, "error recording dm \n"); + + out: + free(path); +} + +void xenstore_record_dm_state(char *state) +{ + xenstore_record_dm("state", state); +} + +void xenstore_process_event(void *opaque) +{ + char **vec, *offset, *bpath = NULL, *buf = NULL, *drv = NULL, *image = NULL; + unsigned int len, num, hd_index; + + vec = xs_read_watch(xsh, &num); + if (!vec) + return; + + if (!strcmp(vec[XS_WATCH_TOKEN], "logdirty")) { + xenstore_process_logdirty_event(); + goto out; + } + + if (!strcmp(vec[XS_WATCH_TOKEN], "dm-command")) { + xenstore_process_dm_command_event(); + goto out; + } + + if (strncmp(vec[XS_WATCH_TOKEN], "hd", 2) || + strlen(vec[XS_WATCH_TOKEN]) != 3) + goto out; + hd_index = vec[XS_WATCH_TOKEN][2] - 'a'; + image = xs_read(xsh, XBT_NULL, vec[XS_WATCH_PATH], &len); + if (image == NULL) + goto out; /* gone */ + + /* Strip off blktap sub-type prefix */ + bpath = strdup(vec[XS_WATCH_PATH]); + if (bpath == NULL) + goto out; + if ((offset = strrchr(bpath, '/')) != NULL) + *offset = '\0'; + if (pasprintf(&buf, "%s/type", bpath) == -1) + goto out; + drv = xs_read(xsh, XBT_NULL, buf, &len); + if (drv && !strcmp(drv, "tap") && ((offset = strchr(image, ':')) != NULL)) + memmove(image, offset+1, strlen(offset+1)+1); + + if (!strcmp(image, bs_table[hd_index]->filename)) + goto out; /* identical */ + + do_eject(0, vec[XS_WATCH_TOKEN]); + bs_table[hd_index]->filename[0] = 0; + if (media_filename[hd_index]) { + free(media_filename[hd_index]); + media_filename[hd_index] = NULL; + } + + if (image[0]) { + media_filename[hd_index] = strdup(image); + xenstore_check_new_media_present(5000); + } + + out: + free(drv); + free(buf); + free(bpath); + free(image); + free(vec); +} + +void xenstore_write_vncport(int display) +{ + char *buf = NULL, *path; + char *portstr = NULL; + + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(logfile, "xs_get_domain_path() error\n"); + goto out; + } + + if (pasprintf(&buf, "%s/console/vnc-port", path) == -1) + goto out; + + if (pasprintf(&portstr, "%d", display) == -1) + goto out; + + if (xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)) == 0) + fprintf(logfile, "xs_write() vncport failed\n"); + + out: + free(portstr); + free(buf); +} + +void xenstore_write_vslots(char *vslots) +{ + char *path = NULL; + int pci_devid = 0; + + if (pasprintf(&path, + "/local/domain/0/backend/pci/%u/%u/vslots", domid, pci_devid) == -1) { + fprintf(logfile, "out of memory when updating vslots.\n"); + goto out; + } + if (!xs_write(xsh, XBT_NULL, path, vslots, strlen(vslots))) + fprintf(logfile, "error updating vslots \n"); + + out: + free(path); +} + +void xenstore_read_vncpasswd(int domid, char *pwbuf, size_t pwbuflen) +{ + char *buf = NULL, *path, *uuid = NULL, *passwd = NULL; + unsigned int i, len; + + pwbuf[0] = '\0'; + + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(logfile, "xs_get_domain_path() error. domid %d.\n", domid); + return; + } + + pasprintf(&buf, "%s/vm", path); + free(path); + uuid = xs_read(xsh, XBT_NULL, buf, &len); + if (uuid == NULL) { + fprintf(logfile, "xs_read(): uuid get error. %s.\n", buf); + free(buf); + return; + } + + pasprintf(&buf, "%s/vncpasswd", uuid); + free(uuid); + passwd = xs_read(xsh, XBT_NULL, buf, &len); + if (passwd == NULL) { + fprintf(logfile, "xs_read(): vncpasswd get error. %s.\n", buf); + free(buf); + return; + } + + if (len >= pwbuflen) + { + fprintf(logfile, "xenstore_read_vncpasswd(): truncated password to avoid buffer overflow\n"); + len = pwbuflen - 1; + } + + for (i=0; i