diff --git a/docs/man/xl.cfg.pod.5.in b/docs/man/xl.cfg.pod.5.in
index 79cb2eaea7..092b051561 100644
--- a/docs/man/xl.cfg.pod.5.in
+++ b/docs/man/xl.cfg.pod.5.in
@@ -1116,6 +1116,39 @@ FIFO-based event channel ABI support up to 131,071 event channels.
 Other guests are limited to 4095 (64-bit x86 and ARM) or 1023 (32-bit
 x86).
 
+=item B<vnvdimms=[ 'VNVDIMM_SPEC', 'VNVDIMM_SPEC', ... ]>
+
+Specifies the virtual NVDIMM devices which are provided to the guest.
+
+Each B<VNVDIMM_SPEC> is a comma-separated list of C<KEY=VALUE> settings
+from the following list:
+
+=over 4
+
+=item B<type=TYPE>
+
+Specifies the type of host backend of the virtual NVDIMM device. Following
+is a list of supported types:
+
+=over 4
+
+=item B<mfn>
+
+backs the virtual NVDIMM device by a contiguous host PMEM region.
+
+=back
+
+=item B<backend=BACKEND>
+
+Specifies the host backend of the virtual NVDIMM device. If C<type=mfn>,
+then B<BACKEND> specifies the start MFN of the host PMEM region.
+
+=item B<nr_pages=NUMBER>
+
+Specifies the number of pages of the host backend.
+
+=back
+
 =back
 
 =head2 Paravirtualised (PV) Guest Specific Options
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 791c9ad05e..b4c2ccb7ff 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -139,7 +139,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
 			libxl_dom_suspend.o libxl_dom_save.o libxl_usb.o \
 			libxl_vtpm.o libxl_nic.o libxl_disk.o libxl_console.o \
 			libxl_cpupool.o libxl_mem.o libxl_sched.o libxl_tmem.o \
-			libxl_9pfs.o libxl_domain.o \
+			libxl_9pfs.o libxl_domain.o libxl_vnvdimm.o \
                         $(LIBXL_OBJS-y)
 LIBXL_OBJS += libxl_genid.o
 LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 91408b47b5..8156c08ed3 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1474,6 +1474,11 @@ int libxl_get_memory_target_0x040700(libxl_ctx *ctx, uint32_t domid,
                                      uint32_t *out_target)
     LIBXL_EXTERNAL_CALLERS_ONLY;
 
