14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0 27ea6c6c1SLuck, Tony /* 37ea6c6c1SLuck, Tony * UEFI Common Platform Error Record (CPER) support 47ea6c6c1SLuck, Tony * 57ea6c6c1SLuck, Tony * Copyright (C) 2010, Intel Corp. 67ea6c6c1SLuck, Tony * Author: Huang Ying <ying.huang@intel.com> 77ea6c6c1SLuck, Tony * 87ea6c6c1SLuck, Tony * CPER is the format used to describe platform hardware error by 97ea6c6c1SLuck, Tony * various tables, such as ERST, BERT and HEST etc. 107ea6c6c1SLuck, Tony * 117ea6c6c1SLuck, Tony * For more information about CPER, please refer to Appendix N of UEFI 127ea6c6c1SLuck, Tony * Specification version 2.4. 137ea6c6c1SLuck, Tony */ 147ea6c6c1SLuck, Tony 157ea6c6c1SLuck, Tony #include <linux/kernel.h> 167ea6c6c1SLuck, Tony #include <linux/module.h> 177ea6c6c1SLuck, Tony #include <linux/time.h> 187ea6c6c1SLuck, Tony #include <linux/cper.h> 197ea6c6c1SLuck, Tony #include <linux/dmi.h> 207ea6c6c1SLuck, Tony #include <linux/acpi.h> 217ea6c6c1SLuck, Tony #include <linux/pci.h> 227ea6c6c1SLuck, Tony #include <linux/aer.h> 238a94471fSTyler Baicar #include <linux/printk.h> 248a94471fSTyler Baicar #include <linux/bcd.h> 25bbcc2e7bSTyler Baicar #include <acpi/ghes.h> 26e9279e83STyler Baicar #include <ras/ras_event.h> 277ea6c6c1SLuck, Tony 283760cd20SChen, Gong static char rcd_decode_str[CPER_REC_LEN]; 293760cd20SChen, Gong 307ea6c6c1SLuck, Tony /* 317ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 327ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 337ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 347ea6c6c1SLuck, Tony */ 357ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 367ea6c6c1SLuck, Tony { 377ea6c6c1SLuck, Tony static atomic64_t seq; 387ea6c6c1SLuck, Tony 397bb49709SArnd Bergmann if (!atomic64_read(&seq)) { 407bb49709SArnd Bergmann time64_t time = ktime_get_real_seconds(); 417bb49709SArnd Bergmann 427bb49709SArnd Bergmann /* 437bb49709SArnd Bergmann * This code is unlikely to still be needed in year 2106, 447bb49709SArnd Bergmann * but just in case, let's use a few more bits for timestamps 457bb49709SArnd Bergmann * after y2038 to be sure they keep increasing monotonically 467bb49709SArnd Bergmann * for the next few hundred years... 477bb49709SArnd Bergmann */ 487bb49709SArnd Bergmann if (time < 0x80000000) 497bb49709SArnd Bergmann atomic64_set(&seq, (ktime_get_real_seconds()) << 32); 507bb49709SArnd Bergmann else 517bb49709SArnd Bergmann atomic64_set(&seq, 0x8000000000000000ull | 527bb49709SArnd Bergmann ktime_get_real_seconds() << 24); 537bb49709SArnd Bergmann } 547ea6c6c1SLuck, Tony 557ea6c6c1SLuck, Tony return atomic64_inc_return(&seq); 567ea6c6c1SLuck, Tony } 577ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id); 587ea6c6c1SLuck, Tony 593760cd20SChen, Gong static const char * const severity_strs[] = { 607ea6c6c1SLuck, Tony "recoverable", 617ea6c6c1SLuck, Tony "fatal", 627ea6c6c1SLuck, Tony "corrected", 637ea6c6c1SLuck, Tony "info", 647ea6c6c1SLuck, Tony }; 657ea6c6c1SLuck, Tony 663760cd20SChen, Gong const char *cper_severity_str(unsigned int severity) 677ea6c6c1SLuck, Tony { 683760cd20SChen, Gong return severity < ARRAY_SIZE(severity_strs) ? 693760cd20SChen, Gong severity_strs[severity] : "unknown"; 707ea6c6c1SLuck, Tony } 713760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_severity_str); 727ea6c6c1SLuck, Tony 737ea6c6c1SLuck, Tony /* 747ea6c6c1SLuck, Tony * cper_print_bits - print strings for set bits 757ea6c6c1SLuck, Tony * @pfx: prefix for each line, including log level and prefix string 767ea6c6c1SLuck, Tony * @bits: bit mask 777ea6c6c1SLuck, Tony * @strs: string array, indexed by bit position 787ea6c6c1SLuck, Tony * @strs_size: size of the string array: @strs 797ea6c6c1SLuck, Tony * 807ea6c6c1SLuck, Tony * For each set bit in @bits, print the corresponding string in @strs. 817ea6c6c1SLuck, Tony * If the output length is longer than 80, multiple line will be 827ea6c6c1SLuck, Tony * printed, with @pfx is printed at the beginning of each line. 837ea6c6c1SLuck, Tony */ 847ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits, 857ea6c6c1SLuck, Tony const char * const strs[], unsigned int strs_size) 867ea6c6c1SLuck, Tony { 877ea6c6c1SLuck, Tony int i, len = 0; 887ea6c6c1SLuck, Tony const char *str; 897ea6c6c1SLuck, Tony char buf[84]; 907ea6c6c1SLuck, Tony 917ea6c6c1SLuck, Tony for (i = 0; i < strs_size; i++) { 927ea6c6c1SLuck, Tony if (!(bits & (1U << i))) 937ea6c6c1SLuck, Tony continue; 947ea6c6c1SLuck, Tony str = strs[i]; 957ea6c6c1SLuck, Tony if (!str) 967ea6c6c1SLuck, Tony continue; 977ea6c6c1SLuck, Tony if (len && len + strlen(str) + 2 > 80) { 987ea6c6c1SLuck, Tony printk("%s\n", buf); 997ea6c6c1SLuck, Tony len = 0; 1007ea6c6c1SLuck, Tony } 1017ea6c6c1SLuck, Tony if (!len) 1027ea6c6c1SLuck, Tony len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 1037ea6c6c1SLuck, Tony else 104b450b30bSTakashi Iwai len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str); 1057ea6c6c1SLuck, Tony } 1067ea6c6c1SLuck, Tony if (len) 1077ea6c6c1SLuck, Tony printk("%s\n", buf); 1087ea6c6c1SLuck, Tony } 1097ea6c6c1SLuck, Tony 1103760cd20SChen, Gong static const char * const proc_type_strs[] = { 1117ea6c6c1SLuck, Tony "IA32/X64", 1127ea6c6c1SLuck, Tony "IA64", 1132f74f09bSTyler Baicar "ARM", 1147ea6c6c1SLuck, Tony }; 1157ea6c6c1SLuck, Tony 1163760cd20SChen, Gong static const char * const proc_isa_strs[] = { 1177ea6c6c1SLuck, Tony "IA32", 1187ea6c6c1SLuck, Tony "IA64", 1197ea6c6c1SLuck, Tony "X64", 1202f74f09bSTyler Baicar "ARM A32/T32", 1212f74f09bSTyler Baicar "ARM A64", 1227ea6c6c1SLuck, Tony }; 1237ea6c6c1SLuck, Tony 124c6d8c8efSTyler Baicar const char * const cper_proc_error_type_strs[] = { 1257ea6c6c1SLuck, Tony "cache error", 1267ea6c6c1SLuck, Tony "TLB error", 1277ea6c6c1SLuck, Tony "bus error", 1287ea6c6c1SLuck, Tony "micro-architectural error", 1297ea6c6c1SLuck, Tony }; 1307ea6c6c1SLuck, Tony 1313760cd20SChen, Gong static const char * const proc_op_strs[] = { 1327ea6c6c1SLuck, Tony "unknown or generic", 1337ea6c6c1SLuck, Tony "data read", 1347ea6c6c1SLuck, Tony "data write", 1357ea6c6c1SLuck, Tony "instruction execution", 1367ea6c6c1SLuck, Tony }; 1377ea6c6c1SLuck, Tony 1383760cd20SChen, Gong static const char * const proc_flag_strs[] = { 1397ea6c6c1SLuck, Tony "restartable", 1407ea6c6c1SLuck, Tony "precise IP", 1417ea6c6c1SLuck, Tony "overflow", 1427ea6c6c1SLuck, Tony "corrected", 1437ea6c6c1SLuck, Tony }; 1447ea6c6c1SLuck, Tony 1457ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx, 1467ea6c6c1SLuck, Tony const struct cper_sec_proc_generic *proc) 1477ea6c6c1SLuck, Tony { 1487ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TYPE) 1497ea6c6c1SLuck, Tony printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 1503760cd20SChen, Gong proc->proc_type < ARRAY_SIZE(proc_type_strs) ? 1513760cd20SChen, Gong proc_type_strs[proc->proc_type] : "unknown"); 1527ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ISA) 1537ea6c6c1SLuck, Tony printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 1543760cd20SChen, Gong proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? 1553760cd20SChen, Gong proc_isa_strs[proc->proc_isa] : "unknown"); 1567ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 1577ea6c6c1SLuck, Tony printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 1587ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->proc_error_type, 159c6d8c8efSTyler Baicar cper_proc_error_type_strs, 160c6d8c8efSTyler Baicar ARRAY_SIZE(cper_proc_error_type_strs)); 1617ea6c6c1SLuck, Tony } 1627ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 1637ea6c6c1SLuck, Tony printk("%s""operation: %d, %s\n", pfx, proc->operation, 1643760cd20SChen, Gong proc->operation < ARRAY_SIZE(proc_op_strs) ? 1653760cd20SChen, Gong proc_op_strs[proc->operation] : "unknown"); 1667ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 1677ea6c6c1SLuck, Tony printk("%s""flags: 0x%02x\n", pfx, proc->flags); 1683760cd20SChen, Gong cper_print_bits(pfx, proc->flags, proc_flag_strs, 1693760cd20SChen, Gong ARRAY_SIZE(proc_flag_strs)); 1707ea6c6c1SLuck, Tony } 1717ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 1727ea6c6c1SLuck, Tony printk("%s""level: %d\n", pfx, proc->level); 1737ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_VERSION) 1747ea6c6c1SLuck, Tony printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 1757ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ID) 1767ea6c6c1SLuck, Tony printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 1777ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 1787ea6c6c1SLuck, Tony printk("%s""target_address: 0x%016llx\n", 1797ea6c6c1SLuck, Tony pfx, proc->target_addr); 1807ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 1817ea6c6c1SLuck, Tony printk("%s""requestor_id: 0x%016llx\n", 1827ea6c6c1SLuck, Tony pfx, proc->requestor_id); 1837ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 1847ea6c6c1SLuck, Tony printk("%s""responder_id: 0x%016llx\n", 1857ea6c6c1SLuck, Tony pfx, proc->responder_id); 1867ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_IP) 1877ea6c6c1SLuck, Tony printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 1887ea6c6c1SLuck, Tony } 1897ea6c6c1SLuck, Tony 1903760cd20SChen, Gong static const char * const mem_err_type_strs[] = { 1917ea6c6c1SLuck, Tony "unknown", 1927ea6c6c1SLuck, Tony "no error", 1937ea6c6c1SLuck, Tony "single-bit ECC", 1947ea6c6c1SLuck, Tony "multi-bit ECC", 1957ea6c6c1SLuck, Tony "single-symbol chipkill ECC", 1967ea6c6c1SLuck, Tony "multi-symbol chipkill ECC", 1977ea6c6c1SLuck, Tony "master abort", 1987ea6c6c1SLuck, Tony "target abort", 1997ea6c6c1SLuck, Tony "parity error", 2007ea6c6c1SLuck, Tony "watchdog timeout", 2017ea6c6c1SLuck, Tony "invalid address", 2027ea6c6c1SLuck, Tony "mirror Broken", 2037ea6c6c1SLuck, Tony "memory sparing", 2047ea6c6c1SLuck, Tony "scrub corrected error", 2057ea6c6c1SLuck, Tony "scrub uncorrected error", 2067ea6c6c1SLuck, Tony "physical memory map-out event", 2077ea6c6c1SLuck, Tony }; 2087ea6c6c1SLuck, Tony 2093760cd20SChen, Gong const char *cper_mem_err_type_str(unsigned int etype) 2103760cd20SChen, Gong { 2113760cd20SChen, Gong return etype < ARRAY_SIZE(mem_err_type_strs) ? 2123760cd20SChen, Gong mem_err_type_strs[etype] : "unknown"; 2133760cd20SChen, Gong } 2143760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_mem_err_type_str); 2153760cd20SChen, Gong 2162dfb7d51SChen, Gong static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 2173760cd20SChen, Gong { 2183760cd20SChen, Gong u32 len, n; 2193760cd20SChen, Gong 2203760cd20SChen, Gong if (!msg) 2213760cd20SChen, Gong return 0; 2223760cd20SChen, Gong 2233760cd20SChen, Gong n = 0; 2243760cd20SChen, Gong len = CPER_REC_LEN - 1; 2253760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_NODE) 2263760cd20SChen, Gong n += scnprintf(msg + n, len - n, "node: %d ", mem->node); 2273760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_CARD) 2283760cd20SChen, Gong n += scnprintf(msg + n, len - n, "card: %d ", mem->card); 2293760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_MODULE) 2303760cd20SChen, Gong n += scnprintf(msg + n, len - n, "module: %d ", mem->module); 2313760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 2323760cd20SChen, Gong n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); 2333760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BANK) 2343760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); 2353760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 2363760cd20SChen, Gong n += scnprintf(msg + n, len - n, "device: %d ", mem->device); 2373760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_ROW) 2383760cd20SChen, Gong n += scnprintf(msg + n, len - n, "row: %d ", mem->row); 2393760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 2403760cd20SChen, Gong n += scnprintf(msg + n, len - n, "column: %d ", mem->column); 2413760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 2423760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bit_position: %d ", 2433760cd20SChen, Gong mem->bit_pos); 2443760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 2453760cd20SChen, Gong n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", 2463760cd20SChen, Gong mem->requestor_id); 2473760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 2483760cd20SChen, Gong n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", 2493760cd20SChen, Gong mem->responder_id); 2503760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 2513760cd20SChen, Gong scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 2523760cd20SChen, Gong mem->target_id); 2533760cd20SChen, Gong 2543760cd20SChen, Gong msg[n] = '\0'; 2553760cd20SChen, Gong return n; 2563760cd20SChen, Gong } 2573760cd20SChen, Gong 2582dfb7d51SChen, Gong static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 2593760cd20SChen, Gong { 2603760cd20SChen, Gong u32 len, n; 2613760cd20SChen, Gong const char *bank = NULL, *device = NULL; 2623760cd20SChen, Gong 2633760cd20SChen, Gong if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 2643760cd20SChen, Gong return 0; 2653760cd20SChen, Gong 2663760cd20SChen, Gong n = 0; 2673760cd20SChen, Gong len = CPER_REC_LEN - 1; 2683760cd20SChen, Gong dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 2693760cd20SChen, Gong if (bank && device) 2703760cd20SChen, Gong n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 2713760cd20SChen, Gong else 2723760cd20SChen, Gong n = snprintf(msg, len, 2733760cd20SChen, Gong "DIMM location: not present. DMI handle: 0x%.4x ", 2743760cd20SChen, Gong mem->mem_dev_handle); 2753760cd20SChen, Gong 2763760cd20SChen, Gong msg[n] = '\0'; 2773760cd20SChen, Gong return n; 2783760cd20SChen, Gong } 2793760cd20SChen, Gong 2802dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 2812dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 2822dfb7d51SChen, Gong { 2832dfb7d51SChen, Gong cmem->validation_bits = mem->validation_bits; 2842dfb7d51SChen, Gong cmem->node = mem->node; 2852dfb7d51SChen, Gong cmem->card = mem->card; 2862dfb7d51SChen, Gong cmem->module = mem->module; 2872dfb7d51SChen, Gong cmem->bank = mem->bank; 2882dfb7d51SChen, Gong cmem->device = mem->device; 2892dfb7d51SChen, Gong cmem->row = mem->row; 2902dfb7d51SChen, Gong cmem->column = mem->column; 2912dfb7d51SChen, Gong cmem->bit_pos = mem->bit_pos; 2922dfb7d51SChen, Gong cmem->requestor_id = mem->requestor_id; 2932dfb7d51SChen, Gong cmem->responder_id = mem->responder_id; 2942dfb7d51SChen, Gong cmem->target_id = mem->target_id; 2952dfb7d51SChen, Gong cmem->rank = mem->rank; 2962dfb7d51SChen, Gong cmem->mem_array_handle = mem->mem_array_handle; 2972dfb7d51SChen, Gong cmem->mem_dev_handle = mem->mem_dev_handle; 2982dfb7d51SChen, Gong } 2992dfb7d51SChen, Gong 3002dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p, 3012dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 3022dfb7d51SChen, Gong { 303dbcf3e06SSteven Rostedt (Red Hat) const char *ret = trace_seq_buffer_ptr(p); 3042dfb7d51SChen, Gong 3052dfb7d51SChen, Gong if (cper_mem_err_location(cmem, rcd_decode_str)) 3062dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3072dfb7d51SChen, Gong if (cper_dimm_err_location(cmem, rcd_decode_str)) 3082dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3092dfb7d51SChen, Gong trace_seq_putc(p, '\0'); 3102dfb7d51SChen, Gong 3112dfb7d51SChen, Gong return ret; 3122dfb7d51SChen, Gong } 3132dfb7d51SChen, Gong 3144c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 3154c62360dSLuck, Tony int len) 3167ea6c6c1SLuck, Tony { 3172dfb7d51SChen, Gong struct cper_mem_err_compact cmem; 3182dfb7d51SChen, Gong 3194c62360dSLuck, Tony /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 3204c62360dSLuck, Tony if (len == sizeof(struct cper_sec_mem_err_old) && 3214c62360dSLuck, Tony (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 3224c62360dSLuck, Tony pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 3234c62360dSLuck, Tony return; 3244c62360dSLuck, Tony } 3257ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 3267ea6c6c1SLuck, Tony printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 3277ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 3287ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 3297ea6c6c1SLuck, Tony pfx, mem->physical_addr); 3307ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 3317ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 3327ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 3332dfb7d51SChen, Gong cper_mem_err_pack(mem, &cmem); 3342dfb7d51SChen, Gong if (cper_mem_err_location(&cmem, rcd_decode_str)) 3353760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3367ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 3377ea6c6c1SLuck, Tony u8 etype = mem->error_type; 3387ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 3393760cd20SChen, Gong cper_mem_err_type_str(etype)); 3407ea6c6c1SLuck, Tony } 3412dfb7d51SChen, Gong if (cper_dimm_err_location(&cmem, rcd_decode_str)) 3423760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3437ea6c6c1SLuck, Tony } 3447ea6c6c1SLuck, Tony 3453760cd20SChen, Gong static const char * const pcie_port_type_strs[] = { 3467ea6c6c1SLuck, Tony "PCIe end point", 3477ea6c6c1SLuck, Tony "legacy PCI end point", 3487ea6c6c1SLuck, Tony "unknown", 3497ea6c6c1SLuck, Tony "unknown", 3507ea6c6c1SLuck, Tony "root port", 3517ea6c6c1SLuck, Tony "upstream switch port", 3527ea6c6c1SLuck, Tony "downstream switch port", 3537ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 3547ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 3557ea6c6c1SLuck, Tony "root complex integrated endpoint device", 3567ea6c6c1SLuck, Tony "root complex event collector", 3577ea6c6c1SLuck, Tony }; 3587ea6c6c1SLuck, Tony 3597ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 3600a00fd5eSLv Zheng const struct acpi_hest_generic_data *gdata) 3617ea6c6c1SLuck, Tony { 3627ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 3637ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 3643760cd20SChen, Gong pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 3653760cd20SChen, Gong pcie_port_type_strs[pcie->port_type] : "unknown"); 3667ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 3677ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 3687ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 3697ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 3707ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 3717ea6c6c1SLuck, Tony pcie->command, pcie->status); 3727ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 3737ea6c6c1SLuck, Tony const __u8 *p; 3747ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 3757ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 3767ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 3777ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 3787ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 3797ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 3807ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 3817ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 3827ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 3837ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 3846fb9367aSLukas Wunner printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 3857ea6c6c1SLuck, Tony } 3867ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 3877ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 3887ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 3897ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 3907ea6c6c1SLuck, Tony printk( 3917ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 3927ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 393b194a77fSXiaofei Tan 394b194a77fSXiaofei Tan /* Fatal errors call __ghes_panic() before AER handler prints this */ 395b194a77fSXiaofei Tan if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 396b194a77fSXiaofei Tan (gdata->error_severity & CPER_SEV_FATAL)) { 397b194a77fSXiaofei Tan struct aer_capability_regs *aer; 398b194a77fSXiaofei Tan 399b194a77fSXiaofei Tan aer = (struct aer_capability_regs *)pcie->aer_info; 400b194a77fSXiaofei Tan printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 401b194a77fSXiaofei Tan pfx, aer->uncor_status, aer->uncor_mask); 402b194a77fSXiaofei Tan printk("%saer_uncor_severity: 0x%08x\n", 403b194a77fSXiaofei Tan pfx, aer->uncor_severity); 404b194a77fSXiaofei Tan printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 405b194a77fSXiaofei Tan aer->header_log.dw0, aer->header_log.dw1, 406b194a77fSXiaofei Tan aer->header_log.dw2, aer->header_log.dw3); 407b194a77fSXiaofei Tan } 4087ea6c6c1SLuck, Tony } 4097ea6c6c1SLuck, Tony 410*3d8c11efSPunit Agrawal static const char * const fw_err_rec_type_strs[] = { 411*3d8c11efSPunit Agrawal "IPF SAL Error Record", 412*3d8c11efSPunit Agrawal "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 413*3d8c11efSPunit Agrawal "SOC Firmware Error Record Type2", 414*3d8c11efSPunit Agrawal }; 415*3d8c11efSPunit Agrawal 416*3d8c11efSPunit Agrawal static void cper_print_fw_err(const char *pfx, 417*3d8c11efSPunit Agrawal struct acpi_hest_generic_data *gdata, 418*3d8c11efSPunit Agrawal const struct cper_sec_fw_err_rec_ref *fw_err) 419*3d8c11efSPunit Agrawal { 420*3d8c11efSPunit Agrawal void *buf = acpi_hest_get_payload(gdata); 421*3d8c11efSPunit Agrawal u32 offset, length = gdata->error_data_length; 422*3d8c11efSPunit Agrawal 423*3d8c11efSPunit Agrawal printk("%s""Firmware Error Record Type: %s\n", pfx, 424*3d8c11efSPunit Agrawal fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 425*3d8c11efSPunit Agrawal fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 426*3d8c11efSPunit Agrawal printk("%s""Revision: %d\n", pfx, fw_err->revision); 427*3d8c11efSPunit Agrawal 428*3d8c11efSPunit Agrawal /* Record Type based on UEFI 2.7 */ 429*3d8c11efSPunit Agrawal if (fw_err->revision == 0) { 430*3d8c11efSPunit Agrawal printk("%s""Record Identifier: %08llx\n", pfx, 431*3d8c11efSPunit Agrawal fw_err->record_identifier); 432*3d8c11efSPunit Agrawal } else if (fw_err->revision == 2) { 433*3d8c11efSPunit Agrawal printk("%s""Record Identifier: %pUl\n", pfx, 434*3d8c11efSPunit Agrawal &fw_err->record_identifier_guid); 435*3d8c11efSPunit Agrawal } 436*3d8c11efSPunit Agrawal 437*3d8c11efSPunit Agrawal /* 438*3d8c11efSPunit Agrawal * The FW error record may contain trailing data beyond the 439*3d8c11efSPunit Agrawal * structure defined by the specification. As the fields 440*3d8c11efSPunit Agrawal * defined (and hence the offset of any trailing data) vary 441*3d8c11efSPunit Agrawal * with the revision, set the offset to account for this 442*3d8c11efSPunit Agrawal * variation. 443*3d8c11efSPunit Agrawal */ 444*3d8c11efSPunit Agrawal if (fw_err->revision == 0) { 445*3d8c11efSPunit Agrawal /* record_identifier_guid not defined */ 446*3d8c11efSPunit Agrawal offset = offsetof(struct cper_sec_fw_err_rec_ref, 447*3d8c11efSPunit Agrawal record_identifier_guid); 448*3d8c11efSPunit Agrawal } else if (fw_err->revision == 1) { 449*3d8c11efSPunit Agrawal /* record_identifier not defined */ 450*3d8c11efSPunit Agrawal offset = offsetof(struct cper_sec_fw_err_rec_ref, 451*3d8c11efSPunit Agrawal record_identifier); 452*3d8c11efSPunit Agrawal } else { 453*3d8c11efSPunit Agrawal offset = sizeof(*fw_err); 454*3d8c11efSPunit Agrawal } 455*3d8c11efSPunit Agrawal 456*3d8c11efSPunit Agrawal buf += offset; 457*3d8c11efSPunit Agrawal length -= offset; 458*3d8c11efSPunit Agrawal 459*3d8c11efSPunit Agrawal print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 460*3d8c11efSPunit Agrawal } 461*3d8c11efSPunit Agrawal 4628a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx, 4638a94471fSTyler Baicar struct acpi_hest_generic_data_v300 *gdata) 4648a94471fSTyler Baicar { 4658a94471fSTyler Baicar __u8 hour, min, sec, day, mon, year, century, *timestamp; 4668a94471fSTyler Baicar 4678a94471fSTyler Baicar if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 4688a94471fSTyler Baicar timestamp = (__u8 *)&(gdata->time_stamp); 4698a94471fSTyler Baicar sec = bcd2bin(timestamp[0]); 4708a94471fSTyler Baicar min = bcd2bin(timestamp[1]); 4718a94471fSTyler Baicar hour = bcd2bin(timestamp[2]); 4728a94471fSTyler Baicar day = bcd2bin(timestamp[4]); 4738a94471fSTyler Baicar mon = bcd2bin(timestamp[5]); 4748a94471fSTyler Baicar year = bcd2bin(timestamp[6]); 4758a94471fSTyler Baicar century = bcd2bin(timestamp[7]); 4768a94471fSTyler Baicar 4778a94471fSTyler Baicar printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 4788a94471fSTyler Baicar (timestamp[3] & 0x1 ? "precise " : "imprecise "), 4798a94471fSTyler Baicar century, year, mon, day, hour, min, sec); 4808a94471fSTyler Baicar } 4818a94471fSTyler Baicar } 4828a94471fSTyler Baicar 483bbcc2e7bSTyler Baicar static void 484bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 485bbcc2e7bSTyler Baicar int sec_no) 4867ea6c6c1SLuck, Tony { 487c0020756SAndy Shevchenko guid_t *sec_type = (guid_t *)gdata->section_type; 4887ea6c6c1SLuck, Tony __u16 severity; 4897ea6c6c1SLuck, Tony char newpfx[64]; 4907ea6c6c1SLuck, Tony 4918a94471fSTyler Baicar if (acpi_hest_get_version(gdata) >= 3) 4928a94471fSTyler Baicar cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 4938a94471fSTyler Baicar 4947ea6c6c1SLuck, Tony severity = gdata->error_severity; 4957ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 4967ea6c6c1SLuck, Tony cper_severity_str(severity)); 4977ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 498c0020756SAndy Shevchenko printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 4997ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 5007ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 5017ea6c6c1SLuck, Tony 50275e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 503c0020756SAndy Shevchenko if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 504bbcc2e7bSTyler Baicar struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 505bbcc2e7bSTyler Baicar 5067ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 5077ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 5087ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 5097ea6c6c1SLuck, Tony else 5107ea6c6c1SLuck, Tony goto err_section_too_small; 511c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 512bbcc2e7bSTyler Baicar struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 513bbcc2e7bSTyler Baicar 5147ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 5154c62360dSLuck, Tony if (gdata->error_data_length >= 5164c62360dSLuck, Tony sizeof(struct cper_sec_mem_err_old)) 5174c62360dSLuck, Tony cper_print_mem(newpfx, mem_err, 5184c62360dSLuck, Tony gdata->error_data_length); 5197ea6c6c1SLuck, Tony else 5207ea6c6c1SLuck, Tony goto err_section_too_small; 521c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 522bbcc2e7bSTyler Baicar struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 523bbcc2e7bSTyler Baicar 5247ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 5257ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 5267ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 5277ea6c6c1SLuck, Tony else 5287ea6c6c1SLuck, Tony goto err_section_too_small; 5292f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 530e8f4194dSAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 5312f74f09bSTyler Baicar struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 5322f74f09bSTyler Baicar 5332f74f09bSTyler Baicar printk("%ssection_type: ARM processor error\n", newpfx); 5342f74f09bSTyler Baicar if (gdata->error_data_length >= sizeof(*arm_err)) 5352f74f09bSTyler Baicar cper_print_proc_arm(newpfx, arm_err); 5362f74f09bSTyler Baicar else 5372f74f09bSTyler Baicar goto err_section_too_small; 5382f74f09bSTyler Baicar #endif 539f9e1bdb9SYazen Ghannam #if defined(CONFIG_UEFI_CPER_X86) 540f9e1bdb9SYazen Ghannam } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 541f9e1bdb9SYazen Ghannam struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 542f9e1bdb9SYazen Ghannam 543f9e1bdb9SYazen Ghannam printk("%ssection_type: IA32/X64 processor error\n", newpfx); 544f9e1bdb9SYazen Ghannam if (gdata->error_data_length >= sizeof(*ia_err)) 545f9e1bdb9SYazen Ghannam cper_print_proc_ia(newpfx, ia_err); 546f9e1bdb9SYazen Ghannam else 547f9e1bdb9SYazen Ghannam goto err_section_too_small; 548f9e1bdb9SYazen Ghannam #endif 549*3d8c11efSPunit Agrawal } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 550*3d8c11efSPunit Agrawal struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 551*3d8c11efSPunit Agrawal 552*3d8c11efSPunit Agrawal printk("%ssection_type: Firmware Error Record Reference\n", 553*3d8c11efSPunit Agrawal newpfx); 554*3d8c11efSPunit Agrawal /* The minimal FW Error Record contains 16 bytes */ 555*3d8c11efSPunit Agrawal if (gdata->error_data_length >= SZ_16) 556*3d8c11efSPunit Agrawal cper_print_fw_err(newpfx, gdata, fw_err); 557*3d8c11efSPunit Agrawal else 558*3d8c11efSPunit Agrawal goto err_section_too_small; 5590fc300f4STyler Baicar } else { 5600fc300f4STyler Baicar const void *err = acpi_hest_get_payload(gdata); 5610fc300f4STyler Baicar 5620fc300f4STyler Baicar printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 5630fc300f4STyler Baicar printk("%ssection length: %#x\n", newpfx, 5640fc300f4STyler Baicar gdata->error_data_length); 5650fc300f4STyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 5660fc300f4STyler Baicar gdata->error_data_length, true); 5670fc300f4STyler Baicar } 5687ea6c6c1SLuck, Tony 5697ea6c6c1SLuck, Tony return; 5707ea6c6c1SLuck, Tony 5717ea6c6c1SLuck, Tony err_section_too_small: 5727ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 5737ea6c6c1SLuck, Tony } 5747ea6c6c1SLuck, Tony 5757ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 5760a00fd5eSLv Zheng const struct acpi_hest_generic_status *estatus) 5777ea6c6c1SLuck, Tony { 5780a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 5797ea6c6c1SLuck, Tony int sec_no = 0; 5807ea6c6c1SLuck, Tony char newpfx[64]; 5817ea6c6c1SLuck, Tony __u16 severity; 5827ea6c6c1SLuck, Tony 5837ea6c6c1SLuck, Tony severity = estatus->error_severity; 5847ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 5857ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 5867ea6c6c1SLuck, Tony "It has been corrected by h/w " 5877ea6c6c1SLuck, Tony "and requires no further action"); 5887ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 58975e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 590bbcc2e7bSTyler Baicar 591c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 5927ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 5937ea6c6c1SLuck, Tony sec_no++; 5947ea6c6c1SLuck, Tony } 5957ea6c6c1SLuck, Tony } 5967ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 5977ea6c6c1SLuck, Tony 5980a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 5997ea6c6c1SLuck, Tony { 6007ea6c6c1SLuck, Tony if (estatus->data_length && 6010a00fd5eSLv Zheng estatus->data_length < sizeof(struct acpi_hest_generic_data)) 6027ea6c6c1SLuck, Tony return -EINVAL; 6037ea6c6c1SLuck, Tony if (estatus->raw_data_length && 6047ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 6057ea6c6c1SLuck, Tony return -EINVAL; 6067ea6c6c1SLuck, Tony 6077ea6c6c1SLuck, Tony return 0; 6087ea6c6c1SLuck, Tony } 6097ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 6107ea6c6c1SLuck, Tony 6110a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 6127ea6c6c1SLuck, Tony { 6130a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 61445b14a4fSRoss Lagerwall unsigned int data_len, record_size; 6157ea6c6c1SLuck, Tony int rc; 6167ea6c6c1SLuck, Tony 6177ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 6187ea6c6c1SLuck, Tony if (rc) 6197ea6c6c1SLuck, Tony return rc; 62045b14a4fSRoss Lagerwall 6217ea6c6c1SLuck, Tony data_len = estatus->data_length; 622bbcc2e7bSTyler Baicar 623c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 62445b14a4fSRoss Lagerwall if (sizeof(struct acpi_hest_generic_data) > data_len) 6257ea6c6c1SLuck, Tony return -EINVAL; 62645b14a4fSRoss Lagerwall 62745b14a4fSRoss Lagerwall record_size = acpi_hest_get_record_size(gdata); 62845b14a4fSRoss Lagerwall if (record_size > data_len) 62945b14a4fSRoss Lagerwall return -EINVAL; 63045b14a4fSRoss Lagerwall 63145b14a4fSRoss Lagerwall data_len -= record_size; 6327ea6c6c1SLuck, Tony } 6337ea6c6c1SLuck, Tony if (data_len) 6347ea6c6c1SLuck, Tony return -EINVAL; 6357ea6c6c1SLuck, Tony 6367ea6c6c1SLuck, Tony return 0; 6377ea6c6c1SLuck, Tony } 6387ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 639