Implement APEI ERST feature to Xen
APEI are ACPI4.0 new features. It consists of ERST, BERT, HEST, and EINJ.
ERST is used to save fault error log to a platform persistent storage,
so that when reboot os can retrieve the error log and handle it.
This patch is used to implement ERST feature to Xen.
It consists of 3-level hierarchy: operation level, action level, and
instruction level.
Instruction do basic io; Action done by sequential instructions parsed from
ACPI ERST table;
Operation done by sequential actions defined by ACPI spec, providing
erst_write/ erst_read/ erst_clear
interfaces to MCE/ NMI/ PCIe error handling mechanism, etc.
Signed-off-by: Liu, Jinsong <jinsong.liu@xxxxxxxxx>
b/xen/drivers/acpi/apei/Makefile | 3
b/xen/drivers/acpi/apei/apei-base.c | 278 +++++++++++
b/xen/drivers/acpi/apei/apei-internal.h | 73 ++
b/xen/drivers/acpi/apei/apei-io.c | 328 +++++++++++++
b/xen/drivers/acpi/apei/erst.c | 787 ++++++++++++++++++++++++++++++++
b/xen/include/acpi/apei.h | 31 +
b/xen/include/xen/cper.h | 80 +++
xen/arch/x86/acpi/boot.c | 2
xen/drivers/acpi/Makefile | 1
xen/include/acpi/actbl1.h | 103 ++++
xen/include/asm-x86/fixmap.h | 3
xen/include/xen/acpi.h | 1
xen/include/xen/spinlock.h | 7
13 files changed, 1697 insertions(+)
diff -r b622e411eef8 xen/arch/x86/acpi/boot.c
--- a/xen/arch/x86/acpi/boot.c Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/arch/x86/acpi/boot.c Fri Aug 20 17:38:07 2010 +0800
@@ -938,6 +938,8 @@ int __init acpi_boot_init(void)
acpi_mmcfg_init();
+ erst_init();
+
return 0;
}
diff -r b622e411eef8 xen/drivers/acpi/Makefile
--- a/xen/drivers/acpi/Makefile Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/drivers/acpi/Makefile Fri Aug 20 17:38:07 2010 +0800
@@ -1,5 +1,6 @@ subdir-y += tables
subdir-y += tables
subdir-y += utilities
+subdir-y += apei
obj-y += tables.o
obj-y += numa.o
diff -r b622e411eef8 xen/drivers/acpi/apei/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/Makefile Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,3 @@
+obj-y += erst.o
+obj-y += apei-base.o
+obj-y += apei-io.o
diff -r b622e411eef8 xen/drivers/acpi/apei/apei-base.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/apei-base.c Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,278 @@
+/*
+ * apei-base.c - ACPI Platform Error Interface (APEI) supporting
+ * infrastructure
+ *
+ * APEI allows to report errors (for example from the chipset) to the
+ * the operating system. This improves NMI handling especially. In
+ * addition it supports error serialization and error injection.
+ *
+ * For more information about APEI, please refer to ACPI Specification
+ * version 4.0, chapter 17.
+ *
+ * This file has Common functions used by more than one APEI table,
+ * including framework of interpreter for ERST and EINJ; resource
+ * management for APEI registers.
+ *
+ * This feature is ported from linux acpi tree
+ * Copyright (C) 2009, Intel Corp.
+ * Author: Huang Ying <ying.huang@xxxxxxxxx>
+ * Ported by: Liu, Jinsong <jinsong.liu@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 <xen/kernel.h>
+#include <xen/errno.h>
+#include <xen/delay.h>
+#include <xen/string.h>
+#include <xen/types.h>
+#include <xen/spinlock.h>
+#include <xen/cper.h>
+#include <asm/io.h>
+#include <acpi/acpi.h>
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+/*
+ * APEI ERST (Error Record Serialization Table) and EINJ (Error
+ * INJection) interpreter framework.
+ */
+
+#define APEI_EXEC_PRESERVE_REGISTER 0x1
+
+int apei_exec_ctx_init(struct apei_exec_context *ctx,
+ struct apei_exec_ins_type *ins_table,
+ u32 instructions,
+ struct acpi_whea_header *action_table,
+ u32 entries)
+{
+ if (!ctx)
+ return -EINVAL;
+
+ ctx->ins_table = ins_table;
+ ctx->instructions = instructions;
+ ctx->action_table = action_table;
+ ctx->entries = entries;
+ return 0;
+}
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val)
+{
+ int rc;
+
+ rc = apei_read(val, &entry->register_region);
+ if (rc)
+ return rc;
+ *val >>= entry->register_region.bit_offset;
+ *val &= entry->mask;
+
+ return 0;
+}
+
+int apei_exec_read_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val = 0;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ ctx->value = val;
+
+ return 0;
+}
+
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+
+ rc = apei_exec_read_register(ctx, entry);
+ if (rc)
+ return rc;
+ ctx->value = (ctx->value == entry->value);
+
+ return 0;
+}
+
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val)
+{
+ int rc;
+
+ val &= entry->mask;
+ val <<= entry->register_region.bit_offset;
+ if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) {
+ u64 valr = 0;
+ rc = apei_read(&valr, &entry->register_region);
+ if (rc)
+ return rc;
+ valr &= ~(entry->mask << entry->register_region.bit_offset);
+ val |= valr;
+ }
+ rc = apei_write(val, &entry->register_region);
+
+ return rc;
+}
+
+int apei_exec_write_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_write_register(entry, ctx->value);
+}
+
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+
+ ctx->value = entry->value;
+ rc = apei_exec_write_register(ctx, entry);
+
+ return rc;
+}
+
+int apei_exec_noop(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return 0;
+}
+
+/*
+ * Interpret the specified action. Go through whole action table,
+ * execute all instructions belong to the action.
+ */
+int apei_exec_run(struct apei_exec_context *ctx, u8 action)
+{
+ int rc;
+ u32 i, ip;
+ struct acpi_whea_header *entry;
+ apei_exec_ins_func_t run;
+
+ ctx->ip = 0;
+
+ /*
+ * "ip" is the instruction pointer of current instruction,
+ * "ctx->ip" specifies the next instruction to executed,
+ * instruction "run" function may change the "ctx->ip" to
+ * implement "goto" semantics.
+ */
+rewind:
+ ip = 0;
+ for (i = 0; i < ctx->entries; i++) {
+ entry = &ctx->action_table[i];
+ if (entry->action != action)
+ continue;
+ if (ip == ctx->ip) {
+ if (entry->instruction >= ctx->instructions ||
+ !ctx->ins_table[entry->instruction].run) {
+ printk(KERN_WARNING
+ "Invalid action table, unknown instruction "
+ "type: %d\n", entry->instruction);
+ return -EINVAL;
+ }
+ run = ctx->ins_table[entry->instruction].run;
+ rc = run(ctx, entry);
+ if (rc < 0)
+ return rc;
+ else if (rc != APEI_EXEC_SET_IP)
+ ctx->ip++;
+ }
+ ip++;
+ if (ctx->ip < ip)
+ goto rewind;
+ }
+
+ return 0;
+}
+
+typedef int (*apei_exec_entry_func_t)(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data);
+
+static int apei_exec_for_each_entry(struct apei_exec_context *ctx,
+ apei_exec_entry_func_t func,
+ void *data,
+ int *end)
+{
+ u8 ins;
+ int i, rc;
+ struct acpi_whea_header *entry;
+ struct apei_exec_ins_type *ins_table = ctx->ins_table;
+
+ for (i = 0; i < ctx->entries; i++) {
+ entry = ctx->action_table + i;
+ ins = entry->instruction;
+ if (end)
+ *end = i;
+ if (ins >= ctx->instructions || !ins_table[ins].run) {
+ printk(KERN_WARNING "Invalid action table, "
+ "unknown instruction type: %d\n", ins);
+ return -EINVAL;
+ }
+ rc = func(ctx, entry, data);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static int pre_map_gar_callback(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data)
+{
+ u8 ins = entry->instruction;
+
+ if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+ return apei_pre_map_gar(&entry->register_region);
+
+ return 0;
+}
+
+/* Pre-map all GARs in action table. */
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx)
+{
+ int rc, end;
+
+ rc = apei_exec_for_each_entry(ctx, pre_map_gar_callback,
+ NULL, &end);
+ if (rc) {
+ struct apei_exec_context ctx_unmap;
+ memcpy(&ctx_unmap, ctx, sizeof(*ctx));
+ ctx_unmap.entries = end;
+ apei_exec_post_unmap_gars(&ctx_unmap);
+ }
+
+ return rc;
+}
+
+static int post_unmap_gar_callback(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry,
+ void *data)
+{
+ u8 ins = entry->instruction;
+
+ if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
+ apei_post_unmap_gar(&entry->register_region);
+
+ return 0;
+}
+
+/* Post-unmap all GAR in action table. */
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx)
+{
+ return apei_exec_for_each_entry(ctx, post_unmap_gar_callback,
+ NULL, NULL);
+}
diff -r b622e411eef8 xen/drivers/acpi/apei/apei-internal.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/apei-internal.h Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,73 @@
+/*
+ * apei-internal.h - ACPI Platform Error Interface internal
+ * definations.
+ */
+
+#ifndef APEI_INTERNAL_H
+#define APEI_INTERNAL_H
+
+struct apei_exec_context;
+
+typedef int (*apei_exec_ins_func_t)(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+
+#define APEI_EXEC_INS_ACCESS_REGISTER 0x0001
+
+struct apei_exec_ins_type {
+ u32 flags;
+ apei_exec_ins_func_t run;
+};
+
+struct apei_exec_context {
+ u32 ip;
+ u64 value;
+ u64 var1;
+ u64 var2;
+ u64 src_base;
+ u64 dst_base;
+ struct apei_exec_ins_type *ins_table;
+ u32 instructions;
+ struct acpi_whea_header *action_table;
+ u32 entries;
+};
+
+int apei_exec_ctx_init(struct apei_exec_context *ctx,
+ struct apei_exec_ins_type *ins_table,
+ u32 instructions,
+ struct acpi_whea_header *action_table,
+ u32 entries);
+
+static inline void apei_exec_ctx_set_input(struct apei_exec_context *ctx,
+ u64 input)
+{
+ ctx->value = input;
+}
+
+static inline u64 apei_exec_ctx_get_output(struct apei_exec_context *ctx)
+{
+ return ctx->value;
+}
+
+int apei_exec_run(struct apei_exec_context *ctx, u8 action);
+
+/* Common instruction implementation */
+
+/* IP has been set in instruction function */
+#define APEI_EXEC_SET_IP 1
+
+int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val);
+int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val);
+int apei_exec_read_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_read_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_write_register(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_write_register_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_noop(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry);
+int apei_exec_pre_map_gars(struct apei_exec_context *ctx);
+int apei_exec_post_unmap_gars(struct apei_exec_context *ctx);
+
+#endif
diff -r b622e411eef8 xen/drivers/acpi/apei/apei-io.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/apei-io.c Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,328 @@
+/*
+ * apei-io.c - APEI IO memory pre-mapping/post-unmapping and access
+ *
+ * Copyright (C) 2009-2010, Intel Corp.
+ * Author: Huang Ying <ying.huang@xxxxxxxxx>
+ * Ported by: Liu, Jinsong <jinsong.liu@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 <xen/kernel.h>
+#include <xen/errno.h>
+#include <xen/delay.h>
+#include <xen/string.h>
+#include <xen/xmalloc.h>
+#include <xen/types.h>
+#include <xen/spinlock.h>
+#include <xen/list.h>
+#include <xen/cper.h>
+#include <xen/prefetch.h>
+#include <asm/fixmap.h>
+#include <asm/io.h>
+#include <acpi/acpi.h>
+#include <acpi/apei.h>
+
+static LIST_HEAD(apei_iomaps);
+/*
+ * Used for mutual exclusion between writers of apei_iomaps list, for
+ * synchronization between readers and writer.
+ */
+static DEFINE_SPINLOCK(apei_iomaps_lock);
+
+struct apei_iomap {
+ struct list_head list;
+ void __iomem *vaddr;
+ unsigned long size;
+ paddr_t paddr;
+};
+
+static struct apei_iomap *__apei_find_iomap(paddr_t paddr,
+ unsigned long size)
+{
+ struct apei_iomap *map;
+
+ list_for_each_entry(map, &apei_iomaps, list) {
+ if (map->paddr + map->size >= paddr + size &&
+ map->paddr <= paddr)
+ return map;
+ }
+ return NULL;
+}
+
+static void __iomem *__apei_ioremap_fast(paddr_t paddr,
+ unsigned long size)
+{
+ struct apei_iomap *map;
+
+ map = __apei_find_iomap(paddr, size);
+ if (map)
+ return map->vaddr + (paddr - map->paddr);
+ else
+ return NULL;
+}
+
+static int apei_range_nr;
+
+static void __iomem *apei_range_map(paddr_t paddr, unsigned long size)
+{
+ int i, pg;
+ int start_nr, cur_nr;
+
+ pg = ((((paddr + size -1) & PAGE_MASK)
+ - (paddr & PAGE_MASK)) >> PAGE_SHIFT) + 1;
+ if (apei_range_nr + pg > FIX_APEI_RANGE_MAX)
+ return NULL;
+
+ start_nr = apei_range_nr + pg -1;
+ for (i = 0; i < pg; i++) {
+ cur_nr = start_nr - i;
+ set_fixmap_nocache(FIX_APEI_RANGE_BASE + cur_nr,
+ paddr + (i << PAGE_SHIFT));
+ apei_range_nr++;
+ }
+
+ return (void __iomem *)fix_to_virt(FIX_APEI_RANGE_BASE + start_nr);
+}
+
+/*
+ * Used to pre-map the specified IO memory area. First try to find
+ * whether the area is already pre-mapped, if it is, return; otherwise,
+ * do the real map, and add the mapping into apei_iomaps list.
+ */
+void __iomem *apei_pre_map(paddr_t paddr, unsigned long size)
+{
+ void __iomem *vaddr;
+ struct apei_iomap *map;
+ unsigned long flags;
+
+ spin_lock_irqsave(&apei_iomaps_lock, flags);
+ vaddr = __apei_ioremap_fast(paddr, size);
+ spin_unlock_irqrestore(&apei_iomaps_lock, flags);
+ if (vaddr)
+ return vaddr;
+
+ map = xmalloc(struct apei_iomap);
+ if (!map)
+ return NULL;
+
+ vaddr = apei_range_map(paddr, size);
+ if (!vaddr) {
+ xfree(map);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&map->list);
+ map->paddr = paddr & PAGE_MASK;
+ map->size = (((paddr + size + PAGE_SIZE -1) & PAGE_MASK)
+ - (paddr & PAGE_MASK));
+ map->vaddr = vaddr;
+
+ spin_lock_irqsave(&apei_iomaps_lock, flags);
+ list_add_tail(&map->list, &apei_iomaps);
+ spin_unlock_irqrestore(&apei_iomaps_lock, flags);
+
+ return map->vaddr + (paddr - map->paddr);
+}
+
+/*
+ * Used to post-unmap the specified IO memory area.
+ */
+static void apei_post_unmap(paddr_t paddr, unsigned long size)
+{
+ struct apei_iomap *map;
+ unsigned long flags;
+
+ spin_lock_irqsave(&apei_iomaps_lock, flags);
+ map = __apei_find_iomap(paddr, size);
+ if (!map);
+ return;
+
+ list_del(&map->list);
+ spin_unlock_irqrestore(&apei_iomaps_lock, flags);
+
+ xfree(map);
+}
+
+/* In NMI handler, should set silent = 1 */
+static int apei_check_gar(struct acpi_generic_address *reg,
+ u64 *paddr, int silent)
+{
+ u32 width, space_id;
+
+ width = reg->bit_width;
+ space_id = reg->space_id;
+ /* Handle possible alignment issues */
+ memcpy(paddr, ®->address, sizeof(*paddr));
+ if (!*paddr) {
+ if (!silent)
+ printk(KERN_WARNING
+ "Invalid physical address in GAR\n");
+ return -EINVAL;
+ }
+
+ if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
+ if (!silent)
+ printk(KERN_WARNING
+ "Invalid bit width in GAR\n");
+ return -EINVAL;
+ }
+
+ if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+ space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
+ if (!silent)
+ printk(KERN_WARNING
+ "Invalid address space type in GAR\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Pre-map, working on GAR */
+int apei_pre_map_gar(struct acpi_generic_address *reg)
+{
+ u64 paddr;
+ void __iomem *vaddr;
+ int rc;
+
+ if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return 0;
+
+ rc = apei_check_gar(reg, &paddr, 0);
+ if (rc)
+ return rc;
+
+ vaddr = apei_pre_map(paddr, reg->bit_width / 8);
+ if (!vaddr)
+ return -EIO;
+
+ return 0;
+}
+
+/* Post-unmap, working on GAR */
+int apei_post_unmap_gar(struct acpi_generic_address *reg)
+{
+ u64 paddr;
+ int rc;
+
+ if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ return 0;
+
+ rc = apei_check_gar(reg, &paddr, 0);
+ if (rc)
+ return rc;
+
+ apei_post_unmap(paddr, reg->bit_width / 8);
+
+ return 0;
+}
+
+static int apei_read_mem(u64 paddr, u64 *val, u32 width)
+{
+ void __iomem *addr;
+ u64 tmpval;
+
+ addr = __apei_ioremap_fast(paddr, width);
+ switch (width) {
+ case 8:
+ *val = readb(addr);
+ break;
+ case 16:
+ *val = readw(addr);
+ break;
+ case 32:
+ *val = readl(addr);
+ break;
+ case 64:
+ tmpval = (u64)readl(addr);
+ tmpval |= ((u64)readl(addr+4)) << 32;
+ *val = tmpval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int apei_write_mem(u64 paddr, u64 val, u32 width)
+{
+ void __iomem *addr;
+ u32 tmpval;
+
+ addr = __apei_ioremap_fast(paddr, width);
+ switch (width) {
+ case 8:
+ writeb(val, addr);
+ break;
+ case 16:
+ writew(val, addr);
+ break;
+ case 32:
+ writel(val, addr);
+ break;
+ case 64:
+ tmpval = (u32)val;
+ writel(tmpval, addr);
+ tmpval = (u32)(val >> 32);
+ writel(tmpval, addr+4);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int apei_read(u64 *val, struct acpi_generic_address *reg)
+{
+ u64 paddr;
+ int rc;
+
+ rc = apei_check_gar(reg, &paddr, 1);
+ if (rc)
+ return rc;
+
+ *val = 0;
+
+ /* currently all erst implementation take bit_width as real range */
+ switch (reg->space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ return apei_read_mem(paddr, val, reg->bit_width);
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
+ default:
+ return -EINVAL;
+ }
+}
+
+int apei_write(u64 val, struct acpi_generic_address *reg)
+{
+ u64 paddr;
+ int rc;
+
+ rc = apei_check_gar(reg, &paddr, 1);
+ if (rc)
+ return rc;
+
+ switch (reg->space_id) {
+ case ACPI_ADR_SPACE_SYSTEM_MEMORY:
+ return apei_write_mem(paddr, val, reg->bit_width);
+ case ACPI_ADR_SPACE_SYSTEM_IO:
+ return acpi_os_write_port(paddr, val, reg->bit_width);
+ default:
+ return -EINVAL;
+ }
+}
diff -r b622e411eef8 xen/drivers/acpi/apei/erst.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/erst.c Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,787 @@
+/*
+ * APEI Error Record Serialization Table support
+ *
+ * ERST is a way provided by APEI to save and retrieve hardware error
+ * infomation to and from a persistent store.
+ *
+ * For more information about ERST, please refer to ACPI Specification
+ * version 4.0, section 17.4.
+ *
+ * This feature is ported from linux acpi tree
+ * Copyright 2010 Intel Corp.
+ * Author: Huang Ying <ying.huang@xxxxxxxxx>
+ * Ported by: Liu, Jinsong <jinsong.liu@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 <xen/kernel.h>
+#include <xen/errno.h>
+#include <xen/delay.h>
+#include <xen/string.h>
+#include <xen/types.h>
+#include <xen/spinlock.h>
+#include <xen/cper.h>
+#include <asm/fixmap.h>
+#include <asm/io.h>
+#include <acpi/acpi.h>
+#include <acpi/apei.h>
+
+#include "apei-internal.h"
+
+/* ERST command status */
+#define ERST_STATUS_SUCCESS 0x0
+#define ERST_STATUS_NOT_ENOUGH_SPACE 0x1
+#define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2
+#define ERST_STATUS_FAILED 0x3
+#define ERST_STATUS_RECORD_STORE_EMPTY 0x4
+#define ERST_STATUS_RECORD_NOT_FOUND 0x5
+
+#define ERST_TAB_ENTRY(tab) \
+ ((struct acpi_whea_header *)((char *)(tab) + \
+ sizeof(struct acpi_table_erst)))
+
+#define SPIN_UNIT 1 /* 1us */
+/* Firmware should respond within 1 miliseconds */
+#define FIRMWARE_TIMEOUT (1 * 1000)
+#define FIRMWARE_MAX_STALL 50 /* 50us */
+
+static struct acpi_table_erst *erst_tab;
+static int erst_enabled;
+
+/* ERST Error Log Address Range atrributes */
+#define ERST_RANGE_RESERVED 0x0001
+#define ERST_RANGE_NVRAM 0x0002
+#define ERST_RANGE_SLOW 0x0004
+
+/*
+ * ERST Error Log Address Range, used as buffer for reading/writing
+ * error records.
+ */
+static struct erst_erange {
+ u64 base;
+ u64 size;
+ void __iomem *vaddr;
+ u32 attr;
+} erst_erange;
+
+/*
+ * Prevent ERST interpreter to run simultaneously, because the
+ * corresponding firmware implementation may not work properly when
+ * invoked simultaneously.
+ *
+ * It is used to provide exclusive accessing for ERST Error Log
+ * Address Range too.
+ */
+static DEFINE_SPINLOCK(erst_lock);
+
+static inline int erst_errno(int command_status)
+{
+ switch (command_status) {
+ case ERST_STATUS_SUCCESS:
+ return 0;
+ case ERST_STATUS_HARDWARE_NOT_AVAILABLE:
+ return -ENODEV;
+ case ERST_STATUS_NOT_ENOUGH_SPACE:
+ return -ENOSPC;
+ case ERST_STATUS_RECORD_STORE_EMPTY:
+ case ERST_STATUS_RECORD_NOT_FOUND:
+ return -ENOENT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int erst_timedout(u64 *t, u64 spin_unit)
+{
+ if ((s64)*t < spin_unit) {
+ printk(XENLOG_WARNING "Firmware does not respond in time\n");
+ return 1;
+ }
+ *t -= spin_unit;
+ udelay(spin_unit);
+ return 0;
+}
+
+static int erst_exec_load_var1(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->var1);
+}
+
+static int erst_exec_load_var2(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->var2);
+}
+
+static int erst_exec_store_var1(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_write_register(entry, ctx->var1);
+}
+
+static int erst_exec_add(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->var1 += ctx->var2;
+ return 0;
+}
+
+static int erst_exec_subtract(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->var1 -= ctx->var2;
+ return 0;
+}
+
+static int erst_exec_add_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ val += ctx->value;
+ rc = __apei_exec_write_register(entry, val);
+ return rc;
+}
+
+static int erst_exec_subtract_value(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ val -= ctx->value;
+ rc = __apei_exec_write_register(entry, val);
+ return rc;
+}
+
+static int erst_exec_stall(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ udelay((ctx->var1 > FIRMWARE_MAX_STALL) ?
+ FIRMWARE_MAX_STALL :
+ ctx->var1);
+ return 0;
+}
+
+static int erst_exec_stall_while_true(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 stall_time = (ctx->var1 > FIRMWARE_MAX_STALL) ?
+ FIRMWARE_MAX_STALL :
+ ctx->var1;
+
+ for (;;) {
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ if (val != ctx->value)
+ break;
+ if (erst_timedout(&timeout, stall_time))
+ return -EIO;
+ }
+ return 0;
+}
+
+static int erst_exec_skip_next_instruction_if_true(
+ struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 val;
+
+ rc = __apei_exec_read_register(entry, &val);
+ if (rc)
+ return rc;
+ if (val == ctx->value) {
+ ctx->ip += 2;
+ return APEI_EXEC_SET_IP;
+ }
+
+ return 0;
+}
+
+static int erst_exec_goto(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ ctx->ip = ctx->value;
+ return APEI_EXEC_SET_IP;
+}
+
+static int erst_exec_set_src_address_base(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->src_base);
+}
+
+static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ return __apei_exec_read_register(entry, &ctx->dst_base);
+}
+
+static int erst_exec_move_data(struct apei_exec_context *ctx,
+ struct acpi_whea_header *entry)
+{
+ int rc;
+ u64 offset;
+
+ rc = __apei_exec_read_register(entry, &offset);
+ if (rc)
+ return rc;
+ memmove((void *)ctx->dst_base + offset,
+ (void *)ctx->src_base + offset,
+ ctx->var2);
+
+ return 0;
+}
+
+static struct apei_exec_ins_type erst_ins_type[] = {
+ [ACPI_ERST_READ_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register,
+ },
+ [ACPI_ERST_READ_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_read_register_value,
+ },
+ [ACPI_ERST_WRITE_REGISTER] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register,
+ },
+ [ACPI_ERST_WRITE_REGISTER_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = apei_exec_write_register_value,
+ },
+ [ACPI_ERST_NOOP] = {
+ .flags = 0,
+ .run = apei_exec_noop,
+ },
+ [ACPI_ERST_LOAD_VAR1] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_load_var1,
+ },
+ [ACPI_ERST_LOAD_VAR2] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_load_var2,
+ },
+ [ACPI_ERST_STORE_VAR1] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_store_var1,
+ },
+ [ACPI_ERST_ADD] = {
+ .flags = 0,
+ .run = erst_exec_add,
+ },
+ [ACPI_ERST_SUBTRACT] = {
+ .flags = 0,
+ .run = erst_exec_subtract,
+ },
+ [ACPI_ERST_ADD_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_add_value,
+ },
+ [ACPI_ERST_SUBTRACT_VALUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_subtract_value,
+ },
+ [ACPI_ERST_STALL] = {
+ .flags = 0,
+ .run = erst_exec_stall,
+ },
+ [ACPI_ERST_STALL_WHILE_TRUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_stall_while_true,
+ },
+ [ACPI_ERST_SKIP_NEXT_IF_TRUE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_skip_next_instruction_if_true,
+ },
+ [ACPI_ERST_GOTO] = {
+ .flags = 0,
+ .run = erst_exec_goto,
+ },
+ [ACPI_ERST_SET_SRC_ADDRESS_BASE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_set_src_address_base,
+ },
+ [ACPI_ERST_SET_DST_ADDRESS_BASE] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_set_dst_address_base,
+ },
+ [ACPI_ERST_MOVE_DATA] = {
+ .flags = APEI_EXEC_INS_ACCESS_REGISTER,
+ .run = erst_exec_move_data,
+ },
+};
+
+static inline void erst_exec_ctx_init(struct apei_exec_context *ctx)
+{
+ apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type),
+ ERST_TAB_ENTRY(erst_tab), erst_tab->entries);
+}
+
+static int erst_get_erange(struct erst_erange *range)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE);
+ if (rc)
+ return rc;
+ range->base = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH);
+ if (rc)
+ return rc;
+ range->size = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES);
+ if (rc)
+ return rc;
+ range->attr = apei_exec_ctx_get_output(&ctx);
+
+ return 0;
+}
+
+static size_t __erst_get_record_count(void)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT);
+ if (rc)
+ return rc;
+ return apei_exec_ctx_get_output(&ctx);
+}
+
+size_t erst_get_record_count(void)
+{
+ size_t count;
+ unsigned long flags;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ spin_lock_irqsave(&erst_lock, flags);
+ count = __erst_get_record_count();
+ spin_unlock_irqrestore(&erst_lock, flags);
+
+ return count;
+}
+
+static int __erst_get_next_record_id(u64 *record_id)
+{
+ struct apei_exec_context ctx;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID);
+ if (rc)
+ return rc;
+ *record_id = apei_exec_ctx_get_output(&ctx);
+
+ return 0;
+}
+
+/*
+ * Get the record ID of an existing error record on the persistent
+ * storage. If there is no error record on the persistent storage, the
+ * returned record_id is APEI_ERST_INVALID_RECORD_ID.
+ */
+int erst_get_next_record_id(u64 *record_id)
+{
+ int rc;
+ unsigned long flags;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ spin_lock_irqsave(&erst_lock, flags);
+ rc = __erst_get_next_record_id(record_id);
+ spin_unlock_irqrestore(&erst_lock, flags);
+
+ return rc;
+}
+
+static int __erst_write_to_storage(u64 offset)
+{
+ struct apei_exec_context ctx;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, offset);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+static int __erst_read_from_storage(u64 record_id, u64 offset)
+{
+ struct apei_exec_context ctx;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, offset);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, record_id);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ };
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+static int __erst_clear_from_storage(u64 record_id)
+{
+ struct apei_exec_context ctx;
+ u64 timeout = FIRMWARE_TIMEOUT;
+ u64 val;
+ int rc;
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR);
+ if (rc)
+ return rc;
+ apei_exec_ctx_set_input(&ctx, record_id);
+ rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID);
+ if (rc)
+ return rc;
+ rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION);
+ if (rc)
+ return rc;
+ for (;;) {
+ rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ if (!val)
+ break;
+ if (erst_timedout(&timeout, SPIN_UNIT))
+ return -EIO;
+ }
+ rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS);
+ if (rc)
+ return rc;
+ val = apei_exec_ctx_get_output(&ctx);
+ rc = apei_exec_run(&ctx, ACPI_ERST_END);
+ if (rc)
+ return rc;
+
+ return erst_errno(val);
+}
+
+/* NVRAM ERST Error Log Address Range is not supported yet */
+static int __erst_write_to_nvram(const struct cper_record_header *record)
+{
+ /* do not print message, because printk is not safe for NMI */
+ return -ENOSYS;
+}
+
+static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset)
+{
+ printk(KERN_WARNING
+ "NVRAM ERST Log Address Range is not implemented yet\n");
+ return -ENOSYS;
+}
+
+static int __erst_clear_from_nvram(u64 record_id)
+{
+ printk(KERN_WARNING
+ "NVRAM ERST Log Address Range is not implemented yet\n");
+ return -ENOSYS;
+}
+
+int erst_write(const struct cper_record_header *record)
+{
+ int rc;
+ unsigned long flags;
+ struct cper_record_header *rcd_erange;
+
+ if (!record)
+ return -EINVAL;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE))
+ return -EINVAL;
+
+ if (erst_erange.attr & ERST_RANGE_NVRAM) {
+ if (!spin_trylock_irqsave(&erst_lock, flags))
+ return -EBUSY;
+ rc = __erst_write_to_nvram(record);
+ spin_unlock_irqrestore(&erst_lock, flags);
+ return rc;
+ }
+
+ if (record->record_length > erst_erange.size)
+ return -EINVAL;
+
+ if (!spin_trylock_irqsave(&erst_lock, flags))
+ return -EBUSY;
+ memcpy(erst_erange.vaddr, record, record->record_length);
+ rcd_erange = erst_erange.vaddr;
+ /* signature for serialization system */
+ memcpy(&rcd_erange->persistence_information, "ER", 2);
+
+ rc = __erst_write_to_storage(0);
+ spin_unlock_irqrestore(&erst_lock, flags);
+
+ return rc;
+}
+
+static int __erst_read_to_erange(u64 record_id, u64 *offset)
+{
+ int rc;
+
+ if (erst_erange.attr & ERST_RANGE_NVRAM)
+ return __erst_read_to_erange_from_nvram(
+ record_id, offset);
+
+ rc = __erst_read_from_storage(record_id, 0);
+ if (rc)
+ return rc;
+ *offset = 0;
+
+ return 0;
+}
+
+static size_t __erst_read(u64 record_id, struct cper_record_header *record,
+ size_t buflen)
+{
+ int rc;
+ u64 offset, len = 0;
+ struct cper_record_header *rcd_tmp;
+
+ rc = __erst_read_to_erange(record_id, &offset);
+ if (rc)
+ return rc;
+ rcd_tmp = erst_erange.vaddr + offset;
+ len = rcd_tmp->record_length;
+ if (len <= buflen)
+ memcpy(record, rcd_tmp, len);
+
+ return len;
+}
+
+/*
+ * If return value > buflen, the buffer size is not big enough,
+ * else if return value < 0, something goes wrong,
+ * else everything is OK, and return value is record length
+ */
+size_t erst_read(u64 record_id, struct cper_record_header *record,
+ size_t buflen)
+{
+ size_t len;
+ unsigned long flags;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ spin_lock_irqsave(&erst_lock, flags);
+ len = __erst_read(record_id, record, buflen);
+ spin_unlock_irqrestore(&erst_lock, flags);
+ return len;
+}
+
+/*
+ * If return value > buflen, the buffer size is not big enough,
+ * else if return value = 0, there is no more record to read,
+ * else if return value < 0, something goes wrong,
+ * else everything is OK, and return value is record length
+ */
+size_t erst_read_next(struct cper_record_header *record, size_t buflen)
+{
+ int rc;
+ size_t len;
+ unsigned long flags;
+ u64 record_id;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ spin_lock_irqsave(&erst_lock, flags);
+ rc = __erst_get_next_record_id(&record_id);
+ if (rc) {
+ spin_unlock_irqrestore(&erst_lock, flags);
+ return rc;
+ }
+ /* no more record */
+ if (record_id == APEI_ERST_INVALID_RECORD_ID) {
+ spin_unlock_irqrestore(&erst_lock, flags);
+ return 0;
+ }
+
+ len = __erst_read(record_id, record, buflen);
+ spin_unlock_irqrestore(&erst_lock, flags);
+
+ return len;
+}
+
+int erst_clear(u64 record_id)
+{
+ int rc;
+ unsigned long flags;
+
+ if (!erst_enabled)
+ return -ENODEV;
+
+ spin_lock_irqsave(&erst_lock, flags);
+ if (erst_erange.attr & ERST_RANGE_NVRAM)
+ rc = __erst_clear_from_nvram(record_id);
+ else
+ rc = __erst_clear_from_storage(record_id);
+ spin_unlock_irqrestore(&erst_lock, flags);
+
+ return rc;
+}
+
+static int erst_check_table(struct acpi_table_erst *erst_tab)
+{
+ if (erst_tab->header_length != sizeof(struct acpi_table_erst))
+ return -EINVAL;
+ if (erst_tab->header.length < sizeof(struct acpi_table_erst))
+ return -EINVAL;
+ if (erst_tab->entries !=
+ (erst_tab->header.length - sizeof(struct acpi_table_erst)) /
+ sizeof(struct acpi_erst_entry))
+ return -EINVAL;
+
+ return 0;
+}
+
+int erst_init(void)
+{
+ int rc = 0;
+ acpi_status status;
+ struct apei_exec_context ctx;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_ERST, 0,
+ (struct acpi_table_header **)&erst_tab);
+ if (status == AE_NOT_FOUND) {
+ printk(KERN_ERR "Table is not found!\n");
+ return -ENODEV;
+ } else if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+ printk(KERN_ERR "Failed to get table, %s\n", msg);
+ return -EINVAL;
+ }
+
+ rc = erst_check_table(erst_tab);
+ if (rc) {
+ printk(KERN_ERR "ERST table is invalid\n");
+ return rc;
+ }
+
+ erst_exec_ctx_init(&ctx);
+ rc = apei_exec_pre_map_gars(&ctx);
+ if (rc)
+ return rc;
+
+ rc = erst_get_erange(&erst_erange);
+ if (rc) {
+ if (rc == -ENODEV)
+ printk(KERN_INFO
+ "The corresponding hardware device or firmware "
+ "implementation is not available.\n");
+ else
+ printk(KERN_ERR
+ "Failed to get Error Log Address Range.\n");
+ goto err_unmap_reg;
+ }
+
+ erst_erange.vaddr = apei_pre_map(erst_erange.base, erst_erange.size);
+ if (!erst_erange.vaddr) {
+ rc = -ENOMEM;
+ goto err_unmap_reg;
+ }
+
+ printk(KERN_INFO "Xen ERST support is initialized.\n");
+ erst_enabled = 1;
+
+ return 0;
+
+err_unmap_reg:
+ apei_exec_post_unmap_gars(&ctx);
+ return rc;
+}
diff -r b622e411eef8 xen/include/acpi/actbl1.h
--- a/xen/include/acpi/actbl1.h Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/include/acpi/actbl1.h Fri Aug 20 17:38:07 2010 +0800
@@ -60,6 +60,7 @@
#define ACPI_SIG_ASF "ASF!" /* Alert Standard Format table */
#define ACPI_SIG_BOOT "BOOT" /* Simple Boot Flag Table */
#define ACPI_SIG_CPEP "CPEP" /* Corrected Platform Error Polling
table */
+#define ACPI_SIG_ERST "ERST" /* Error Record Serialization Table */
#define ACPI_SIG_DBGP "DBGP" /* Debug Port table */
#define ACPI_SIG_DMAR "DMAR" /* DMA Remapping table */
#define ACPI_SIG_ECDT "ECDT" /* Embedded Controller Boot Resources
Table */
@@ -91,6 +92,18 @@ struct acpi_subtable_header {
struct acpi_subtable_header {
u8 type;
u8 length;
+};
+
+/* Subtable header for WHEA tables (EINJ, ERST, WDAT) */
+
+struct acpi_whea_header {
+ u8 action;
+ u8 instruction;
+ u8 flags;
+ u8 reserved;
+ struct acpi_generic_address register_region;
+ u64 value; /* Value used with Read/Write register */
+ u64 mask; /* Bitmask required for this register
instruction */
};
/*******************************************************************************
@@ -346,6 +359,96 @@ struct acpi_table_ecdt {
u32 uid; /* Unique ID - must be same as the EC _UID
method */
u8 gpe; /* The GPE for the EC */
u8 id[1]; /* Full namepath of the EC in the ACPI
namespace */
+};
+
+/*******************************************************************************
+ *
+ * ERST - Error Record Serialization Table (ACPI 4.0)
+ * Version 1
+ *
+
******************************************************************************/
+
+struct acpi_table_erst {
+ struct acpi_table_header header; /* Common ACPI table header */
+ u32 header_length;
+ u32 reserved;
+ u32 entries;
+};
+
+/* ERST Serialization Entries (actions) */
+
+struct acpi_erst_entry {
+ struct acpi_whea_header whea_header; /* Common header for WHEA
tables */
+};
+
+/* Masks for Flags field above */
+
+#define ACPI_ERST_PRESERVE (1)
+
+/* Values for Action field above */
+
+enum acpi_erst_actions {
+ ACPI_ERST_BEGIN_WRITE = 0,
+ ACPI_ERST_BEGIN_READ = 1,
+ ACPI_ERST_BEGIN_CLEAR = 2,
+ ACPI_ERST_END = 3,
+ ACPI_ERST_SET_RECORD_OFFSET = 4,
+ ACPI_ERST_EXECUTE_OPERATION = 5,
+ ACPI_ERST_CHECK_BUSY_STATUS = 6,
+ ACPI_ERST_GET_COMMAND_STATUS = 7,
+ ACPI_ERST_GET_RECORD_ID = 8,
+ ACPI_ERST_SET_RECORD_ID = 9,
+ ACPI_ERST_GET_RECORD_COUNT = 10,
+ ACPI_ERST_BEGIN_DUMMY_WRIITE = 11,
+ ACPI_ERST_NOT_USED = 12,
+ ACPI_ERST_GET_ERROR_RANGE = 13,
+ ACPI_ERST_GET_ERROR_LENGTH = 14,
+ ACPI_ERST_GET_ERROR_ATTRIBUTES = 15,
+ ACPI_ERST_ACTION_RESERVED = 16 /* 16 and greater are reserved */
+};
+
+/* Values for Instruction field above */
+
+enum acpi_erst_instructions {
+ ACPI_ERST_READ_REGISTER = 0,
+ ACPI_ERST_READ_REGISTER_VALUE = 1,
+ ACPI_ERST_WRITE_REGISTER = 2,
+ ACPI_ERST_WRITE_REGISTER_VALUE = 3,
+ ACPI_ERST_NOOP = 4,
+ ACPI_ERST_LOAD_VAR1 = 5,
+ ACPI_ERST_LOAD_VAR2 = 6,
+ ACPI_ERST_STORE_VAR1 = 7,
+ ACPI_ERST_ADD = 8,
+ ACPI_ERST_SUBTRACT = 9,
+ ACPI_ERST_ADD_VALUE = 10,
+ ACPI_ERST_SUBTRACT_VALUE = 11,
+ ACPI_ERST_STALL = 12,
+ ACPI_ERST_STALL_WHILE_TRUE = 13,
+ ACPI_ERST_SKIP_NEXT_IF_TRUE = 14,
+ ACPI_ERST_GOTO = 15,
+ ACPI_ERST_SET_SRC_ADDRESS_BASE = 16,
+ ACPI_ERST_SET_DST_ADDRESS_BASE = 17,
+ ACPI_ERST_MOVE_DATA = 18,
+ ACPI_ERST_INSTRUCTION_RESERVED = 19 /* 19 and greater are reserved
*/
+};
+
+/* Command status return values */
+
+enum acpi_erst_command_status {
+ ACPI_ERST_SUCESS = 0,
+ ACPI_ERST_NO_SPACE = 1,
+ ACPI_ERST_NOT_AVAILABLE = 2,
+ ACPI_ERST_FAILURE = 3,
+ ACPI_ERST_RECORD_EMPTY = 4,
+ ACPI_ERST_NOT_FOUND = 5,
+ ACPI_ERST_STATUS_RESERVED = 6 /* 6 and greater are reserved */
+};
+
+/* Error Record Serialization Information */
+
+struct acpi_erst_info {
+ u16 signature; /* Should be "ER" */
+ u8 data[48];
};
/*******************************************************************************
diff -r b622e411eef8 xen/include/acpi/apei.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/acpi/apei.h Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,31 @@
+/*
+ * apei.h - ACPI Platform Error Interface
+ */
+
+#ifndef ACPI_APEI_H
+#define ACPI_APEI_H
+
+#include <xen/acpi.h>
+#include <xen/cper.h>
+
+#define APEI_ERST_INVALID_RECORD_ID 0xffffffffffffffffULL
+
+#define FIX_APEI_RANGE_MAX 64
+
+int erst_write(const struct cper_record_header *record);
+size_t erst_get_record_count(void);
+int erst_get_next_record_id(u64 *record_id);
+size_t erst_read(u64 record_id, struct cper_record_header *record,
+ size_t buflen);
+size_t erst_read_next(struct cper_record_header *record, size_t buflen);
+int erst_clear(u64 record_id);
+
+void __iomem *apei_pre_map(paddr_t paddr, unsigned long size);
+
+int apei_pre_map_gar(struct acpi_generic_address *reg);
+int apei_post_unmap_gar(struct acpi_generic_address *reg);
+
+int apei_read(u64 *val, struct acpi_generic_address *reg);
+int apei_write(u64 val, struct acpi_generic_address *reg);
+
+#endif
diff -r b622e411eef8 xen/include/asm-x86/fixmap.h
--- a/xen/include/asm-x86/fixmap.h Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/include/asm-x86/fixmap.h Fri Aug 20 17:38:07 2010 +0800
@@ -20,6 +20,7 @@
#include <xen/iommu.h>
#include <asm/amd-iommu.h>
#include <asm/msi.h>
+#include <acpi/apei.h>
/*
* Here we define all the compile-time 'special' virtual
@@ -52,6 +53,8 @@ enum fixed_addresses {
FIX_MSIX_IO_RESERV_BASE,
FIX_MSIX_IO_RESERV_END = FIX_MSIX_IO_RESERV_BASE + FIX_MSIX_MAX_PAGES -1,
FIX_TBOOT_MAP_ADDRESS,
+ FIX_APEI_RANGE_BASE,
+ FIX_APEI_RANGE_END = FIX_APEI_RANGE_BASE + FIX_APEI_RANGE_MAX -1,
__end_of_fixed_addresses
};
diff -r b622e411eef8 xen/include/xen/acpi.h
--- a/xen/include/xen/acpi.h Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/include/xen/acpi.h Fri Aug 20 17:38:07 2010 +0800
@@ -264,6 +264,7 @@ int acpi_boot_init (void);
int acpi_boot_init (void);
int acpi_boot_table_init (void);
int acpi_numa_init (void);
+int erst_init(void);
int acpi_table_init (void);
int acpi_table_parse(char *id, acpi_table_handler handler);
diff -r b622e411eef8 xen/include/xen/cper.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/xen/cper.h Fri Aug 20 17:38:07 2010 +0800
@@ -0,0 +1,80 @@
+/*
+ * UEFI Common Platform Error Record
+ *
+ * Copyright (C) 2010, Intel Corp.
+ * Author: Huang Ying <ying.huang@xxxxxxxxx>
+ * Ported by: Liu, Jinsong <jinsong.liu@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
+ */
+
+#ifndef LINUX_CPER_H
+#define LINUX_CPER_H
+
+#include <xen/types.h>
+
+typedef struct {
+ __u8 b[16];
+} uuid_le;
+
+/* CPER record signature and the size */
+#define CPER_SIG_RECORD "CPER"
+#define CPER_SIG_SIZE 4
+/* Used in signature_end field in struct cper_record_header */
+#define CPER_SIG_END 0xffffffff
+
+/*
+ * All tables and structs must be byte-packed to match CPER
+ * specification, since the tables are provided by the system BIOS
+ */
+#pragma pack(1)
+
+struct cper_record_header {
+ char signature[CPER_SIG_SIZE]; /* must be CPER_SIG_RECORD */
+ __u16 revision; /* must be CPER_RECORD_REV */
+ __u32 signature_end; /* must be CPER_SIG_END */
+ __u16 section_count;
+ __u32 error_severity;
+ __u32 validation_bits;
+ __u32 record_length;
+ __u64 timestamp;
+ uuid_le platform_id;
+ uuid_le partition_id;
+ uuid_le creator_id;
+ uuid_le notification_type;
+ __u64 record_id;
+ __u32 flags;
+ __u64 persistence_information;
+ __u8 reserved[12]; /* must be zero */
+};
+
+struct cper_section_descriptor {
+ __u32 section_offset; /* Offset in bytes of the
+ * section body from the base
+ * of the record header */
+ __u32 section_length;
+ __u16 revision; /* must be CPER_RECORD_REV */
+ __u8 validation_bits;
+ __u8 reserved; /* must be zero */
+ __u32 flags;
+ uuid_le section_type;
+ uuid_le fru_id;
+ __u32 section_severity;
+ __u8 fru_text[20];
+};
+
+/* Reset to default packing */
+#pragma pack()
+
+#endif
diff -r b622e411eef8 xen/include/xen/spinlock.h
--- a/xen/include/xen/spinlock.h Thu Jun 24 21:56:03 2010 +0100
+++ b/xen/include/xen/spinlock.h Fri Aug 20 17:38:07 2010 +0800
@@ -181,6 +181,13 @@ int _rw_is_write_locked(rwlock_t *lock);
#define spin_is_locked(l) _spin_is_locked(l)
#define spin_trylock(l) _spin_trylock(l)
+#define spin_trylock_irqsave(lock, flags) \
+({ \
+ local_irq_save(flags); \
+ spin_trylock(lock) ? \
+ 1 : ({ local_irq_restore(flags); 0; }); \
+})
+
/* Ensure a lock is quiescent between two critical operations. */
#define spin_barrier(l) _spin_barrier(l)
#define spin_barrier_irq(l) _spin_barrier_irq(l)
apei-erst-1.patch
Description: apei-erst-1.patch
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|