[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, ®->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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |