[RFC] ACPI, APEI, Generic Hardware Error Source (GHES) injecting support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The testing of Generic Hardware Error Source (GHES) is quite
difficult, because special hardware is needed to trigger the hardware
error. So a software based hardware error injector for GHES is
implemented.

Error notification is not provided in this patch.  So you still need
some NMI/SCI/IRQ injecting support to make it work.

Signed-off-by: Huang Ying <ying.huang@xxxxxxxxx>
---
 drivers/acpi/apei/Kconfig         |   10 ++
 drivers/acpi/apei/Makefile        |    1 
 drivers/acpi/apei/apei-internal.h |    8 ++
 drivers/acpi/apei/ghes-inj.c      |  132 ++++++++++++++++++++++++++++++++++++++
 drivers/acpi/apei/ghes.c          |   15 ++++
 5 files changed, 165 insertions(+), 1 deletion(-)
 create mode 100644 drivers/acpi/apei/ghes-inj.c

--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -54,3 +54,13 @@ config ACPI_APEI_ERST_DEBUG
 	  error information to and from a persistent store. Enable this
 	  if you want to debugging and testing the ERST kernel support
 	  and firmware implementation.
+
+config ACPI_APEI_GHES_INJ
+	tristate "APEI Generic Hardware Error Source (GHES) Injecting Support"
+	depends on ACPI_APEI_GHES
+	help
+	  GHES provides a way to report platform hardware errors (such
+	  as that from chipset).
+
+	  The injector can inject fake hardware error record. This is
+	  used for GHES debugging/testing.
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -2,5 +2,6 @@ obj-$(CONFIG_ACPI_APEI)		+= apei.o
 obj-$(CONFIG_ACPI_APEI_GHES)	+= ghes.o
 obj-$(CONFIG_ACPI_APEI_EINJ)	+= einj.o
 obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
+obj-$(CONFIG_ACPI_APEI_GHES_INJ) += ghes-inj.o
 
 apei-y := apei-base.o hest.o cper.o erst.o
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -33,6 +33,14 @@ struct apei_exec_context {
 	u32 entries;
 };
 
