All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Luck, Tony" <tony.luck@intel.com>
To: Borislav Petkov <bp@alien8.de>
Cc: Qiuxu Zhuo <qiuxu.zhuo@intel.com>,
	Tony Luck <tony.luck@intel.com>,
	Aristeu Rozanski <aris@redhat.com>,
	Mauro Carvalho Chehab <mchehab@s-opensource.com>,
	linux-edac@vger.kernel.org
Subject: [4/5] EDAC, dsm_edac: Wrap ACPI DSM methods for address translation
Date: Mon, 24 Sep 2018 13:16:12 -0700	[thread overview]
Message-ID: <20180924201613.14070-5-tony.luck@intel.com> (raw)

From: Qiuxu Zhuo <qiuxu.zhuo@intel.com>

Some machines provide BIOS ACPI DSM (Device Specific Method)
to translate memory system address to DRAM address (socket, memory
controller, channel, rank, bank, row, column). This patch wraps
the DSM methods, and registers them to EDAC core.

Specification for this translation method is at:

	https://cdrdv2.intel.com/v1/dl/getContent/603354

Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
---

The specification is currently behind a click-through license. I'll see
if I can get that fixed.

 drivers/edac/Kconfig    |   9 ++
 drivers/edac/Makefile   |   1 +
 drivers/edac/dsm_edac.c | 223 ++++++++++++++++++++++++++++++++++++++++
 drivers/edac/edac_mc.c  |  36 +++++++
 drivers/edac/edac_mc.h  |  64 ++++++++++++
 5 files changed, 333 insertions(+)
 create mode 100644 drivers/edac/dsm_edac.c

diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 57304b2e989f..1b2e0ae6158f 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -222,6 +222,15 @@ config EDAC_I7300
 	  Support for error detection and correction the Intel
 	  Clarksboro MCH (Intel 7300 chipset).
 
+config EDAC_DSM
+	tristate "Address translation interface via ACPI DSM"
+	depends on X86_64 && ACPI
+	help
+	  Some of machines provide BIOS ACPI DSM (Device Specific Method)
+	  to translate memory system address to DRAM address (socket, memory
+	  controller, channel, rank, bank, row, column). By enabling this
+	  option, the wrapped DSM methods are registered to EDAC core.
+
 config EDAC_SBRIDGE
 	tristate "Intel Sandy-Bridge/Ivy-Bridge/Haswell Integrated MC"
 	depends on PCI && X86_64 && X86_MCE_INTEL && PCI_MMCONFIG
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 02b43a7d8c3e..0238c9127730 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_EDAC_I5100)		+= i5100_edac.o
 obj-$(CONFIG_EDAC_I5400)		+= i5400_edac.o
 obj-$(CONFIG_EDAC_I7300)		+= i7300_edac.o
 obj-$(CONFIG_EDAC_I7CORE)		+= i7core_edac.o
+obj-$(CONFIG_EDAC_DSM)			+= dsm_edac.o
 obj-$(CONFIG_EDAC_SBRIDGE)		+= sb_edac.o
 obj-$(CONFIG_EDAC_SKX)			+= skx_edac.o
 obj-$(CONFIG_EDAC_PND2)			+= pnd2_edac.o
