diff --git a/.gitignore b/.gitignore
index 18ef56a780..0e5d5ceaab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -206,6 +206,7 @@ tools/fuzz/x86_instruction_emulator/x86_emulate
 tools/fuzz/x86_instruction_emulator/x86-emulate.[ch]
 tools/helpers/init-xenstore-domain
 tools/helpers/xen-init-dom0
+tools/helpers/late-init-pv
 tools/hotplug/common/hotplugpath.sh
 tools/hotplug/FreeBSD/rc.d/xencommons
 tools/hotplug/FreeBSD/rc.d/xendriverdomain
diff --git a/tools/helpers/Makefile b/tools/helpers/Makefile
index 8d78ab1e90..c32481202d 100644
--- a/tools/helpers/Makefile
+++ b/tools/helpers/Makefile
@@ -14,6 +14,7 @@ ifeq ($(CONFIG_ARM),y)
 PROGS += init-dom0less
 endif
 endif
+PROGS += late-init-pv
 
 XEN_INIT_DOM0_OBJS = xen-init-dom0.o init-dom-json.o
 $(XEN_INIT_DOM0_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
@@ -36,6 +37,13 @@ $(INIT_DOM0LESS_OBJS): CFLAGS += $(CFLAGS_libxenlight)
 $(INIT_DOM0LESS_OBJS): CFLAGS += $(CFLAGS_libxenctrl)
 $(INIT_DOM0LESS_OBJS): CFLAGS += $(CFLAGS_libxenevtchn)
 
+LATE_INIT_PV_OBJS = late-init-pv.o builder-hypfs.o xs-helpers.o
+$(LATE_INIT_PV_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
+$(LATE_INIT_PV_OBJS): CFLAGS += $(CFLAGS_libxenguest)
+$(LATE_INIT_PV_OBJS): CFLAGS += $(CFLAGS_libxenctrl)
+$(LATE_INIT_PV_OBJS): CFLAGS += $(CFLAGS_libxenhypfs)
+$(LATE_INIT_PV_OBJS): CFLAGS += $(CFLAGS_libxenstore)
+
 .PHONY: all
 all: $(PROGS)
 
@@ -48,6 +56,9 @@ init-xenstore-domain: $(INIT_XENSTORE_DOMAIN_OBJS)
 init-dom0less: $(INIT_DOM0LESS_OBJS)
 	$(CC) $(LDFLAGS) -o $@ $(INIT_DOM0LESS_OBJS) $(LDLIBS_libxenctrl) $(LDLIBS_libxenevtchn) $(LDLIBS_libxentoollog) $(LDLIBS_libxenstore) $(LDLIBS_libxenlight) $(LDLIBS_libxenguest) $(LDLIBS_libxenforeignmemory) $(APPEND_LDFLAGS)
 
+late-init-pv: $(LATE_INIT_PV_OBJS)
+	$(CC) $(LDFLAGS) -o $@ $(LATE_INIT_PV_OBJS) $(LDLIBS_libxentoollog) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) $(LDLIBS_libxenhypfs) $(APPEND_LDFLAGS)
+
 .PHONY: install
 install: all
 	$(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
diff --git a/tools/helpers/builder-hypfs.c b/tools/helpers/builder-hypfs.c
new file mode 100644
index 0000000000..d123426cfa
--- /dev/null
+++ b/tools/helpers/builder-hypfs.c
@@ -0,0 +1,253 @@
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <xenhypfs.h>
+
+#include "late-init-pv.h"
+
+/* general size for static path array */
+#define HYPFS_MAX_PATH 100
+
+bool has_builder_hypfs(xenhypfs_handle *hdl, uint32_t domid)
+{
+    struct xenhypfs_dirent *ent;
+    char path[HYPFS_MAX_PATH];
+    unsigned int n;
+
+    snprintf(path, HYPFS_MAX_PATH, "/builder/%d", domid);
+
+    ent = xenhypfs_readdir(hdl, path, &n);
+    if ( ent )
+    {
+        free(ent);
+        return true;
+    }
+
+    return false;
+}
+
+static int read_hypfs_bool(xenhypfs_handle *fshdl, const char *path, bool *val)
+{
+    struct xenhypfs_dirent *dirent;
+    void *raw_value;
+
+    errno = 0;
+
+    raw_value = xenhypfs_read_raw(fshdl, path, &dirent);
+    if ( raw_value == NULL )
+    {
+        errno = EIO;
+        return false;
+    }
+
+    if ( dirent->type != xenhypfs_type_bool )
+    {
+        errno = EINVAL;
+        return false;
+    }
+
+    *val = *(bool *)raw_value;
+
+    free(raw_value); free(dirent);
+    return true;
+}
+
+static bool read_hypfs_uint(
+    xenhypfs_handle *fshdl, const char *path, size_t sz, void *val)
+{
+    struct xenhypfs_dirent *dirent;
+    void *raw_value;
+
+    errno = 0;
+
+    raw_value = xenhypfs_read_raw(fshdl, path, &dirent);
+    if ( raw_value == NULL )
+    {
+        errno = EIO;
+        return false;
+    }
+
+    if ( (dirent->type != xenhypfs_type_uint) ||
+         (dirent->size != sz) )
+    {
+        errno = EINVAL;
+        return false;
+    }
+
+    switch ( sz )
+    {
+    case sizeof(uint8_t):
+        *(uint8_t *)val = *(uint8_t *)raw_value;
+        break;
+    case sizeof(uint16_t):
+        *(uint16_t *)val = *(uint16_t *)raw_value;
+        break;
+    case sizeof(uint32_t):
+        *(uint32_t *)val = *(uint32_t *)raw_value;
+        break;
+    case sizeof(uint64_t):
+        *(uint64_t *)val = *(uint64_t *)raw_value;
+        break;
+    default:
+        free(raw_value); free(dirent);
+        errno = EINVAL;
+        return false;
+    }
+
+    free(raw_value); free(dirent);
+    return true;
+}
+
+static uint8_t read_hypfs_uint8(xenhypfs_handle *fshdl, const char *path)
+{
+    uint8_t value;
+
+    if ( !read_hypfs_uint(fshdl, path, sizeof(value), &value) )
+    {
+        fprintf(stderr, "error: unable to read uint8_t from %s \n", path);
+        return 0;
+    }
+
+    return value;
+}
+
+static uint16_t read_hypfs_uint16(xenhypfs_handle *fshdl, const char *path)
+{
+    uint16_t value;
+
+    if ( !read_hypfs_uint(fshdl, path, sizeof(value), &value) )
+    {
+        fprintf(stderr, "error: unable to read uint16_t from %s \n", path);
+        return 0;
+    }
+
+    return value;
+}
+
+static uint32_t read_hypfs_uint32(xenhypfs_handle *fshdl, const char *path)
+{
+    uint32_t value;
+
+    if ( !read_hypfs_uint(fshdl, path, sizeof(value), &value) )
+    {
+        fprintf(stderr, "error: unable to read uint32_t from %s \n", path);
+        return 0;
+    }
+
+    return value;
+}
+
+static uint64_t read_hypfs_uint64(xenhypfs_handle *fshdl, const char *path)
+{
+    uint64_t value;
+
+    if ( !read_hypfs_uint(fshdl, path, sizeof(value), &value) )
+    {
+        fprintf(stderr, "error: unable to read uint64_t from %s \n", path);
+        return 0;
+    }
+
+    return value;
+}
+
+static bool is_constructed(xenhypfs_handle *fshdl, uint32_t domid)
+{
+    char path[HYPFS_MAX_PATH];
+    bool constructed;
+
+    snprintf(path, HYPFS_MAX_PATH, "/builder/%d/constructed", domid);
+
+    if ( !read_hypfs_bool(fshdl, path, &constructed) )
+    {
+        fprintf(stderr, "error: unable to read constructed field\n");
+        return false;
+    }
+
+    return constructed;
+}
+
+#define XS_PATH   "/builder/%d/xenstore"
+#define CONS_PATH "/builder/%d/devices/console"
+
+int read_hypfs_tree(xenhypfs_handle *hdl, struct domain_info *di)
+{
+    char path[HYPFS_MAX_PATH];
+
+    if ( !is_constructed(hdl, di->domid) )
+    {
+        fprintf(stderr, "error: domain %d did not get constructed\n",
+                di->domid);
+        return -EEXIST;
+    }
+
+    if ( !di->override_uuid )
+    {
+        snprintf(path, HYPFS_MAX_PATH, "/builder/%d/uuid", di->domid);
+        di->uuid = xenhypfs_read(hdl, path);
+    }
+
+    snprintf(path, HYPFS_MAX_PATH, "/builder/%d/ncpus", di->domid);
+    di->num_cpu = read_hypfs_uint32(hdl, path);
+    if ( errno != 0 )
+    {
+        fprintf(stderr, "error: unable to read number of cpus\n");
+        return -errno;
+    }
+
+    snprintf(path, HYPFS_MAX_PATH, "/builder/%d/mem_size", di->domid);
+    di->mem_info.target = read_hypfs_uint32(hdl, path);
+    if ( errno != 0 )
+    {
+        fprintf(stderr, "error: unable to read memory size\n");
+        return -errno;
+    }
+
+    snprintf(path, HYPFS_MAX_PATH, "/builder/%d/mem_max", di->domid);
+    di->mem_info.max = read_hypfs_uint32(hdl, path);
+    if ( errno != 0 )
+    {
+        fprintf(stderr, "error: unable to read max memory\n");
+        return -errno;
+    }
+
+    /* Xenstore */
+    snprintf(path, HYPFS_MAX_PATH, XS_PATH "/evtchn", di->domid);
+    di->xs_info.evtchn_port = read_hypfs_uint32(hdl, path);
+    if ( errno != 0 )
+    {
+        fprintf(stderr, "error: unable to read xenstore event channel port\n");
+        return -errno;
+    }
+
+    snprintf(path, HYPFS_MAX_PATH, XS_PATH "/mfn", di->domid);
+    di->xs_info.mfn = read_hypfs_uint64(hdl, path);
+    if ( errno != 0 )
+    {
+        fprintf(stderr, "error: unable to read xenstore page mfn\n");
+        return -errno;
+    }
+
+    /* Console */
+    if ( di->cons_info.enable )
+    {
+        snprintf(path, HYPFS_MAX_PATH, CONS_PATH "/evtchn", di->domid);
+        di->cons_info.evtchn_port = read_hypfs_uint32(hdl, path);
+        if ( errno != 0 )
+        {
+            fprintf(stderr, "error: unable to read xenstore event channel port\n");
+            return -errno;
+        }
+
+        snprintf(path, HYPFS_MAX_PATH, CONS_PATH "/mfn", di->domid);
+        di->cons_info.mfn = read_hypfs_uint64(hdl, path);
+        if ( errno != 0 )
+        {
+            fprintf(stderr, "error: unable to read xenstore page mfn\n");
+            return -errno;
+        }
+    }
+
+    return 0;
+}
+
diff --git a/tools/helpers/hypfs-helpers.h b/tools/helpers/hypfs-helpers.h
new file mode 100644
index 0000000000..2b2de5967f
--- /dev/null
+++ b/tools/helpers/hypfs-helpers.h
@@ -0,0 +1,9 @@
+#ifndef __HYPFS_HELPERS_H
+#define __HYPFS_HELPERS_H
+
+#include "late-init-pv.h"
+
+bool has_builder_hypfs(xenhypfs_handle *hdl, uint32_t domid);
+int read_hypfs_tree(xenhypfs_handle *hdl, struct domain_info *di);
+
+#endif
diff --git a/tools/helpers/late-init-pv.c b/tools/helpers/late-init-pv.c
new file mode 100644
index 0000000000..e1602be6d5
--- /dev/null
+++ b/tools/helpers/late-init-pv.c
@@ -0,0 +1,287 @@
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xenguest.h>
+#include <xenhypfs.h>
+#include <xenstore.h>
+#include <xentoollog.h>
+#include <xen/io/xenbus.h>
+
+#include "hypfs-helpers.h"
+#include "late-init-pv.h"
+#include "xs-helpers.h"
+
+static struct option options[] = {
+    { "uuid", 1, NULL, 'u' },
+    { "console", 0, NULL, 'c' },
+    { "force", 0, NULL, 'f' },
+    { "domain", 1, NULL, 'd' },
+    { "verbose", 0, NULL, 'v' },
+    { NULL, 0, NULL, 0 }
+};
+
+static void usage(void)
+{
+    fprintf(stderr,
+"Usage:\n"
+"\n"
+"late-init-pv <options>\n"
+"\n"
+"where options may include:\n"
+"\n"
+"  --uuid <UUID string>     override the UUID to use for the domain\n"
+"  --console                configure the console\n"
+"  --force                  for @introduceDomain even if xenstore entries exist\n"
+"  --domain <domain id>     domain id of the domain to be initialized\n"
+"  -v[v[v]]                 verbosity constructing xenstore tree\n");
+}
+
+#define XS_DOM_PERM(x, d, k, v)                                             \
+    ret = do_xs_write_dom_with_perm(x, d, k, v, perms, num_perms);          \
+    if ( ret != 0 ) return ret                                              \
+
+#define XS_DIR_PERM(x, p, k, v)                                             \
+    ret = do_xs_write_dir_node_with_perm(x, p, k, v, perms, num_perms);     \
+    if ( ret != 0 ) return ret                                              \
+
+static int create_xs_entries(
+    struct xs_handle *xsh, uint16_t curr_domid, struct domain_info *di)
+{
+    char value[16];
+    struct xs_permissions perms[2] = {
+        {.id = curr_domid, .perms = XS_PERM_NONE},
+        {.id = di->domid, .perms = XS_PERM_READ},
+    };
+    uint32_t num_perms = (sizeof(perms) / sizeof((perms)[0]));
+    int ret = 0;
+
+    while ( do_xs_start_transaction(xsh) == 0 )
+    {
+        XS_DOM_PERM(xsh, di->domid, "", "");
+
+        snprintf(value, 16, "%d", di->domid);
+        XS_DOM_PERM(xsh, di->domid, "domid", value);
+
+        XS_DOM_PERM(xsh, di->domid, "memory", "");
+        snprintf(value, 16, "%d", di->mem_info.target);
+        XS_DOM_PERM(xsh, di->domid, "memory/target", value);
+
+        if ( di->mem_info.max )
+            snprintf(value, 16, "%d", di->mem_info.max);
+        else
+            snprintf(value, 16, "%d", di->mem_info.target);
+        XS_DOM_PERM(xsh, di->domid, "memory/static-max", value);
+
+        XS_DOM_PERM(xsh, di->domid, "store", "");
+        snprintf(value, 16, "%d", di->xs_info.evtchn_port);
+        XS_DOM_PERM(xsh, di->domid, "store/port", value);
+
+        snprintf(value, 16, "%ld", di->xs_info.mfn);
+        XS_DOM_PERM(xsh, di->domid, "store/ring-ref", value);
+
+        if ( di->cons_info.enable )
+        {
+            char be_path[64], fe_path[64];
+
+            snprintf(fe_path, 64, "/local/domain/%d/console", di->domid);
+            snprintf(be_path, 64, "/local/domain/%d/backend/console/%d/0",
+                     di->cons_info.be_domid, di->domid);
+
+            /* Backend entries */
+            XS_DIR_PERM(xsh, be_path, "", "");
+            snprintf(value, 16, "%d", di->domid);
+            XS_DIR_PERM(xsh, be_path, "frontend-id", value);
+            XS_DIR_PERM(xsh, be_path, "frontend", fe_path);
+            XS_DIR_PERM(xsh, be_path, "online", "1");
+            XS_DIR_PERM(xsh, be_path, "protocol", "vt100");
+
+            snprintf(value, 16, "%d", XenbusStateInitialising);
+            XS_DIR_PERM(xsh, be_path, "state", value);
+
+            /* Frontend entries */
+            XS_DOM_PERM(xsh, di->domid, "console", "");
+            snprintf(value, 16, "%d", di->cons_info.be_domid);
+            XS_DIR_PERM(xsh, fe_path, "backend", be_path);
+            XS_DIR_PERM(xsh, fe_path, "backend-id", value);
+            XS_DIR_PERM(xsh, fe_path, "limit", "1048576");
+            XS_DIR_PERM(xsh, fe_path, "type", "xenconsoled");
+            XS_DIR_PERM(xsh, fe_path, "output", "pty");
+            XS_DIR_PERM(xsh, fe_path, "tty", "");
+
+            snprintf(value, 16, "%d", di->cons_info.evtchn_port);
+            XS_DIR_PERM(xsh, fe_path, "port", value);
+
+            snprintf(value, 16, "%ld", di->cons_info.mfn);
+            XS_DIR_PERM(xsh, fe_path, "ring-ref", value);
+
+        }
+
+        ret = do_xs_end_transaction(xsh);
+        switch ( ret )
+        {
+        case 0:
+            break; /* proceed to loop break */
+        case -EAGAIN:
+            continue; /* try again */
+        default:
+            return ret; /* failed */
+        }
+
+        break;
+    }
+
+    return ret;
+}
+
+static bool init_domain(struct xs_handle *xsh, struct domain_info *di)
+{
+    xc_interface *xch = xc_interface_open(0, 0, 0);
+    xen_pfn_t con_mfn = 0L;
+    /*xc_dom_gnttab_seed will do nothing of front == back */
+    uint32_t con_domid = di->domid;
+    int ret;
+
+    /* console */
+    if ( di->cons_info.enable )
+    {
+        con_domid = di->cons_info.be_domid;
+        con_mfn = di->cons_info.mfn;
+    }
+
+    ret = xc_dom_gnttab_seed(xch, di->domid, di->is_hvm, con_mfn,
+            di->xs_info.mfn, con_domid, di->xs_info.be_domid);
+    if ( ret != 0 )
+    {
+        fprintf(stderr, "error (%d) setting up grant tables for dom%d\n",
+                ret, di->domid);
+        xc_interface_close(xch);
+        return false;
+    }
+
+    xc_interface_close(xch);
+
+    return xs_introduce_domain(xsh, di->domid, di->xs_info.mfn,
+                               di->xs_info.evtchn_port);
+}
+
+int main(int argc, char** argv)
+{
+    int opt, rv;
+    bool force = false;
+    struct xs_handle *xsh = NULL;
+    xenhypfs_handle *xhfs = NULL;
+    xentoollog_level minmsglevel = XTL_PROGRESS;
+    xentoollog_logger *logger = NULL;
+    struct domain_info di = { .domid = ~0 };
+
+    while ( (opt = getopt_long(argc, argv, "cfd:v", options, NULL)) != -1 )
+    {
+        switch ( opt )
+        {
+        case 'u':
+            di.override_uuid = true;
+            di.uuid = optarg;
+            break;
+        case 'c':
+            di.cons_info.enable = true;
+            break;
+        case 'f':
+            force = true;
+            break;
+        case 'd':
+            di.domid = strtol(optarg, NULL, 10);
+            break;
+        case 'v':
+            if ( minmsglevel )
+                minmsglevel--;
+            break;
+        default:
+            usage();
+            return 2;
+        }
+    }
+
+    if ( optind != argc || di.domid == ~0 )
+    {
+        usage();
+        return 1;
+    }
+
+    logger = (xentoollog_logger *)xtl_createlogger_stdiostream(stderr,
+                                                               minmsglevel, 0);
+
+    xhfs = xenhypfs_open(logger, 0);
+    if ( !xhfs )
+    {
+        fprintf(stderr, "error: unable to acces xen hypfs\n");
+        rv = 2;
+        goto out;
+    }
+
+    if ( !has_builder_hypfs(xhfs, di.domid) )
+    {
+        fprintf(stderr, "error: hypfs entry for domain %d not present\n",
+                di.domid);
+        rv = 3;
+        goto out;
+    }
+
+    if ( read_hypfs_tree(xhfs, &di) != 0 )
+    {
+        fprintf(stderr, "error: unable to parse hypfs for domain %d\n",
+                di.domid);
+        rv = 4;
+        goto out;
+    }
+
+    xsh = xs_open(0);
+    if ( xsh == NULL )
+    {
+        fprintf(stderr, "error: unable to connect to xenstored\n");
+        rv = 5;
+        goto out;
+    }
+
+    if ( xs_is_domain_introduced(xsh, di.domid) )
+    {
+        if ( !force )
+        {
+            fprintf(stderr, "error: domain %d already introduced\n", di.domid);
+            rv = 6;
+            goto out;
+        }
+        else
+        {
+            fprintf(stderr, "warning: re-introducting domain %d\n", di.domid);
+        }
+    }
+
+    /* TODO: hardcdoding local domain to 0 for testing purposes */
+    if ( (rv = create_xs_entries(xsh, 0, &di)) != 0 )
+    {
+        fprintf(stderr, "error(%d): unable create xenstore entries\n", rv);
+        rv = 7;
+        goto out;
+    }
+
+    init_domain(xsh, &di);
+    rv = 0;
+
+out:
+    if ( xsh )
+        xs_close(xsh);
+
+    if ( xhfs )
+        xenhypfs_close(xhfs);
+
+    if ( logger )
+        xtl_logger_destroy(logger);
+
+    return rv;
+}
diff --git a/tools/helpers/late-init-pv.h b/tools/helpers/late-init-pv.h
new file mode 100644
index 0000000000..5d66e7870f
--- /dev/null
+++ b/tools/helpers/late-init-pv.h
@@ -0,0 +1,29 @@
+#ifndef __LATE_INIT_PV_H
+#define __LATE_INIT_PV_H
+
+struct domain_info {
+    uint16_t domid;
+    bool is_hvm;
+    bool override_uuid;
+    const char *uuid;
+    uint32_t num_cpu;
+    uint32_t max_cpu;
+    struct {
+        uint32_t target;
+        uint32_t max;
+        uint32_t video;
+    } mem_info;
+    struct {
+        uint16_t be_domid;
+        uint32_t evtchn_port;
+        uint64_t mfn;
+    } xs_info;
+    struct {
+        bool enable;
+        uint16_t be_domid;
+        uint32_t evtchn_port;
+        uint64_t mfn;
+    } cons_info;
+};
+
+#endif
diff --git a/tools/helpers/xs-helpers.c b/tools/helpers/xs-helpers.c
new file mode 100644
index 0000000000..a4d2bebbbd
--- /dev/null
+++ b/tools/helpers/xs-helpers.c
@@ -0,0 +1,117 @@
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <xenstore.h>
+
+#define MAX_XS_PAATH 100
+
+static xs_transaction_t t_id = XBT_NULL;
+
+int do_xs_start_transaction(struct xs_handle *xsh)
+{
+    t_id = xs_transaction_start(xsh);
+    if (t_id == XBT_NULL)
+        return -errno;
+
+    return 0;
+}
+
+int do_xs_end_transaction(struct xs_handle *xsh)
+{
+    if ( t_id == XBT_NULL )
+        return -EINVAL;
+
+    if (!xs_transaction_end(xsh, t_id, false))
+        return -errno;
+
+    return 0;
+}
+
+int do_xs_write(struct xs_handle *xsh, char *path, char *val)
+{
+    if ( !xs_write(xsh, t_id, path, val, strlen(val)) )
+    {
+        fprintf(stderr, "failed write: %s\n", path);
+        return -errno;
+    }
+
+    return 0;
+}
+
+int do_xs_perms(
+    struct xs_handle *xsh, char *path, struct xs_permissions *perms,
+    uint32_t num_perms)
+{
+    if ( !xs_set_permissions(xsh, t_id, path, perms, num_perms) )
+    {
+        fprintf(stderr, "failed set perm: %s\n", path);
+        return -errno;
+    }
+
+    return 0;
+}
+
+int do_xs_write_dir_node_with_perm(
+    struct xs_handle *xsh, char *dir, char *node, char *val,
+    struct xs_permissions *perms, uint32_t num_perms)
+{
+    char full_path[MAX_XS_PAATH];
+    int ret = 0;
+
+    /*
+     * mainly for creating a value holding node, but
+     * also support creating directory nodes.
+     */
+    if ( strlen(node) != 0 )
+        snprintf(full_path, MAX_XS_PAATH, "%s/%s", dir, node);
+    else
+        snprintf(full_path, MAX_XS_PAATH, "%s", dir);
+
+    ret = do_xs_write(xsh, full_path, val);
+    if ( ret < 0 )
+        return ret;
+
+    if ( perms != NULL && num_perms > 0 )
+        ret = do_xs_perms(xsh, full_path, perms, num_perms);
+
+    return ret;
+}
+
+int do_xs_write_dir_node(
+    struct xs_handle *xsh, char *dir, char *node, char *val)
+{
+    return do_xs_write_dir_node_with_perm(xsh, dir, node, val, NULL, 0);
+}
+
+int do_xs_write_dom_with_perm(
+    struct xs_handle *xsh, uint32_t domid, char *path, char *val,
+    struct xs_permissions *perms, uint32_t num_perms)
+{
+    char full_path[MAX_XS_PAATH];
+    int ret = 0;
+
+    /*
+     * mainly for creating a value holding node, but
+     * also support creating directory nodes.
+     */
+    if ( strlen(path) != 0 )
+        snprintf(full_path, MAX_XS_PAATH, "/local/domain/%d/%s", domid, path);
+    else
+        snprintf(full_path, MAX_XS_PAATH, "/local/domain/%d", domid);
+
+    ret = do_xs_write(xsh, full_path, val);
+    if ( ret < 0 )
+        return ret;
+
+    if ( perms != NULL && num_perms > 0 )
+        ret = do_xs_perms(xsh, full_path, perms, num_perms);
+
+    return ret;
+}
+
+int do_xs_write_dom(
+    struct xs_handle *xsh, uint32_t domid, char *path, char *val)
+{
+    return do_xs_write_dom_with_perm(xsh, domid, path, val, NULL, 0);
+}
diff --git a/tools/helpers/xs-helpers.h b/tools/helpers/xs-helpers.h
new file mode 100644
index 0000000000..f57fcab843
--- /dev/null
+++ b/tools/helpers/xs-helpers.h
@@ -0,0 +1,27 @@
+#ifndef __XS_HELPERS_H
+#define __XS_HELPERS_H
+
+#include <xenstore.h>
+
+int do_xs_start_transaction(struct xs_handle *xsh);
+int do_xs_end_transaction(struct xs_handle *xsh);
+
+int do_xs_write(struct xs_handle *xsh, char *path, char *val);
+int do_xs_perms(
+    struct xs_handle *xsh, char *path, struct xs_permissions *perms,
+    uint32_t num_perms);
+
+int do_xs_write_dir_node_with_perm(
+    struct xs_handle *xsh, char *dir, char *node, char *val,
+    struct xs_permissions *perms, uint32_t num_perms);
+int do_xs_write_dir_node(
+    struct xs_handle *xsh, char *dir, char *node, char *val);
+
+int do_xs_write_dom_with_perm(
+    struct xs_handle *xsh, uint32_t domid, char *path, char *val,
+    struct xs_permissions *perms, uint32_t num_perms);
+int do_xs_write_dom(
+    struct xs_handle *xsh, uint32_t domid, char *path, char *val);
+
+#endif
+
