diff mbox

[RFC,XEN,v2,02/15] xen: probe pmem regions via ACPI NFIT

Message ID 20170320000949.24675-3-haozhong.zhang@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Haozhong Zhang March 20, 2017, 12:09 a.m. UTC
Probe the address ranges of pmem regions via ACPI NFIT and report them
to Xen hypervisor.

Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
---
Cc: Jan Beulich <jbeulich@suse.com>
Cc: Andrew Cooper <andrew.cooper3@citrix.com>
---
 xen/arch/x86/acpi/boot.c  |   4 ++
 xen/common/Makefile       |   1 +
 xen/common/pmem.c         | 106 ++++++++++++++++++++++++++++++++++++++++++
 xen/drivers/acpi/Makefile |   2 +
 xen/drivers/acpi/nfit.c   | 116 ++++++++++++++++++++++++++++++++++++++++++++++
 xen/include/acpi/actbl.h  |   1 +
 xen/include/acpi/actbl1.h |  42 +++++++++++++++++
 xen/include/xen/acpi.h    |   4 ++
 xen/include/xen/pmem.h    |  28 +++++++++++
 9 files changed, 304 insertions(+)
 create mode 100644 xen/common/pmem.c
 create mode 100644 xen/drivers/acpi/nfit.c
 create mode 100644 xen/include/xen/pmem.h
diff mbox

Patch

diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c
index 33c9133812..83d73868d6 100644
--- a/xen/arch/x86/acpi/boot.c
+++ b/xen/arch/x86/acpi/boot.c
@@ -754,5 +754,9 @@  int __init acpi_boot_init(void)
 
 	acpi_table_parse(ACPI_SIG_BGRT, acpi_invalidate_bgrt);
 
+#ifdef CONFIG_PMEM
+	acpi_nfit_init();
+#endif
+
 	return 0;
 }
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 0fed30bcc6..16c273b6d4 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -29,6 +29,7 @@  obj-y += notifier.o
 obj-y += page_alloc.o
 obj-$(CONFIG_HAS_PDX) += pdx.o
 obj-$(CONFIG_PERF_COUNTERS) += perfc.o
+obj-$(CONFIG_PMEM) += pmem.o
 obj-y += preempt.o
 obj-y += random.o
 obj-y += rangeset.o
diff --git a/xen/common/pmem.c b/xen/common/pmem.c
new file mode 100644
index 0000000000..3c150cf1dd
--- /dev/null
+++ b/xen/common/pmem.c
@@ -0,0 +1,106 @@ 
+/*
+ * xen/common/pmem.c
+ *
+ * Copyright (C) 2017, Intel Corporation.
+ *
+ * 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 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
+ * 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/errno.h>
+#include <xen/list.h>
+#include <xen/pmem.h>
+#include <xen/spinlock.h>
+
+/*
+ * All pmem regions probed via SPA range structures of ACPI NFIT are
+ * linked in pmem_regions.
+ */
+static DEFINE_SPINLOCK(pmem_regions_lock);
+static LIST_HEAD(pmem_regions);
+
+struct pmem {
+    struct list_head link;      /* link to pmem_list */
+    unsigned long smfn;         /* start MFN of the whole pmem region */
+    unsigned long emfn;         /* end MFN of the whole pmem region */
+};
+
+static bool check_overlap(unsigned long smfn1, unsigned long emfn1,
+                          unsigned long smfn2, unsigned long emfn2)
+{
+    return smfn1 < emfn2 && smfn2 < emfn1;
+}
+
+static struct pmem *alloc_pmem_struct(unsigned long smfn, unsigned long emfn)
+{
+    struct pmem *pmem = xzalloc(struct pmem);
+
+    if ( !pmem )
+        return NULL;
+
+    pmem->smfn = smfn;
+    pmem->emfn = emfn;
+
+    return pmem;
+}
+
+static int pmem_list_add(struct list_head *list, struct pmem *entry)
+{
+    struct list_head *cur;
+    unsigned long smfn = entry->smfn, emfn = entry->emfn;
+
+    list_for_each_prev(cur, list)
+    {
+        struct pmem *cur_pmem = list_entry(cur, struct pmem, link);
+        unsigned long cur_smfn = cur_pmem->smfn;
+        unsigned long cur_emfn = cur_pmem->emfn;
+
+        if ( check_overlap(smfn, emfn, cur_smfn, cur_emfn) )
+            return -EINVAL;
+
+        if ( cur_smfn < smfn )
+            break;
+    }
+
+    list_add(&entry->link, cur);
+
+    return 0;
+}
+
+/**
+ * Register a pmem region to Xen. It's used by Xen hypervisor to collect
+ * all pmem regions can be used later.
+ *
+ * Parameters:
+ *  smfn, emfn: start and end MFNs of the pmem region
+ *
+ * Return:
+ *  On success, return 0. Otherwise, an error number is returned.
+ */
+int pmem_register(unsigned long smfn, unsigned long emfn)
+{
+    int rc;
+    struct pmem *pmem;
+
+    if ( smfn >= emfn )
+        return -EINVAL;
+
+    pmem = alloc_pmem_struct(smfn, emfn);
+    if ( !pmem )
+        return -ENOMEM;
+
+    spin_lock(&pmem_regions_lock);
+    rc = pmem_list_add(&pmem_regions, pmem);
+    spin_unlock(&pmem_regions_lock);
+
+    return rc;
+}
diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile
index 444b11d583..cef9d90222 100644
--- a/xen/drivers/acpi/Makefile
+++ b/xen/drivers/acpi/Makefile
@@ -9,3 +9,5 @@  obj-$(CONFIG_HAS_CPUFREQ) += pmstat.o
 
 obj-$(CONFIG_X86) += hwregs.o
 obj-$(CONFIG_X86) += reboot.o
