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> 27*abdbf1a2SSmita Koralahalli #include "cper_cxl.h" 287ea6c6c1SLuck, Tony 297ea6c6c1SLuck, Tony /* 307ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 317ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 327ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 337ea6c6c1SLuck, Tony */ 347ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 357ea6c6c1SLuck, Tony { 367ea6c6c1SLuck, Tony static atomic64_t seq; 377ea6c6c1SLuck, Tony 387bb49709SArnd Bergmann if (!atomic64_read(&seq)) { 397bb49709SArnd Bergmann time64_t time = ktime_get_real_seconds(); 407bb49709SArnd Bergmann 417bb49709SArnd Bergmann /* 427bb49709SArnd Bergmann * This code is unlikely to still be needed in year 2106, 437bb49709SArnd Bergmann * but just in case, let's use a few more bits for timestamps 447bb49709SArnd Bergmann * after y2038 to be sure they keep increasing monotonically 457bb49709SArnd Bergmann * for the next few hundred years... 467bb49709SArnd Bergmann */ 477bb49709SArnd Bergmann if (time < 0x80000000) 487bb49709SArnd Bergmann atomic64_set(&seq, (ktime_get_real_seconds()) << 32); 497bb49709SArnd Bergmann else 507bb49709SArnd Bergmann atomic64_set(&seq, 0x8000000000000000ull | 517bb49709SArnd Bergmann ktime_get_real_seconds() << 24); 527bb49709SArnd Bergmann } 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 103b450b30bSTakashi Iwai len += scnprintf(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 215bdae7965SShuai Xue const char *cper_mem_err_status_str(u64 status) 216bdae7965SShuai Xue { 217bdae7965SShuai Xue switch ((status >> 8) & 0xff) { 218bdae7965SShuai Xue case 1: return "Error detected internal to the component"; 219bdae7965SShuai Xue case 4: return "Storage error in DRAM memory"; 220bdae7965SShuai Xue case 5: return "Storage error in TLB"; 221bdae7965SShuai Xue case 6: return "Storage error in cache"; 222bdae7965SShuai Xue case 7: return "Error in one or more functional units"; 223bdae7965SShuai Xue case 8: return "Component failed self test"; 224bdae7965SShuai Xue case 9: return "Overflow or undervalue of internal queue"; 225bdae7965SShuai Xue case 16: return "Error detected in the bus"; 226bdae7965SShuai Xue case 17: return "Virtual address not found on IO-TLB or IO-PDIR"; 227bdae7965SShuai Xue case 18: return "Improper access error"; 228bdae7965SShuai Xue case 19: return "Access to a memory address which is not mapped to any component"; 229bdae7965SShuai Xue case 20: return "Loss of Lockstep"; 230bdae7965SShuai Xue case 21: return "Response not associated with a request"; 231bdae7965SShuai Xue case 22: return "Bus parity error - must also set the A, C, or D Bits"; 232bdae7965SShuai Xue case 23: return "Detection of a protocol error"; 233bdae7965SShuai Xue case 24: return "Detection of a PATH_ERROR"; 234bdae7965SShuai Xue case 25: return "Bus operation timeout"; 235bdae7965SShuai Xue case 26: return "A read was issued to data that has been poisoned"; 236bdae7965SShuai Xue default: return "Reserved"; 237bdae7965SShuai Xue } 238bdae7965SShuai Xue } 239bdae7965SShuai Xue EXPORT_SYMBOL_GPL(cper_mem_err_status_str); 240bdae7965SShuai Xue 241ed27b5dfSShuai Xue int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 2423760cd20SChen, Gong { 2433760cd20SChen, Gong u32 len, n; 2443760cd20SChen, Gong 2453760cd20SChen, Gong if (!msg) 2463760cd20SChen, Gong return 0; 2473760cd20SChen, Gong 2483760cd20SChen, Gong n = 0; 2495eff88ddSRasmus Villemoes len = CPER_REC_LEN; 2503760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_NODE) 2513760cd20SChen, Gong n += scnprintf(msg + n, len - n, "node:%d ", mem->node); 2523760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_CARD) 2533760cd20SChen, Gong n += scnprintf(msg + n, len - n, "card:%d ", mem->card); 2543760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_MODULE) 2553760cd20SChen, Gong n += scnprintf(msg + n, len - n, "module:%d ", mem->module); 2563760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 2573760cd20SChen, Gong n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank); 2583760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BANK) 2593760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank); 260612b5d50SAlex Kluver if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) 261612b5d50SAlex Kluver n += scnprintf(msg + n, len - n, "bank_group:%d ", 262612b5d50SAlex Kluver mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); 263612b5d50SAlex Kluver if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) 264612b5d50SAlex Kluver n += scnprintf(msg + n, len - n, "bank_address:%d ", 265612b5d50SAlex Kluver mem->bank & CPER_MEM_BANK_ADDRESS_MASK); 2663760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 2673760cd20SChen, Gong n += scnprintf(msg + n, len - n, "device:%d ", mem->device); 2689baf68ccSAlex Kluver if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { 2699baf68ccSAlex Kluver u32 row = mem->row; 2709baf68ccSAlex Kluver 2719baf68ccSAlex Kluver row |= cper_get_mem_extension(mem->validation_bits, mem->extended); 2729baf68ccSAlex Kluver n += scnprintf(msg + n, len - n, "row:%d ", row); 2739baf68ccSAlex Kluver } 2743760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 2753760cd20SChen, Gong n += scnprintf(msg + n, len - n, "column:%d ", mem->column); 2763760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 2773760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bit_position:%d ", 2783760cd20SChen, Gong mem->bit_pos); 2793760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 2803760cd20SChen, Gong n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ", 2813760cd20SChen, Gong mem->requestor_id); 2823760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 2833760cd20SChen, Gong n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ", 2843760cd20SChen, Gong mem->responder_id); 2853760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 2865eff88ddSRasmus Villemoes n += scnprintf(msg + n, len - n, "target_id:0x%016llx ", 2873760cd20SChen, Gong mem->target_id); 288612b5d50SAlex Kluver if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 2895eff88ddSRasmus Villemoes n += scnprintf(msg + n, len - n, "chip_id:%d ", 290612b5d50SAlex Kluver mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 2913760cd20SChen, Gong 2923760cd20SChen, Gong return n; 2933760cd20SChen, Gong } 2943760cd20SChen, Gong 295ed27b5dfSShuai Xue int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 2963760cd20SChen, Gong { 2973760cd20SChen, Gong u32 len, n; 2983760cd20SChen, Gong const char *bank = NULL, *device = NULL; 2993760cd20SChen, Gong 3003760cd20SChen, Gong if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 3013760cd20SChen, Gong return 0; 3023760cd20SChen, Gong 303942859d9SRasmus Villemoes len = CPER_REC_LEN; 3043760cd20SChen, Gong dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 3053760cd20SChen, Gong if (bank && device) 3063760cd20SChen, Gong n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 3073760cd20SChen, Gong else 3083760cd20SChen, Gong n = snprintf(msg, len, 3093760cd20SChen, Gong "DIMM location: not present. DMI handle: 0x%.4x ", 3103760cd20SChen, Gong mem->mem_dev_handle); 3113760cd20SChen, Gong 3123760cd20SChen, Gong return n; 3133760cd20SChen, Gong } 3143760cd20SChen, Gong 3152dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 3162dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 3172dfb7d51SChen, Gong { 3182dfb7d51SChen, Gong cmem->validation_bits = mem->validation_bits; 3192dfb7d51SChen, Gong cmem->node = mem->node; 3202dfb7d51SChen, Gong cmem->card = mem->card; 3212dfb7d51SChen, Gong cmem->module = mem->module; 3222dfb7d51SChen, Gong cmem->bank = mem->bank; 3232dfb7d51SChen, Gong cmem->device = mem->device; 3242dfb7d51SChen, Gong cmem->row = mem->row; 3252dfb7d51SChen, Gong cmem->column = mem->column; 3262dfb7d51SChen, Gong cmem->bit_pos = mem->bit_pos; 3272dfb7d51SChen, Gong cmem->requestor_id = mem->requestor_id; 3282dfb7d51SChen, Gong cmem->responder_id = mem->responder_id; 3292dfb7d51SChen, Gong cmem->target_id = mem->target_id; 3309baf68ccSAlex Kluver cmem->extended = mem->extended; 3312dfb7d51SChen, Gong cmem->rank = mem->rank; 3322dfb7d51SChen, Gong cmem->mem_array_handle = mem->mem_array_handle; 3332dfb7d51SChen, Gong cmem->mem_dev_handle = mem->mem_dev_handle; 3342dfb7d51SChen, Gong } 3352dfb7d51SChen, Gong 3362dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p, 3372dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 3382dfb7d51SChen, Gong { 339dbcf3e06SSteven Rostedt (Red Hat) const char *ret = trace_seq_buffer_ptr(p); 340b3a72ca8SArd Biesheuvel char rcd_decode_str[CPER_REC_LEN]; 3412dfb7d51SChen, Gong 3422dfb7d51SChen, Gong if (cper_mem_err_location(cmem, rcd_decode_str)) 3432dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3442dfb7d51SChen, Gong if (cper_dimm_err_location(cmem, rcd_decode_str)) 3452dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 3462dfb7d51SChen, Gong trace_seq_putc(p, '\0'); 3472dfb7d51SChen, Gong 3482dfb7d51SChen, Gong return ret; 3492dfb7d51SChen, Gong } 3502dfb7d51SChen, Gong 3514c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 3524c62360dSLuck, Tony int len) 3537ea6c6c1SLuck, Tony { 3542dfb7d51SChen, Gong struct cper_mem_err_compact cmem; 355b3a72ca8SArd Biesheuvel char rcd_decode_str[CPER_REC_LEN]; 3562dfb7d51SChen, Gong 3574c62360dSLuck, Tony /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 3584c62360dSLuck, Tony if (len == sizeof(struct cper_sec_mem_err_old) && 3594c62360dSLuck, Tony (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 3604c62360dSLuck, Tony pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 3614c62360dSLuck, Tony return; 3624c62360dSLuck, Tony } 3637ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 364bdae7965SShuai Xue printk("%s error_status: %s (0x%016llx)\n", 365bdae7965SShuai Xue pfx, cper_mem_err_status_str(mem->error_status), 366bdae7965SShuai Xue mem->error_status); 3677ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 3687ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 3697ea6c6c1SLuck, Tony pfx, mem->physical_addr); 3707ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 3717ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 3727ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 3732dfb7d51SChen, Gong cper_mem_err_pack(mem, &cmem); 3742dfb7d51SChen, Gong if (cper_mem_err_location(&cmem, rcd_decode_str)) 3753760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3767ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 3777ea6c6c1SLuck, Tony u8 etype = mem->error_type; 3787ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 3793760cd20SChen, Gong cper_mem_err_type_str(etype)); 3807ea6c6c1SLuck, Tony } 3812dfb7d51SChen, Gong if (cper_dimm_err_location(&cmem, rcd_decode_str)) 3823760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 3837ea6c6c1SLuck, Tony } 3847ea6c6c1SLuck, Tony 3853760cd20SChen, Gong static const char * const pcie_port_type_strs[] = { 3867ea6c6c1SLuck, Tony "PCIe end point", 3877ea6c6c1SLuck, Tony "legacy PCI end point", 3887ea6c6c1SLuck, Tony "unknown", 3897ea6c6c1SLuck, Tony "unknown", 3907ea6c6c1SLuck, Tony "root port", 3917ea6c6c1SLuck, Tony "upstream switch port", 3927ea6c6c1SLuck, Tony "downstream switch port", 3937ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 3947ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 3957ea6c6c1SLuck, Tony "root complex integrated endpoint device", 3967ea6c6c1SLuck, Tony "root complex event collector", 3977ea6c6c1SLuck, Tony }; 3987ea6c6c1SLuck, Tony 3997ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 4000a00fd5eSLv Zheng const struct acpi_hest_generic_data *gdata) 4017ea6c6c1SLuck, Tony { 4027ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 4037ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 4043760cd20SChen, Gong pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 4053760cd20SChen, Gong pcie_port_type_strs[pcie->port_type] : "unknown"); 4067ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 4077ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 4087ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 4097ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 4107ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 4117ea6c6c1SLuck, Tony pcie->command, pcie->status); 4127ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 4137ea6c6c1SLuck, Tony const __u8 *p; 4147ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 4157ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 4167ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 4177ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 4187ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 4197ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 4207ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 4217ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 4227ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 4237ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 4246fb9367aSLukas Wunner printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 4257ea6c6c1SLuck, Tony } 4267ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 4277ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 4287ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 4297ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 4307ea6c6c1SLuck, Tony printk( 4317ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 4327ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 433b194a77fSXiaofei Tan 434b194a77fSXiaofei Tan /* Fatal errors call __ghes_panic() before AER handler prints this */ 435b194a77fSXiaofei Tan if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 436b194a77fSXiaofei Tan (gdata->error_severity & CPER_SEV_FATAL)) { 437b194a77fSXiaofei Tan struct aer_capability_regs *aer; 438b194a77fSXiaofei Tan 439b194a77fSXiaofei Tan aer = (struct aer_capability_regs *)pcie->aer_info; 440b194a77fSXiaofei Tan printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 441b194a77fSXiaofei Tan pfx, aer->uncor_status, aer->uncor_mask); 442b194a77fSXiaofei Tan printk("%saer_uncor_severity: 0x%08x\n", 443b194a77fSXiaofei Tan pfx, aer->uncor_severity); 444b194a77fSXiaofei Tan printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 445b194a77fSXiaofei Tan aer->header_log.dw0, aer->header_log.dw1, 446b194a77fSXiaofei Tan aer->header_log.dw2, aer->header_log.dw3); 447b194a77fSXiaofei Tan } 4487ea6c6c1SLuck, Tony } 4497ea6c6c1SLuck, Tony 4503d8c11efSPunit Agrawal static const char * const fw_err_rec_type_strs[] = { 4513d8c11efSPunit Agrawal "IPF SAL Error Record", 4523d8c11efSPunit Agrawal "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 4533d8c11efSPunit Agrawal "SOC Firmware Error Record Type2", 4543d8c11efSPunit Agrawal }; 4553d8c11efSPunit Agrawal 4563d8c11efSPunit Agrawal static void cper_print_fw_err(const char *pfx, 4573d8c11efSPunit Agrawal struct acpi_hest_generic_data *gdata, 4583d8c11efSPunit Agrawal const struct cper_sec_fw_err_rec_ref *fw_err) 4593d8c11efSPunit Agrawal { 4603d8c11efSPunit Agrawal void *buf = acpi_hest_get_payload(gdata); 4613d8c11efSPunit Agrawal u32 offset, length = gdata->error_data_length; 4623d8c11efSPunit Agrawal 4633d8c11efSPunit Agrawal printk("%s""Firmware Error Record Type: %s\n", pfx, 4643d8c11efSPunit Agrawal fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 4653d8c11efSPunit Agrawal fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 4663d8c11efSPunit Agrawal printk("%s""Revision: %d\n", pfx, fw_err->revision); 4673d8c11efSPunit Agrawal 4683d8c11efSPunit Agrawal /* Record Type based on UEFI 2.7 */ 4693d8c11efSPunit Agrawal if (fw_err->revision == 0) { 4703d8c11efSPunit Agrawal printk("%s""Record Identifier: %08llx\n", pfx, 4713d8c11efSPunit Agrawal fw_err->record_identifier); 4723d8c11efSPunit Agrawal } else if (fw_err->revision == 2) { 4733d8c11efSPunit Agrawal printk("%s""Record Identifier: %pUl\n", pfx, 4743d8c11efSPunit Agrawal &fw_err->record_identifier_guid); 4753d8c11efSPunit Agrawal } 4763d8c11efSPunit Agrawal 4773d8c11efSPunit Agrawal /* 4783d8c11efSPunit Agrawal * The FW error record may contain trailing data beyond the 4793d8c11efSPunit Agrawal * structure defined by the specification. As the fields 4803d8c11efSPunit Agrawal * defined (and hence the offset of any trailing data) vary 4813d8c11efSPunit Agrawal * with the revision, set the offset to account for this 4823d8c11efSPunit Agrawal * variation. 4833d8c11efSPunit Agrawal */ 4843d8c11efSPunit Agrawal if (fw_err->revision == 0) { 4853d8c11efSPunit Agrawal /* record_identifier_guid not defined */ 4863d8c11efSPunit Agrawal offset = offsetof(struct cper_sec_fw_err_rec_ref, 4873d8c11efSPunit Agrawal record_identifier_guid); 4883d8c11efSPunit Agrawal } else if (fw_err->revision == 1) { 4893d8c11efSPunit Agrawal /* record_identifier not defined */ 4903d8c11efSPunit Agrawal offset = offsetof(struct cper_sec_fw_err_rec_ref, 4913d8c11efSPunit Agrawal record_identifier); 4923d8c11efSPunit Agrawal } else { 4933d8c11efSPunit Agrawal offset = sizeof(*fw_err); 4943d8c11efSPunit Agrawal } 4953d8c11efSPunit Agrawal 4963d8c11efSPunit Agrawal buf += offset; 4973d8c11efSPunit Agrawal length -= offset; 4983d8c11efSPunit Agrawal 4993d8c11efSPunit Agrawal print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 5003d8c11efSPunit Agrawal } 5013d8c11efSPunit Agrawal 5028a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx, 5038a94471fSTyler Baicar struct acpi_hest_generic_data_v300 *gdata) 5048a94471fSTyler Baicar { 5058a94471fSTyler Baicar __u8 hour, min, sec, day, mon, year, century, *timestamp; 5068a94471fSTyler Baicar 5078a94471fSTyler Baicar if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 5088a94471fSTyler Baicar timestamp = (__u8 *)&(gdata->time_stamp); 5098a94471fSTyler Baicar sec = bcd2bin(timestamp[0]); 5108a94471fSTyler Baicar min = bcd2bin(timestamp[1]); 5118a94471fSTyler Baicar hour = bcd2bin(timestamp[2]); 5128a94471fSTyler Baicar day = bcd2bin(timestamp[4]); 5138a94471fSTyler Baicar mon = bcd2bin(timestamp[5]); 5148a94471fSTyler Baicar year = bcd2bin(timestamp[6]); 5158a94471fSTyler Baicar century = bcd2bin(timestamp[7]); 5168a94471fSTyler Baicar 5178a94471fSTyler Baicar printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 5188a94471fSTyler Baicar (timestamp[3] & 0x1 ? "precise " : "imprecise "), 5198a94471fSTyler Baicar century, year, mon, day, hour, min, sec); 5208a94471fSTyler Baicar } 5218a94471fSTyler Baicar } 5228a94471fSTyler Baicar 523bbcc2e7bSTyler Baicar static void 524bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 525bbcc2e7bSTyler Baicar int sec_no) 5267ea6c6c1SLuck, Tony { 527c0020756SAndy Shevchenko guid_t *sec_type = (guid_t *)gdata->section_type; 5287ea6c6c1SLuck, Tony __u16 severity; 5297ea6c6c1SLuck, Tony char newpfx[64]; 5307ea6c6c1SLuck, Tony 5318a94471fSTyler Baicar if (acpi_hest_get_version(gdata) >= 3) 5328a94471fSTyler Baicar cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 5338a94471fSTyler Baicar 5347ea6c6c1SLuck, Tony severity = gdata->error_severity; 5357ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 5367ea6c6c1SLuck, Tony cper_severity_str(severity)); 5377ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 538c0020756SAndy Shevchenko printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 5397ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 5407ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 5417ea6c6c1SLuck, Tony 54275e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 543c0020756SAndy Shevchenko if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 544bbcc2e7bSTyler Baicar struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 545bbcc2e7bSTyler Baicar 5467ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 5477ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 5487ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 5497ea6c6c1SLuck, Tony else 5507ea6c6c1SLuck, Tony goto err_section_too_small; 551c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 552bbcc2e7bSTyler Baicar struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 553bbcc2e7bSTyler Baicar 5547ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 5554c62360dSLuck, Tony if (gdata->error_data_length >= 5564c62360dSLuck, Tony sizeof(struct cper_sec_mem_err_old)) 5574c62360dSLuck, Tony cper_print_mem(newpfx, mem_err, 5584c62360dSLuck, Tony gdata->error_data_length); 5597ea6c6c1SLuck, Tony else 5607ea6c6c1SLuck, Tony goto err_section_too_small; 561c0020756SAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 562bbcc2e7bSTyler Baicar struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 563bbcc2e7bSTyler Baicar 5647ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 5657ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 5667ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 5677ea6c6c1SLuck, Tony else 5687ea6c6c1SLuck, Tony goto err_section_too_small; 5692f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 570e8f4194dSAndy Shevchenko } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 5712f74f09bSTyler Baicar struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 5722f74f09bSTyler Baicar 5732f74f09bSTyler Baicar printk("%ssection_type: ARM processor error\n", newpfx); 5742f74f09bSTyler Baicar if (gdata->error_data_length >= sizeof(*arm_err)) 5752f74f09bSTyler Baicar cper_print_proc_arm(newpfx, arm_err); 5762f74f09bSTyler Baicar else 5772f74f09bSTyler Baicar goto err_section_too_small; 5782f74f09bSTyler Baicar #endif 579f9e1bdb9SYazen Ghannam #if defined(CONFIG_UEFI_CPER_X86) 580f9e1bdb9SYazen Ghannam } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 581f9e1bdb9SYazen Ghannam struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 582f9e1bdb9SYazen Ghannam 583f9e1bdb9SYazen Ghannam printk("%ssection_type: IA32/X64 processor error\n", newpfx); 584f9e1bdb9SYazen Ghannam if (gdata->error_data_length >= sizeof(*ia_err)) 585f9e1bdb9SYazen Ghannam cper_print_proc_ia(newpfx, ia_err); 586f9e1bdb9SYazen Ghannam else 587f9e1bdb9SYazen Ghannam goto err_section_too_small; 588f9e1bdb9SYazen Ghannam #endif 5893d8c11efSPunit Agrawal } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 5903d8c11efSPunit Agrawal struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 5913d8c11efSPunit Agrawal 5923d8c11efSPunit Agrawal printk("%ssection_type: Firmware Error Record Reference\n", 5933d8c11efSPunit Agrawal newpfx); 5943d8c11efSPunit Agrawal /* The minimal FW Error Record contains 16 bytes */ 5953d8c11efSPunit Agrawal if (gdata->error_data_length >= SZ_16) 5963d8c11efSPunit Agrawal cper_print_fw_err(newpfx, gdata, fw_err); 5973d8c11efSPunit Agrawal else 5983d8c11efSPunit Agrawal goto err_section_too_small; 599*abdbf1a2SSmita Koralahalli } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { 600*abdbf1a2SSmita Koralahalli struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata); 601*abdbf1a2SSmita Koralahalli 602*abdbf1a2SSmita Koralahalli printk("%ssection_type: CXL Protocol Error\n", newpfx); 603*abdbf1a2SSmita Koralahalli if (gdata->error_data_length >= sizeof(*prot_err)) 604*abdbf1a2SSmita Koralahalli cper_print_prot_err(newpfx, prot_err); 605*abdbf1a2SSmita Koralahalli else 606*abdbf1a2SSmita Koralahalli goto err_section_too_small; 6070fc300f4STyler Baicar } else { 6080fc300f4STyler Baicar const void *err = acpi_hest_get_payload(gdata); 6090fc300f4STyler Baicar 6100fc300f4STyler Baicar printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 6110fc300f4STyler Baicar printk("%ssection length: %#x\n", newpfx, 6120fc300f4STyler Baicar gdata->error_data_length); 6130fc300f4STyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 6140fc300f4STyler Baicar gdata->error_data_length, true); 6150fc300f4STyler Baicar } 6167ea6c6c1SLuck, Tony 6177ea6c6c1SLuck, Tony return; 6187ea6c6c1SLuck, Tony 6197ea6c6c1SLuck, Tony err_section_too_small: 6207ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 6217ea6c6c1SLuck, Tony } 6227ea6c6c1SLuck, Tony 6237ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 6240a00fd5eSLv Zheng const struct acpi_hest_generic_status *estatus) 6257ea6c6c1SLuck, Tony { 6260a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 6277ea6c6c1SLuck, Tony int sec_no = 0; 6287ea6c6c1SLuck, Tony char newpfx[64]; 6297ea6c6c1SLuck, Tony __u16 severity; 6307ea6c6c1SLuck, Tony 6317ea6c6c1SLuck, Tony severity = estatus->error_severity; 6327ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 6337ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 6347ea6c6c1SLuck, Tony "It has been corrected by h/w " 6357ea6c6c1SLuck, Tony "and requires no further action"); 6367ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 63775e4fd31SBorislav Petkov snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 638bbcc2e7bSTyler Baicar 639c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 6407ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 6417ea6c6c1SLuck, Tony sec_no++; 6427ea6c6c1SLuck, Tony } 6437ea6c6c1SLuck, Tony } 6447ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 6457ea6c6c1SLuck, Tony 6460a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 6477ea6c6c1SLuck, Tony { 6487ea6c6c1SLuck, Tony if (estatus->data_length && 6490a00fd5eSLv Zheng estatus->data_length < sizeof(struct acpi_hest_generic_data)) 6507ea6c6c1SLuck, Tony return -EINVAL; 6517ea6c6c1SLuck, Tony if (estatus->raw_data_length && 6527ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 6537ea6c6c1SLuck, Tony return -EINVAL; 6547ea6c6c1SLuck, Tony 6557ea6c6c1SLuck, Tony return 0; 6567ea6c6c1SLuck, Tony } 6577ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 6587ea6c6c1SLuck, Tony 6590a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 6607ea6c6c1SLuck, Tony { 6610a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 66245b14a4fSRoss Lagerwall unsigned int data_len, record_size; 6637ea6c6c1SLuck, Tony int rc; 6647ea6c6c1SLuck, Tony 6657ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 6667ea6c6c1SLuck, Tony if (rc) 6677ea6c6c1SLuck, Tony return rc; 66845b14a4fSRoss Lagerwall 6697ea6c6c1SLuck, Tony data_len = estatus->data_length; 670bbcc2e7bSTyler Baicar 671c4335fddSgengdongjiu apei_estatus_for_each_section(estatus, gdata) { 6721be72c8eSShuai Xue if (acpi_hest_get_size(gdata) > data_len) 6737ea6c6c1SLuck, Tony return -EINVAL; 67445b14a4fSRoss Lagerwall 67545b14a4fSRoss Lagerwall record_size = acpi_hest_get_record_size(gdata); 67645b14a4fSRoss Lagerwall if (record_size > data_len) 67745b14a4fSRoss Lagerwall return -EINVAL; 67845b14a4fSRoss Lagerwall 67945b14a4fSRoss Lagerwall data_len -= record_size; 6807ea6c6c1SLuck, Tony } 6817ea6c6c1SLuck, Tony if (data_len) 6827ea6c6c1SLuck, Tony return -EINVAL; 6837ea6c6c1SLuck, Tony 6847ea6c6c1SLuck, Tony return 0; 6857ea6c6c1SLuck, Tony } 6867ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 687