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

[Xen-changelog] [xen-unstable] Implement ACPI APEI ERST feature



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1282467138 -3600
# Node ID 1666addd3d95a2f31b5eba3b2a96d5d11f819faf
# Parent  59ff5820534f4c5ec006d1ddca0f4356634c3b22
Implement ACPI APEI ERST feature

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 instru= ction
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>
---
 xen/arch/x86/acpi/boot.c              |    2 
 xen/drivers/acpi/Makefile             |    1 
 xen/drivers/acpi/apei/Makefile        |    3 
 xen/drivers/acpi/apei/apei-base.c     |  278 ++++++++++++
 xen/drivers/acpi/apei/apei-internal.h |   73 +++
 xen/drivers/acpi/apei/apei-io.c       |  328 ++++++++++++++
 xen/drivers/acpi/apei/erst.c          |  787 ++++++++++++++++++++++++++++++++++
 xen/include/acpi/actbl1.h             |  103 ++++
 xen/include/acpi/apei.h               |   31 +
 xen/include/asm-x86/fixmap.h          |    3 
 xen/include/xen/acpi.h                |    1 
 xen/include/xen/cper.h                |   80 +++
 xen/include/xen/spinlock.h            |    7 
 13 files changed, 1697 insertions(+)

diff -r 59ff5820534f -r 1666addd3d95 xen/arch/x86/acpi/boot.c
--- a/xen/arch/x86/acpi/boot.c  Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/arch/x86/acpi/boot.c  Sun Aug 22 09:52:18 2010 +0100
@@ -991,6 +991,8 @@ int __init acpi_boot_init(void)
 
        acpi_mmcfg_init();
 
+       erst_init();
+
        return 0;
 }
 
diff -r 59ff5820534f -r 1666addd3d95 xen/drivers/acpi/Makefile
--- a/xen/drivers/acpi/Makefile Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/drivers/acpi/Makefile Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 xen/drivers/acpi/apei/Makefile
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/Makefile    Sun Aug 22 09:52:18 2010 +0100
@@ -0,0 +1,3 @@
+obj-y += erst.o
+obj-y += apei-base.o
+obj-y += apei-io.o
diff -r 59ff5820534f -r 1666addd3d95 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 Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 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     Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 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   Sun Aug 22 09:52:18 2010 +0100
@@ -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, &reg->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 59ff5820534f -r 1666addd3d95 xen/drivers/acpi/apei/erst.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/drivers/acpi/apei/erst.c      Sun Aug 22 09:52:18 2010 +0100
@@ -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 *)(unsigned long)(ctx->dst_base + offset),
+               (void *)(unsigned long)(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 59ff5820534f -r 1666addd3d95 xen/include/acpi/actbl1.h
--- a/xen/include/acpi/actbl1.h Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/include/acpi/actbl1.h Sun Aug 22 09:52:18 2010 +0100
@@ -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 */
@@ -93,6 +94,18 @@ struct acpi_subtable_header {
        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 */
+};
+
 
/*******************************************************************************
  *
  * ASF - Alert Standard Format table (Signature "ASF!")
@@ -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 59ff5820534f -r 1666addd3d95 xen/include/acpi/apei.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/acpi/apei.h   Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 xen/include/asm-x86/fixmap.h
--- a/xen/include/asm-x86/fixmap.h      Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/include/asm-x86/fixmap.h      Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 xen/include/xen/acpi.h
--- a/xen/include/xen/acpi.h    Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/include/xen/acpi.h    Sun Aug 22 09:52:18 2010 +0100
@@ -285,6 +285,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 59ff5820534f -r 1666addd3d95 xen/include/xen/cper.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/xen/include/xen/cper.h    Sun Aug 22 09:52:18 2010 +0100
@@ -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 59ff5820534f -r 1666addd3d95 xen/include/xen/spinlock.h
--- a/xen/include/xen/spinlock.h        Sun Aug 22 09:37:08 2010 +0100
+++ b/xen/include/xen/spinlock.h        Sun Aug 22 09:52:18 2010 +0100
@@ -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)

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.