On Tue, May 10, 2011 at 11:08:41AM +0800, Huang Ying wrote: > 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. Should we add that to this patch, otherwise it seems like the injection isn't very useful or intuitive from the end-user perspective that they have to provide their own notification source (ie NMI/SCI/MCE/IRQ). Cheers, Don > > 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