diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c
index 8e6c96dcf6..f52a2c6dc5 100644
--- a/xen/arch/x86/acpi/boot.c
+++ b/xen/arch/x86/acpi/boot.c
@@ -732,5 +732,9 @@ int __init acpi_boot_init(void)
 
 	acpi_table_parse(ACPI_SIG_BGRT, acpi_invalidate_bgrt);
 
+#ifdef CONFIG_NVDIMM_PMEM
+	acpi_nfit_init();
+#endif
+
 	return 0;
 }
diff --git a/xen/drivers/acpi/nfit.c b/xen/drivers/acpi/nfit.c
index e099378ee0..b88a587b8d 100644
--- a/xen/drivers/acpi/nfit.c
+++ b/xen/drivers/acpi/nfit.c
@@ -31,11 +31,143 @@ static const uint8_t nfit_spa_pmem_guid[] =
     0xac, 0x43, 0x0d, 0x33, 0x18, 0xb7, 0x8c, 0xdb,
 };
 
+struct nfit_spa_desc {
+    struct list_head link;
+    struct acpi_nfit_system_address *acpi_table;
+};
+
+struct nfit_memdev_desc {
+    struct list_head link;
+    struct acpi_nfit_memory_map *acpi_table;
+    struct nfit_spa_desc *spa_desc;
+};
+
 struct acpi_nfit_desc {
     struct acpi_table_nfit *acpi_table;
+    struct list_head spa_list;
+    struct list_head memdev_list;
 };
 
-static struct acpi_nfit_desc nfit_desc;
+static struct acpi_nfit_desc nfit_desc = {
+    .spa_list = LIST_HEAD_INIT(nfit_desc.spa_list),
+    .memdev_list = LIST_HEAD_INIT(nfit_desc.memdev_list),
+};
+
+static void __init acpi_nfit_del_subtables(struct acpi_nfit_desc *desc)
+{
+    struct nfit_spa_desc *spa, *spa_next;
+    struct nfit_memdev_desc *memdev, *memdev_next;
+
+    list_for_each_entry_safe(spa, spa_next, &desc->spa_list, link)
+    {
+        list_del(&spa->link);
+        xfree(spa);
+    }
+    list_for_each_entry_safe (memdev, memdev_next, &desc->memdev_list, link)
+    {
+        list_del(&memdev->link);
+        xfree(memdev);
+    }
+}
+
+static int __init acpi_nfit_add_subtables(struct acpi_nfit_desc *desc)
+{
+    struct acpi_table_nfit *nfit_table = desc->acpi_table;
+    uint32_t hdr_offset = sizeof(*nfit_table);
+    uint32_t nfit_length = nfit_table->header.length;
+    struct acpi_nfit_header *hdr;
+    struct nfit_spa_desc *spa_desc;
+    struct nfit_memdev_desc *memdev_desc;
+    int ret = 0;
+
+#define INIT_DESC(desc, acpi_hdr, acpi_type, desc_list) \
+    do {                                                \
+        (desc) = xzalloc(typeof(*(desc)));              \
+        if ( unlikely(!(desc)) ) {                      \
+            ret = -ENOMEM;                              \
+            goto nomem;                                 \
+        }                                               \
+        (desc)->acpi_table = (acpi_type *)(acpi_hdr);   \
+        INIT_LIST_HEAD(&(desc)->link);                  \
+        list_add_tail(&(desc)->link, (desc_list));      \
+    } while ( 0 )
+
+    while ( hdr_offset < nfit_length )
+    {
+        hdr = (void *)nfit_table + hdr_offset;
+        hdr_offset += hdr->length;
+
+        switch ( hdr->type )
+        {
+        case ACPI_NFIT_TYPE_SYSTEM_ADDRESS:
+            INIT_DESC(spa_desc, hdr, struct acpi_nfit_system_address,
+                      &desc->spa_list);
+            break;
+
+        case ACPI_NFIT_TYPE_MEMORY_MAP:
+            INIT_DESC(memdev_desc, hdr, struct acpi_nfit_memory_map,
+                      &desc->memdev_list);
+            break;
+
+        default:
+            continue;
+        }
+    }
+
+#undef INIT_DESC
+
+    return 0;
+
+ nomem:
+    acpi_nfit_del_subtables(desc);
+
+    return ret;
+}
+
+static void __init acpi_nfit_link_subtables(struct acpi_nfit_desc *desc)
+{
+    struct nfit_spa_desc *spa_desc;
+    struct nfit_memdev_desc *memdev_desc;
+    uint16_t spa_idx;
+
+    list_for_each_entry(memdev_desc, &desc->memdev_list, link)
+    {
+        spa_idx = memdev_desc->acpi_table->range_index;
+        list_for_each_entry(spa_desc, &desc->spa_list, link)
+        {
+            if ( spa_desc->acpi_table->range_index == spa_idx )
+                break;
+        }
+        memdev_desc->spa_desc = spa_desc;
+    }
+}
+
+static void __init acpi_nfit_register_pmem(struct acpi_nfit_desc *desc)
+{
+    struct nfit_spa_desc *spa_desc;
+    struct nfit_memdev_desc *memdev_desc;
+    struct acpi_nfit_system_address *spa;
+    unsigned long smfn, emfn;
+
+    list_for_each_entry(memdev_desc, &desc->memdev_list, link)
+    {
+        spa_desc = memdev_desc->spa_desc;
+
+        if ( !spa_desc ||
+             (memdev_desc->acpi_table->flags &
+              (ACPI_NFIT_MEM_SAVE_FAILED | ACPI_NFIT_MEM_RESTORE_FAILED |
+               ACPI_NFIT_MEM_FLUSH_FAILED | ACPI_NFIT_MEM_NOT_ARMED |
+               ACPI_NFIT_MEM_MAP_FAILED)) )
+            continue;
+
+        spa = spa_desc->acpi_table;
+        if ( memcmp(spa->range_guid, nfit_spa_pmem_guid, 16) )
+            continue;
+        smfn = paddr_to_pfn(spa->address);
+        emfn = paddr_to_pfn(spa->address + spa->length);
+        printk(XENLOG_INFO "NFIT: PMEM MFNs 0x%lx - 0x%lx\n", smfn, emfn);
+    }
+}
 
 void __init acpi_nfit_boot_init(void)
 {
@@ -53,6 +185,25 @@ void __init acpi_nfit_boot_init(void)
                      PAGE_HYPERVISOR);
 }
 
