@@ -37,6 +37,7 @@ obj-y += radix-tree.o
obj-y += rbtree.o
obj-y += rcupdate.o
obj-y += rwlock.o
+obj-y += save.o
obj-y += shutdown.o
obj-y += softirq.o
obj-y += sort.o
new file mode 100644
@@ -0,0 +1,339 @@
+/*
+ * save.c: save and load state common to all domain types.
+ *
+ * Copyright Amazon.com Inc. or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/compile.h>
+#include <xen/save.h>
+
+struct domain_ctxt_state {
+ struct domain *d;
+ struct domain_context_record rec;
+ size_t len; /* for internal accounting */
+ union {
+ const struct domain_save_ctxt_ops *save;
+ const struct domain_load_ctxt_ops *load;
+ } ops;
+ void *priv;
+ bool dry_run;
+};
+
+static struct {
+ const char *name;
+ domain_save_ctxt_type save;
+ domain_load_ctxt_type load;
+} fns[DOMAIN_CONTEXT_NR_TYPES];
+
+void __init domain_register_ctxt_type(unsigned int type, const char *name,
+ domain_save_ctxt_type save,
+ domain_load_ctxt_type load)
+{
+ BUG_ON(type >= ARRAY_SIZE(fns));
+
+ ASSERT(!fns[type].save);
+ ASSERT(!fns[type].load);
+
+ fns[type].name = name;
+ fns[type].save = save;
+ fns[type].load = load;
+}
+
+int domain_save_ctxt_rec_begin(struct domain_ctxt_state *c,
+ unsigned int type, unsigned int instance)
+{
+ struct domain_context_record rec = { .type = type, .instance = instance };
+ int rc;
+
+ c->rec = rec;
+ c->len = 0;
+
+ rc = c->ops.save->begin(c->priv, &c->rec);
+ if ( rc )
+ return rc;
+
+ return 0;
+}
+
+int domain_save_ctxt_rec_data(struct domain_ctxt_state *c, const void *src,
+ size_t len)
+{
+ int rc = c->ops.save->append(c->priv, src, len);
+
+ if ( !rc )
+ c->len += len;
+
+ return rc;
+}
+
+int domain_save_ctxt_rec_end(struct domain_ctxt_state *c)
+{
+ size_t len = c->len;
+ size_t pad = ROUNDUP(len, DOMAIN_CONTEXT_RECORD_ALIGN) - len;
+ int rc;
+
+ if ( pad )
+ {
+ static const uint8_t zeroes[DOMAIN_CONTEXT_RECORD_ALIGN] = {};
+
+ rc = c->ops.save->append(c->priv, zeroes, pad);
+ if ( rc )
+ return rc;
+ }
+
+ if ( !c->dry_run )
+ gdprintk(XENLOG_DEBUG, "%pd save: %s[%u] +%zu (+%zu)\n", c->d,
+ fns[c->rec.type].name, c->rec.instance,
+ len, pad);
+
+ rc = c->ops.save->end(c->priv, c->len);
+
+ return rc;
+}
+
+int domain_save_ctxt(struct domain *d, const struct domain_save_ctxt_ops *ops,
+ void *priv, bool dry_run)
+{
+ struct domain_ctxt_state c = {
+ .d = d,
+ .ops.save = ops,
+ .priv = priv,
+ .dry_run = dry_run,
+ };
+ domain_save_ctxt_type save;
+ unsigned int type;
+ int rc;
+
+ ASSERT(d != current->domain);
+
+ save = fns[DOMAIN_CONTEXT_START].save;
+ BUG_ON(!save);
+
+ rc = save(d, &c, dry_run);
+ if ( rc )
+ return rc;
+
+ domain_pause(d);
+
+ for ( type = DOMAIN_CONTEXT_START + 1; type < ARRAY_SIZE(fns); type++ )
+ {
+ save = fns[type].save;
+ if ( !save )
+ continue;
+
+ rc = save(d, &c, dry_run);
+ if ( rc )
+ break;
+ }
+
+ domain_unpause(d);
+
+ if ( rc )
+ return rc;
+
+ save = fns[DOMAIN_CONTEXT_END].save;
+ BUG_ON(!save);
+
+ return save(d, &c, dry_run);
+}
+
+int domain_load_ctxt_rec_begin(struct domain_ctxt_state *c,
+ unsigned int type, unsigned int *instance)
+{
+ if ( type != c->rec.type )
+ {
+ ASSERT_UNREACHABLE();
+ return -EINVAL;
+ }
+
+ *instance = c->rec.instance;
+ c->len = 0;
+
+ return 0;
+}
+
+int domain_load_ctxt_rec_data(struct domain_ctxt_state *c, void *dst,
+ size_t len)
+{
+ int rc = 0;
+
+ c->len += len;
+ if (c->len > c->rec.length)
+ return -ENODATA;
+
+ if ( dst )
+ rc = c->ops.load->read(c->priv, dst, len);
+ else /* sink data */
+ {
+ uint8_t ignore;
+
+ while ( !rc && len-- )
+ rc = c->ops.load->read(c->priv, &ignore, sizeof(ignore));
+ }
+
+ return rc;
+}
+
+int domain_load_ctxt_rec_end(struct domain_ctxt_state *c, bool ignore_data)
+{
+ size_t len = c->len;
+ size_t pad = ROUNDUP(len, DOMAIN_CONTEXT_RECORD_ALIGN) - len;
+
+ gdprintk(XENLOG_DEBUG, "%pd load: %s[%u] +%zu (+%zu)\n", c->d,
+ fns[c->rec.type].name, c->rec.instance,
+ len, pad);
+
+ while ( pad-- )
+ {
+ uint8_t zero;
+ int rc = c->ops.load->read(c->priv, &zero, sizeof(zero));
+
+ if ( rc )
+ return rc;
+
+ if ( zero )
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int domain_load_ctxt(struct domain *d, const struct domain_load_ctxt_ops *ops,
+ void *priv)
+{
+ struct domain_ctxt_state c = { .d = d, .ops.load = ops, .priv = priv, };
+ domain_load_ctxt_type load;
+ int rc;
+
+ ASSERT(d != current->domain);
+
+ rc = c.ops.load->read(c.priv, &c.rec, sizeof(c.rec));
+ if ( rc )
+ return rc;
+
+ load = fns[DOMAIN_CONTEXT_START].load;
+ BUG_ON(!load);
+
+ rc = load(d, &c);
+ if ( rc )
+ return rc;
+
+ domain_pause(d);
+
+ for (;;)
+ {
+ unsigned int type;
+
+ rc = c.ops.load->read(c.priv, &c.rec, sizeof(c.rec));
+ if ( rc )
+ break;
+
+ type = c.rec.type;
+ if ( type == DOMAIN_CONTEXT_END )
+ break;
+
+ rc = -EINVAL;
+ if ( type >= ARRAY_SIZE(fns) )
+ break;
+
+ load = fns[type].load;
+ if ( !load )
+ break;
+
+ rc = load(d, &c);
+ if ( rc )
+ break;
+ }
+
+ domain_unpause(d);
+
+ if ( rc )
+ return rc;
+
+ load = fns[DOMAIN_CONTEXT_END].load;
+ BUG_ON(!load);
+
+ return load(d, &c);
+}
+
+static int save_start(struct domain *d, struct domain_ctxt_state *c,
+ bool dry_run)
+{
+ static const struct domain_context_start s = {
+ .xen_major = XEN_VERSION,
+ .xen_minor = XEN_SUBVERSION,
+ };
+
+ return domain_save_ctxt_rec(c, DOMAIN_CONTEXT_START, 0, &s, sizeof(s));
+}
+
+static int load_start(struct domain *d, struct domain_ctxt_state *c)
+{
+ static struct domain_context_start s;
+ unsigned int i;
+ int rc = domain_load_ctxt_rec(c, DOMAIN_CONTEXT_START, &i, &s, sizeof(s));
+
+ if ( rc )
+ return rc;
+
+ if ( i )
+ return -EINVAL;
+
+ /*
+ * Make sure we are not attempting to load an image generated by a newer
+ * version of Xen.
+ */
+ if ( s.xen_major > XEN_VERSION && s.xen_minor > XEN_SUBVERSION )
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+DOMAIN_REGISTER_CTXT_TYPE(START, save_start, load_start);
+
+static int save_end(struct domain *d, struct domain_ctxt_state *c,
+ bool dry_run)
+{
+ static const struct domain_context_end e = {};
+
+ return domain_save_ctxt_rec(c, DOMAIN_CONTEXT_END, 0, &e, sizeof(e));
+}
+
+static int load_end(struct domain *d, struct domain_ctxt_state *c)
+{
+ unsigned int i;
+ int rc = domain_load_ctxt_rec(c, DOMAIN_CONTEXT_END, &i, NULL,
+ sizeof(struct domain_context_end));
+
+ if ( rc )
+ return rc;
+
+ if ( i )
+ return -EINVAL;
+
+ return 0;
+}
+
+DOMAIN_REGISTER_CTXT_TYPE(END, save_end, load_end);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
new file mode 100644
@@ -0,0 +1,160 @@
+/*
+ * save.h: support routines for save/restore
+ *
+ * Copyright Amazon.com Inc. or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XEN_SAVE_H
+#define XEN_SAVE_H
+
+#include <xen/init.h>
+#include <xen/sched.h>
+#include <xen/types.h>
+
+#include <public/save.h>
+
+struct domain_ctxt_state;
+
+int domain_save_ctxt_rec_begin(struct domain_ctxt_state *c, unsigned int type,
+ unsigned int instance);
+int domain_save_ctxt_rec_data(struct domain_ctxt_state *c, const void *data,
+ size_t len);
+int domain_save_ctxt_rec_end(struct domain_ctxt_state *c);
+
+static inline int domain_save_ctxt_rec(struct domain_ctxt_state *c,
+ unsigned int type, unsigned int instance,
+ const void *src, size_t len)
+{
+ int rc;
+
+ rc = domain_save_ctxt_rec_begin(c, type, instance);
+ if ( rc )
+ return rc;
+
+ rc = domain_save_ctxt_rec_data(c, src, len);
+ if ( rc )
+ return rc;
+
+ return domain_save_ctxt_rec_end(c);
+}
+
+int domain_load_ctxt_rec_begin(struct domain_ctxt_state *c, unsigned int type,
+ unsigned int *instance);
+int domain_load_ctxt_rec_data(struct domain_ctxt_state *c, void *data,
+ size_t len);
+int domain_load_ctxt_rec_end(struct domain_ctxt_state *c, bool ignore_data);
+
+static inline int domain_load_ctxt_rec(struct domain_ctxt_state *c,
+ unsigned int type,
+ unsigned int *instance, void *dst,
+ size_t len)
+{
+ int rc;
+
+ rc = domain_load_ctxt_rec_begin(c, type, instance);
+ if ( rc )
+ return rc;
+
+ rc = domain_load_ctxt_rec_data(c, dst, len);
+ if ( rc )
+ return rc;
+
+ return domain_load_ctxt_rec_end(c, false);
+}
+
+/*
+ * The 'dry_run' flag indicates that the caller of domain_save_ctxt() (see below)
+ * is not trying to actually acquire the data, only the size of the data.
+ * The save handler can therefore limit work to only that which is necessary
+ * to call domain_save_ctxt_rec_data() the correct number of times with accurate
+ * values for 'len'.
+ *
+ * NOTE: the domain pointer argument to domain_save_ctxt_type is not const as
+ * some handlers may need to acquire locks.
+ */
+typedef int (*domain_save_ctxt_type)(struct domain *d,
+ struct domain_ctxt_state *c,
+ bool dry_run);
+typedef int (*domain_load_ctxt_type)(struct domain *d,
+ struct domain_ctxt_state *c);
+
+void domain_register_ctxt_type(unsigned int type, const char *name,
+ domain_save_ctxt_type save,
+ domain_load_ctxt_type load);
+
+/*
+ * Register save and load handlers for a record type.
+ *
+ * Save handlers will be invoked in an order which copes with any inter-
+ * entry dependencies. For now this means that HEADER will come first and
+ * END will come last, all others being invoked in order of 'typecode'.
+ *
+ * Load handlers will be invoked in the order of entries present in the
+ * buffer.
+ */
+#define DOMAIN_REGISTER_CTXT_TYPE(x, s, l) \
+ static int __init __domain_register_##x##_ctxt_type(void) \
+ { \
+ domain_register_ctxt_type( \
+ DOMAIN_CONTEXT_ ## x, \
+ #x, \
+ &(s), \
+ &(l)); \
+ \
+ return 0; \
+ } \
+ __initcall(__domain_register_##x##_ctxt_type);
+
+/* Callback functions */
+struct domain_save_ctxt_ops {
+ /*
+ * Begin a new entry with the given record (only type and instance are
+ * valid).
+ */
+ int (*begin)(void *priv, const struct domain_context_record *rec);
+ /* Append data/padding to the buffer */
+ int (*append)(void *priv, const void *data, size_t len);
+ /*
+ * Complete the entry by updating the record with the total length of the
+ * appended data (not including padding).
+ */
+ int (*end)(void *priv, size_t len);
+};
+
+struct domain_load_ctxt_ops {
+ /* Read data/padding from the buffer */
+ int (*read)(void *priv, void *data, size_t len);
+};
+
+/*
+ * Entry points:
+ *
+ * ops: These are callback functions provided by the caller that will
+ * be used to write to (in the save case) or read from (in the
+ * load case) the context buffer. See above for more detail.
+ * priv: This is a pointer that will be passed to the copy function to
+ * allow it to identify the context buffer and the current state
+ * of the save or load operation.
+ * dry_run: If this is set then the caller of domain_save() is only trying
+ * to acquire the total size of the data, not the data itself.
+ * In this case the caller may supply different ops to avoid doing
+ * unnecessary work.
+ */
+int domain_save_ctxt(struct domain *d, const struct domain_save_ctxt_ops *ops,
+ void *priv, bool dry_run);
+int domain_load_ctxt(struct domain *d, const struct domain_load_ctxt_ops *ops,
+ void *priv);
+
+#endif /* XEN_SAVE_H */