]> xenbits.xensource.com Git - qemu-xen-4.3-testing.git/commitdiff
wip xen landing from xen-unstable 17192:59b8768d0d0d
authorIan Jackson <iwj@mariner.uk.xensource.com>
Wed, 5 Mar 2008 18:10:39 +0000 (18:10 +0000)
committerIan Jackson <Ian.Jackson@eu.citrix.com>
Mon, 12 May 2008 11:20:10 +0000 (12:20 +0100)
19 files changed:
.gitignore
hw/xen_console.c [new file with mode: 0644]
hw/xen_console.h [new file with mode: 0644]
hw/xen_machine_fv.c [new file with mode: 0644]
hw/xen_machine_pv.c [new file with mode: 0644]
hw/xen_platform.c [new file with mode: 0644]
hw/xenfb.c [new file with mode: 0644]
hw/xenfb.h [new file with mode: 0644]
i386-dm/config.h [new file with mode: 0644]
i386-dm/config.mak [new file with mode: 0644]
i386-dm/cpu.h [new file with mode: 0644]
i386-dm/hooks.mak [new file with mode: 0644]
i386-stubdom/config.mak [new file with mode: 0644]
i386-stubdom/hooks.mak [new file with mode: 0644]
xen-config-host.mak [new file with mode: 0644]
xen-config.mak [new file with mode: 0644]
xen-hooks.mak [new file with mode: 0644]
xen-make-target-dir [new file with mode: 0755]
xenstore.c [new file with mode: 0644]

index d47fcc4c1bfb3b15d7595764099d47cf9d76bde6..4189d92d143d690c60fb9d7e7bcc92ffd4c6f40c 100644 (file)
@@ -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 (file)
index 0000000..fbd33f0
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ *  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:
+ */
diff --git a/hw/xen_console.h b/hw/xen_console.h
new file mode 100644 (file)
index 0000000..ece3577
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *  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);
diff --git a/hw/xen_machine_fv.c b/hw/xen_machine_fv.c
new file mode 100644 (file)
index 0000000..2d02b5e
--- /dev/null
@@ -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 <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:
+ */
diff --git a/hw/xen_machine_pv.c b/hw/xen_machine_pv.c
new file mode 100644 (file)
index 0000000..41f051d
--- /dev/null
@@ -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 (file)
index 0000000..597e2c0
--- /dev/null
@@ -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 <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");
+}
diff --git a/hw/xenfb.c b/hw/xenfb.c
new file mode 100644 (file)
index 0000000..affbcaa
--- /dev/null
@@ -0,0 +1,1411 @@
+#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:
+ */
diff --git a/hw/xenfb.h b/hw/xenfb.h
new file mode 100644 (file)
index 0000000..2f6ef2b
--- /dev/null
@@ -0,0 +1,13 @@
+#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
diff --git a/i386-dm/config.h b/i386-dm/config.h
new file mode 100644 (file)
index 0000000..1b423bf
--- /dev/null
@@ -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 (file)
index 0000000..1f48b2f
--- /dev/null
@@ -0,0 +1 @@
+include ../xen-config.mak
diff --git a/i386-dm/cpu.h b/i386-dm/cpu.h
new file mode 100644 (file)
index 0000000..6071a85
--- /dev/null
@@ -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 (file)
index 0000000..bb21544
--- /dev/null
@@ -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 (file)
index 0000000..1f48b2f
--- /dev/null
@@ -0,0 +1 @@
+include ../xen-config.mak
diff --git a/i386-stubdom/hooks.mak b/i386-stubdom/hooks.mak
new file mode 100644 (file)
index 0000000..33b9bce
--- /dev/null
@@ -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 (file)
index 0000000..9e9b589
--- /dev/null
@@ -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 (file)
index 0000000..6d99287
--- /dev/null
@@ -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 (file)
index 0000000..9804a05
--- /dev/null
@@ -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 (executable)
index 0000000..08e6a0a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/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
diff --git a/xenstore.c b/xenstore.c
new file mode 100644 (file)
index 0000000..e1c2532
--- /dev/null
@@ -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 <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;
+}