+int libxl_vnvdimm_copy_config(libxl_ctx *ctx,
+                              libxl_domain_config *dst,
+                              const libxl_domain_config *src)
+                              LIBXL_EXTERNAL_CALLERS_ONLY;
+
 /*
  * WARNING
  * This memory management API is unstable even in Xen 4.2.
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 4acc0457f4..ad236de34a 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -240,6 +240,10 @@ libxl_checkpointed_stream = Enumeration("checkpointed_stream", [
     (2, "COLO"),
     ])
 
+libxl_vnvdimm_backend_type = Enumeration("vnvdimm_backend_type", [
+    (0, "mfn"),
+    ])
+
 #
 # Complex libxl types
 #
@@ -780,6 +784,16 @@ libxl_device_channel = Struct("device_channel", [
            ])),
 ])
 
+libxl_device_vnvdimm = Struct("device_vnvdimm", [
+    ("backend_domid",   libxl_domid),
+    ("backend_domname", string),
+    ("devid",           libxl_devid),
+    ("nr_pages",        uint64),
+    ("u", KeyedUnion(None, libxl_vnvdimm_backend_type, "backend_type",
+            [("mfn", uint64),
+            ])),
+])
+
 libxl_domain_config = Struct("domain_config", [
     ("c_info", libxl_domain_create_info),
     ("b_info", libxl_domain_build_info),
@@ -798,6 +812,7 @@ libxl_domain_config = Struct("domain_config", [
     ("channels", Array(libxl_device_channel, "num_channels")),
     ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
     ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
+    ("vnvdimms", Array(libxl_device_vnvdimm, "num_vnvdimms")),
 
     ("on_poweroff", libxl_action_on_shutdown),
     ("on_reboot", libxl_action_on_shutdown),
diff --git a/tools/libxl/libxl_vnvdimm.c b/tools/libxl/libxl_vnvdimm.c
new file mode 100644
index 0000000000..4de8f04303
--- /dev/null
+++ b/tools/libxl/libxl_vnvdimm.c
@@ -0,0 +1,49 @@
+/*
+ * tools/libxl/libxl_vnvdimm.c
+ *
+ * Copyright (C) 2017,  Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License, version 2.1, as published by the Free Software Foundation.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xenctrl.h>
+
+#include "libxl_internal.h"
+
+int libxl_vnvdimm_copy_config(libxl_ctx *ctx,
+                              libxl_domain_config *dst,
+                              const libxl_domain_config *src)
+{
+    GC_INIT(ctx);
+    unsigned int nr = src->num_vnvdimms;
+    libxl_device_vnvdimm *vnvdimms;
+    int rc = 0;
+
+    if (!nr)
+        goto out;
+
+    vnvdimms = libxl__calloc(NOGC, nr, sizeof(*vnvdimms));
+    if (!vnvdimms) {
+        rc = ERROR_NOMEM;
+        goto out;
+    }
+
+    dst->num_vnvdimms = nr;
+    while (nr--)
+        libxl_device_vnvdimm_copy(ctx, &vnvdimms[nr], &src->vnvdimms[nr]);
+    dst->vnvdimms = vnvdimms;
+
+ out:
+    GC_FREE;
+    return rc;
+}
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index ed562a1956..388a135dbf 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -804,13 +804,111 @@ int parse_usbdev_config(libxl_device_usbdev *usbdev, char *token)
     return 0;
 }
 
+static int parse_vnvdimm_config(libxl_device_vnvdimm *vnvdimm, char *token)
+{
+    char *oparg, *endptr;
+    unsigned long val;
+
+    if (MATCH_OPTION("type", token, oparg)) {
+        if (libxl_vnvdimm_backend_type_from_string(oparg,
+                                                   &vnvdimm->backend_type)) {
+            fprintf(stderr,
+                    "ERROR: invalid vNVDIMM backend type '%s'\n",
+                    oparg);
+            return 1;
+        }
+    } else if (MATCH_OPTION("nr_pages", token, oparg)) {
+        val = strtoul(oparg, &endptr, 0);
+        if (endptr == oparg || val == ULONG_MAX)
+        {
+            fprintf(stderr,
+                    "ERROR: invalid number of vNVDIMM backend pages '%s'\n",
+                    oparg);
+            return 1;
+        }
+        vnvdimm->nr_pages = val;
+    } else if (MATCH_OPTION("backend", token, oparg)) {
+        /* Skip: handled by parse_vnvdimms() */
+    } else {
+        fprintf(stderr, "ERROR: unknown string '%s' in vnvdimm spec\n", token);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * vnvdimms = [ 'type=<mfn>, backend=<base_mfn>, nr_pages=<N>', ... ]
+ */
+static void parse_vnvdimms(XLU_Config *config, libxl_domain_config *d_config)
+{
+    XLU_ConfigList *vnvdimms;
+    const char *buf;
+    int rc;
+
+    rc = xlu_cfg_get_list(config, "vnvdimms", &vnvdimms, 0, 0);
+    if ( rc )
+        return;
+
+#if !defined(__linux__)
+    fprintf(stderr, "ERROR: 'vnvdimms' is only supported on Linux\n");
+    exit(-ERROR_FAIL);
+#endif
+
+    d_config->num_vnvdimms = 0;
+    d_config->vnvdimms = NULL;
+
+    while ((buf = xlu_cfg_get_listitem(vnvdimms,
+                                       d_config->num_vnvdimms)) != NULL) {
+        libxl_device_vnvdimm *vnvdimm =
+            ARRAY_EXTEND_INIT(d_config->vnvdimms, d_config->num_vnvdimms,
+                              libxl_device_vnvdimm_init);
+        char *buf2 = strdup(buf), *backend = NULL, *p, *endptr;
+        unsigned long mfn;
+
+        p = strtok(buf2, ",");
+        if (!p)
+            goto skip_nvdimm;
+
+        do {
+            while (*p == ' ')
+                p++;
+
+            rc = 0;
+            if (!MATCH_OPTION("backend", p, backend))
+                rc = parse_vnvdimm_config(vnvdimm, p);
+            if (rc)
+                exit(-ERROR_FAIL);
+        } while ((p = strtok(NULL, ",")) != NULL);
+
+        switch (vnvdimm->backend_type)
+        {
+        case LIBXL_VNVDIMM_BACKEND_TYPE_MFN:
+            mfn = strtoul(backend, &endptr, 0);
+            if (endptr == backend || mfn == ULONG_MAX)
+            {
+                fprintf(stderr,
+                        "ERROR: invalid start MFN of host NVDIMM '%s'\n",
+                        backend);
+                exit(-ERROR_FAIL);
+            }
+            vnvdimm->u.mfn = mfn;
+
+            break;
+        }
+
+    skip_nvdimm:
+        free(buf2);
+    }
+}
+
 void parse_config_data(const char *config_source,
                        const char *config_data,
                        int config_len,
                        libxl_domain_config *d_config)
 {
     const char *buf;
-    long l, vcpus = 0, nr_dm_acpi_pages;
+    long l, vcpus = 0, nr_dm_acpi_pages = 0;
     XLU_Config *config;
     XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms,
                    *usbctrls, *usbdevs, *p9devs;
@@ -1942,6 +2040,16 @@ skip_usbdev:
             exit(-ERROR_FAIL);
         }
         b_info->u.hvm.dm_acpi_pages = nr_dm_acpi_pages;
+
+        /* parse 'vnvdimms' */
+        parse_vnvdimms(config, d_config);
+
+        /*
+         * If 'dm_acpi_pages' is not specified, reserve one DM ACPI
+         * page for vNVDIMM devices.
+         */
+        if (d_config->vnvdimms && !nr_dm_acpi_pages)
+            b_info->u.hvm.dm_acpi_pages = 1;
     }
 
     /* If we've already got vfb=[] for PV guest then ignore top level
diff --git a/tools/xl/xl_vmcontrol.c b/tools/xl/xl_vmcontrol.c
index 89c2b25ded..1bdc173e04 100644
--- a/tools/xl/xl_vmcontrol.c
+++ b/tools/xl/xl_vmcontrol.c
@@ -381,12 +381,25 @@ static void reload_domain_config(uint32_t domid,
     if (rc) {
         LOG("failed to retrieve guest configuration (rc=%d). "
             "reusing old configuration", rc);
-        libxl_domain_config_dispose(&d_config_new);
+        goto error_out;
     } else {
+        rc = libxl_vnvdimm_copy_config(ctx, &d_config_new, d_config);
+        if (rc) {
+            LOG("failed to copy vnvdimm configuration (rc=%d). "
+                "reusing old configuration", rc);
+            libxl_domain_config_dispose(&d_config_new);
+            goto error_out;
+        }
+
         libxl_domain_config_dispose(d_config);
         /* Steal allocations */
         memcpy(d_config, &d_config_new, sizeof(libxl_domain_config));
     }
+
+    return;
+
+ error_out:
+    libxl_domain_config_dispose(&d_config_new);
 }
 
 /* Can update r_domid if domain is destroyed */
