XenStore provides a hierarchical storage for Xen configuration
ala OpenFirmware.  Itself it's an interrupt driven producer/
consumer interface with two 1kb queues for input and output.

It's required in order to do virtual device discovery and
device configuration (MAC address, various paramethers).

OK?

---
 sys/dev/pv/files.pv   |   1 +
 sys/dev/pv/xen.c      |   5 +
 sys/dev/pv/xenstore.c | 793 ++++++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/pv/xenvar.h   |  45 +++
 4 files changed, 844 insertions(+)
 create mode 100644 sys/dev/pv/xenstore.c

diff --git sys/dev/pv/files.pv sys/dev/pv/files.pv
index 1e5c9bd..461ea08 100644
--- sys/dev/pv/files.pv
+++ sys/dev/pv/files.pv
@@ -15,5 +15,6 @@ file  dev/pv/vmt.c                    vmt     needs-flag
 
 # Xen
 device xen {}
 attach xen at pvbus
 file   dev/pv/xen.c                    xen     needs-flag
+file   dev/pv/xenstore.c               xen
diff --git sys/dev/pv/xen.c sys/dev/pv/xen.c
index 2fa7283..2c7c161 100644
--- sys/dev/pv/xen.c
+++ sys/dev/pv/xen.c
@@ -47,10 +47,12 @@ int         xen_match(struct device *, void *, void *);
 void   xen_attach(struct device *, struct device *, void *);
 void   xen_deferred(void *);
 void   xen_resume(struct device *);
 int    xen_activate(struct device *, int);
 
+int    xs_attach(struct xen_softc *);
+
 struct cfdriver xen_cd = {
        NULL, "xen", DV_DULL
 };
 
 struct cfattach xen_ca = {
@@ -99,10 +101,13 @@ xen_attach(struct device *parent, struct device *self, 
void *aux)
        xen_init_cbvec(sc);
 
        if (xen_init_interrupts(sc))
                return;
 
+       if (xs_attach(sc))
+               return;
+
        mountroothook_establish(xen_deferred, sc);
 }
 
 void
 xen_deferred(void *arg)
