Skip to content

Commit

Permalink
Merge branch 'apei' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
lenb committed Aug 15, 2010
2 parents f2a6618 + 2ff729d commit feb29c5
Show file tree
Hide file tree
Showing 8 changed files with 386 additions and 95 deletions.
4 changes: 2 additions & 2 deletions arch/x86/kernel/cpu/mcheck/mce-apei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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));

Expand Down
9 changes: 9 additions & 0 deletions drivers/acpi/apei/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions drivers/acpi/apei/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,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

apei-y := apei-base.o hest.o cper.o erst.o
4 changes: 2 additions & 2 deletions drivers/acpi/apei/apei-base.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
207 changes: 207 additions & 0 deletions drivers/acpi/apei/erst-dbg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* 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 <[email protected]>
*
* 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

0 comments on commit feb29c5

Please sign in to comment.