.. | .. |
---|
| 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) |
---|