diff options
Diffstat (limited to 'drivers/acpi/apei/hest.c')
| -rw-r--r-- | drivers/acpi/apei/hest.c | 119 |
1 files changed, 91 insertions, 28 deletions
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index b1e9f81ebeea..20d757687e3d 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * APEI Hardware Error Souce Table support + * APEI Hardware Error Source Table support * * HEST describes error sources in detail; communicates operational * parameters (i.e. severity levels, masking bits, and threshold @@ -12,15 +13,6 @@ * * Copyright 2009 Intel Corp. * Author: Huang Ying <ying.huang@intel.com> - * - * 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. */ #include <linux/kernel.h> @@ -32,6 +24,7 @@ #include <linux/io.h> #include <linux/platform_device.h> #include <acpi/apei.h> +#include <acpi/ghes.h> #include "apei-internal.h" @@ -44,6 +37,20 @@ EXPORT_SYMBOL_GPL(hest_disable); static struct acpi_table_hest *__read_mostly hest_tab; +/* + * Since GHES_ASSIST is not supported, skip initialization of GHES_ASSIST + * structures for MCA. + * During HEST parsing, detected MCA error sources are cached from early + * table entries so that the Flags and Source Id fields from these cached + * values are then referred to in later table entries to determine if the + * encountered GHES_ASSIST structure should be initialized. + */ +static struct { + struct acpi_hest_ia_corrected *cmc; + struct acpi_hest_ia_machine_check *mc; + struct acpi_hest_ia_deferred_check *dmc; +} mces; + static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, @@ -53,8 +60,15 @@ static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { [ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge), [ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic), [ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2), + [ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1, }; +static inline bool is_generic_error(struct acpi_hest_header *hest_hdr) +{ + return hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR || + hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2; +} + static int hest_esrc_len(struct acpi_hest_header *hest_hdr) { u16 hest_type = hest_hdr->type; @@ -70,18 +84,57 @@ static int hest_esrc_len(struct acpi_hest_header *hest_hdr) cmc = (struct acpi_hest_ia_corrected *)hest_hdr; len = sizeof(*cmc) + cmc->num_hardware_banks * sizeof(struct acpi_hest_ia_error_bank); + mces.cmc = cmc; } else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) { struct acpi_hest_ia_machine_check *mc; mc = (struct acpi_hest_ia_machine_check *)hest_hdr; len = sizeof(*mc) + mc->num_hardware_banks * sizeof(struct acpi_hest_ia_error_bank); + mces.mc = mc; + } else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) { + struct acpi_hest_ia_deferred_check *mc; + mc = (struct acpi_hest_ia_deferred_check *)hest_hdr; + len = sizeof(*mc) + mc->num_hardware_banks * + sizeof(struct acpi_hest_ia_error_bank); + mces.dmc = mc; } BUG_ON(len == -1); return len; }; -int apei_hest_parse(apei_hest_func_t func, void *data) +/* + * GHES and GHESv2 structures share the same format, starting from + * Source Id and ending in Error Status Block Length (inclusive). + */ +static bool is_ghes_assist_struct(struct acpi_hest_header *hest_hdr) +{ + struct acpi_hest_generic *ghes; + u16 related_source_id; + + if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR && + hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2) + return false; + + ghes = (struct acpi_hest_generic *)hest_hdr; + related_source_id = ghes->related_source_id; + + if (mces.cmc && mces.cmc->flags & ACPI_HEST_GHES_ASSIST && + related_source_id == mces.cmc->header.source_id) + return true; + if (mces.mc && mces.mc->flags & ACPI_HEST_GHES_ASSIST && + related_source_id == mces.mc->header.source_id) + return true; + if (mces.dmc && mces.dmc->flags & ACPI_HEST_GHES_ASSIST && + related_source_id == mces.dmc->header.source_id) + return true; + + return false; +} + +typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data); + +static int apei_hest_parse(apei_hest_func_t func, void *data) { struct acpi_hest_header *hest_hdr; int i, rc, len; @@ -93,20 +146,25 @@ int apei_hest_parse(apei_hest_func_t func, void *data) for (i = 0; i < hest_tab->error_source_count; i++) { len = hest_esrc_len(hest_hdr); if (!len) { - pr_warning(FW_WARN HEST_PFX - "Unknown or unused hardware error source " - "type: %d for hardware error source: %d.\n", - hest_hdr->type, hest_hdr->source_id); + pr_warn(FW_WARN HEST_PFX + "Unknown or unused hardware error source " + "type: %d for hardware error source: %d.\n", + hest_hdr->type, hest_hdr->source_id); return -EINVAL; } if ((void *)hest_hdr + len > (void *)hest_tab + hest_tab->header.length) { - pr_warning(FW_BUG HEST_PFX + pr_warn(FW_BUG HEST_PFX "Table contents overflow for hardware error source: %d.\n", hest_hdr->source_id); return -EINVAL; } + if (is_ghes_assist_struct(hest_hdr)) { + hest_hdr = (void *)hest_hdr + len; + continue; + } + rc = func(hest_hdr, data); if (rc) return rc; @@ -116,7 +174,6 @@ int apei_hest_parse(apei_hest_func_t func, void *data) return 0; } -EXPORT_SYMBOL_GPL(apei_hest_parse); /* * Check if firmware advertises firmware first mode. We need FF bit to be set @@ -142,8 +199,7 @@ static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void { int *count = data; - if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR || - hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2) + if (is_generic_error(hest_hdr)) (*count)++; return 0; } @@ -154,8 +210,7 @@ static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) struct ghes_arr *ghes_arr = data; int rc, i; - if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR && - hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2) + if (!is_generic_error(hest_hdr)) return 0; if (!((struct acpi_hest_generic *)hest_hdr)->enabled) @@ -165,8 +220,8 @@ static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) ghes_dev = ghes_arr->ghes_devs[i]; hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data; if (hdr->source_id == hest_hdr->source_id) { - pr_warning(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n", - hdr->source_id); + pr_warn(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n", + hdr->source_id); return -EIO; } } @@ -203,6 +258,11 @@ static int __init hest_ghes_dev_register(unsigned int ghes_count) rc = apei_hest_parse(hest_parse_ghes, &ghes_arr); if (rc) goto err; + + rc = ghes_estatus_pool_init(ghes_count); + if (rc) + goto err; + out: kfree(ghes_arr.ghes_devs); return rc; @@ -215,7 +275,7 @@ err: static int __init setup_hest_disable(char *str) { hest_disable = HEST_DISABLED; - return 0; + return 1; } __setup("hest_disable", setup_hest_disable); @@ -223,7 +283,7 @@ __setup("hest_disable", setup_hest_disable); void __init acpi_hest_init(void) { acpi_status status; - int rc = -ENODEV; + int rc; unsigned int ghes_count = 0; if (hest_disable) { @@ -239,8 +299,8 @@ void __init acpi_hest_init(void) } else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err(HEST_PFX "Failed to get table, %s\n", msg); - rc = -EINVAL; - goto err; + hest_disable = HEST_DISABLED; + return; } rc = apei_hest_parse(hest_parse_cmc, NULL); @@ -251,7 +311,9 @@ void __init acpi_hest_init(void) rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); if (rc) goto err; - rc = hest_ghes_dev_register(ghes_count); + + if (ghes_count) + rc = hest_ghes_dev_register(ghes_count); if (rc) goto err; } @@ -260,4 +322,5 @@ void __init acpi_hest_init(void) return; err: hest_disable = HEST_DISABLED; + acpi_put_table((struct acpi_table_header *)hest_tab); } |
