Loading arch/x86/kernel/cpu/mcheck/mce-apei.c +2 −2 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ int apei_write_mce(struct mce *m) rcd.hdr.revision = CPER_RECORD_REV; rcd.hdr.signature_end = CPER_SIG_END; rcd.hdr.section_count = 1; rcd.hdr.error_severity = CPER_SER_FATAL; rcd.hdr.error_severity = CPER_SEV_FATAL; /* timestamp, platform_id, partition_id are all invalid */ rcd.hdr.validation_bits = 0; rcd.hdr.record_length = sizeof(rcd); Loading @@ -96,7 +96,7 @@ int apei_write_mce(struct mce *m) rcd.sec_hdr.validation_bits = 0; rcd.sec_hdr.flags = CPER_SEC_PRIMARY; rcd.sec_hdr.section_type = CPER_SECTION_TYPE_MCE; rcd.sec_hdr.section_severity = CPER_SER_FATAL; rcd.sec_hdr.section_severity = CPER_SEV_FATAL; memcpy(&rcd.mce, m, sizeof(*m)); Loading drivers/acpi/apei/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -28,3 +28,12 @@ config ACPI_APEI_EINJ EINJ provides a hardware error injection mechanism, it is mainly used for debugging and testing the other parts of APEI and some other RAS features. config ACPI_APEI_ERST_DEBUG tristate "APEI Error Record Serialization Table (ERST) Debug Support" depends on ACPI_APEI help ERST is a way provided by APEI to save and retrieve hardware error infomation to and from a persistent store. Enable this if you want to debugging and testing the ERST kernel support and firmware implementation. drivers/acpi/apei/Makefile +1 −0 Original line number Diff line number Diff line 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 apei-y := apei-base.o hest.o cper.o erst.o drivers/acpi/apei/apei-base.c +2 −2 Original line number Diff line number Diff line Loading @@ -482,14 +482,14 @@ int apei_resources_request(struct apei_resources *resources, list_for_each_entry(res, &resources->ioport, list) { if (res == res_bak) break; release_mem_region(res->start, res->end - res->start); release_region(res->start, res->end - res->start); } res_bak = NULL; err_unmap_iomem: list_for_each_entry(res, &resources->iomem, list) { if (res == res_bak) break; release_region(res->start, res->end - res->start); release_mem_region(res->start, res->end - res->start); } return -EINVAL; } Loading drivers/acpi/apei/erst-dbg.c 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * APEI Error Record Serialization Table debug support * * ERST is a way provided by APEI to save and retrieve hardware error * infomation to and from a persistent store. This file provide the * debugging/testing support for ERST kernel support and firmware * implementation. * * Copyright 2010 Intel Corp. * Author: Huang Ying <ying.huang@intel.com> * * 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 <acpi/apei.h> #include <linux/miscdevice.h> #include "apei-internal.h" #define ERST_DBG_PFX "ERST DBG: " #define ERST_DBG_RECORD_LEN_MAX 4096 static void *erst_dbg_buf; static unsigned int erst_dbg_buf_len; /* Prevent erst_dbg_read/write from being invoked concurrently */ static DEFINE_MUTEX(erst_dbg_mutex); static int erst_dbg_open(struct inode *inode, struct file *file) { if (erst_disable) return -ENODEV; return nonseekable_open(inode, file); } static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { int rc; u64 record_id; u32 record_count; switch (cmd) { case APEI_ERST_CLEAR_RECORD: rc = copy_from_user(&record_id, (void __user *)arg, sizeof(record_id)); if (rc) return -EFAULT; return erst_clear(record_id); case APEI_ERST_GET_RECORD_COUNT: rc = erst_get_record_count(); if (rc < 0) return rc; record_count = rc; rc = put_user(record_count, (u32 __user *)arg); if (rc) return rc; return 0; default: return -ENOTTY; } } static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off) { int rc; ssize_t len = 0; u64 id; if (*off != 0) return -EINVAL; if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) return -EINTR; retry_next: rc = erst_get_next_record_id(&id); if (rc) goto out; /* no more record */ if (id == APEI_ERST_INVALID_RECORD_ID) goto out; retry: rc = len = erst_read(id, erst_dbg_buf, erst_dbg_buf_len); /* The record may be cleared by others, try read next record */ if (rc == -ENOENT) goto retry_next; if (rc < 0) goto out; if (len > ERST_DBG_RECORD_LEN_MAX) { pr_warning(ERST_DBG_PFX "Record (ID: 0x%llx) length is too long: %zd\n", id, len); rc = -EIO; goto out; } if (len > erst_dbg_buf_len) { kfree(erst_dbg_buf); rc = -ENOMEM; erst_dbg_buf = kmalloc(len, GFP_KERNEL); if (!erst_dbg_buf) goto out; erst_dbg_buf_len = len; goto retry; } rc = -EINVAL; if (len > usize) goto out; rc = -EFAULT; if (copy_to_user(ubuf, erst_dbg_buf, len)) goto out; rc = 0; out: mutex_unlock(&erst_dbg_mutex); return rc ? rc : len; } static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf, size_t usize, loff_t *off) { int rc; struct cper_record_header *rcd; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (usize > ERST_DBG_RECORD_LEN_MAX) { pr_err(ERST_DBG_PFX "Too long record to be written\n"); return -EINVAL; } if (mutex_lock_interruptible(&erst_dbg_mutex)) return -EINTR; if (usize > erst_dbg_buf_len) { kfree(erst_dbg_buf); rc = -ENOMEM; erst_dbg_buf = kmalloc(usize, GFP_KERNEL); if (!erst_dbg_buf) goto out; erst_dbg_buf_len = usize; } rc = copy_from_user(erst_dbg_buf, ubuf, usize); if (rc) { rc = -EFAULT; goto out; } rcd = erst_dbg_buf; rc = -EINVAL; if (rcd->record_length != usize) goto out; rc = erst_write(erst_dbg_buf); out: mutex_unlock(&erst_dbg_mutex); return rc < 0 ? rc : usize; } static const struct file_operations erst_dbg_ops = { .owner = THIS_MODULE, .open = erst_dbg_open, .read = erst_dbg_read, .write = erst_dbg_write, .unlocked_ioctl = erst_dbg_ioctl, }; static struct miscdevice erst_dbg_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "erst_dbg", .fops = &erst_dbg_ops, }; static __init int erst_dbg_init(void) { return misc_register(&erst_dbg_dev); } static __exit void erst_dbg_exit(void) { misc_deregister(&erst_dbg_dev); kfree(erst_dbg_buf); } module_init(erst_dbg_init); module_exit(erst_dbg_exit); MODULE_AUTHOR("Huang Ying"); MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support"); MODULE_LICENSE("GPL"); Loading
arch/x86/kernel/cpu/mcheck/mce-apei.c +2 −2 Original line number Diff line number Diff line Loading @@ -80,7 +80,7 @@ int apei_write_mce(struct mce *m) rcd.hdr.revision = CPER_RECORD_REV; rcd.hdr.signature_end = CPER_SIG_END; rcd.hdr.section_count = 1; rcd.hdr.error_severity = CPER_SER_FATAL; rcd.hdr.error_severity = CPER_SEV_FATAL; /* timestamp, platform_id, partition_id are all invalid */ rcd.hdr.validation_bits = 0; rcd.hdr.record_length = sizeof(rcd); Loading @@ -96,7 +96,7 @@ int apei_write_mce(struct mce *m) rcd.sec_hdr.validation_bits = 0; rcd.sec_hdr.flags = CPER_SEC_PRIMARY; rcd.sec_hdr.section_type = CPER_SECTION_TYPE_MCE; rcd.sec_hdr.section_severity = CPER_SER_FATAL; rcd.sec_hdr.section_severity = CPER_SEV_FATAL; memcpy(&rcd.mce, m, sizeof(*m)); Loading
drivers/acpi/apei/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -28,3 +28,12 @@ config ACPI_APEI_EINJ EINJ provides a hardware error injection mechanism, it is mainly used for debugging and testing the other parts of APEI and some other RAS features. config ACPI_APEI_ERST_DEBUG tristate "APEI Error Record Serialization Table (ERST) Debug Support" depends on ACPI_APEI help ERST is a way provided by APEI to save and retrieve hardware error infomation to and from a persistent store. Enable this if you want to debugging and testing the ERST kernel support and firmware implementation.
drivers/acpi/apei/Makefile +1 −0 Original line number Diff line number Diff line 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 apei-y := apei-base.o hest.o cper.o erst.o
drivers/acpi/apei/apei-base.c +2 −2 Original line number Diff line number Diff line Loading @@ -482,14 +482,14 @@ int apei_resources_request(struct apei_resources *resources, list_for_each_entry(res, &resources->ioport, list) { if (res == res_bak) break; release_mem_region(res->start, res->end - res->start); release_region(res->start, res->end - res->start); } res_bak = NULL; err_unmap_iomem: list_for_each_entry(res, &resources->iomem, list) { if (res == res_bak) break; release_region(res->start, res->end - res->start); release_mem_region(res->start, res->end - res->start); } return -EINVAL; } Loading
drivers/acpi/apei/erst-dbg.c 0 → 100644 +207 −0 Original line number Diff line number Diff line /* * APEI Error Record Serialization Table debug support * * ERST is a way provided by APEI to save and retrieve hardware error * infomation to and from a persistent store. This file provide the * debugging/testing support for ERST kernel support and firmware * implementation. * * Copyright 2010 Intel Corp. * Author: Huang Ying <ying.huang@intel.com> * * 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 <acpi/apei.h> #include <linux/miscdevice.h> #include "apei-internal.h" #define ERST_DBG_PFX "ERST DBG: " #define ERST_DBG_RECORD_LEN_MAX 4096 static void *erst_dbg_buf; static unsigned int erst_dbg_buf_len; /* Prevent erst_dbg_read/write from being invoked concurrently */ static DEFINE_MUTEX(erst_dbg_mutex); static int erst_dbg_open(struct inode *inode, struct file *file) { if (erst_disable) return -ENODEV; return nonseekable_open(inode, file); } static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { int rc; u64 record_id; u32 record_count; switch (cmd) { case APEI_ERST_CLEAR_RECORD: rc = copy_from_user(&record_id, (void __user *)arg, sizeof(record_id)); if (rc) return -EFAULT; return erst_clear(record_id); case APEI_ERST_GET_RECORD_COUNT: rc = erst_get_record_count(); if (rc < 0) return rc; record_count = rc; rc = put_user(record_count, (u32 __user *)arg); if (rc) return rc; return 0; default: return -ENOTTY; } } static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off) { int rc; ssize_t len = 0; u64 id; if (*off != 0) return -EINVAL; if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) return -EINTR; retry_next: rc = erst_get_next_record_id(&id); if (rc) goto out; /* no more record */ if (id == APEI_ERST_INVALID_RECORD_ID) goto out; retry: rc = len = erst_read(id, erst_dbg_buf, erst_dbg_buf_len); /* The record may be cleared by others, try read next record */ if (rc == -ENOENT) goto retry_next; if (rc < 0) goto out; if (len > ERST_DBG_RECORD_LEN_MAX) { pr_warning(ERST_DBG_PFX "Record (ID: 0x%llx) length is too long: %zd\n", id, len); rc = -EIO; goto out; } if (len > erst_dbg_buf_len) { kfree(erst_dbg_buf); rc = -ENOMEM; erst_dbg_buf = kmalloc(len, GFP_KERNEL); if (!erst_dbg_buf) goto out; erst_dbg_buf_len = len; goto retry; } rc = -EINVAL; if (len > usize) goto out; rc = -EFAULT; if (copy_to_user(ubuf, erst_dbg_buf, len)) goto out; rc = 0; out: mutex_unlock(&erst_dbg_mutex); return rc ? rc : len; } static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf, size_t usize, loff_t *off) { int rc; struct cper_record_header *rcd; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (usize > ERST_DBG_RECORD_LEN_MAX) { pr_err(ERST_DBG_PFX "Too long record to be written\n"); return -EINVAL; } if (mutex_lock_interruptible(&erst_dbg_mutex)) return -EINTR; if (usize > erst_dbg_buf_len) { kfree(erst_dbg_buf); rc = -ENOMEM; erst_dbg_buf = kmalloc(usize, GFP_KERNEL); if (!erst_dbg_buf) goto out; erst_dbg_buf_len = usize; } rc = copy_from_user(erst_dbg_buf, ubuf, usize); if (rc) { rc = -EFAULT; goto out; } rcd = erst_dbg_buf; rc = -EINVAL; if (rcd->record_length != usize) goto out; rc = erst_write(erst_dbg_buf); out: mutex_unlock(&erst_dbg_mutex); return rc < 0 ? rc : usize; } static const struct file_operations erst_dbg_ops = { .owner = THIS_MODULE, .open = erst_dbg_open, .read = erst_dbg_read, .write = erst_dbg_write, .unlocked_ioctl = erst_dbg_ioctl, }; static struct miscdevice erst_dbg_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "erst_dbg", .fops = &erst_dbg_ops, }; static __init int erst_dbg_init(void) { return misc_register(&erst_dbg_dev); } static __exit void erst_dbg_exit(void) { misc_deregister(&erst_dbg_dev); kfree(erst_dbg_buf); } module_init(erst_dbg_init); module_exit(erst_dbg_exit); MODULE_AUTHOR("Huang Ying"); MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support"); MODULE_LICENSE("GPL");