diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 17:56:45 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-07-26 17:56:45 -0700 |
commit | e663107fa1edda4d8a0d5b8ce704d71f8e27de43 (patch) | |
tree | 2ca367a9cdeb97ce613e792256b64831189af7b3 /drivers/acpi/apei | |
parent | 6453dbdda30428a3c56568c96fe70ea3612f07e2 (diff) | |
parent | 54d0b14ad7cc4ff3c710f092a93638f359c1b14b (diff) |
Merge tag 'acpi-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull ACPI updates from Rafael Wysocki:
"The new feaures here are the support for ACPI overlays (allowing ACPI
tables to be loaded at any time from EFI variables or via configfs)
and the LPI (Low-Power Idle) support. Also notable is the ACPI-based
NUMA support for ARM64.
Apart from that we have two new drivers, for the DPTF (Dynamic Power
and Thermal Framework) power participant device and for the Intel
Broxton WhiskeyCove PMIC, some more PMIC-related changes, support for
the Boot Error Record Table (BERT) in APEI and support for
platform-initiated graceful shutdown.
Plus two new pieces of documentation and usual assorted fixes and
cleanups in quite a few places.
Specifics:
- Support for ACPI SSDT overlays allowing Secondary System
Description Tables (SSDTs) to be loaded at any time from EFI
variables or via configfs (Octavian Purdila, Mika Westerberg).
- Support for the ACPI LPI (Low-Power Idle) feature introduced in
ACPI 6.0 and allowing processor idle states to be represented in
ACPI tables in a hierarchical way (with the help of Processor
Container objects) and support for ACPI idle states management on
ARM64, based on LPI (Sudeep Holla).
- General improvements of ACPI support for NUMA and ARM64 support for
ACPI-based NUMA (Hanjun Guo, David Daney, Robert Richter).
- General improvements of the ACPI table upgrade mechanism and ARM64
support for that feature (Aleksey Makarov, Jon Masters).
- Support for the Boot Error Record Table (BERT) in APEI and
improvements of kernel messages printed by the error injection code
(Huang Ying, Borislav Petkov).
- New driver for the Intel Broxton WhiskeyCove PMIC operation region
and support for the REGS operation region on Broxton, PMIC code
cleanups (Bin Gao, Felipe Balbi, Paul Gortmaker).
- New driver for the power participant device which is part of the
Dynamic Power and Thermal Framework (DPTF) and DPTF-related code
reorganization (Srinivas Pandruvada).
- Support for the platform-initiated graceful shutdown feature
introduced in ACPI 6.1 (Prashanth Prakash).
- ACPI button driver update related to lid input events generated
automatically on initialization and system resume that have been
problematic for some time (Lv Zheng).
- ACPI EC driver cleanups (Lv Zheng).
- Documentation of the ACPICA release automation process and the
in-kernel ACPI AML debugger (Lv Zheng).
- New blacklist entry and two fixes for the ACPI backlight driver
(Alex Hung, Arvind Yadav, Ralf Gerbig).
- Cleanups of the ACPI pci_slot driver (Joe Perches, Paul Gortmaker).
- ACPI CPPC code changes to make it more robust against possible
defects in ACPI tables and new symbol definitions for PCC (Hoan
Tran).
- System reboot code modification to execute the ACPI _PTS (Prepare
To Sleep) method in addition to _TTS (Ocean He).
- ACPICA-related change to carry out lock ordering checks in ACPICA
if ACPICA debug is enabled in the kernel (Lv Zheng).
- Assorted minor fixes and cleanups (Andy Shevchenko, Baoquan He,
Bhaktipriya Shridhar, Paul Gortmaker, Rafael Wysocki)"
* tag 'acpi-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (71 commits)
ACPI: enable ACPI_PROCESSOR_IDLE on ARM64
arm64: add support for ACPI Low Power Idle(LPI)
drivers: firmware: psci: initialise idle states using ACPI LPI
cpuidle: introduce CPU_PM_CPU_IDLE_ENTER macro for ARM{32, 64}
arm64: cpuidle: drop __init section marker to arm_cpuidle_init
ACPI / processor_idle: Add support for Low Power Idle(LPI) states
ACPI / processor_idle: introduce ACPI_PROCESSOR_CSTATE
ACPI / DPTF: move int340x_thermal.c to the DPTF folder
ACPI / DPTF: Add DPTF power participant driver
ACPI / lpat: make it explicitly non-modular
ACPI / dock: make dock explicitly non-modular
ACPI / PCI: make pci_slot explicitly non-modular
ACPI / PMIC: remove modular references from non-modular code
ACPICA: Linux: Enable ACPI_MUTEX_DEBUG for Linux kernel
ACPI: Rename configfs.c to acpi_configfs.c to prevent link error
ACPI / debugger: Add AML debugger documentation
ACPI: Add documentation describing ACPICA release automation
ACPI: add support for loading SSDTs via configfs
ACPI: add support for configfs
efi / ACPI: load SSTDs from EFI variables
...
Diffstat (limited to 'drivers/acpi/apei')
-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/einj.c | 57 |
4 files changed, 188 insertions, 23 deletions
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/einj.c b/drivers/acpi/apei/einj.c index 559c1173de1c..eebb7e39c49c 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c @@ -33,7 +33,8 @@ #include "apei-internal.h" -#define EINJ_PFX "EINJ: " +#undef pr_fmt +#define pr_fmt(fmt) "EINJ: " fmt #define SPIN_UNIT 100 /* 100ns */ /* Firmware should respond within 1 milliseconds */ @@ -179,8 +180,7 @@ static int einj_get_available_error_type(u32 *type) static int einj_timedout(u64 *t) { if ((s64)*t < SPIN_UNIT) { - pr_warning(FW_WARN EINJ_PFX - "Firmware does not respond in time\n"); + pr_warning(FW_WARN "Firmware does not respond in time\n"); return 1; } *t -= SPIN_UNIT; @@ -307,8 +307,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { - pr_err(EINJ_PFX - "Can not request [mem %#010llx-%#010llx] for Trigger table\n", + pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n", (unsigned long long)trigger_paddr, (unsigned long long)trigger_paddr + sizeof(*trigger_tab) - 1); @@ -316,13 +315,12 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, } trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); if (!trigger_tab) { - pr_err(EINJ_PFX "Failed to map trigger table!\n"); + pr_err("Failed to map trigger table!\n"); goto out_rel_header; } rc = einj_check_trigger_header(trigger_tab); if (rc) { - pr_warning(FW_BUG EINJ_PFX - "The trigger error action table is invalid\n"); + pr_warning(FW_BUG "Invalid trigger error action table.\n"); goto out_rel_header; } @@ -336,8 +334,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, table_size - sizeof(*trigger_tab), "APEI EINJ Trigger Table"); if (!r) { - pr_err(EINJ_PFX -"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", + pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", (unsigned long long)trigger_paddr + sizeof(*trigger_tab), (unsigned long long)trigger_paddr + table_size - 1); goto out_rel_header; @@ -345,7 +342,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type, iounmap(trigger_tab); trigger_tab = ioremap_cache(trigger_paddr, table_size); if (!trigger_tab) { - pr_err(EINJ_PFX "Failed to map trigger table!\n"); + pr_err("Failed to map trigger table!\n"); goto out_rel_entry; } trigger_entry = (struct acpi_whea_header *) @@ -695,34 +692,42 @@ static int __init einj_init(void) struct dentry *fentry; struct apei_exec_context ctx; - if (acpi_disabled) + if (acpi_disabled) { + pr_warn("ACPI disabled.\n"); return -ENODEV; + } status = acpi_get_table(ACPI_SIG_EINJ, 0, (struct acpi_table_header **)&einj_tab); - if (status == AE_NOT_FOUND) + if (status == AE_NOT_FOUND) { + pr_warn("EINJ table not found.\n"); return -ENODEV; + } else if (ACPI_FAILURE(status)) { - const char *msg = acpi_format_exception(status); - pr_err(EINJ_PFX "Failed to get table, %s\n", msg); + pr_err("Failed to get EINJ table: %s\n", + acpi_format_exception(status)); return -EINVAL; } rc = einj_check_table(einj_tab); if (rc) { - pr_warning(FW_BUG EINJ_PFX "EINJ table is invalid\n"); + pr_warn(FW_BUG "Invalid EINJ table.n"); return -EINVAL; } rc = -ENOMEM; einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir()); - if (!einj_debug_dir) + if (!einj_debug_dir) { + pr_err("Error creating debugfs node.\n"); goto err_cleanup; + } + fentry = debugfs_create_file("available_error_type", S_IRUSR, einj_debug_dir, NULL, &available_error_type_fops); if (!fentry) goto err_cleanup; + fentry = debugfs_create_file("error_type", S_IRUSR | S_IWUSR, einj_debug_dir, NULL, &error_type_fops); if (!fentry) @@ -735,14 +740,22 @@ static int __init einj_init(void) apei_resources_init(&einj_resources); einj_exec_ctx_init(&ctx); rc = apei_exec_collect_resources(&ctx, &einj_resources); - if (rc) + if (rc) { + pr_err("Error collecting EINJ resources.\n"); goto err_fini; + } + rc = apei_resources_request(&einj_resources, "APEI EINJ"); - if (rc) + if (rc) { + pr_err("Error requesting memory/port resources.\n"); goto err_fini; + } + rc = apei_exec_pre_map_gars(&ctx); - if (rc) + if (rc) { + pr_err("Error pre-mapping GARs.\n"); goto err_release; + } rc = -ENOMEM; einj_param = einj_get_parameter_address(); @@ -787,7 +800,7 @@ static int __init einj_init(void) goto err_unmap; } - pr_info(EINJ_PFX "Error INJection is initialized.\n"); + pr_info("Error INJection is initialized.\n"); return 0; @@ -798,6 +811,7 @@ err_unmap: sizeof(struct einj_parameter); acpi_os_unmap_iomem(einj_param, size); + pr_err("Error creating param extension debugfs nodes.\n"); } apei_exec_post_unmap_gars(&ctx); err_release: @@ -805,6 +819,7 @@ err_release: err_fini: apei_resources_fini(&einj_resources); err_cleanup: + pr_err("Error creating primary debugfs nodes.\n"); debugfs_remove_recursive(einj_debug_dir); return rc; |