+
+obj-$(CONFIG_PMEM) += nfit.o
diff --git a/xen/drivers/acpi/nfit.c b/xen/drivers/acpi/nfit.c
new file mode 100644
index 0000000000..ceac121dd2
--- /dev/null
+++ b/xen/drivers/acpi/nfit.c
@@ -0,0 +1,116 @@ 
+/*
+ * xen/drivers/acpi/nfit.c
+ *
+ * Copyright (C) 2017, Intel Corporation.
+ *
+ * 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 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
+ * 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/acpi.h>
+#include <xen/errno.h>
+#include <xen/init.h>
+#include <xen/mm.h>
+#include <xen/pfn.h>
+#include <xen/pmem.h>
+
+static struct acpi_table_nfit *nfit __read_mostly = NULL;
+
+/* ACPI 6.1: GUID of a byte addressable persistent memory region */
+static const uint8_t nfit_spa_pmem_uuid[] =
+{
+    0x79, 0xd3, 0xf0, 0x66, 0xf3, 0xb4, 0x74, 0x40,
+    0xac, 0x43, 0x0d, 0x33, 0x18, 0xb7, 0x8c, 0xdb,
+};
+
+/**
+ * Enumerate each sub-table of NFIT.
+ *
+ * For a sub-table of type @type, @parse_cb() (if not NULL) is called
+ * to parse the sub-table. @parse_cb() returns 0 on success, and
+ * returns non-zero error code on errors.
+ *
+ * Parameters:
+ *  nfit:      NFIT
+ *  type:      the type of sub-table that will be parsed
+ *  parse_cb:  the function used to parse each sub-table
+ *  arg:       the argument passed to @parse_cb()
+ *
+ * Return:
+ *  0 on success, non-zero on failure
+ */
+static int acpi_nfit_foreach_subtable(
+    struct acpi_table_nfit *nfit, enum acpi_nfit_type type,
+    int (*parse_cb)(const struct acpi_nfit_header *, void *arg), void *arg)
+{
+    struct acpi_table_header *table = (struct acpi_table_header *)nfit;
+    struct acpi_nfit_header *hdr;
+    uint32_t hdr_offset = sizeof(*nfit);
+    int ret = 0;
+
+    while ( hdr_offset < table->length )
+    {
+        hdr = (void *)nfit + hdr_offset;
+        hdr_offset += hdr->length;
+        if ( hdr->type == type && parse_cb )
+        {
+            ret = parse_cb(hdr, arg);
+            if ( ret )
+                break;
+        }
+    }
+
+    return ret;
+}
+
+static int __init acpi_nfit_spa_probe_pmem(const struct acpi_nfit_header *hdr,
+                                           void *opaque)
+{
+    struct acpi_nfit_system_address *spa =
+        (struct acpi_nfit_system_address *)hdr;
+    unsigned long smfn = paddr_to_pfn(spa->address);
+    unsigned long emfn = paddr_to_pfn(spa->address + spa->length);
+    int rc;
+
+    if ( memcmp(spa->range_guid, nfit_spa_pmem_uuid, 16) )
+        return 0;
+
+    rc = pmem_register(smfn, emfn);
+    if ( rc )
+        printk(XENLOG_ERR
+               "NFIT: failed to add pmem mfns: 0x%lx - 0x%lx, err %d\n",
+               smfn, emfn, rc);
+    else
+        printk(XENLOG_INFO "NFIT: pmem mfn 0x%lx - 0x%lx\n", smfn, emfn);
+
+    /* ignore the error and continue to add the next pmem range */
+    return 0;
+}
+
+void __init acpi_nfit_init(void)
+{
+    acpi_status status;
+    acpi_physical_address nfit_addr;
+    acpi_native_uint nfit_len;
+
+    status = acpi_get_table_phys(ACPI_SIG_NFIT, 0, &nfit_addr, &nfit_len);
+    if ( ACPI_FAILURE(status) )
+         return;
+
+    map_pages_to_xen((unsigned long)__va(nfit_addr), PFN_DOWN(nfit_addr),
+                     PFN_UP(nfit_addr + nfit_len) - PFN_DOWN(nfit_addr),
+                     PAGE_HYPERVISOR);
+    nfit = (struct acpi_table_nfit *)__va(nfit_addr);
+
+    acpi_nfit_foreach_subtable(nfit, ACPI_NFIT_TYPE_SYSTEM_ADDRESS,
+                               acpi_nfit_spa_probe_pmem, NULL);
+}
diff --git a/xen/include/acpi/actbl.h b/xen/include/acpi/actbl.h
index 3079176992..6e113b0873 100644
--- a/xen/include/acpi/actbl.h
+++ b/xen/include/acpi/actbl.h
@@ -71,6 +71,7 @@ 
 #define ACPI_SIG_XSDT           "XSDT"	/* Extended  System Description Table */
 #define ACPI_SIG_SSDT           "SSDT"	/* Secondary System Description Table */
 #define ACPI_RSDP_NAME          "RSDP"	/* Short name for RSDP, not signature */
