diff options
author | Graeme Gregory <graeme.gregory@linaro.org> | 2016-02-01 10:11:57 +0000 |
---|---|---|
committer | Graeme Gregory <graeme.gregory@linaro.org> | 2016-02-01 10:11:57 +0000 |
commit | b71cf239267e6797023b16a5d4483cbe2c582d47 (patch) | |
tree | 2de1330ffe13ef9dd36c6283dbcfd1bebdd3dfb3 | |
parent | 8dc96ffb33aa83cf4a4703517e43cfcdaee4dbce (diff) | |
parent | 3e841a5becb944727d74464b4a5944b008c2ed16 (diff) | |
download | leg-kernel-b71cf239267e6797023b16a5d4483cbe2c582d47.tar.gz |
Merge tag 'topic-apei-20160122.0' into leg-kernel
-rw-r--r-- | Documentation/kernel-parameters.txt | 3 | ||||
-rw-r--r-- | arch/arm64/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/acpi.h | 9 | ||||
-rw-r--r-- | arch/x86/Kconfig | 1 | ||||
-rw-r--r-- | drivers/acpi/apei/Kconfig | 3 | ||||
-rw-r--r-- | drivers/acpi/apei/Makefile | 2 | ||||
-rw-r--r-- | drivers/acpi/apei/apei-internal.h | 2 | ||||
-rw-r--r-- | drivers/acpi/apei/bert.c | 150 | ||||
-rw-r--r-- | drivers/acpi/apei/hest.c | 19 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 2 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 3 |
11 files changed, 186 insertions, 9 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 742f69d18fc8..198045ea651e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -555,6 +555,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. bootmem_debug [KNL] Enable bootmem allocator debug messages. + bert_disable [ACPI] + Disable BERT OS support on buggy BIOSes. + bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards) bttv.radio= Most important insmod options are available as kernel args too. diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 116421f54a97..ca7125870722 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -5,6 +5,7 @@ config ARM64 select ACPI_PCI_HOST_GENERIC if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ACPI_SPCR_TABLE if ACPI + select HAVE_ACPI_APEI if ACPI select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_GCOV_PROFILE_ALL diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 6db9c6ff53fb..cc1705c59315 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -17,6 +17,7 @@ #include <asm/cputype.h> #include <asm/smp_plat.h> +#include <asm/tlbflush.h> /* Macros for consistency checks of the GICC subtable of MADT */ #define ACPI_MADT_GICC_LENGTH \ @@ -94,7 +95,12 @@ static inline const char *acpi_get_enable_method(int cpu) #ifdef CONFIG_ACPI_APEI pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr); -#endif + +static inline void arch_apei_flush_tlb_one(unsigned long addr) +{ + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +} +#endif /* CONFIG_ACPI_APEI */ #ifdef CONFIG_ACPI_NUMA int arm64_acpi_numa_init(void); @@ -103,5 +109,4 @@ void acpi_numa_set_node_info(unsigned int cpu, u64 hwid); static inline int arm64_acpi_numa_init(void) { return -ENODEV; } static inline void acpi_numa_set_node_info(unsigned int cpu, u64 hwid) { } #endif /* CONFIG_ACPI_NUMA */ - #endif /*_ASM_ACPI_H*/ diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 102d7d1c958f..0338cdd9d962 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -75,6 +75,7 @@ config X86 select GENERIC_TIME_VSYSCALL select HAVE_ACPI_APEI if ACPI select HAVE_ACPI_APEI_NMI if ACPI + select HAVE_ACPI_APEI_HEST_IA32 if ACPI select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_AOUT if X86_32 select HAVE_ARCH_AUDITSYSCALL diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index b0140c8fc733..a60bb000a15d 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -4,6 +4,9 @@ config HAVE_ACPI_APEI config HAVE_ACPI_APEI_NMI bool +config HAVE_ACPI_APEI_HEST_IA32 + bool + config ACPI_APEI bool "ACPI Platform Error Interface (APEI)" select MISC_FILESYSTEMS diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index 5d575a955940..e50573de25f1 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o -apei-y := apei-base.o hest.o erst.o +apei-y := apei-base.o hest.o erst.o bert.o diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index 16129c78b489..6e9f14c0a71b 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -1,6 +1,6 @@ /* * apei-internal.h - ACPI Platform Error Interface internal - * definations. + * definitions. */ #ifndef APEI_INTERNAL_H diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c new file mode 100644 index 000000000000..a05b5c0cf181 --- /dev/null +++ b/drivers/acpi/apei/bert.c @@ -0,0 +1,150 @@ +/* + * APEI Boot Error Record Table (BERT) support + * + * Copyright 2011 Intel Corp. + * Author: Huang Ying <ying.huang@intel.com> + * + * Under normal circumstances, when a hardware error occurs, the error + * handler receives control and processes the error. This gives OSPM a + * chance to process the error condition, report it, and optionally attempt + * recovery. In some cases, the system is unable to process an error. + * For example, system firmware or a management controller may choose to + * reset the system or the system might experience an uncontrolled crash + * or reset.The boot error source is used to report unhandled errors that + * occurred in a previous boot. This mechanism is described in the BERT + * table. + * + * For more information about BERT, please refer to ACPI Specification + * version 4.0, section 17.3.1 + * + * This file is licensed under GPLv2. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <linux/io.h> + +#include "apei-internal.h" + +#undef pr_fmt +#define pr_fmt(fmt) "BERT: " fmt + +static int bert_disable; + +static void __init bert_print_all(struct acpi_bert_region *region, + unsigned int region_len) +{ + struct acpi_hest_generic_status *estatus = + (struct acpi_hest_generic_status *)region; + int remain = region_len; + u32 estatus_len; + + if (!estatus->block_status) + return; + + while (remain > sizeof(struct acpi_bert_region)) { + if (cper_estatus_check(estatus)) { + pr_err(FW_BUG "Invalid error record.\n"); + return; + } + + estatus_len = cper_estatus_len(estatus); + if (remain < estatus_len) { + pr_err(FW_BUG "Truncated status block (length: %u).\n", + estatus_len); + return; + } + + pr_info_once("Error records from previous boot:\n"); + + cper_estatus_print(KERN_INFO HW_ERR, estatus); + + /* + * Because the boot error source is "one-time polled" type, + * clear Block Status of current Generic Error Status Block, + * once it's printed. + */ + estatus->block_status = 0; + + estatus = (void *)estatus + estatus_len; + /* No more error records. */ + if (!estatus->block_status) + return; + + remain -= estatus_len; + } +} + +static int __init setup_bert_disable(char *str) +{ + bert_disable = 1; + + return 0; +} +__setup("bert_disable", setup_bert_disable); + +static int __init bert_check_table(struct acpi_table_bert *bert_tab) +{ + if (bert_tab->header.length < sizeof(struct acpi_table_bert) || + bert_tab->region_length < sizeof(struct acpi_bert_region)) + return -EINVAL; + + return 0; +} + +static int __init bert_init(void) +{ + struct acpi_bert_region *boot_error_region; + struct acpi_table_bert *bert_tab; + unsigned int region_len; + acpi_status status; + int rc = 0; + + if (acpi_disabled) + return 0; + + if (bert_disable) { + pr_info("Boot Error Record Table support is disabled.\n"); + return 0; + } + + status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab); + if (status == AE_NOT_FOUND) + return 0; + + if (ACPI_FAILURE(status)) { + pr_err("get table failed, %s.\n", acpi_format_exception(status)); + return -EINVAL; + } + + rc = bert_check_table(bert_tab); + if (rc) { + pr_err(FW_BUG "table invalid.\n"); + return rc; + } + + region_len = bert_tab->region_length; + if (!request_mem_region(bert_tab->address, region_len, "APEI BERT")) { + pr_err("Can't request iomem region <%016llx-%016llx>.\n", + (unsigned long long)bert_tab->address, + (unsigned long long)bert_tab->address + region_len - 1); + return -EIO; + } + + boot_error_region = ioremap_cache(bert_tab->address, region_len); + if (boot_error_region) { + bert_print_all(boot_error_region, region_len); + iounmap(boot_error_region); + } else { + rc = -ENOMEM; + } + + release_mem_region(bert_tab->address, region_len); + + return rc; +} + +late_initcall(bert_init); diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index 20b3fcf4007c..c708c954c79a 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -117,15 +117,27 @@ int apei_hest_parse(apei_hest_func_t func, void *data) } EXPORT_SYMBOL_GPL(apei_hest_parse); +#ifdef CONFIG_HAVE_ACPI_APEI_HEST_IA32 /* * Check if firmware advertises firmware first mode. We need FF bit to be set * along with a set of MC banks which work in FF mode. */ static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data) { - return arch_apei_enable_cmcff(hest_hdr, data); + if (!acpi_disable_cmcff) + return !arch_apei_enable_cmcff(hest_hdr, data); + + return 0; } +static inline int __init hest_ia_init(void) +{ + return apei_hest_parse(hest_parse_cmc, NULL); +} +#else +static inline int __init hest_ia_init(void) { return 0; } +#endif + struct ghes_arr { struct platform_device **ghes_devs; unsigned int count; @@ -232,8 +244,9 @@ void __init acpi_hest_init(void) goto err; } - if (!acpi_disable_cmcff) - apei_hest_parse(hest_parse_cmc, NULL); + rc = hest_ia_init(); + if (rc) + goto err; if (!ghes_disable) { rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d483e2ae41cd..010216bea084 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -34,7 +34,6 @@ #include <linux/acpi.h> #include <linux/slab.h> #include <linux/dmi.h> -#include <acpi/apei.h> /* for acpi_hest_init() */ #include "internal.h" @@ -999,7 +998,6 @@ out_release_info: void __init acpi_pci_root_init(void) { - acpi_hest_init(); if (acpi_pci_disabled) return; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 20c8cbaf736b..96721e28e49a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -13,6 +13,8 @@ #include <linux/nls.h> #include <linux/dma-mapping.h> +#include <acpi/apei.h> /* for acpi_hest_init() */ + #include <asm/pgtable.h> #include "internal.h" @@ -1912,6 +1914,7 @@ int __init acpi_scan_init(void) { int result; + acpi_hest_init(); acpi_pci_root_init(); acpi_pci_link_init(); acpi_processor_init(); |