+struct ghes_inject_data {
+	unsigned long error_status_address;
+	u16 source_id;
+	unsigned short valid : 1;
+};
+
+extern struct ghes_inject_data ghes_inject_data;
+
 void apei_exec_ctx_init(struct apei_exec_context *ctx,
 			struct apei_exec_ins_type *ins_table,
 			u32 instructions,
--- /dev/null
+++ b/drivers/acpi/apei/ghes-inj.c
@@ -0,0 +1,132 @@
+/*
+ * APEI Generic Hardware Error Source (GHES) injector support
+ *
+ * Fake hardware error record can be injected. This is used for for
+ * GHES debugging/testing.
+ *
+ * Copyright 2010,2011 Intel Corp.
+ *   Author: Huang Ying <ying.huang@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+#define GHES_INJ_PFX		"GHES-INJ: "
+
+#define GHES_INJ_BUF_LEN_MAX	4096
+
+static void *ghes_inj_buf;
+static unsigned int ghes_inj_buf_len;
+
+/* Prevent erst_inj_buf from being accessed concurrently */
+static DEFINE_MUTEX(ghes_inj_mutex);
+
+static ssize_t ghes_inj_write(struct file *filp, const char __user *ubuf,
+			      size_t usize, loff_t *off)
+{
+	int rc;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (*off != 0)
+		return -EINVAL;
+
+	if (usize > GHES_INJ_BUF_LEN_MAX)
+		return -EINVAL;
+
+	if (mutex_lock_interruptible(&ghes_inj_mutex))
+		return -EINTR;
+	ghes_inject_data.valid = 0;
+	/* Wait for all consumers finish using the injecting buffer */
+	synchronize_rcu();
+	if (usize > ghes_inj_buf_len) {
+		void *p;
+		rc = -ENOMEM;
+		p = kmalloc(usize, GFP_KERNEL);
+		if (!p)
+			goto out;
+		kfree(ghes_inj_buf);
+		ghes_inj_buf = p;
+		ghes_inj_buf_len = usize;
+	}
+	rc = copy_from_user(ghes_inj_buf, ubuf, usize);
+	if (rc) {
+		rc = -EFAULT;
+		goto out;
+	}
+	ghes_inject_data.error_status_address = __pa(ghes_inj_buf);
+	/*
+	 * ghes_injiect_data.valid must be set after other fields are
+	 * written
+	 */
+	smp_wmb();
+	ghes_inject_data.valid = 1;
+out:
+	mutex_unlock(&ghes_inj_mutex);
+	return rc ? rc : usize;
+}
+
+static const struct file_operations ghes_inj_fops = {
+	.owner		= THIS_MODULE,
+	.write		= ghes_inj_write,
+};
+
+static struct dentry *ghes_debug_dir;
+
+static __init int ghes_inj_init(void)
+{
+	struct dentry *f;
+	int rc = -ENOMEM;
+
+	ghes_debug_dir = debugfs_create_dir("ghes", apei_get_debugfs_dir());
+	if (!ghes_debug_dir)
+		return rc;
+	f = debugfs_create_file("inject", S_IWUSR, ghes_debug_dir,
+				NULL, &ghes_inj_fops);
+	if (!f)
+		goto err_cleanup;
+	f = debugfs_create_u16("inject_source_id", S_IRUSR | S_IWUSR,
+			       ghes_debug_dir, &ghes_inject_data.source_id);
+	if (!f)
+		goto err_cleanup;
+
+	return 0;
+err_cleanup:
+	debugfs_remove_recursive(ghes_debug_dir);
+	return rc;
+}
+
+static __exit void ghes_inj_exit(void)
+{
+	debugfs_remove_recursive(ghes_debug_dir);
+	ghes_inject_data.valid = 0;
+	/* Wait for all consumers finish using the injecting buffer */
+	synchronize_rcu();
+	kfree(ghes_inj_buf);
+}
+
+module_init(ghes_inj_init);
+module_exit(ghes_inj_exit);
+
+MODULE_AUTHOR("Huang Ying");
+MODULE_DESCRIPTION("APEI Generic Hardware Error Source (GHES) injecting support");
+MODULE_LICENSE("GPL");
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -153,6 +153,9 @@ static unsigned long ghes_estatus_pool_s
 static struct llist_head ghes_estatus_llist;
 static struct irq_work ghes_proc_irq_work;
 
+struct ghes_inject_data ghes_inject_data;
+EXPORT_SYMBOL_GPL(ghes_inject_data);
+
 static int ghes_ioremap_init(void)
 {
 	ghes_ioremap_area = __get_vm_area(PAGE_SIZE * GHES_IOREMAP_PAGES,
@@ -371,7 +374,13 @@ static int ghes_read_estatus(struct ghes
 	u32 len;
 	int rc;
 
-	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
+	if (!ghes_inject_data.valid ||
+	    ghes_inject_data.source_id != g->header.source_id)
+		rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
+	else {
+		buf_paddr = ghes_inject_data.error_status_address;
+		rc = 0;
+	}
 	if (rc) {
 		if (!silent && printk_ratelimit())
 			pr_warning(FW_WARN GHES_PFX
@@ -420,6 +429,10 @@ static void ghes_clear_estatus(struct gh
 	ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
 			      sizeof(ghes->estatus->block_status), 0);
 	ghes->flags &= ~GHES_TO_CLEAR;
+
+	if (ghes_inject_data.valid &&
+	    ghes_inject_data.source_id == ghes->generic->header.source_id)
+		ghes_inject_data.valid = 0;
 }
 
 static void ghes_do_proc(const struct acpi_hest_generic_status *estatus)
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux