aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/apei/ghes.c38
-rw-r--r--drivers/acpi/apei/hest.c38
2 files changed, 67 insertions, 9 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index ec9b57d428a1..8ec37bbdd699 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -409,6 +409,34 @@ static void ghes_clear_estatus(struct ghes *ghes)
409 ghes->flags &= ~GHES_TO_CLEAR; 409 ghes->flags &= ~GHES_TO_CLEAR;
410} 410}
411 411
412static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev)
413{
414#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE
415 unsigned long pfn;
416 int sec_sev = ghes_severity(gdata->error_severity);
417 struct cper_sec_mem_err *mem_err;
418 mem_err = (struct cper_sec_mem_err *)(gdata + 1);
419
420 if (sec_sev == GHES_SEV_CORRECTED &&
421 (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED) &&
422 (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS)) {
423 pfn = mem_err->physical_addr >> PAGE_SHIFT;
424 if (pfn_valid(pfn))
425 memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE);
426 else if (printk_ratelimit())
427 pr_warn(FW_WARN GHES_PFX
428 "Invalid address in generic error data: %#llx\n",
429 mem_err->physical_addr);
430 }
431 if (sev == GHES_SEV_RECOVERABLE &&
432 sec_sev == GHES_SEV_RECOVERABLE &&
433 mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
434 pfn = mem_err->physical_addr >> PAGE_SHIFT;
435 memory_failure_queue(pfn, 0, 0);
436 }
437#endif
438}
439
412static void ghes_do_proc(struct ghes *ghes, 440static void ghes_do_proc(struct ghes *ghes,
413 const struct acpi_hest_generic_status *estatus) 441 const struct acpi_hest_generic_status *estatus)
414{ 442{
@@ -428,15 +456,7 @@ static void ghes_do_proc(struct ghes *ghes,
428 apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, 456 apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED,
429 mem_err); 457 mem_err);
430#endif 458#endif
431#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE 459 ghes_handle_memory_failure(gdata, sev);
432 if (sev == GHES_SEV_RECOVERABLE &&
433 sec_sev == GHES_SEV_RECOVERABLE &&
434 mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) {
435 unsigned long pfn;
436 pfn = mem_err->physical_addr >> PAGE_SHIFT;
437 memory_failure_queue(pfn, 0, 0);
438 }
439#endif
440 } 460 }
441#ifdef CONFIG_ACPI_APEI_PCIEAER 461#ifdef CONFIG_ACPI_APEI_PCIEAER
442 else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, 462 else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index f5ef5d54e4ac..502024502b13 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -36,6 +36,7 @@
36#include <linux/io.h> 36#include <linux/io.h>
37#include <linux/platform_device.h> 37#include <linux/platform_device.h>
38#include <acpi/apei.h> 38#include <acpi/apei.h>
39#include <asm/mce.h>
39 40
40#include "apei-internal.h" 41#include "apei-internal.h"
41 42
@@ -121,6 +122,40 @@ int apei_hest_parse(apei_hest_func_t func, void *data)
121} 122}
122EXPORT_SYMBOL_GPL(apei_hest_parse); 123EXPORT_SYMBOL_GPL(apei_hest_parse);
123 124
125/*
126 * Check if firmware advertises firmware first mode. We need FF bit to be set
127 * along with a set of MC banks which work in FF mode.
128 */
129static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
130{
131 int i;
132 struct acpi_hest_ia_corrected *cmc;
133 struct acpi_hest_ia_error_bank *mc_bank;
134
135 if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
136 return 0;
137
138 cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
139 if (!cmc->enabled)
140 return 0;
141
142 /*
143 * We expect HEST to provide a list of MC banks that report errors
144 * in firmware first mode. Otherwise, return non-zero value to
145 * indicate that we are done parsing HEST.
146 */
147 if (!(cmc->flags & ACPI_HEST_FIRMWARE_FIRST) || !cmc->num_hardware_banks)
148 return 1;
149
150 pr_info(HEST_PFX "Enabling Firmware First mode for corrected errors.\n");
151
152 mc_bank = (struct acpi_hest_ia_error_bank *)(cmc + 1);
153 for (i = 0; i < cmc->num_hardware_banks; i++, mc_bank++)
154 mce_disable_bank(mc_bank->bank_number);
155
156 return 1;
157}
158
124struct ghes_arr { 159struct ghes_arr {
125 struct platform_device **ghes_devs; 160 struct platform_device **ghes_devs;
126 unsigned int count; 161 unsigned int count;
@@ -227,6 +262,9 @@ void __init acpi_hest_init(void)
227 goto err; 262 goto err;
228 } 263 }
229 264
265 if (!acpi_disable_cmcff)
266 apei_hest_parse(hest_parse_cmc, NULL);
267
230 if (!ghes_disable) { 268 if (!ghes_disable) {
231 rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); 269 rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
232 if (rc) 270 if (rc)