*.[oa]
*~
+
+i386-dm/Makefile
--- /dev/null
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <xs.h>
+#include <xen/io/console.h>
+#include <xenctrl.h>
+
+#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:
+ */
--- /dev/null
+/*
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Copyright (C) Red Hat 2007
+ *
+ * Xen Console
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along 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);
--- /dev/null
+/*
+ * 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 <xenbus.h>
+#endif
+#include <xen/hvm/params.h>
+#include <sys/mman.h>
+
+#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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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 <xenguest.h>
+
+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");
+}
--- /dev/null
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xen/io/xenbus.h>
+#include <xen/io/fbif.h>
+#include <xen/io/kbdif.h>
+#include <xen/io/protocols.h>
+#include <stdbool.h>
+#include <xen/event_channel.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <xs.h>
+
+#include "xenfb.h"
+
+#ifdef CONFIG_STUBDOM
+#include <semaphore.h>
+#include <sched.h>
+#include <fbfront.h>
+#endif
+
+#ifndef BTN_LEFT
+#define BTN_LEFT 0x110 /* from <linux/input.h> */
+#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 <<state) & ((1 << XenbusStateUnknown) |
+ (1 << XenbusStateConnected)))) {
+ fprintf(stderr, "FB: Carry on waiting\n");
+ return 1;
+ }
+
+ /* Don't unwatch frontend - we need to detect shutdown */
+ /*xs_unwatch(dev->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 <<state) & ((1 << XenbusStateUnknown)
+ | (1 << XenbusStateInitialising)
+ | (1 << XenbusStateClosed)
+#if 1 /* TODO fudging state to permit restarting; to be removed */
+ | (1 << XenbusStateInitWait)
+ | (1 << XenbusStateConnected)
+ | (1 << XenbusStateClosing)
+#endif
+ ))) {
+ fprintf(stderr, "FB: Carry on waiting\n");
+ return 1;
+ }
+
+ xs_unwatch(dev->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:
+ */
--- /dev/null
+#ifndef _XENFB_H_
+#define _XENFB_H_
+
+#include "vl.h"
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct xenfb;
+
+struct xenfb *xenfb_new(int domid, DisplayState *ds);
+void xenfb_shutdown(struct xenfb *xenfb);
+
+#endif
--- /dev/null
+#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
--- /dev/null
+include ../xen-config.mak
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+OBJS += $(SOUND_HW) $(AUDIODRV) mixeng.o
+CPPFLAGS += -DHAS_AUDIO
+QEMU_PROG=qemu-dm
--- /dev/null
+include ../xen-config.mak
--- /dev/null
+include ../xen-hooks.mak
+
+OBJS += block-vbd.o
+OBJS += tpm_tis.o
+
+QEMU_STUBDOM= libqemu.a
+
+PROGS=$(QEMU_STUBDOM)
+
+$(QEMU_STUBDOM): $(OBJS)
+ $(AR) rcs $@ $^
--- /dev/null
+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)
--- /dev/null
+include ../config-host.mak
+XEN_ROOT ?= ../../..
+include $(XEN_ROOT)/tools/Rules.mk
--- /dev/null
+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
--- /dev/null
+#!/bin/sh
+set -e
+if [ $# != 1 ]; then echo >&2 "usage: $0 <target-dir>"; 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
--- /dev/null
+/*
+ * 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 <unistd.h>
+#ifndef CONFIG_STUBDOM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+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<len; i++)
+ pwbuf[i] = passwd[i];
+ pwbuf[len] = '\0';
+ passwd[0] = '\0';
+ if (xs_write(xsh, XBT_NULL, buf, passwd, 1) == 0)
+ fprintf(logfile, "xs_write() vncpasswd failed.\n");
+
+ free(passwd);
+ free(buf);
+}
+
+
+/*
+ * get all device instances of a certain type
+ */
+char **xenstore_domain_get_devices(struct xs_handle *handle,
+ const char *devtype, unsigned int *num)
+{
+ char *path;
+ char *buf = NULL;
+ char **e = NULL;
+
+ path = xs_get_domain_path(handle, domid);
+ if (path == NULL)
+ goto out;
+
+ if (pasprintf(&buf, "%s/device/%s", path,devtype) == -1)
+ goto out;
+
+ e = xs_directory(handle, XBT_NULL, buf, num);
+
+ out:
+ free(path);
+ free(buf);
+ return e;
+}
+
+/*
+ * Check whether a domain has devices of the given type
+ */
+int xenstore_domain_has_devtype(struct xs_handle *handle, const char *devtype)
+{
+ int rc = 0;
+ unsigned int num;
+ char **e = xenstore_domain_get_devices(handle, devtype, &num);
+ if (e)
+ rc = 1;
+ free(e);
+ return rc;
+}
+
+/*
+ * Function that creates a path to a variable of an instance of a
+ * certain device
+ */
+static char *get_device_variable_path(const char *devtype, const char *inst,
+ const char *var)
+{
+ char *buf = NULL;
+ if (pasprintf(&buf, "/local/domain/0/backend/%s/%d/%s/%s",
+ devtype,
+ domid,
+ inst,
+ var) == -1) {
+ free(buf);
+ buf = NULL;
+ }
+ return buf;
+}
+
+char *xenstore_backend_read_variable(struct xs_handle *handle,
+ const char *devtype, const char *inst,
+ const char *var)
+{
+ char *value = NULL;
+ char *buf = NULL;
+ unsigned int len;
+
+ buf = get_device_variable_path(devtype, inst, var);
+ if (NULL == buf)
+ goto out;
+
+ value = xs_read(handle, XBT_NULL, buf, &len);
+
+ free(buf);
+
+ out:
+ return value;
+}
+
+/*
+ Read the hotplug status variable from the backend given the type
+ of device and its instance.
+*/
+char *xenstore_read_hotplug_status(struct xs_handle *handle,
+ const char *devtype, const char *inst)
+{
+ return xenstore_backend_read_variable(handle, devtype, inst,
+ "hotplug-status");
+}
+
+/*
+ Subscribe to the hotplug status of a device given the type of device and
+ its instance.
+ In case an error occurrs, a negative number is returned.
+ */
+int xenstore_subscribe_to_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token)
+{
+ int rc = 0;
+ char *path = get_device_variable_path(devtype, inst, "hotplug-status");
+
+ if (path == NULL)
+ return -1;
+
+ if (0 == xs_watch(handle, path, token))
+ rc = -2;
+
+ free(path);
+
+ return rc;
+}
+
+/*
+ * Unsubscribe from a subscription to the status of a hotplug variable of
+ * a device.
+ */
+int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
+ const char *devtype,
+ const char *inst,
+ const char *token)
+{
+ int rc = 0;
+ char *path;
+ path = get_device_variable_path(devtype, inst, "hotplug-status");
+ if (path == NULL)
+ return -1;
+
+ if (0 == xs_unwatch(handle, path, token))
+ rc = -2;
+
+ free(path);
+
+ return rc;
+}
+
+char *xenstore_vm_read(int domid, char *key, unsigned int *len)
+{
+ char *buf = NULL, *path = NULL, *value = NULL;
+
+ if (xsh == NULL)
+ goto out;
+
+ path = xs_get_domain_path(xsh, domid);
+ if (path == NULL) {
+ fprintf(logfile, "xs_get_domain_path(%d): error\n", domid);
+ goto out;
+ }
+
+ pasprintf(&buf, "%s/vm", path);
+ free(path);
+ path = xs_read(xsh, XBT_NULL, buf, NULL);
+ if (path == NULL) {
+ fprintf(logfile, "xs_read(%s): read error\n", buf);
+ goto out;
+ }
+
+ pasprintf(&buf, "%s/%s", path, key);
+ value = xs_read(xsh, XBT_NULL, buf, len);
+ if (value == NULL) {
+ fprintf(logfile, "xs_read(%s): read error\n", buf);
+ goto out;
+ }
+
+ out:
+ free(path);
+ free(buf);
+ return value;
+}
+
+int xenstore_vm_write(int domid, char *key, char *value)
+{
+ char *buf = NULL, *path = NULL;
+ int rc = -1;
+
+ if (xsh == NULL)
+ goto out;
+
+ path = xs_get_domain_path(xsh, domid);
+ if (path == NULL) {
+ fprintf(logfile, "xs_get_domain_path: error\n");
+ goto out;
+ }
+
+ pasprintf(&buf, "%s/vm", path);
+ free(path);
+ path = xs_read(xsh, XBT_NULL, buf, NULL);
+ if (path == NULL) {
+ fprintf(logfile, "xs_read(%s): read error\n", buf);
+ goto out;
+ }
+
+ pasprintf(&buf, "%s/%s", path, key);
+ rc = xs_write(xsh, XBT_NULL, buf, value, strlen(value));
+ if (rc == 0) {
+ fprintf(logfile, "xs_write(%s, %s): write error\n", buf, key);
+ goto out;
+ }
+
+ out:
+ free(path);
+ free(buf);
+ return rc;
+}