diff --git a/drivers/edac/dsm_edac.c b/drivers/edac/dsm_edac.c
new file mode 100644
index 000000000000..ae18d3d1c438
--- /dev/null
+++ b/drivers/edac/dsm_edac.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Address translation interface via ACPI DSM.
+ * Copyright (C) 2018 Intel Corporation
+ */
+
+#include <linux/acpi.h>
+#include "edac_module.h"
+#include "edac_mc.h"
+
+#define DSM_REV				0x1
+#define DSM_IDX_GET_ADDR_PARAMS		0x1
+#define DSM_IDX_FORWARD_TRANSLATE	0x2
+#define ACPI_ADXL_PATH			"\\_SB.ADXL"
+#define MAX_PARAMS			31
+
+#define dsm_printk(level, fmt, arg...)	\
+	edac_printk(level, "dsm", fmt, ##arg)
+
+static acpi_handle handle;
+static union acpi_object *params;
+static const guid_t dsm_guid = GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F, 0xAF, 0xDA,
+					 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D);
+
+static int dsm_adxl_check_obj(union acpi_object *obj)
+{
+	union acpi_object *o;
+
+	if (!obj) {
+		dsm_printk(KERN_ERR, "Empty obj\n");
+		return -ENODEV;
+	}
+
+	if (obj->package.count != 2) {
+		dsm_printk(KERN_ERR, "Bad pkg cnt %d\n", obj->package.count);
+		return -EINVAL;
+	}
+
+	o = obj->package.elements;
+	if (o->type != ACPI_TYPE_INTEGER) {
+		dsm_printk(KERN_ERR, "Bad 1st element type %d\n", o->type);
+		return -EINVAL;
+	}
+	if (o->integer.value) {
+		dsm_printk(KERN_ERR, "Bad ret val %llu\n", o->integer.value);
+		return -EINVAL;
+	}
+
+	o = obj->package.elements + 1;
+	if (o->type != ACPI_TYPE_PACKAGE) {
+		dsm_printk(KERN_ERR, "Bad 2nd element type %d\n", o->type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static union acpi_object *_dsm_adxl_get_params(void)
+{
+	union acpi_object *obj;
+
+	obj = acpi_evaluate_dsm_typed(handle, &dsm_guid, DSM_REV,
+				      DSM_IDX_GET_ADDR_PARAMS,
+				      NULL, ACPI_TYPE_PACKAGE);
+
+	if (dsm_adxl_check_obj(obj)) {
+		ACPI_FREE(obj);
+		return NULL;
+	}
+
+	return obj;
+}
+
+static int dsm_adxl_check(char component_names[][32],
+			  int component_indices[])
+{
+	union acpi_object *p;
+	int i, j, cnt;
+
+	p = params->package.elements + 1;
+	cnt = min_t(u32, p->package.count, MAX_PARAMS);
+	p = p->package.elements;
+
+	for (i = 0; i < MAX_PARAMS && component_names[i][0]; i++) {
+		for (j = 0; j < cnt; j++) {
+			if (!strcmp(component_names[i], p[j].string.pointer)) {
+				component_indices[i] = j;
+				break;
+			}
+		}
+		if (j == cnt)
+			goto err;
+	}
+	component_indices[i] = -1;
+
+	return 0;
+err:
+	dsm_printk(KERN_ERR, "'%s' is not matched from DSM parameters:",
+		   component_names[i]);
+	for (j = 0; j < cnt; j++)
+		dsm_printk(KERN_CONT, " %s", p[j].string.pointer);
+	dsm_printk(KERN_CONT, "\n");
+
+	return -ENODEV;
+}
+
+static int dsm_adxl_decode(u64 addr, char *msg, int msglen,
+			   int component_indices[], ...)
+
+{
+	union acpi_object argv4[2], *results, *r, *p;
+	int i, rc, cnt, len = 0;
+	va_list ap;
+	u64 *valp;
+
+	va_start(ap, component_indices);
+	argv4[0].type = ACPI_TYPE_PACKAGE;
+	argv4[0].package.count = 1;
+	argv4[0].package.elements = &argv4[1];
+	argv4[1].integer.type = ACPI_TYPE_INTEGER;
+	argv4[1].integer.value = addr;
+
+	results = acpi_evaluate_dsm_typed(handle, &dsm_guid, DSM_REV,
+					  DSM_IDX_FORWARD_TRANSLATE, argv4,
+					  ACPI_TYPE_PACKAGE);
+
+	rc = dsm_adxl_check_obj(results);
+	if (rc)
+		goto end;
+
+	r = results->package.elements + 1;
+	p = params->package.elements + 1;
+	cnt = min_t(u32, min(p->package.count, r->package.count), MAX_PARAMS);
+	r = r->package.elements;
+	p = p->package.elements;
+
+	for (i = 0; i < MAX_PARAMS && component_indices[i] != -1; i++) {
+		valp = va_arg(ap, u64 *);
+		*valp = r[component_indices[i]].integer.value;
+	}
+
+	for (i = 0; i < cnt; i++) {
+		if (r[i].integer.value == -1)
+			continue;
+
+		len += snprintf(msg + len, msglen - len, " %s:0x%llx",
+				p[i].string.pointer, r[i].integer.value);
+
+		if (msglen - len <= 0)
+			break;
+	}
+
+end:
+	ACPI_FREE(results);
+	va_end(ap);
+	return rc;
+}
+
+static bool dsm_adxl_detect(void)
+{
+	char *path = ACPI_ADXL_PATH;
+	acpi_status status;
+
+	status = acpi_get_handle(NULL, path, &handle);
+	if (ACPI_FAILURE(status)) {
+		dsm_printk(KERN_ERR, "No ACPI handle for path %s\n", path);
+		return false;
+	}
+
+	if (!acpi_has_method(handle, "_DSM")) {
+		dsm_printk(KERN_ERR, "No DSM method\n");
+		return false;
+	}
+
+	if (!acpi_check_dsm(handle, &dsm_guid, DSM_REV,
+			    DSM_IDX_GET_ADDR_PARAMS |
+			    DSM_IDX_FORWARD_TRANSLATE)) {
+		dsm_printk(KERN_ERR, "No ADXL DSM methods\n");
+		return false;
+	}
+
+	params = _dsm_adxl_get_params();
+	if (!params) {
+		dsm_printk(KERN_ERR, "Failed to get params\n");
+		return false;
+	}
+
+	return true;
+}
+
+static const struct edac_dsm_operations dsm_ops = {
+	.check	= dsm_adxl_check,
+	.decode	= dsm_adxl_decode,
+	.owner	= THIS_MODULE,
+};
+
+static int __init dsm_init(void)
+{
+	edac_dbg(2, "\n");
+
+	if (!dsm_adxl_detect())
+		return -ENODEV;
+
+	edac_dsm_register_ops(&dsm_ops);
+
+	return 0;
+}
+
+static void __exit dsm_exit(void)
+{
+	edac_dbg(2, "\n");
+
+	edac_dsm_unregister_ops(&dsm_ops);
+
+	ACPI_FREE(params);
+}
+
+module_init(dsm_init);
+module_exit(dsm_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Qiuxu Zhuo");
+MODULE_DESCRIPTION("Address translation interface via ACPI DSM");
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 7d3edd713932..ba27feafc976 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -57,6 +57,8 @@ static const char *edac_mc_owner;
 
 static struct bus_type mc_bus[EDAC_MAX_MCS];
 
+static const struct edac_dsm_operations *edac_dsm_ops;
+
 int edac_get_report_status(void)
 {
 	return edac_report;
@@ -1259,3 +1261,37 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
 	edac_raw_mc_handle_error(type, mci, e);
 }
 EXPORT_SYMBOL_GPL(edac_mc_handle_error);
+
+const struct edac_dsm_operations *edac_dsm_get_ops(void)
+{
+	if (edac_dsm_ops && try_module_get(edac_dsm_ops->owner))
+		return edac_dsm_ops;
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(edac_dsm_get_ops);
+
+void edac_dsm_put_ops(const struct edac_dsm_operations *ops)
+{
+	if (ops)
+		module_put(ops->owner);
+}
+EXPORT_SYMBOL_GPL(edac_dsm_put_ops);
+
+int edac_dsm_register_ops(const struct edac_dsm_operations *ops)
+{
+	if (edac_dsm_ops)
+		return -EEXIST;
+
+	edac_dsm_ops = ops;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(edac_dsm_register_ops);
+
+void edac_dsm_unregister_ops(const struct edac_dsm_operations *ops)
+{
+	if (edac_dsm_ops == ops)
+		edac_dsm_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(edac_dsm_unregister_ops);
diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h
index 4165e15995ad..f2183d06de77 100644
--- a/drivers/edac/edac_mc.h
+++ b/drivers/edac/edac_mc.h
@@ -254,6 +254,70 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type,
 			  const char *msg,
 			  const char *other_detail);
 
+/*
+ * edac DSM APIs
+ */
+
+struct edac_dsm_operations {
+	struct module *owner;
+	/*
+	 * check() - Check whether the speficified address component names
+	 *           are supported.
+	 *
+	 * @component_names:	List of address component name
+	 *			strings to be checked.
+	 * @component_indices:  Array of address component name indices
+	 *			to be filled.
+	 *
+	 * Returns: 0 on success, or an error code on failure.
+	 */
+	int (*check)(char component_names[][32], int component_indices[]);
+	/*
+	 * decode() - Decode memory system address to DRAM address
+	 *	      (socket, MC, channel, rank, bank, row, column).
+	 * @addr:		Memory system address
+	 * @component_indices:	Array of address component name
+	 *			indices from check()
+	 * @msg:		String holds detail of decoded DRAM addresses
+	 * @msglen:		Length of the string
+	 *
+	 * Returns: 0 on success, or an error code on failure.
+	 */
+	int (*decode)(u64 addr, char *msg, int msglen,
+		      int component_indices[], ...);
+};
+
+/**
+ * edac_dsm_get_ops() - Get EDAC DSM operations and increase the
+ *			refcount of module.
+ *
+ * Returns: On success, return a pointer to struct edac_dsm_operations,
+ *	%NULL otherwise
+ */
+const struct edac_dsm_operations *edac_dsm_get_ops(void);
+
+/**
+ * edac_dsm_put_ops() - Put EDAC DSM operations and decrease the
+ *			refcount of module.
+ */
+void edac_dsm_put_ops(const struct edac_dsm_operations *ops);
+
+/**
+ * edac_dsm_register_ops() - Register EDAC DSM operations to EDAC core.
+ *
+ * @ops: A struct edac_dsm_operations pointer
+ *
+ * Returns: On success, return 0, an error code otherwise
+ */
+int edac_dsm_register_ops(const struct edac_dsm_operations *ops);
+
+/**
+ * edac_dsm_unregister_ops() - Unregister EDAC DSM operations from EDAC core.
+ *
+ * @ops: A struct edac_dsm_operations pointer
+ */
+void edac_dsm_unregister_ops(const struct edac_dsm_operations *ops);
+
 /*
  * edac misc APIs
  */

             reply	other threads:[~2018-09-24 20:16 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-24 20:16 Luck, Tony [this message]
2018-09-26 17:33 [4/5] EDAC, dsm_edac: Wrap ACPI DSM methods for address translation Borislav Petkov
2018-09-26 18:22 Luck, Tony
2018-10-03 17:58 Luck, Tony
2018-10-04  9:31 Borislav Petkov
2018-10-05 22:25 Luck, Tony
2018-10-06 20:44 Borislav Petkov
2018-10-08 16:57 Luck, Tony
2018-10-09 10:28 Rafael J. Wysocki
2018-10-09 11:43 Qiuxu Zhuo
2018-10-09 15:14 Borislav Petkov
2018-10-09 15:22 Luck, Tony
2018-10-09 15:25 Rafael J. Wysocki
2018-10-09 15:29 Rafael J. Wysocki
2018-10-09 16:17 Borislav Petkov
2018-10-09 18:29 Luck, Tony
2018-10-09 18:33 Luck, Tony

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180924201613.14070-5-tony.luck@intel.com \
    --to=tony.luck@intel.com \
    --cc=aris@redhat.com \
    --cc=bp@alien8.de \
    --cc=linux-edac@vger.kernel.org \
    --cc=mchehab@s-opensource.com \
    --cc=qiuxu.zhuo@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.