diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/apei/ghes.c | 38 | ||||
-rw-r--r-- | drivers/acpi/apei/hest.c | 38 |
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 | ||
412 | static 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 | |||
412 | static void ghes_do_proc(struct ghes *ghes, | 440 | static 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 | } |
122 | EXPORT_SYMBOL_GPL(apei_hest_parse); | 123 | EXPORT_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 | */ | ||
129 | static 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 | |||
124 | struct ghes_arr { | 159 | struct 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) |