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