| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Extended Error Log driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2013 Intel Corp. |
|---|
| 5 | 6 | * Author: Chen, Gong <gong.chen@intel.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This file is licensed under GPLv2. |
|---|
| 8 | 7 | */ |
|---|
| 9 | 8 | |
|---|
| 10 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 13 | 12 | #include <linux/ratelimit.h> |
|---|
| 14 | 13 | #include <linux/edac.h> |
|---|
| 15 | 14 | #include <linux/ras.h> |
|---|
| 15 | +#include <acpi/ghes.h> |
|---|
| 16 | 16 | #include <asm/cpu.h> |
|---|
| 17 | 17 | #include <asm/mce.h> |
|---|
| 18 | 18 | |
|---|
| .. | .. |
|---|
| 42 | 42 | u32 entries; /* Valid L1 Directory entries per logical processor */ |
|---|
| 43 | 43 | u8 rev1[12]; |
|---|
| 44 | 44 | }; |
|---|
| 45 | | - |
|---|
| 46 | | -static int old_edac_report_status; |
|---|
| 47 | 45 | |
|---|
| 48 | 46 | static u8 extlog_dsm_uuid[] __initdata = "663E35AF-CC10-41A4-88EA-5470AF055295"; |
|---|
| 49 | 47 | |
|---|
| .. | .. |
|---|
| 141 | 139 | int cpu = mce->extcpu; |
|---|
| 142 | 140 | struct acpi_hest_generic_status *estatus, *tmp; |
|---|
| 143 | 141 | struct acpi_hest_generic_data *gdata; |
|---|
| 144 | | - const guid_t *fru_id = &guid_null; |
|---|
| 145 | | - char *fru_text = ""; |
|---|
| 142 | + const guid_t *fru_id; |
|---|
| 143 | + char *fru_text; |
|---|
| 146 | 144 | guid_t *sec_type; |
|---|
| 147 | 145 | static u32 err_seq; |
|---|
| 148 | 146 | |
|---|
| 149 | 147 | estatus = extlog_elog_entry_check(cpu, bank); |
|---|
| 150 | | - if (estatus == NULL) |
|---|
| 148 | + if (estatus == NULL || (mce->kflags & MCE_HANDLED_CEC)) |
|---|
| 151 | 149 | return NOTIFY_DONE; |
|---|
| 152 | 150 | |
|---|
| 153 | 151 | memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN); |
|---|
| .. | .. |
|---|
| 163 | 161 | |
|---|
| 164 | 162 | /* log event via trace */ |
|---|
| 165 | 163 | err_seq++; |
|---|
| 166 | | - gdata = (struct acpi_hest_generic_data *)(tmp + 1); |
|---|
| 167 | | - if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) |
|---|
| 168 | | - fru_id = (guid_t *)gdata->fru_id; |
|---|
| 169 | | - if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) |
|---|
| 170 | | - fru_text = gdata->fru_text; |
|---|
| 171 | | - sec_type = (guid_t *)gdata->section_type; |
|---|
| 172 | | - if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { |
|---|
| 173 | | - struct cper_sec_mem_err *mem = (void *)(gdata + 1); |
|---|
| 174 | | - if (gdata->error_data_length >= sizeof(*mem)) |
|---|
| 175 | | - trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, |
|---|
| 176 | | - (u8)gdata->error_severity); |
|---|
| 164 | + apei_estatus_for_each_section(tmp, gdata) { |
|---|
| 165 | + if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) |
|---|
| 166 | + fru_id = (guid_t *)gdata->fru_id; |
|---|
| 167 | + else |
|---|
| 168 | + fru_id = &guid_null; |
|---|
| 169 | + if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) |
|---|
| 170 | + fru_text = gdata->fru_text; |
|---|
| 171 | + else |
|---|
| 172 | + fru_text = ""; |
|---|
| 173 | + sec_type = (guid_t *)gdata->section_type; |
|---|
| 174 | + if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { |
|---|
| 175 | + struct cper_sec_mem_err *mem = (void *)(gdata + 1); |
|---|
| 176 | + |
|---|
| 177 | + if (gdata->error_data_length >= sizeof(*mem)) |
|---|
| 178 | + trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, |
|---|
| 179 | + (u8)gdata->error_severity); |
|---|
| 180 | + } |
|---|
| 177 | 181 | } |
|---|
| 178 | 182 | |
|---|
| 179 | 183 | out: |
|---|
| 180 | | - return NOTIFY_STOP; |
|---|
| 184 | + mce->kflags |= MCE_HANDLED_EXTLOG; |
|---|
| 185 | + return NOTIFY_OK; |
|---|
| 181 | 186 | } |
|---|
| 182 | 187 | |
|---|
| 183 | 188 | static bool __init extlog_get_l1addr(void) |
|---|
| .. | .. |
|---|
| 229 | 234 | !extlog_get_l1addr()) |
|---|
| 230 | 235 | return -ENODEV; |
|---|
| 231 | 236 | |
|---|
| 232 | | - if (edac_get_report_status() == EDAC_REPORTING_FORCE) { |
|---|
| 233 | | - pr_warn("Not loading eMCA, error reporting force-enabled through EDAC.\n"); |
|---|
| 234 | | - return -EPERM; |
|---|
| 235 | | - } |
|---|
| 236 | | - |
|---|
| 237 | 237 | rc = -EINVAL; |
|---|
| 238 | 238 | /* get L1 header to fetch necessary information */ |
|---|
| 239 | 239 | l1_hdr_size = sizeof(struct extlog_l1_head); |
|---|
| .. | .. |
|---|
| 281 | 281 | if (elog_buf == NULL) |
|---|
| 282 | 282 | goto err_release_elog; |
|---|
| 283 | 283 | |
|---|
| 284 | | - /* |
|---|
| 285 | | - * eMCA event report method has higher priority than EDAC method, |
|---|
| 286 | | - * unless EDAC event report method is mandatory. |
|---|
| 287 | | - */ |
|---|
| 288 | | - old_edac_report_status = edac_get_report_status(); |
|---|
| 289 | | - edac_set_report_status(EDAC_REPORTING_DISABLED); |
|---|
| 290 | 284 | mce_register_decode_chain(&extlog_mce_dec); |
|---|
| 291 | 285 | /* enable OS to be involved to take over management from BIOS */ |
|---|
| 292 | 286 | ((struct extlog_l1_head *)extlog_l1_addr)->flags |= FLAG_OS_OPTIN; |
|---|
| .. | .. |
|---|
| 308 | 302 | |
|---|
| 309 | 303 | static void __exit extlog_exit(void) |
|---|
| 310 | 304 | { |
|---|
| 311 | | - edac_set_report_status(old_edac_report_status); |
|---|
| 312 | 305 | mce_unregister_decode_chain(&extlog_mce_dec); |
|---|
| 313 | 306 | ((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN; |
|---|
| 314 | 307 | if (extlog_l1_addr) |
|---|