diff --git sys/dev/pv/xenstore.c sys/dev/pv/xenstore.c
new file mode 100644
index 0000000..3abbf45
--- /dev/null
+++ sys/dev/pv/xenstore.c
@@ -0,0 +1,793 @@
+/*
+ * Copyright (c) 2015 Mike Belopuhov
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/atomic.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/mutex.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <dev/pv/xenreg.h>
+#include <dev/pv/xenvar.h>
+
+/*
+ * The XenStore interface is a simple storage system that is a means of
+ * communicating state and configuration data between the Xen Domain 0
+ * and the various guest domains.  All configuration data other than
+ * a small amount of essential information required during the early
+ * boot process of launching a Xen aware guest, is managed using the
+ * XenStore.
+ *
+ * The XenStore is ASCII string based, and has a structure and semantics
+ * similar to a filesystem.  There are files and directories that are
+ * able to contain files or other directories.  The depth of the hierachy
+ * is only limited by the XenStore's maximum path length.
+ *
+ * The communication channel between the XenStore service and other
+ * domains is via two, guest specific, ring buffers in a shared memory
+ * area.  One ring buffer is used for communicating in each direction.
+ * The grant table references for this shared memory are given to the
+ * guest via HVM hypercalls.
+ *
+ * The XenStore communication relies on an event channel and thus
+ * interrupts. Several Xen services depend on the XenStore, most
+ * notably the XenBus used to discover and manage Xen devices.
+ */
+
+const struct
+{
+       const char              *xse_errstr;
+       int                      xse_errnum;
+} xs_errors[] = {
+       { "EINVAL",     EINVAL },
+       { "EACCES",     EACCES },
+       { "EEXIST",     EEXIST },
+       { "EISDIR",     EISDIR },
+       { "ENOENT",     ENOENT },
+       { "ENOMEM",     ENOMEM },
+       { "ENOSPC",     ENOSPC },
+       { "EIO",        EIO },
+       { "ENOTEMPTY",  ENOTEMPTY },
+       { "ENOSYS",     ENOSYS },
+       { "EROFS",      EROFS },
+       { "EBUSY",      EBUSY },
+       { "EAGAIN",     EAGAIN },
+       { "EISCONN",    EISCONN },
+       { NULL,         -1 },
+};
+
+struct xs_msghdr
+{
+       /* Message type */
+       uint32_t                 xmh_type;
+       /* Request identifier, echoed in daemon's response.  */
+       uint32_t                 xmh_rid;
+       /* Transaction id (0 if not related to a transaction). */
+       uint32_t                 xmh_tid;
+       /* Length of data following this. */
+       uint32_t                 xmh_len;
+       /* Generally followed by nul-terminated string(s). */
+} __packed;
+
+/*
+ * A minimum output buffer size needed to store an error string.
+ */
+#define XS_ERR_PAYLOAD         16
+
+/*
+ * Although Xen source code implies that the limit is 4k, in practice
+ * Mike has figured out that we can only send 2k bytes of payload w/o
+ * receiving a ENOSPC.  We set it to an even smaller value however,
+ * because there's no real need to use large buffers for anything.
+ */
+#define XS_MAX_PAYLOAD         1024
+
+struct xs_msg {
+       struct xs_msghdr         xsm_hdr;
+       int                      xsm_read;
+       int                      xsm_dlen;
+       uint8_t                 *xsm_data;
+       TAILQ_ENTRY(xs_msg)      xsm_link;
+};
+TAILQ_HEAD(xs_msgq, xs_msg);
+
+#define XS_RING_SIZE           1024
+
+struct xs_ring {
+       uint8_t                 xsr_req[XS_RING_SIZE];
+       uint8_t                 xsr_rsp[XS_RING_SIZE];
+       uint32_t                xsr_req_cons;
+       uint32_t                xsr_req_prod;
+       uint32_t                xsr_rsp_cons;
+       uint32_t                xsr_rsp_prod;
+} __packed;
+
+#define XST_DELAY              1       /* in seconds */
+
+/*
+ * Container for all XenStore related state.
+ */
+struct xs_softc {
+       struct xen_softc        *xs_sc;
+
+       evtchn_port_t            xs_port;
+       xen_intr_handle_t        xs_ih;
+
+       struct xs_ring          *xs_ring;
+
+       struct xs_msg            xs_msgs[10];
+       struct xs_msg           *xs_rmsg;
+
+       struct xs_msgq           xs_free;
+       struct xs_msgq           xs_reqs;
+       struct xs_msgq           xs_rsps;
+
+       volatile uint            xs_rid;
+
+       const char              *xs_wchan;
+       const char              *xs_rchan;
+
+       struct mutex             xs_reqlck;     /* request queue mutex */
+       struct mutex             xs_rsplck;     /* response queue mutex */
+       struct mutex             xs_frqlck;     /* free queue mutex */
+
+       uint                     xs_rngsem;
+};
+
+struct xs_msg  *xs_get_msg(struct xs_softc *, int);
+void            xs_put_msg(struct xs_softc *, struct xs_msg *);
+int             xs_ring_get(struct xs_softc *, void *, size_t);
+int             xs_ring_put(struct xs_softc *, void *, size_t);
+void            xs_intr(void *);
+int             xs_start(struct xs_transaction *, struct xs_msg *,
+                   struct iovec *, int);
+struct xs_msg  *xs_reply(struct xs_transaction *, uint);
+int             xs_parse(struct xs_transaction *, struct xs_msg *,
+                    struct iovec **, int *);
+
+int
+xs_attach(struct xen_softc *sc)
+{
+        struct xen_hvm_param xhv;
+       struct xs_softc *xs;
+       paddr_t pa;
+       int i;
+
+       if ((xs = malloc(sizeof(*xs), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
+               printf("%s: failed to allocate xenstore softc\n",
+                   sc->sc_dev.dv_xname);
+               return (-1);
+       }
+       sc->sc_xs = xs;
+       xs->xs_sc = sc;
+
+       /* Fetch event channel port */
+       memset(&xhv, 0, sizeof(xhv));
+       xhv.domid = DOMID_SELF;
+       xhv.index = HVM_PARAM_STORE_EVTCHN;
+       if (xen_hypercall(sc, hvm_op, 2, HVMOP_get_param, &xhv))
+               goto fail_1;
+       xs->xs_port = xhv.value;
+
+       DPRINTF("%s: xenstore event channel %d\n", sc->sc_dev.dv_xname,
+           xs->xs_port);
+
+       /* Fetch a frame number (PA) of a shared xenstore page */
+       memset(&xhv, 0, sizeof(xhv));
+       xhv.domid = DOMID_SELF;
+       xhv.index = HVM_PARAM_STORE_PFN;
+       if (xen_hypercall(sc, hvm_op, 2, HVMOP_get_param, &xhv))
+               goto fail_1;
+       pa = ptoa(xhv.value);
+       /* Allocate a page of virtual memory */
+       xs->xs_ring = km_alloc(PAGE_SIZE, &kv_any, &kp_none, &kd_nowait);
+       if (xs->xs_ring == NULL)
+               goto fail_1;
+       /* Map in the xenstore page into our KVA */
+       pa |= PMAP_NOCACHE;
+       pmap_kenter_pa((vaddr_t)xs->xs_ring, pa, PROT_READ | PROT_WRITE);
+       pmap_update(pmap_kernel());
+
+       DPRINTF("%s: xenstore ring at va %p pa %#lx\n", sc->sc_dev.dv_xname,
+           xs->xs_ring, pa & ~PMAP_NOCACHE);
+
+       if (xen_intr_establish(xs->xs_port, &xs->xs_ih, xs_intr, xs, "xs0"))
+               goto fail_2;
+
+       DPRINTF("%s: xenstore interrupt established for port %d\n",
+           sc->sc_dev.dv_xname, xs->xs_ih);
+
+       xs->xs_wchan = "xswrite";
+       xs->xs_rchan = "xsread";
+
+       TAILQ_INIT(&xs->xs_free);
+       TAILQ_INIT(&xs->xs_reqs);
+       TAILQ_INIT(&xs->xs_rsps);
+       for (i = 0; i < nitems(xs->xs_msgs); i++)
+               TAILQ_INSERT_TAIL(&xs->xs_free, &xs->xs_msgs[i], xsm_link);
+
+       mtx_init(&xs->xs_reqlck, IPL_NET);
+       mtx_init(&xs->xs_rsplck, IPL_NET);
+       mtx_init(&xs->xs_frqlck, IPL_NET);
+
+       return (0);
+
+ fail_2:
+       pmap_kremove((vaddr_t)xs->xs_ring, PAGE_SIZE);
+       pmap_update(pmap_kernel());
+       km_free(xs->xs_ring, PAGE_SIZE, &kv_any, &kp_none);
+       xs->xs_ring = NULL;
+ fail_1:
+       free(xs, sizeof(*xs), M_DEVBUF);
+       sc->sc_xs = NULL;
+       return (-1);
+}
+
+int
+xs_resume(struct xen_softc *sc)
+{
+       struct xs_softc *xs = sc->sc_xs;
+
+       xs->xs_ring->xsr_rsp_prod = xs->xs_ring->xsr_rsp_cons;
+
+       if (xen_intr_disestablish(xs->xs_ih))
+               return (-1);
+
+       if (xen_intr_establish(xs->xs_port, &xs->xs_ih, xs_intr, xs, "xs0"))
+               return (-1);
+
+       DPRINTF("%s: xenstore interrupt established for port %d\n",
+           sc->sc_dev.dv_xname, xs->xs_ih);
+
+       return (0);
+}
+
+static inline int
+xs_sem_get(uint *semaphore)
+{
+       if (atomic_inc_int_nv(semaphore) != 1) {
+               /* we're out of luck */
+               if (atomic_dec_int_nv(semaphore) == 0)
+                       wakeup(semaphore);
+               return (0);
+       }
+       return (1);
+}
+
+static inline void
+xs_sem_put(uint *semaphore)
+{
+       if (atomic_dec_int_nv(semaphore) == 0)
+               wakeup(semaphore);
+}
+
+struct xs_msg *
+xs_get_msg(struct xs_softc *xs, int waitok)
+{
+       static const char *chan = "xsalloc";
+       struct xs_msg *xsm;
+
+       mtx_enter(&xs->xs_frqlck);
+       for (;;) {
+               xsm = TAILQ_FIRST(&xs->xs_free);
+               if (xsm != NULL) {
+                       TAILQ_REMOVE(&xs->xs_free, xsm, xsm_link);
+                       break;
+               }
+               if (!waitok) {
+                       mtx_leave(&xs->xs_frqlck);
+                       delay(XST_DELAY * 1000 >> 2);
+                       mtx_enter(&xs->xs_frqlck);
+               } else
+                       msleep(chan, &xs->xs_frqlck, PRIBIO, chan,
+                           XST_DELAY * hz >> 2);
+       }
+       mtx_leave(&xs->xs_frqlck);
+       return (xsm);
+}
+
+void
+xs_put_msg(struct xs_softc *xs, struct xs_msg *xsm)
+{
+       memset(xsm, 0, sizeof(*xsm));
+       mtx_enter(&xs->xs_frqlck);
+       TAILQ_INSERT_TAIL(&xs->xs_free, xsm, xsm_link);
+       mtx_leave(&xs->xs_frqlck);
+}
+
+int
+xs_geterror(struct xs_msg *xsm)
+{
+       int i;
+
+       for (i = 0; i < nitems(xs_errors); i++)
+               if (strcmp(xs_errors[i].xse_errstr, xsm->xsm_data) == 0)
+                       break;
+       return (xs_errors[i].xse_errnum);
+}
+
+static inline int
+xs_ring_avail(struct xs_ring *xsr, int req)
+{
+       int cons = req ? xsr->xsr_req_cons : xsr->xsr_rsp_cons;
+       int prod = req ? xsr->xsr_req_prod : xsr->xsr_rsp_prod;
+
+       membar_consumer();
+#ifdef XEN_DEBUG
+       KASSERT(prod <= XS_RING_SIZE && cons < XS_RING_SIZE);
+#endif
+       if (prod > cons)
+               return (prod - cons);
+       else
+               return (XS_RING_SIZE - cons + prod);
+       return (0);
+}
+
+static inline void
+xs_ring_reset(struct xs_softc *xs, int req)
+{
+       struct xs_ring *xsr = xs->xs_ring;
+
+       if (req) {
+               xsr->xsr_req_cons = 0;
+               xsr->xsr_req_prod = 0;
+       } else {
+               xsr->xsr_rsp_prod = 0;
+               xsr->xsr_rsp_cons = 0;
+       }
+       membar_producer();
+}
+
+static inline int
+xs_output(struct xs_transaction *xst, uint8_t *bp, int len)
+{
+       struct xs_softc *xs = xst->xst_sc;
+       int chunk, s;
+
+       while (len > 0) {
+               chunk = xs_ring_put(xs, bp, MIN(len, XS_RING_SIZE));
+               if (chunk < 0)
+                       return (-1);
+               if (chunk > 0) {
+                       len -= chunk;
+                       bp += chunk;
+                       if (xs->xs_ring->xsr_req_prod < XS_RING_SIZE)
+                               continue;
+               }
+               /* Squeaky wheel gets the kick */
+               xen_intr_signal(xs->xs_ih);
+               /*
+                * chunk == 0: we need to wait for hv to consume
+                * what has already been written;
+                *
+                * Alternatively we have managed to fill the ring
+                * and must wait for HV to collect the data.
+                */
+               while (xs->xs_ring->xsr_req_prod > xs->xs_ring->xsr_req_cons) {
+                       if (xst->xst_flags & XST_POLL) {
+                               delay(XST_DELAY * 1000 >> 2);
+                               s = splnet();
+                               xs_intr(xs);
+                               splx(s);
+                       } else
+                               tsleep(xs->xs_wchan, PRIBIO, xs->xs_wchan,
+                                   XST_DELAY * hz >> 2);
+                       membar_sync();
+               }
+               /* It's safe to do a reset here because cons == prod == 1024 */
+               if (xs->xs_ring->xsr_req_prod == XS_RING_SIZE)
+                       xs_ring_reset(xs, 1);
+       }
+       return (0);
+}
+
+int
+xs_start(struct xs_transaction *xst, struct xs_msg *xsm, struct iovec *iov,
+    int iov_cnt)
+{
+       struct xs_softc *xs = xst->xst_sc;
+       int i;
+
+       while (!xs_sem_get(&xs->xs_rngsem)) {
+               if (xst->xst_flags & XST_POLL)
+                       delay(XST_DELAY * 1000 >> 2);
+               else
+                       tsleep(&xs->xs_rngsem, PRIBIO, "xsaccess",
+                           XST_DELAY * hz >> 2);
+       }
+
+#if 0
+       /* A large hammer, should not be needed */
+       xs_ring_reset(xs, 1);
+       xen_intr_signal(xs->xs_ih);
+#endif
+
+       /* Header */
+       if (xs_output(xst, (uint8_t *)&xsm->xsm_hdr,
+           sizeof(xsm->xsm_hdr)) == -1) {
+               printf("%s: failed to write the header\n", __func__);
+               return (-1);
+       }
+
+       /* Data loop */
+       for (i = 0; i < iov_cnt; i++) {
+               if (xs_output(xst, iov[i].iov_base, iov[i].iov_len) == -1) {
+                       printf("%s: failed on iovec #%d len %ld\n", __func__,
+                           i, iov[i].iov_len);
+                       return (-1);
+               }
+       }
+
+       mtx_enter(&xs->xs_reqlck);
+       TAILQ_INSERT_TAIL(&xs->xs_reqs, xsm, xsm_link);
+       mtx_leave(&xs->xs_reqlck);
+
+       xen_intr_signal(xs->xs_ih);
+
+       xs_sem_put(&xs->xs_rngsem);
+
+       return (0);
+}
+
+struct xs_msg *
+xs_reply(struct xs_transaction *xst, uint rid)
+{
+       struct xs_softc *xs = xst->xst_sc;
+       struct xs_msg *xsm;
+       int s;
+
+       mtx_enter(&xs->xs_rsplck);
+       for (;;) {
+               TAILQ_FOREACH(xsm, &xs->xs_rsps, xsm_link) {
+                       if (xsm->xsm_hdr.xmh_tid == xst->xst_id &&
+                           xsm->xsm_hdr.xmh_rid == rid)
+                               break;
+               }
+               if (xsm != NULL) {
+                       TAILQ_REMOVE(&xs->xs_rsps, xsm, xsm_link);
+                       break;
+               }
+               if (xst->xst_flags & XST_POLL) {
+                       mtx_leave(&xs->xs_rsplck);
+                       delay(XST_DELAY * 1000 >> 2);
+                       s = splnet();
+                       xs_intr(xs);
+                       splx(s);
+                       mtx_enter(&xs->xs_rsplck);
+               } else
+                       msleep(xs->xs_rchan, &xs->xs_rsplck, PRIBIO,
+                           xs->xs_rchan, XST_DELAY * hz >> 2);
+       }
+       mtx_leave(&xs->xs_rsplck);
+       return (xsm);
+}
+
+int
+xs_ring_put(struct xs_softc *xs, void *src, size_t size)
+{
+       struct xs_ring *xsr = xs->xs_ring;
+       int cons = xsr->xsr_req_cons;
+       int prod = xsr->xsr_req_prod;
+       int left = XS_RING_SIZE - prod;
+
+       membar_consumer();
+#ifdef XEN_DEBUG
+       KASSERT(prod <= XS_RING_SIZE && cons < XS_RING_SIZE);
+#endif
+       if (size > XS_RING_SIZE)
+               return (-1);
+       if (cons > prod)
+               size = MIN(size, cons - prod);
+       else
+               size = MIN(size, left);
+       memcpy(&xsr->xsr_req[prod], src, size);
+       membar_producer();
+       xsr->xsr_req_prod += size; /* This never goes above the ring size */
+       return (size);
+}
+
+int
+xs_ring_get(struct xs_softc *xs, void *dst, size_t size)
+{
+       struct xs_ring *xsr = xs->xs_ring;
+       int cons = xsr->xsr_rsp_cons;
+       int prod = xsr->xsr_rsp_prod;
+       int left = XS_RING_SIZE - cons;
+
+       membar_consumer();
+#ifdef XEN_DEBUG
+       KASSERT(prod <= XS_RING_SIZE && cons < XS_RING_SIZE);
+#endif
+       if (size > XS_RING_SIZE)
+               return (-1);
+       if (prod == cons)
+               return (0);
+       if (prod > cons)
+               size = MIN(size, prod - cons);
+       else
+               size = MIN(size, left);
+       memcpy(dst, &xsr->xsr_rsp[cons], size);
+       membar_producer();
+       xsr->xsr_rsp_cons += size; /* This never goes above the ring size */
+       return (size);
+}
+
+void
+xs_intr(void *arg)
+{
+       struct xs_softc *xs = arg;
+       struct xs_ring *xsr = xs->xs_ring;
+       struct xen_softc *sc = xs->xs_sc;
+       struct xs_msg *xsm = xs->xs_rmsg;
+       struct xs_msghdr xmh;
+       int avail, len;
+
+       membar_sync();
+
+       if (xsr->xsr_rsp_cons == xsr->xsr_rsp_prod)
+               return;
+
+       avail = xs_ring_avail(xsr, 0);
+
+       /* Response processing */
+
+       if (xs->xs_rmsg == NULL) {
+               if (avail < sizeof(xmh)) {
+                       printf("%s: incomplete header: %d\n",
+                           sc->sc_dev.dv_xname, avail);
+                       goto out;
+               }
+               avail -= sizeof(xmh);
+
+               if (TAILQ_EMPTY(&xs->xs_reqs)) {
+                       printf("%s: missing requests\n", sc->sc_dev.dv_xname);
+                       goto out;
+               }
+
+               if ((len = xs_ring_get(xs, &xmh, sizeof(xmh))) != sizeof(xmh)) {
+                       printf("%s: message too short: %d\n",
+                           sc->sc_dev.dv_xname, len);
+                       goto out;
+               }
+
+               TAILQ_FOREACH(xsm, &xs->xs_reqs, xsm_link) {
+                       if (xsm->xsm_hdr.xmh_rid == xmh.xmh_rid)
+                               break;
+               }
+               if (xsm == NULL) {
+                       printf("%s: received unexpected message id %u\n",
+                           sc->sc_dev.dv_xname, xmh.xmh_rid);
+                       goto out;
+               }
+
+               memcpy(&xsm->xsm_hdr, &xmh, sizeof(xmh));
+               xs->xs_rmsg = xsm;
+
+               if (avail == 0)
+                       goto out;
+       }
+
+       if (xsm->xsm_hdr.xmh_len > xsm->xsm_dlen)
+               panic("message too large: %d vs %d for type %d, rid %u",
+                   xsm->xsm_hdr.xmh_len, xsm->xsm_dlen, xsm->xsm_hdr.xmh_type,
+                   xsm->xsm_hdr.xmh_rid);
+
+       len = MIN(xsm->xsm_hdr.xmh_len - xsm->xsm_read, avail);
+       if ((len = xs_ring_get(xs, &xsm->xsm_data[xsm->xsm_read], len)) <= 0) {
+               printf("%s: read failure %d\n", sc->sc_dev.dv_xname, len);
+               goto out;
+       }
+       xsm->xsm_read += len;
+
+       /* Notify reader that we've managed to read the whole message */
+       if (xsm->xsm_read == xsm->xsm_hdr.xmh_len) {
+               xs->xs_rmsg = NULL;
+               mtx_enter(&xs->xs_rsplck);
+               TAILQ_REMOVE(&xs->xs_reqs, xsm, xsm_link);
+               TAILQ_INSERT_TAIL(&xs->xs_rsps, xsm, xsm_link);
+               mtx_leave(&xs->xs_rsplck);
+               wakeup(xs->xs_rchan);
+
+               xs_ring_reset(xs, 0);
+       }
+
+       /* It's safe to do a reset here because cons == prod == 1024 */
+       if (xs->xs_ring->xsr_rsp_prod == XS_RING_SIZE)
+               xs_ring_reset(xs, 0);
+
+ out:
+       /* Wakeup sleeping writes (if any) */
+       wakeup(xs->xs_wchan);
+       xen_intr_signal(xs->xs_ih);
+}
+
+static inline int
+xs_get_buf(struct xs_transaction *xst, struct xs_msg *xsm, int len)
+{
+       unsigned char *buf = NULL;
+
+       buf = malloc(len, M_DEVBUF, M_ZERO | (xst->xst_flags & XST_POLL ?
+           M_NOWAIT : M_WAITOK));
+       if (buf == NULL)
+               return (-1);
+       xsm->xsm_dlen = len;
+       xsm->xsm_data = buf;
+       return (0);
+}
+
+static inline void
+xs_put_buf(struct xs_transaction *xst, struct xs_msg *xsm)
+{
+       free(xsm->xsm_data, M_DEVBUF, xsm->xsm_dlen);
+       xsm->xsm_data = NULL;
+}
+
+void
+xs_resfree(struct xs_transaction *xst, struct iovec *iov, int iov_cnt)
+{
+       int i;
+
+       for (i = 0; i < iov_cnt; i++)
+               free(iov[i].iov_base, M_DEVBUF, iov[i].iov_len);
+       free(iov, M_DEVBUF, sizeof(struct iovec) * iov_cnt);
+}
+
+int
+xs_parse(struct xs_transaction *xst, struct xs_msg *xsm, struct iovec **iov,
+    int *iov_cnt)
+{
+       char *bp, *cp;
+       int i, flags;
+
+       flags = M_ZERO | (xst->xst_flags & XST_POLL ? M_NOWAIT : M_WAITOK);
+
+       *iov_cnt = 0;
+       /* Make sure that the data is NUL terminated */
+       xsm->xsm_data[xsm->xsm_hdr.xmh_len - 1] = '\0';
+       for (i = 0; i < xsm->xsm_hdr.xmh_len; i++)
+               if (i > 0 && xsm->xsm_data[i] == '\0')
+                       (*iov_cnt)++;
+       if (!*iov_cnt)
+               return (0);
+       *iov = mallocarray(*iov_cnt, sizeof(struct iovec), M_DEVBUF, flags);
+       if (*iov == NULL)
+               return (-1);
+       bp = xsm->xsm_data;
+       for (i = 0; i < *iov_cnt; i++) {
+               for (cp = bp;
+                    cp - (caddr_t)xsm->xsm_data < xsm->xsm_hdr.xmh_len; cp++)
+                       if (*cp == '\0')
+                               break;
+               (*iov)[i].iov_len = cp - bp + 1;
+               (*iov)[i].iov_base = malloc((*iov)[i].iov_len, M_DEVBUF, flags);
+               if (!(*iov)[i].iov_base)
+                       goto cleanup;
+               memcpy((*iov)[i].iov_base, bp, (*iov)[i].iov_len);
+               bp = ++cp;
+       }
+
+       return (0);
+
+ cleanup:
+       xs_resfree(xst, *iov, *iov_cnt);
+       return (ENOMEM);
+}
+
+int
+xs_cmd(struct xs_transaction *xst, int cmd, const char *path,
+    struct iovec **iov, int *iov_cnt)
+{
+       struct xs_softc *xs = xst->xst_sc;
+       struct xs_msg *xsm;
+       struct iovec ov[10];    /* output vector */
+       int datalen = XS_ERR_PAYLOAD;
+       int ov_cnt = 0;
+       enum { READ, WRITE } mode = READ;
+       int i, error = 0;
+
+       if (cmd >= XS_MAX)
+               return (-1);
+
+       switch (cmd) {
+       case XS_TRANSACTION_START:
+               ov[0].iov_base = "";
+               ov[0].iov_len = 1;
+               ov_cnt++;
+               break;
+       case XS_TRANSACTION_END:
+               mode = WRITE;
+               break;
+       case XS_MKDIR:
+       case XS_RM:
+       case XS_WRITE:
+               mode = WRITE;
+               /* FALLTHROUGH */
+       default:
+               if (mode == READ)
+                       datalen = XS_MAX_PAYLOAD;
+               break;
+       }
+
+       if (path) {
+               ov[ov_cnt].iov_base = (void *)path;
+               ov[ov_cnt++].iov_len = strlen(path) + 1; /* +NUL */
+       }
+
+       if (mode == WRITE && iov && iov_cnt && *iov_cnt > 0) {
+               for (i = 0; i < *iov_cnt && ov_cnt < nitems(ov);
+                    i++, ov_cnt++) {
+                       ov[ov_cnt].iov_base = (*iov)[i].iov_base;
+                       ov[ov_cnt].iov_len = (*iov)[i].iov_len;
+               }
+               KASSERT(ov_cnt < nitems(ov));
+       }
+
+       xsm = xs_get_msg(xs, !(xst->xst_flags & XST_POLL));
+
+       if (xs_get_buf(xst, xsm, datalen)) {
+               xs_put_msg(xs, xsm);
+               return (-1);
+       }
+
+       xsm->xsm_hdr.xmh_tid = xst->xst_id;
+       xsm->xsm_hdr.xmh_type = cmd;
+       xsm->xsm_hdr.xmh_rid = atomic_inc_int_nv(&xs->xs_rid);
+
+       for (i = 0; i < ov_cnt; i++)
+               xsm->xsm_hdr.xmh_len += ov[i].iov_len;
+
+       if (xsm->xsm_hdr.xmh_len >= XS_MAX_PAYLOAD) {
+               printf("%s: message type %d with payload above the limit\n",
+                   xs->xs_sc->sc_dev.dv_xname, cmd);
+               xs_put_buf(xst, xsm);
+               xs_put_msg(xs, xsm);
+               return (-1);
+       }
+
+       if (xs_start(xst, xsm, ov, ov_cnt)) {
+               printf("%s: message type %d transmission failed\n",
+                   xs->xs_sc->sc_dev.dv_xname, cmd);
+               xs_put_buf(xst, xsm);
+               xs_put_msg(xs, xsm);
+               return (-1);
+       }
+
+       xsm = xs_reply(xst, xsm->xsm_hdr.xmh_rid);
+
+       if (xsm->xsm_hdr.xmh_type == XS_ERROR) {
+               error = xs_geterror(xsm);
+               DPRINTF("%s: xenstore request %d error %s\n",
+                   xs->xs_sc->sc_dev.dv_xname, cmd, xsm->xsm_data);
+       } else if (mode == READ) {
+               KASSERT(iov && iov_cnt);
+               error = xs_parse(xst, xsm, iov, iov_cnt);
+       }
+#ifdef XEN_DEBUG
+       else
+               if (strcmp(xsm->xsm_data, "OK"))
+                       printf("%s: xenstore request %d failed: %s\n",
+                           xs->xs_sc->sc_dev.dv_xname, cmd, xsm->xsm_data);
+#endif
+
+       xs_put_buf(xst, xsm);
+       xs_put_msg(xs, xsm);
+
+       return (error);
+}
diff --git sys/dev/pv/xenvar.h sys/dev/pv/xenvar.h
index e58d233..9d73921 100644
--- sys/dev/pv/xenvar.h
+++ sys/dev/pv/xenvar.h
@@ -46,10 +46,15 @@ struct xen_softc {
 
        int                      sc_cbvec;      /* callback was installed */
        uint64_t                 sc_irq;        /* IDT vector number */
        struct evcount           sc_evcnt;      /* upcall counter */
        SLIST_HEAD(, xen_intsrc) sc_intrs;
+
+       /*
+        * Xenstore
+        */
+       struct xs_softc         *sc_xs;         /* xenstore softc */
 };
 
 extern struct xen_softc *xen_sc;
 
 /*
@@ -74,6 +79,46 @@ void xen_intr_signal(xen_intr_handle_t);
 int    xen_intr_establish(evtchn_port_t, xen_intr_handle_t *, void (*)(void *),
            void *, char *);
 int    xen_intr_disestablish(xen_intr_handle_t);
 void   xen_intr_enable(void);
 
+/*
+ *  XenStore
+ */
+enum {
+       XS_DEBUG,
+       XS_DIRECTORY,
+       XS_READ,
+       XS_GET_PERMS,
+       XS_WATCH,
+       XS_UNWATCH,
+       XS_TRANSACTION_START,
+       XS_TRANSACTION_END,
+       XS_INTRODUCE,
+       XS_RELEASE,
+       XS_GET_DOMAIN_PATH,
+       XS_WRITE,
+       XS_MKDIR,
+       XS_RM,
+       XS_SET_PERMS,
+       XS_WATCH_EVENT,
+       XS_ERROR,
+       XS_IS_DOMAIN_INTRODUCED,
+       XS_RESUME,
+       XS_SET_TARGET,
+       XS_RESTRICT,
+       XS_RESET_WATCHES,
+       XS_MAX
+};
+
+struct xs_transaction {
+       uint32_t                 xst_id;
+       uint32_t                 xst_flags;
+#define XST_POLL               0x0001
+       struct xs_softc         *xst_sc;
+};
+
+int    xs_cmd(struct xs_transaction *, int, const char *, struct iovec **,
+           int *);
+void   xs_resfree(struct xs_transaction *, struct iovec *, int);
+
 #endif /* _XENVAR_H_ */
-- 
2.6.3

Reply via email to