+void __init acpi_nfit_init(void)
+{
+    if ( !nfit_desc.acpi_table )
+        return;
+
+    /* Collect all SPA and memory map sub-tables. */
+    if ( acpi_nfit_add_subtables(&nfit_desc) )
+    {
+        printk(XENLOG_ERR "NFIT: no memory for NFIT management\n");
+        return;
+    }
+
+    /* Link descriptors of SPA and memory map sub-tables. */
+    acpi_nfit_link_subtables(&nfit_desc);
+
+    /* Register valid pmem regions to Xen hypervisor. */
+    acpi_nfit_register_pmem(&nfit_desc);
+}
+
 /**
  * Search pmem regions overlapped with the specified address range.
  *
diff --git a/xen/include/acpi/actbl1.h b/xen/include/acpi/actbl1.h
index 94d8d7775c..037652916a 100644
--- a/xen/include/acpi/actbl1.h
+++ b/xen/include/acpi/actbl1.h
@@ -946,6 +946,32 @@ struct acpi_nfit_system_address {
 	u64 memory_mapping;
 };
 
+/* 1: Memory Device to System Address Range Map Structure */
+struct acpi_nfit_memory_map {
+	struct acpi_nfit_header header;
+	u32 device_handle;
+	u16 physical_id;
+	u16 region_id;
+	u16 range_index;
+	u16 region_index;
+	u64 region_size;
+	u64 region_offset;
+	u64 address;
+	u16 interleave_index;
+	u16 interleave_ways;
+	u16 flags;
+	u16 reserved;		/* Reserved, must be zero */
+};
+
+/* Flags in struct acpi_nfit_memory_map */
+#define ACPI_NFIT_MEM_SAVE_FAILED		(1)	/* 00: Last SAVE to Memory Device failed */
+#define ACPI_NFIT_MEM_RESTORE_FAILED	(1<<1)	/* 01: Last RESTORE from Memory Device failed */
+#define ACPI_NFIT_MEM_FLUSH_FAILED		(1<<2)	/* 02: Platform flush failed */
+#define ACPI_NFIT_MEM_NOT_ARMED			(1<<3)	/* 03: Memory Device is not armed */
+#define ACPI_NFIT_MEM_HEALTH_OBSERVED	(1<<4)	/* 04: Memory Device observed SMART/health events */
+#define ACPI_NFIT_MEM_HEALTH_ENABLED	(1<<5)	/* 05: SMART/health events enabled */
+#define ACPI_NFIT_MEM_MAP_FAILED		(1<<6)	/* 06: Mapping to SPA failed */
+
 /*******************************************************************************
  *
  * SBST - Smart Battery Specification Table
diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h
index 1bd8f9f4e4..088f01255d 100644
--- a/xen/include/xen/acpi.h
+++ b/xen/include/xen/acpi.h
@@ -185,6 +185,7 @@ void acpi_nfit_boot_init(void);
 bool acpi_nfit_boot_search_pmem(unsigned long smfn, unsigned long emfn,
                                 unsigned long *ret_smfn,
                                 unsigned long *ret_emfn);
+void acpi_nfit_init(void);
 #endif /* CONFIG_NVDIMM_PMEM */
 
 #endif /*_LINUX_ACPI_H*/