+#define ACPI_SIG_NFIT           "NFIT"	/* NVDIMM Firmware Interface Table */
 
 /*
  * All tables and structures must be byte-packed to match the ACPI
diff --git a/xen/include/acpi/actbl1.h b/xen/include/acpi/actbl1.h
index e1991362dc..a59ac11325 100644
--- a/xen/include/acpi/actbl1.h
+++ b/xen/include/acpi/actbl1.h
@@ -905,6 +905,48 @@  struct acpi_msct_proximity {
 
 /*******************************************************************************
  *
+ * NFIT - NVDIMM Interface Table (ACPI 6.0+)
+ *        Version 1
+ *
+ ******************************************************************************/
+
+struct acpi_table_nfit {
+	struct acpi_table_header header;	/* Common ACPI table header */
+	u32 reserved;						/* Reserved, must be zero */
+};
+
+/* Subtable header for NFIT */
+
+struct acpi_nfit_header {
+	u16 type;
+	u16 length;
+};
+
+/* Values for subtable type in struct acpi_nfit_header */
+
+enum acpi_nfit_type {
+	ACPI_NFIT_TYPE_SYSTEM_ADDRESS = 0,
+};
+
+/*
+ * NFIT Subtables
+ */
+
+/* type 0: System Physical Address Range Structure */
+struct acpi_nfit_system_address {
+	struct acpi_nfit_header header;
+	u16 range_index;
+	u16 flags;
+	u32 reserved;		/* Reseved, must be zero */
+	u32 proximity_domain;
+	u8 range_guid[16];
+	u64 address;
+	u64 length;
+	u64 memory_mapping;
+};
+
+/*******************************************************************************
+ *
  * SBST - Smart Battery Specification Table
  *        Version 1
  *
diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h
index 30ec0eec5f..8edb9a275e 100644
--- a/xen/include/xen/acpi.h
+++ b/xen/include/xen/acpi.h
@@ -180,4 +180,8 @@  void acpi_reboot(void);
 void acpi_dmar_zap(void);
 void acpi_dmar_reinstate(void);
 
+#ifdef CONFIG_PMEM
+void acpi_nfit_init(void);
+#endif /* CONFIG_PMEM */
+
 #endif /*_LINUX_ACPI_H*/
diff --git a/xen/include/xen/pmem.h b/xen/include/xen/pmem.h
new file mode 100644
index 0000000000..1144e86f98
--- /dev/null
+++ b/xen/include/xen/pmem.h
@@ -0,0 +1,28 @@ 
+/*
+ * xen/include/xen/pmem.h
+ *
+ * Copyright (C) 2017, Intel Corporation.
+ *
+ * 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 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
+ * 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_PMEM_H__
+#define __XEN_PMEM_H__
+#ifdef CONFIG_PMEM
+
+#include <xen/types.h>
+
+int pmem_register(unsigned long smfn, unsigned long emfn);
+
+#endif /* CONFIG_PMEM */
+#endif /* __XEN_PMEM_H__ */