@@ -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
@@ -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)
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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
+