17ea6c6c1SLuck, Tony /* 27ea6c6c1SLuck, Tony * UEFI Common Platform Error Record (CPER) support 37ea6c6c1SLuck, Tony * 47ea6c6c1SLuck, Tony * Copyright (C) 2010, Intel Corp. 57ea6c6c1SLuck, Tony * Author: Huang Ying <ying.huang@intel.com> 67ea6c6c1SLuck, Tony * 77ea6c6c1SLuck, Tony * CPER is the format used to describe platform hardware error by 87ea6c6c1SLuck, Tony * various tables, such as ERST, BERT and HEST etc. 97ea6c6c1SLuck, Tony * 107ea6c6c1SLuck, Tony * For more information about CPER, please refer to Appendix N of UEFI 117ea6c6c1SLuck, Tony * Specification version 2.4. 127ea6c6c1SLuck, Tony * 137ea6c6c1SLuck, Tony * This program is free software; you can redistribute it and/or 147ea6c6c1SLuck, Tony * modify it under the terms of the GNU General Public License version 157ea6c6c1SLuck, Tony * 2 as published by the Free Software Foundation. 167ea6c6c1SLuck, Tony * 177ea6c6c1SLuck, Tony * This program is distributed in the hope that it will be useful, 187ea6c6c1SLuck, Tony * but WITHOUT ANY WARRANTY; without even the implied warranty of 197ea6c6c1SLuck, Tony * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 207ea6c6c1SLuck, Tony * GNU General Public License for more details. 217ea6c6c1SLuck, Tony * 227ea6c6c1SLuck, Tony * You should have received a copy of the GNU General Public License 237ea6c6c1SLuck, Tony * along with this program; if not, write to the Free Software 247ea6c6c1SLuck, Tony * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 257ea6c6c1SLuck, Tony */ 267ea6c6c1SLuck, Tony 277ea6c6c1SLuck, Tony #include <linux/kernel.h> 287ea6c6c1SLuck, Tony #include <linux/module.h> 297ea6c6c1SLuck, Tony #include <linux/time.h> 307ea6c6c1SLuck, Tony #include <linux/cper.h> 317ea6c6c1SLuck, Tony #include <linux/dmi.h> 327ea6c6c1SLuck, Tony #include <linux/acpi.h> 337ea6c6c1SLuck, Tony #include <linux/pci.h> 347ea6c6c1SLuck, Tony #include <linux/aer.h> 358a94471fSTyler Baicar #include <linux/printk.h> 368a94471fSTyler Baicar #include <linux/bcd.h> 37bbcc2e7bSTyler Baicar #include <acpi/ghes.h> 38e9279e83STyler Baicar #include <ras/ras_event.h> 397ea6c6c1SLuck, Tony 403760cd20SChen, Gong static char rcd_decode_str[CPER_REC_LEN]; 413760cd20SChen, Gong 427ea6c6c1SLuck, Tony /* 437ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 447ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 457ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 467ea6c6c1SLuck, Tony */ 477ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 487ea6c6c1SLuck, Tony { 497ea6c6c1SLuck, Tony static atomic64_t seq; 507ea6c6c1SLuck, Tony 517ea6c6c1SLuck, Tony if (!atomic64_read(&seq)) 527ea6c6c1SLuck, Tony atomic64_set(&seq, ((u64)get_seconds()) << 32); 537ea6c6c1SLuck, Tony 547ea6c6c1SLuck, Tony return atomic64_inc_return(&seq); 557ea6c6c1SLuck, Tony } 567ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id); 577ea6c6c1SLuck, Tony 583760cd20SChen, Gong static const char * const severity_strs[] = { 597ea6c6c1SLuck, Tony "recoverable", 607ea6c6c1SLuck, Tony "fatal", 617ea6c6c1SLuck, Tony "corrected", 627ea6c6c1SLuck, Tony "info", 637ea6c6c1SLuck, Tony }; 647ea6c6c1SLuck, Tony 653760cd20SChen, Gong const char *cper_severity_str(unsigned int severity) 667ea6c6c1SLuck, Tony { 673760cd20SChen, Gong return severity < ARRAY_SIZE(severity_strs) ? 683760cd20SChen, Gong severity_strs[severity] : "unknown"; 697ea6c6c1SLuck, Tony } 703760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_severity_str); 717ea6c6c1SLuck, Tony 727ea6c6c1SLuck, Tony /* 737ea6c6c1SLuck, Tony * cper_print_bits - print strings for set bits 747ea6c6c1SLuck, Tony * @pfx: prefix for each line, including log level and prefix string 757ea6c6c1SLuck, Tony * @bits: bit mask 767ea6c6c1SLuck, Tony * @strs: string array, indexed by bit position 777ea6c6c1SLuck, Tony * @strs_size: size of the string array: @strs 787ea6c6c1SLuck, Tony * 797ea6c6c1SLuck, Tony * For each set bit in @bits, print the corresponding string in @strs. 807ea6c6c1SLuck, Tony * If the output length is longer than 80, multiple line will be 817ea6c6c1SLuck, Tony * printed, with @pfx is printed at the beginning of each line. 827ea6c6c1SLuck, Tony */ 837ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits, 847ea6c6c1SLuck, Tony const char * const strs[], unsigned int strs_size) 857ea6c6c1SLuck, Tony { 867ea6c6c1SLuck, Tony int i, len = 0; 877ea6c6c1SLuck, Tony const char *str; 887ea6c6c1SLuck, Tony char buf[84]; 897ea6c6c1SLuck, Tony 907ea6c6c1SLuck, Tony for (i = 0; i < strs_size; i++) { 917ea6c6c1SLuck, Tony if (!(bits & (1U << i))) 927ea6c6c1SLuck, Tony continue; 937ea6c6c1SLuck, Tony str = strs[i]; 947ea6c6c1SLuck, Tony if (!str) 957ea6c6c1SLuck, Tony continue; 967ea6c6c1SLuck, Tony if (len && len + strlen(str) + 2 > 80) { 977ea6c6c1SLuck, Tony printk("%s\n", buf); 987ea6c6c1SLuck, Tony len = 0; 997ea6c6c1SLuck, Tony } 1007ea6c6c1SLuck, Tony if (!len) 1017ea6c6c1SLuck, Tony len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 1027ea6c6c1SLuck, Tony else 1037ea6c6c1SLuck, Tony len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); 1047ea6c6c1SLuck, Tony } 1057ea6c6c1SLuck, Tony if (len) 1067ea6c6c1SLuck, Tony printk("%s\n", buf); 1077ea6c6c1SLuck, Tony } 1087ea6c6c1SLuck, Tony 1093760cd20SChen, Gong static const char * const proc_type_strs[] = { 1107ea6c6c1SLuck, Tony "IA32/X64", 1117ea6c6c1SLuck, Tony "IA64", 1122f74f09bSTyler Baicar "ARM", 1137ea6c6c1SLuck, Tony }; 1147ea6c6c1SLuck, Tony 1153760cd20SChen, Gong static const char * const proc_isa_strs[] = { 1167ea6c6c1SLuck, Tony "IA32", 1177ea6c6c1SLuck, Tony "IA64", 1187ea6c6c1SLuck, Tony "X64", 1192f74f09bSTyler Baicar "ARM A32/T32", 1202f74f09bSTyler Baicar "ARM A64", 1217ea6c6c1SLuck, Tony }; 1227ea6c6c1SLuck, Tony 123c6d8c8efSTyler Baicar const char * const cper_proc_error_type_strs[] = { 1247ea6c6c1SLuck, Tony "cache error", 1257ea6c6c1SLuck, Tony "TLB error", 1267ea6c6c1SLuck, Tony "bus error", 1277ea6c6c1SLuck, Tony "micro-architectural error", 1287ea6c6c1SLuck, Tony }; 1297ea6c6c1SLuck, Tony 1303760cd20SChen, Gong static const char * const proc_op_strs[] = { 1317ea6c6c1SLuck, Tony "unknown or generic", 1327ea6c6c1SLuck, Tony "data read", 1337ea6c6c1SLuck, Tony "data write", 1347ea6c6c1SLuck, Tony "instruction execution", 1357ea6c6c1SLuck, Tony }; 1367ea6c6c1SLuck, Tony 1373760cd20SChen, Gong static const char * const proc_flag_strs[] = { 1387ea6c6c1SLuck, Tony "restartable", 1397ea6c6c1SLuck, Tony "precise IP", 1407ea6c6c1SLuck, Tony "overflow", 1417ea6c6c1SLuck, Tony "corrected", 1427ea6c6c1SLuck, Tony }; 1437ea6c6c1SLuck, Tony 1447ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx, 1457ea6c6c1SLuck, Tony const struct cper_sec_proc_generic *proc) 1467ea6c6c1SLuck, Tony { 1477ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TYPE) 1487ea6c6c1SLuck, Tony printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 1493760cd20SChen, Gong proc->proc_type < ARRAY_SIZE(proc_type_strs) ? 1503760cd20SChen, Gong proc_type_strs[proc->proc_type] : "unknown"); 1517ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ISA) 1527ea6c6c1SLuck, Tony printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 1533760cd20SChen, Gong proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? 1543760cd20SChen, Gong proc_isa_strs[proc->proc_isa] : "unknown"); 1557ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 1567ea6c6c1SLuck, Tony printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 1577ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->proc_error_type, 158c6d8c8efSTyler Baicar cper_proc_error_type_strs, 159c6d8c8efSTyler Baicar ARRAY_SIZE(cper_proc_error_type_strs)); 1607ea6c6c1SLuck, Tony } 1617ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 1627ea6c6c1SLuck, Tony printk("%s""operation: %d, %s\n", pfx, proc->operation, 1633760cd20SChen, Gong proc->operation < ARRAY_SIZE(proc_op_strs) ? 1643760cd20SChen, Gong proc_op_strs[proc->operation] : "unknown"); 1657ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 1667ea6c6c1SLuck, Tony printk("%s""flags: 0x%02x\n", pfx, proc->flags); 1673760cd20SChen, Gong cper_print_bits(pfx, proc->flags, proc_flag_strs, 1683760cd20SChen, Gong ARRAY_SIZE(proc_flag_strs)); 1697ea6c6c1SLuck, Tony } 1707ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 1717ea6c6c1SLuck, Tony printk("%s""level: %d\n", pfx, proc->level); 1727ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_VERSION) 1737ea6c6c1SLuck, Tony printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 1747ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ID) 1757ea6c6c1SLuck, Tony printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 1767ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 1777ea6c6c1SLuck, Tony printk("%s""target_address: 0x%016llx\n", 1787ea6c6c1SLuck, Tony pfx, proc->target_addr); 1797ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 1807ea6c6c1SLuck, Tony printk("%s""requestor_id: 0x%016llx\n", 1817ea6c6c1SLuck, Tony pfx, proc->requestor_id); 1827ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 1837ea6c6c1SLuck, Tony printk("%s""responder_id: 0x%016llx\n", 1847ea6c6c1SLuck, Tony pfx, proc->responder_id); 1857ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_IP) 1867ea6c6c1SLuck, Tony printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 1877ea6c6c1SLuck, Tony } 1887ea6c6c1SLuck, Tony 1893760cd20SChen, Gong static const char * const mem_err_type_strs[] = { 1907ea6c6c1SLuck, Tony "unknown", 1917ea6c6c1SLuck, Tony "no error", 1927ea6c6c1SLuck, Tony "single-bit ECC", 1937ea6c6c1SLuck, Tony "multi-bit ECC", 1947ea6c6c1SLuck, Tony "single-symbol chipkill ECC", 1957ea6c6c1SLuck, Tony "multi-symbol chipkill ECC", 1967ea6c6c1SLuck, Tony "master abort", 1977ea6c6c1SLuck, Tony "target abort", 1987ea6c6c1SLuck, Tony "parity error", 1997ea6c6c1SLuck, Tony "watchdog timeout", 2007ea6c6c1SLuck, Tony "invalid address", 2017ea6c6c1SLuck, Tony "mirror Broken", 2027ea6c6c1SLuck, Tony "memory sparing", 2037ea6c6c1SLuck, Tony "scrub corrected error", 2047ea6c6c1SLuck, Tony "scrub uncorrected error", 2057ea6c6c1SLuck, Tony "physical memory map-out event", 2067ea6c6c1SLuck, Tony }; 2077ea6c6c1SLuck, Tony 2083760cd20SChen, Gong const char *cper_mem_err_type_str(unsigned int etype) 2093760cd20SChen, Gong { 2103760cd20SChen, Gong return etype < ARRAY_SIZE(mem_err_type_strs) ? 2113760cd20SChen, Gong mem_err_type_strs[etype] : "unknown"; 2123760cd20SChen, Gong } 2133760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_mem_err_type_str); 2143760cd20SChen, Gong 2152dfb7d51SChen, Gong static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 2163760cd20SChen, Gong { 2173760cd20SChen, Gong u32 len, n; 2183760cd20SChen, Gong 2193760cd20SChen, Gong if (!msg) 2203760cd20SChen, Gong return 0; 2213760cd20SChen, Gong 2223760cd20SChen, Gong n = 0; 2233760cd20SChen, Gong len = CPER_REC_LEN - 1; 2243760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_NODE) 2253760cd20SChen, Gong n += scnprintf(msg + n, len - n, "node: %d ", mem->node); 2263760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_CARD) 2273760cd20SChen, Gong n += scnprintf(msg + n, len - n, "card: %d ", mem->card); 2283760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_MODULE) 2293760cd20SChen, Gong n += scnprintf(msg + n, len - n, "module: %d ", mem->module); 2303760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 2313760cd20SChen, Gong n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); 2323760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BANK) 2333760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); 2343760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 2353760cd20SChen, Gong n += scnprintf(msg + n, len - n, "device: %d ", mem->device); 2363760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_ROW) 2373760cd20SChen, Gong n += scnprintf(msg + n, len - n, "row: %d ", mem->row); 2383760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 2393760cd20SChen, Gong n += scnprintf(msg + n, len - n, "column: %d ", mem->column); 2403760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 2413760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bit_position: %d ", 2423760cd20SChen, Gong mem->bit_pos); 2433760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 2443760cd20SChen, Gong n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", 2453760cd20SChen, Gong mem->requestor_id); 2463760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 2473760cd20SChen, Gong n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", 2483760cd20SChen, Gong mem->responder_id); 2493760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 2503760cd20SChen, Gong scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 2513760cd20SChen, Gong mem->target_id); 2523760cd20SChen, Gong 2533760cd20SChen, Gong msg[n] = '\0'; 2543760cd20SChen, Gong return n; 2553760cd20SChen, Gong } 2563760cd20SChen, Gong 2572dfb7d51SChen, Gong static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 2583760cd20SChen, Gong { 2593760cd20SChen, Gong u32 len, n; 2603760cd20SChen, Gong const char *bank = NULL, *device = NULL; 2613760cd20SChen, Gong 2623760cd20SChen, Gong if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 2633760cd20SChen, Gong return 0; 2643760cd20SChen, Gong 2653760cd20SChen, Gong n = 0; 2663760cd20SChen, Gong len = CPER_REC_LEN - 1; 2673760cd20SChen, Gong dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 2683760cd20SChen, Gong if (bank && device) 2693760cd20SChen, Gong n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 2703760cd20SChen, Gong else 2713760cd20SChen, Gong n = snprintf(msg, len, 2723760cd20SChen, Gong "DIMM location: not present. DMI handle: 0x%.4x ", 2733760cd20SChen, Gong mem->mem_dev_handle); 2743760cd20SChen, Gong 2753760cd20SChen, Gong msg[n] = '\0'; 2763760cd20SChen, Gong return n; 2773760cd20SChen, Gong } 2783760cd20SChen, Gong 2792dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 2802dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 2812dfb7d51SChen, Gong { 2822dfb7d51SChen, Gong cmem->validation_bits = mem->validation_bits; 2832dfb7d51SChen, Gong cmem->node = mem->node; 2842dfb7d51SChen, Gong cmem->card = mem->card; 2852dfb7d51SChen, Gong cmem->module = mem->module; 2862dfb7d51SChen, Gong cmem->bank = mem->bank; 2872dfb7d51SChen, Gong cmem->device = mem->device; 2882dfb7d51SChen, Gong cmem->row = mem->row; 2892dfb7d51SChen, Gong cmem->column = mem->column; 2902dfb7d51SChen, Gong cmem->bit_pos = mem->bit_pos; 2912dfb7d51SChen, Gong cmem->requestor_id = mem->requestor_id; 2922dfb7d51SChen, Gong cmem->responder_id = mem->responder_id; 2932dfb7d51SChen, Gong cmem->target_id = mem->target_id; 2942dfb7d51SChen, Gong cmem->rank = mem->rank; 2952dfb7d51SChen, Gong cmem->mem_array_handle = mem->mem_array_handle; 2962dfb7d51SChen, Gong cmem->mem_dev_handle = mem->mem_dev_handle; 2972dfb7d51SChen, Gong } 2982dfb7d51SChen, Gong 2992dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p, 3002dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 3012dfb7d51SChen, Gong { 302dbcf3e06SSteven Rostedt (Red Hat) const char *ret = trace_seq_buffer_ptr(p); 3032dfb7d51SChen, Gong 3042dfb7d51SChen, Gong if (cper_mem_err_location(cmem, rcd_decode_str)) 3052dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3062dfb7d51SChen, Gong if (cper_dimm_err_location(cmem, rcd_decode_str)) 3072dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3082dfb7d51SChen, Gong trace_seq_putc(p, '\0'); 3092dfb7d51SChen, Gong 3102dfb7d51SChen, Gong return ret; 3112dfb7d51SChen, Gong } 3122dfb7d51SChen, Gong 3134c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 3144c62360dSLuck, Tony int len) 3157ea6c6c1SLuck, Tony { 3162dfb7d51SChen, Gong struct cper_mem_err_compact cmem; 3172dfb7d51SChen, Gong 3184c62360dSLuck, Tony /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 3194c62360dSLuck, Tony if (len == sizeof(struct cper_sec_mem_err_old) && 3204c62360dSLuck, Tony (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 3214c62360dSLuck, Tony pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 3224c62360dSLuck, Tony return; 3234c62360dSLuck, Tony } 3247ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 3257ea6c6c1SLuck, Tony printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 3267ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 3277ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 3287ea6c6c1SLuck, Tony pfx, mem->physical_addr); 3297ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 3307ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 3317ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 3322dfb7d51SChen, Gong cper_mem_err_pack(mem, &cmem); 3332dfb7d51SChen, Gong if (cper_mem_err_location(&cmem, rcd_decode_str)) 3343760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3357ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 3367ea6c6c1SLuck, Tony u8 etype = mem->error_type; 3377ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 3383760cd20SChen, Gong cper_mem_err_type_str(etype)); 3397ea6c6c1SLuck, Tony } 3402dfb7d51SChen, Gong if (cper_dimm_err_location(&cmem, rcd_decode_str)) 3413760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3427ea6c6c1SLuck, Tony } 3437ea6c6c1SLuck, Tony 3443760cd20SChen, Gong static const char * const pcie_port_type_strs[] = { 3457ea6c6c1SLuck, Tony "PCIe end point", 3467ea6c6c1SLuck, Tony "legacy PCI end point", 3477ea6c6c1SLuck, Tony "unknown", 3487ea6c6c1SLuck, Tony "unknown", 3497ea6c6c1SLuck, Tony "root port", 3507ea6c6c1SLuck, Tony "upstream switch port", 3517ea6c6c1SLuck, Tony "downstream switch port", 3527ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 3537ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 3547ea6c6c1SLuck, Tony "root complex integrated endpoint device", 3557ea6c6c1SLuck, Tony "root complex event collector", 3567ea6c6c1SLuck, Tony }; 3577ea6c6c1SLuck, Tony 3587ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 3590a00fd5eSLv Zheng const struct acpi_hest_generic_data *gdata) 3607ea6c6c1SLuck, Tony { 3617ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 3627ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 3633760cd20SChen, Gong pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 3643760cd20SChen, Gong pcie_port_type_strs[pcie->port_type] : "unknown"); 3657ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 3667ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 3677ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 3687ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 3697ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 3707ea6c6c1SLuck, Tony pcie->command, pcie->status); 3717ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 3727ea6c6c1SLuck, Tony const __u8 *p; 3737ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 3747ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 3757ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 3767ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 3777ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 3787ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 3797ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 3807ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 3817ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 3827ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 3837ea6c6c1SLuck, Tony printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); 3847ea6c6c1SLuck, Tony } 3857ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 3867ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 3877ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 3887ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 3897ea6c6c1SLuck, Tony printk( 3907ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 3917ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 3927ea6c6c1SLuck, Tony } 3937ea6c6c1SLuck, Tony 3948a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx, 3958a94471fSTyler Baicar struct acpi_hest_generic_data_v300 *gdata) 3968a94471fSTyler Baicar { 3978a94471fSTyler Baicar __u8 hour, min, sec, day, mon, year, century, *timestamp; 3988a94471fSTyler Baicar 3998a94471fSTyler Baicar if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 4008a94471fSTyler Baicar timestamp = (__u8 *)&(gdata->time_stamp); 4018a94471fSTyler Baicar sec = bcd2bin(timestamp[0]); 4028a94471fSTyler Baicar min = bcd2bin(timestamp[1]); 4038a94471fSTyler Baicar hour = bcd2bin(timestamp[2]); 4048a94471fSTyler Baicar day = bcd2bin(timestamp[4]); 4058a94471fSTyler Baicar mon = bcd2bin(timestamp[5]); 4068a94471fSTyler Baicar year = bcd2bin(timestamp[6]); 4078a94471fSTyler Baicar century = bcd2bin(timestamp[7]); 4088a94471fSTyler Baicar 4098a94471fSTyler Baicar printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 4108a94471fSTyler Baicar (timestamp[3] & 0x1 ? "precise " : "imprecise "), 4118a94471fSTyler Baicar century, year, mon, day, hour, min, sec); 4128a94471fSTyler Baicar } 4138a94471fSTyler Baicar } 4148a94471fSTyler Baicar 415bbcc2e7bSTyler Baicar static void 416bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 417bbcc2e7bSTyler Baicar int sec_no) 4187ea6c6c1SLuck, Tony { 419c0020756SAndy Shevchenko guid_t *sec_type = (guid_t *)gdata->section_type; 4207ea6c6c1SLuck, Tony __u16 severity; 4217ea6c6c1SLuck, Tony char newpfx[64]; 4227ea6c6c1SLuck, Tony 4238a94471fSTyler Baicar if (acpi_hest_get_version(gdata) >= 3) 4248a94471fSTyler Baicar cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 4258a94471fSTyler Baicar 4267ea6c6c1SLuck, Tony severity = gdata->error_severity; 4277ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 4287ea6c6c1SLuck, Tony cper_severity_str(severity)); 4297ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 430c0020756SAndy Shevchenko printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 4317ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 4327ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 4337ea6c6c1SLuck, Tony 43475e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 435c0020756SAndy Shevchenko if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 436bbcc2e7bSTyler Baicar struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 437bbcc2e7bSTyler Baicar 4387ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 4397ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 4407ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 4417ea6c6c1SLuck, Tony else 4427ea6c6c1SLuck, Tony goto err_section_too_small; 443c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 444bbcc2e7bSTyler Baicar struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 445bbcc2e7bSTyler Baicar 4467ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 4474c62360dSLuck, Tony if (gdata->error_data_length >= 4484c62360dSLuck, Tony sizeof(struct cper_sec_mem_err_old)) 4494c62360dSLuck, Tony cper_print_mem(newpfx, mem_err, 4504c62360dSLuck, Tony gdata->error_data_length); 4517ea6c6c1SLuck, Tony else 4527ea6c6c1SLuck, Tony goto err_section_too_small; 453c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 454bbcc2e7bSTyler Baicar struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 455bbcc2e7bSTyler Baicar 4567ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 4577ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 4587ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 4597ea6c6c1SLuck, Tony else 4607ea6c6c1SLuck, Tony goto err_section_too_small; 4612f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 4622f74f09bSTyler Baicar } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_ARM)) { 4632f74f09bSTyler Baicar struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 4642f74f09bSTyler Baicar 4652f74f09bSTyler Baicar printk("%ssection_type: ARM processor error\n", newpfx); 4662f74f09bSTyler Baicar if (gdata->error_data_length >= sizeof(*arm_err)) 4672f74f09bSTyler Baicar cper_print_proc_arm(newpfx, arm_err); 4682f74f09bSTyler Baicar else 4692f74f09bSTyler Baicar goto err_section_too_small; 4702f74f09bSTyler Baicar #endif 471*f9e1bdb9SYazen Ghannam #if defined(CONFIG_UEFI_CPER_X86) 472*f9e1bdb9SYazen Ghannam } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 473*f9e1bdb9SYazen Ghannam struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 474*f9e1bdb9SYazen Ghannam 475*f9e1bdb9SYazen Ghannam printk("%ssection_type: IA32/X64 processor error\n", newpfx); 476*f9e1bdb9SYazen Ghannam if (gdata->error_data_length >= sizeof(*ia_err)) 477*f9e1bdb9SYazen Ghannam cper_print_proc_ia(newpfx, ia_err); 478*f9e1bdb9SYazen Ghannam else 479*f9e1bdb9SYazen Ghannam goto err_section_too_small; 480*f9e1bdb9SYazen Ghannam #endif 4810fc300f4STyler Baicar } else { 4820fc300f4STyler Baicar const void *err = acpi_hest_get_payload(gdata); 4830fc300f4STyler Baicar 4840fc300f4STyler Baicar printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 4850fc300f4STyler Baicar printk("%ssection length: %#x\n", newpfx, 4860fc300f4STyler Baicar gdata->error_data_length); 4870fc300f4STyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 4880fc300f4STyler Baicar gdata->error_data_length, true); 4890fc300f4STyler Baicar } 4907ea6c6c1SLuck, Tony 4917ea6c6c1SLuck, Tony return; 4927ea6c6c1SLuck, Tony 4937ea6c6c1SLuck, Tony err_section_too_small: 4947ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 4957ea6c6c1SLuck, Tony } 4967ea6c6c1SLuck, Tony 4977ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 4980a00fd5eSLv Zheng const struct acpi_hest_generic_status *estatus) 4997ea6c6c1SLuck, Tony { 5000a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 5017ea6c6c1SLuck, Tony int sec_no = 0; 5027ea6c6c1SLuck, Tony char newpfx[64]; 5037ea6c6c1SLuck, Tony __u16 severity; 5047ea6c6c1SLuck, Tony 5057ea6c6c1SLuck, Tony severity = estatus->error_severity; 5067ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 5077ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 5087ea6c6c1SLuck, Tony "It has been corrected by h/w " 5097ea6c6c1SLuck, Tony "and requires no further action"); 5107ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 51175e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 512bbcc2e7bSTyler Baicar 513c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 5147ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 5157ea6c6c1SLuck, Tony sec_no++; 5167ea6c6c1SLuck, Tony } 5177ea6c6c1SLuck, Tony } 5187ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 5197ea6c6c1SLuck, Tony 5200a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 5217ea6c6c1SLuck, Tony { 5227ea6c6c1SLuck, Tony if (estatus->data_length && 5230a00fd5eSLv Zheng estatus->data_length < sizeof(struct acpi_hest_generic_data)) 5247ea6c6c1SLuck, Tony return -EINVAL; 5257ea6c6c1SLuck, Tony if (estatus->raw_data_length && 5267ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 5277ea6c6c1SLuck, Tony return -EINVAL; 5287ea6c6c1SLuck, Tony 5297ea6c6c1SLuck, Tony return 0; 5307ea6c6c1SLuck, Tony } 5317ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 5327ea6c6c1SLuck, Tony 5330a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 5347ea6c6c1SLuck, Tony { 5350a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 5367ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 5377ea6c6c1SLuck, Tony int rc; 5387ea6c6c1SLuck, Tony 5397ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 5407ea6c6c1SLuck, Tony if (rc) 5417ea6c6c1SLuck, Tony return rc; 5427ea6c6c1SLuck, Tony data_len = estatus->data_length; 543bbcc2e7bSTyler Baicar 544c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 545bbcc2e7bSTyler Baicar gedata_len = acpi_hest_get_error_length(gdata); 546bbcc2e7bSTyler Baicar if (gedata_len > data_len - acpi_hest_get_size(gdata)) 5477ea6c6c1SLuck, Tony return -EINVAL; 548bbcc2e7bSTyler Baicar data_len -= acpi_hest_get_record_size(gdata); 5497ea6c6c1SLuck, Tony } 5507ea6c6c1SLuck, Tony if (data_len) 5517ea6c6c1SLuck, Tony return -EINVAL; 5527ea6c6c1SLuck, Tony 5537ea6c6c1SLuck, Tony return 0; 5547ea6c6c1SLuck, Tony } 5557ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 556