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> 38*e9279e83STyler Baicar #include <ras/ras_event.h> 397ea6c6c1SLuck, Tony 407ea6c6c1SLuck, Tony #define INDENT_SP " " 413760cd20SChen, Gong 423760cd20SChen, Gong static char rcd_decode_str[CPER_REC_LEN]; 433760cd20SChen, Gong 447ea6c6c1SLuck, Tony /* 457ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 467ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 477ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 487ea6c6c1SLuck, Tony */ 497ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 507ea6c6c1SLuck, Tony { 517ea6c6c1SLuck, Tony static atomic64_t seq; 527ea6c6c1SLuck, Tony 537ea6c6c1SLuck, Tony if (!atomic64_read(&seq)) 547ea6c6c1SLuck, Tony atomic64_set(&seq, ((u64)get_seconds()) << 32); 557ea6c6c1SLuck, Tony 567ea6c6c1SLuck, Tony return atomic64_inc_return(&seq); 577ea6c6c1SLuck, Tony } 587ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id); 597ea6c6c1SLuck, Tony 603760cd20SChen, Gong static const char * const severity_strs[] = { 617ea6c6c1SLuck, Tony "recoverable", 627ea6c6c1SLuck, Tony "fatal", 637ea6c6c1SLuck, Tony "corrected", 647ea6c6c1SLuck, Tony "info", 657ea6c6c1SLuck, Tony }; 667ea6c6c1SLuck, Tony 673760cd20SChen, Gong const char *cper_severity_str(unsigned int severity) 687ea6c6c1SLuck, Tony { 693760cd20SChen, Gong return severity < ARRAY_SIZE(severity_strs) ? 703760cd20SChen, Gong severity_strs[severity] : "unknown"; 717ea6c6c1SLuck, Tony } 723760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_severity_str); 737ea6c6c1SLuck, Tony 747ea6c6c1SLuck, Tony /* 757ea6c6c1SLuck, Tony * cper_print_bits - print strings for set bits 767ea6c6c1SLuck, Tony * @pfx: prefix for each line, including log level and prefix string 777ea6c6c1SLuck, Tony * @bits: bit mask 787ea6c6c1SLuck, Tony * @strs: string array, indexed by bit position 797ea6c6c1SLuck, Tony * @strs_size: size of the string array: @strs 807ea6c6c1SLuck, Tony * 817ea6c6c1SLuck, Tony * For each set bit in @bits, print the corresponding string in @strs. 827ea6c6c1SLuck, Tony * If the output length is longer than 80, multiple line will be 837ea6c6c1SLuck, Tony * printed, with @pfx is printed at the beginning of each line. 847ea6c6c1SLuck, Tony */ 857ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits, 867ea6c6c1SLuck, Tony const char * const strs[], unsigned int strs_size) 877ea6c6c1SLuck, Tony { 887ea6c6c1SLuck, Tony int i, len = 0; 897ea6c6c1SLuck, Tony const char *str; 907ea6c6c1SLuck, Tony char buf[84]; 917ea6c6c1SLuck, Tony 927ea6c6c1SLuck, Tony for (i = 0; i < strs_size; i++) { 937ea6c6c1SLuck, Tony if (!(bits & (1U << i))) 947ea6c6c1SLuck, Tony continue; 957ea6c6c1SLuck, Tony str = strs[i]; 967ea6c6c1SLuck, Tony if (!str) 977ea6c6c1SLuck, Tony continue; 987ea6c6c1SLuck, Tony if (len && len + strlen(str) + 2 > 80) { 997ea6c6c1SLuck, Tony printk("%s\n", buf); 1007ea6c6c1SLuck, Tony len = 0; 1017ea6c6c1SLuck, Tony } 1027ea6c6c1SLuck, Tony if (!len) 1037ea6c6c1SLuck, Tony len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 1047ea6c6c1SLuck, Tony else 1057ea6c6c1SLuck, Tony len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); 1067ea6c6c1SLuck, Tony } 1077ea6c6c1SLuck, Tony if (len) 1087ea6c6c1SLuck, Tony printk("%s\n", buf); 1097ea6c6c1SLuck, Tony } 1107ea6c6c1SLuck, Tony 1113760cd20SChen, Gong static const char * const proc_type_strs[] = { 1127ea6c6c1SLuck, Tony "IA32/X64", 1137ea6c6c1SLuck, Tony "IA64", 1142f74f09bSTyler Baicar "ARM", 1157ea6c6c1SLuck, Tony }; 1167ea6c6c1SLuck, Tony 1173760cd20SChen, Gong static const char * const proc_isa_strs[] = { 1187ea6c6c1SLuck, Tony "IA32", 1197ea6c6c1SLuck, Tony "IA64", 1207ea6c6c1SLuck, Tony "X64", 1212f74f09bSTyler Baicar "ARM A32/T32", 1222f74f09bSTyler Baicar "ARM A64", 1237ea6c6c1SLuck, Tony }; 1247ea6c6c1SLuck, Tony 1253760cd20SChen, Gong static const char * const proc_error_type_strs[] = { 1267ea6c6c1SLuck, Tony "cache error", 1277ea6c6c1SLuck, Tony "TLB error", 1287ea6c6c1SLuck, Tony "bus error", 1297ea6c6c1SLuck, Tony "micro-architectural error", 1307ea6c6c1SLuck, Tony }; 1317ea6c6c1SLuck, Tony 1323760cd20SChen, Gong static const char * const proc_op_strs[] = { 1337ea6c6c1SLuck, Tony "unknown or generic", 1347ea6c6c1SLuck, Tony "data read", 1357ea6c6c1SLuck, Tony "data write", 1367ea6c6c1SLuck, Tony "instruction execution", 1377ea6c6c1SLuck, Tony }; 1387ea6c6c1SLuck, Tony 1393760cd20SChen, Gong static const char * const proc_flag_strs[] = { 1407ea6c6c1SLuck, Tony "restartable", 1417ea6c6c1SLuck, Tony "precise IP", 1427ea6c6c1SLuck, Tony "overflow", 1437ea6c6c1SLuck, Tony "corrected", 1447ea6c6c1SLuck, Tony }; 1457ea6c6c1SLuck, Tony 1467ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx, 1477ea6c6c1SLuck, Tony const struct cper_sec_proc_generic *proc) 1487ea6c6c1SLuck, Tony { 1497ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TYPE) 1507ea6c6c1SLuck, Tony printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 1513760cd20SChen, Gong proc->proc_type < ARRAY_SIZE(proc_type_strs) ? 1523760cd20SChen, Gong proc_type_strs[proc->proc_type] : "unknown"); 1537ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ISA) 1547ea6c6c1SLuck, Tony printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 1553760cd20SChen, Gong proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? 1563760cd20SChen, Gong proc_isa_strs[proc->proc_isa] : "unknown"); 1577ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 1587ea6c6c1SLuck, Tony printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 1597ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->proc_error_type, 1603760cd20SChen, Gong proc_error_type_strs, 1613760cd20SChen, Gong ARRAY_SIZE(proc_error_type_strs)); 1627ea6c6c1SLuck, Tony } 1637ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 1647ea6c6c1SLuck, Tony printk("%s""operation: %d, %s\n", pfx, proc->operation, 1653760cd20SChen, Gong proc->operation < ARRAY_SIZE(proc_op_strs) ? 1663760cd20SChen, Gong proc_op_strs[proc->operation] : "unknown"); 1677ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 1687ea6c6c1SLuck, Tony printk("%s""flags: 0x%02x\n", pfx, proc->flags); 1693760cd20SChen, Gong cper_print_bits(pfx, proc->flags, proc_flag_strs, 1703760cd20SChen, Gong ARRAY_SIZE(proc_flag_strs)); 1717ea6c6c1SLuck, Tony } 1727ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 1737ea6c6c1SLuck, Tony printk("%s""level: %d\n", pfx, proc->level); 1747ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_VERSION) 1757ea6c6c1SLuck, Tony printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 1767ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ID) 1777ea6c6c1SLuck, Tony printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 1787ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 1797ea6c6c1SLuck, Tony printk("%s""target_address: 0x%016llx\n", 1807ea6c6c1SLuck, Tony pfx, proc->target_addr); 1817ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 1827ea6c6c1SLuck, Tony printk("%s""requestor_id: 0x%016llx\n", 1837ea6c6c1SLuck, Tony pfx, proc->requestor_id); 1847ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 1857ea6c6c1SLuck, Tony printk("%s""responder_id: 0x%016llx\n", 1867ea6c6c1SLuck, Tony pfx, proc->responder_id); 1877ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_IP) 1887ea6c6c1SLuck, Tony printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 1897ea6c6c1SLuck, Tony } 1907ea6c6c1SLuck, Tony 1912f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 1922f74f09bSTyler Baicar static const char * const arm_reg_ctx_strs[] = { 1932f74f09bSTyler Baicar "AArch32 general purpose registers", 1942f74f09bSTyler Baicar "AArch32 EL1 context registers", 1952f74f09bSTyler Baicar "AArch32 EL2 context registers", 1962f74f09bSTyler Baicar "AArch32 secure context registers", 1972f74f09bSTyler Baicar "AArch64 general purpose registers", 1982f74f09bSTyler Baicar "AArch64 EL1 context registers", 1992f74f09bSTyler Baicar "AArch64 EL2 context registers", 2002f74f09bSTyler Baicar "AArch64 EL3 context registers", 2012f74f09bSTyler Baicar "Misc. system register structure", 2022f74f09bSTyler Baicar }; 2032f74f09bSTyler Baicar 2042f74f09bSTyler Baicar static void cper_print_proc_arm(const char *pfx, 2052f74f09bSTyler Baicar const struct cper_sec_proc_arm *proc) 2062f74f09bSTyler Baicar { 2072f74f09bSTyler Baicar int i, len, max_ctx_type; 2082f74f09bSTyler Baicar struct cper_arm_err_info *err_info; 2092f74f09bSTyler Baicar struct cper_arm_ctx_info *ctx_info; 2102f74f09bSTyler Baicar char newpfx[64]; 2112f74f09bSTyler Baicar 2122f74f09bSTyler Baicar printk("%sMIDR: 0x%016llx\n", pfx, proc->midr); 2132f74f09bSTyler Baicar 2142f74f09bSTyler Baicar len = proc->section_length - (sizeof(*proc) + 2152f74f09bSTyler Baicar proc->err_info_num * (sizeof(*err_info))); 2162f74f09bSTyler Baicar if (len < 0) { 2172f74f09bSTyler Baicar printk("%ssection length: %d\n", pfx, proc->section_length); 2182f74f09bSTyler Baicar printk("%ssection length is too small\n", pfx); 2192f74f09bSTyler Baicar printk("%sfirmware-generated error record is incorrect\n", pfx); 2202f74f09bSTyler Baicar printk("%sERR_INFO_NUM is %d\n", pfx, proc->err_info_num); 2212f74f09bSTyler Baicar return; 2222f74f09bSTyler Baicar } 2232f74f09bSTyler Baicar 2242f74f09bSTyler Baicar if (proc->validation_bits & CPER_ARM_VALID_MPIDR) 2252f74f09bSTyler Baicar printk("%sMultiprocessor Affinity Register (MPIDR): 0x%016llx\n", 2262f74f09bSTyler Baicar pfx, proc->mpidr); 2272f74f09bSTyler Baicar 2282f74f09bSTyler Baicar if (proc->validation_bits & CPER_ARM_VALID_AFFINITY_LEVEL) 2292f74f09bSTyler Baicar printk("%serror affinity level: %d\n", pfx, 2302f74f09bSTyler Baicar proc->affinity_level); 2312f74f09bSTyler Baicar 2322f74f09bSTyler Baicar if (proc->validation_bits & CPER_ARM_VALID_RUNNING_STATE) { 2332f74f09bSTyler Baicar printk("%srunning state: 0x%x\n", pfx, proc->running_state); 2342f74f09bSTyler Baicar printk("%sPower State Coordination Interface state: %d\n", 2352f74f09bSTyler Baicar pfx, proc->psci_state); 2362f74f09bSTyler Baicar } 2372f74f09bSTyler Baicar 2382f74f09bSTyler Baicar snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 2392f74f09bSTyler Baicar 2402f74f09bSTyler Baicar err_info = (struct cper_arm_err_info *)(proc + 1); 2412f74f09bSTyler Baicar for (i = 0; i < proc->err_info_num; i++) { 2422f74f09bSTyler Baicar printk("%sError info structure %d:\n", pfx, i); 2432f74f09bSTyler Baicar 2442f74f09bSTyler Baicar printk("%snum errors: %d\n", pfx, err_info->multiple_error + 1); 2452f74f09bSTyler Baicar 2462f74f09bSTyler Baicar if (err_info->validation_bits & CPER_ARM_INFO_VALID_FLAGS) { 2472f74f09bSTyler Baicar if (err_info->flags & CPER_ARM_INFO_FLAGS_FIRST) 2482f74f09bSTyler Baicar printk("%sfirst error captured\n", newpfx); 2492f74f09bSTyler Baicar if (err_info->flags & CPER_ARM_INFO_FLAGS_LAST) 2502f74f09bSTyler Baicar printk("%slast error captured\n", newpfx); 2512f74f09bSTyler Baicar if (err_info->flags & CPER_ARM_INFO_FLAGS_PROPAGATED) 2522f74f09bSTyler Baicar printk("%spropagated error captured\n", 2532f74f09bSTyler Baicar newpfx); 2542f74f09bSTyler Baicar if (err_info->flags & CPER_ARM_INFO_FLAGS_OVERFLOW) 2552f74f09bSTyler Baicar printk("%soverflow occurred, error info is incomplete\n", 2562f74f09bSTyler Baicar newpfx); 2572f74f09bSTyler Baicar } 2582f74f09bSTyler Baicar 2592f74f09bSTyler Baicar printk("%serror_type: %d, %s\n", newpfx, err_info->type, 2602f74f09bSTyler Baicar err_info->type < ARRAY_SIZE(proc_error_type_strs) ? 2612f74f09bSTyler Baicar proc_error_type_strs[err_info->type] : "unknown"); 2622f74f09bSTyler Baicar if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO) 2632f74f09bSTyler Baicar printk("%serror_info: 0x%016llx\n", newpfx, 2642f74f09bSTyler Baicar err_info->error_info); 2652f74f09bSTyler Baicar if (err_info->validation_bits & CPER_ARM_INFO_VALID_VIRT_ADDR) 2662f74f09bSTyler Baicar printk("%svirtual fault address: 0x%016llx\n", 2672f74f09bSTyler Baicar newpfx, err_info->virt_fault_addr); 2682f74f09bSTyler Baicar if (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR) 2692f74f09bSTyler Baicar printk("%sphysical fault address: 0x%016llx\n", 2702f74f09bSTyler Baicar newpfx, err_info->physical_fault_addr); 2712f74f09bSTyler Baicar err_info += 1; 2722f74f09bSTyler Baicar } 2732f74f09bSTyler Baicar 2742f74f09bSTyler Baicar ctx_info = (struct cper_arm_ctx_info *)err_info; 2752f74f09bSTyler Baicar max_ctx_type = ARRAY_SIZE(arm_reg_ctx_strs) - 1; 2762f74f09bSTyler Baicar for (i = 0; i < proc->context_info_num; i++) { 2772f74f09bSTyler Baicar int size = sizeof(*ctx_info) + ctx_info->size; 2782f74f09bSTyler Baicar 2792f74f09bSTyler Baicar printk("%sContext info structure %d:\n", pfx, i); 2802f74f09bSTyler Baicar if (len < size) { 2812f74f09bSTyler Baicar printk("%ssection length is too small\n", newpfx); 2822f74f09bSTyler Baicar printk("%sfirmware-generated error record is incorrect\n", pfx); 2832f74f09bSTyler Baicar return; 2842f74f09bSTyler Baicar } 2852f74f09bSTyler Baicar if (ctx_info->type > max_ctx_type) { 2862f74f09bSTyler Baicar printk("%sInvalid context type: %d (max: %d)\n", 2872f74f09bSTyler Baicar newpfx, ctx_info->type, max_ctx_type); 2882f74f09bSTyler Baicar return; 2892f74f09bSTyler Baicar } 2902f74f09bSTyler Baicar printk("%sregister context type: %s\n", newpfx, 2912f74f09bSTyler Baicar arm_reg_ctx_strs[ctx_info->type]); 2922f74f09bSTyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, 2932f74f09bSTyler Baicar (ctx_info + 1), ctx_info->size, 0); 2942f74f09bSTyler Baicar len -= size; 2952f74f09bSTyler Baicar ctx_info = (struct cper_arm_ctx_info *)((long)ctx_info + size); 2962f74f09bSTyler Baicar } 2972f74f09bSTyler Baicar 2982f74f09bSTyler Baicar if (len > 0) { 2992f74f09bSTyler Baicar printk("%sVendor specific error info has %u bytes:\n", pfx, 3002f74f09bSTyler Baicar len); 3012f74f09bSTyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, ctx_info, 3022f74f09bSTyler Baicar len, true); 3032f74f09bSTyler Baicar } 3042f74f09bSTyler Baicar } 3052f74f09bSTyler Baicar #endif 3062f74f09bSTyler Baicar 3073760cd20SChen, Gong static const char * const mem_err_type_strs[] = { 3087ea6c6c1SLuck, Tony "unknown", 3097ea6c6c1SLuck, Tony "no error", 3107ea6c6c1SLuck, Tony "single-bit ECC", 3117ea6c6c1SLuck, Tony "multi-bit ECC", 3127ea6c6c1SLuck, Tony "single-symbol chipkill ECC", 3137ea6c6c1SLuck, Tony "multi-symbol chipkill ECC", 3147ea6c6c1SLuck, Tony "master abort", 3157ea6c6c1SLuck, Tony "target abort", 3167ea6c6c1SLuck, Tony "parity error", 3177ea6c6c1SLuck, Tony "watchdog timeout", 3187ea6c6c1SLuck, Tony "invalid address", 3197ea6c6c1SLuck, Tony "mirror Broken", 3207ea6c6c1SLuck, Tony "memory sparing", 3217ea6c6c1SLuck, Tony "scrub corrected error", 3227ea6c6c1SLuck, Tony "scrub uncorrected error", 3237ea6c6c1SLuck, Tony "physical memory map-out event", 3247ea6c6c1SLuck, Tony }; 3257ea6c6c1SLuck, Tony 3263760cd20SChen, Gong const char *cper_mem_err_type_str(unsigned int etype) 3273760cd20SChen, Gong { 3283760cd20SChen, Gong return etype < ARRAY_SIZE(mem_err_type_strs) ? 3293760cd20SChen, Gong mem_err_type_strs[etype] : "unknown"; 3303760cd20SChen, Gong } 3313760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_mem_err_type_str); 3323760cd20SChen, Gong 3332dfb7d51SChen, Gong static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 3343760cd20SChen, Gong { 3353760cd20SChen, Gong u32 len, n; 3363760cd20SChen, Gong 3373760cd20SChen, Gong if (!msg) 3383760cd20SChen, Gong return 0; 3393760cd20SChen, Gong 3403760cd20SChen, Gong n = 0; 3413760cd20SChen, Gong len = CPER_REC_LEN - 1; 3423760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_NODE) 3433760cd20SChen, Gong n += scnprintf(msg + n, len - n, "node: %d ", mem->node); 3443760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_CARD) 3453760cd20SChen, Gong n += scnprintf(msg + n, len - n, "card: %d ", mem->card); 3463760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_MODULE) 3473760cd20SChen, Gong n += scnprintf(msg + n, len - n, "module: %d ", mem->module); 3483760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 3493760cd20SChen, Gong n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); 3503760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BANK) 3513760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); 3523760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 3533760cd20SChen, Gong n += scnprintf(msg + n, len - n, "device: %d ", mem->device); 3543760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_ROW) 3553760cd20SChen, Gong n += scnprintf(msg + n, len - n, "row: %d ", mem->row); 3563760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 3573760cd20SChen, Gong n += scnprintf(msg + n, len - n, "column: %d ", mem->column); 3583760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 3593760cd20SChen, Gong n += scnprintf(msg + n, len - n, "bit_position: %d ", 3603760cd20SChen, Gong mem->bit_pos); 3613760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 3623760cd20SChen, Gong n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", 3633760cd20SChen, Gong mem->requestor_id); 3643760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 3653760cd20SChen, Gong n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", 3663760cd20SChen, Gong mem->responder_id); 3673760cd20SChen, Gong if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 3683760cd20SChen, Gong scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 3693760cd20SChen, Gong mem->target_id); 3703760cd20SChen, Gong 3713760cd20SChen, Gong msg[n] = '\0'; 3723760cd20SChen, Gong return n; 3733760cd20SChen, Gong } 3743760cd20SChen, Gong 3752dfb7d51SChen, Gong static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 3763760cd20SChen, Gong { 3773760cd20SChen, Gong u32 len, n; 3783760cd20SChen, Gong const char *bank = NULL, *device = NULL; 3793760cd20SChen, Gong 3803760cd20SChen, Gong if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 3813760cd20SChen, Gong return 0; 3823760cd20SChen, Gong 3833760cd20SChen, Gong n = 0; 3843760cd20SChen, Gong len = CPER_REC_LEN - 1; 3853760cd20SChen, Gong dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 3863760cd20SChen, Gong if (bank && device) 3873760cd20SChen, Gong n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 3883760cd20SChen, Gong else 3893760cd20SChen, Gong n = snprintf(msg, len, 3903760cd20SChen, Gong "DIMM location: not present. DMI handle: 0x%.4x ", 3913760cd20SChen, Gong mem->mem_dev_handle); 3923760cd20SChen, Gong 3933760cd20SChen, Gong msg[n] = '\0'; 3943760cd20SChen, Gong return n; 3953760cd20SChen, Gong } 3963760cd20SChen, Gong 3972dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 3982dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 3992dfb7d51SChen, Gong { 4002dfb7d51SChen, Gong cmem->validation_bits = mem->validation_bits; 4012dfb7d51SChen, Gong cmem->node = mem->node; 4022dfb7d51SChen, Gong cmem->card = mem->card; 4032dfb7d51SChen, Gong cmem->module = mem->module; 4042dfb7d51SChen, Gong cmem->bank = mem->bank; 4052dfb7d51SChen, Gong cmem->device = mem->device; 4062dfb7d51SChen, Gong cmem->row = mem->row; 4072dfb7d51SChen, Gong cmem->column = mem->column; 4082dfb7d51SChen, Gong cmem->bit_pos = mem->bit_pos; 4092dfb7d51SChen, Gong cmem->requestor_id = mem->requestor_id; 4102dfb7d51SChen, Gong cmem->responder_id = mem->responder_id; 4112dfb7d51SChen, Gong cmem->target_id = mem->target_id; 4122dfb7d51SChen, Gong cmem->rank = mem->rank; 4132dfb7d51SChen, Gong cmem->mem_array_handle = mem->mem_array_handle; 4142dfb7d51SChen, Gong cmem->mem_dev_handle = mem->mem_dev_handle; 4152dfb7d51SChen, Gong } 4162dfb7d51SChen, Gong 4172dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p, 4182dfb7d51SChen, Gong struct cper_mem_err_compact *cmem) 4192dfb7d51SChen, Gong { 420dbcf3e06SSteven Rostedt (Red Hat) const char *ret = trace_seq_buffer_ptr(p); 4212dfb7d51SChen, Gong 4222dfb7d51SChen, Gong if (cper_mem_err_location(cmem, rcd_decode_str)) 4232dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 4242dfb7d51SChen, Gong if (cper_dimm_err_location(cmem, rcd_decode_str)) 4252dfb7d51SChen, Gong trace_seq_printf(p, "%s", rcd_decode_str); 4262dfb7d51SChen, Gong trace_seq_putc(p, '\0'); 4272dfb7d51SChen, Gong 4282dfb7d51SChen, Gong return ret; 4292dfb7d51SChen, Gong } 4302dfb7d51SChen, Gong 4314c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 4324c62360dSLuck, Tony int len) 4337ea6c6c1SLuck, Tony { 4342dfb7d51SChen, Gong struct cper_mem_err_compact cmem; 4352dfb7d51SChen, Gong 4364c62360dSLuck, Tony /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 4374c62360dSLuck, Tony if (len == sizeof(struct cper_sec_mem_err_old) && 4384c62360dSLuck, Tony (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 4394c62360dSLuck, Tony pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 4404c62360dSLuck, Tony return; 4414c62360dSLuck, Tony } 4427ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 4437ea6c6c1SLuck, Tony printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 4447ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 4457ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 4467ea6c6c1SLuck, Tony pfx, mem->physical_addr); 4477ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 4487ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 4497ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 4502dfb7d51SChen, Gong cper_mem_err_pack(mem, &cmem); 4512dfb7d51SChen, Gong if (cper_mem_err_location(&cmem, rcd_decode_str)) 4523760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 4537ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 4547ea6c6c1SLuck, Tony u8 etype = mem->error_type; 4557ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 4563760cd20SChen, Gong cper_mem_err_type_str(etype)); 4577ea6c6c1SLuck, Tony } 4582dfb7d51SChen, Gong if (cper_dimm_err_location(&cmem, rcd_decode_str)) 4593760cd20SChen, Gong printk("%s%s\n", pfx, rcd_decode_str); 4607ea6c6c1SLuck, Tony } 4617ea6c6c1SLuck, Tony 4623760cd20SChen, Gong static const char * const pcie_port_type_strs[] = { 4637ea6c6c1SLuck, Tony "PCIe end point", 4647ea6c6c1SLuck, Tony "legacy PCI end point", 4657ea6c6c1SLuck, Tony "unknown", 4667ea6c6c1SLuck, Tony "unknown", 4677ea6c6c1SLuck, Tony "root port", 4687ea6c6c1SLuck, Tony "upstream switch port", 4697ea6c6c1SLuck, Tony "downstream switch port", 4707ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 4717ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 4727ea6c6c1SLuck, Tony "root complex integrated endpoint device", 4737ea6c6c1SLuck, Tony "root complex event collector", 4747ea6c6c1SLuck, Tony }; 4757ea6c6c1SLuck, Tony 4767ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 4770a00fd5eSLv Zheng const struct acpi_hest_generic_data *gdata) 4787ea6c6c1SLuck, Tony { 4797ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 4807ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 4813760cd20SChen, Gong pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 4823760cd20SChen, Gong pcie_port_type_strs[pcie->port_type] : "unknown"); 4837ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 4847ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 4857ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 4867ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 4877ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 4887ea6c6c1SLuck, Tony pcie->command, pcie->status); 4897ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 4907ea6c6c1SLuck, Tony const __u8 *p; 4917ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 4927ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 4937ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 4947ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 4957ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 4967ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 4977ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 4987ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 4997ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 5007ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 5017ea6c6c1SLuck, Tony printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); 5027ea6c6c1SLuck, Tony } 5037ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 5047ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 5057ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 5067ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 5077ea6c6c1SLuck, Tony printk( 5087ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 5097ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 5107ea6c6c1SLuck, Tony } 5117ea6c6c1SLuck, Tony 5128a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx, 5138a94471fSTyler Baicar struct acpi_hest_generic_data_v300 *gdata) 5148a94471fSTyler Baicar { 5158a94471fSTyler Baicar __u8 hour, min, sec, day, mon, year, century, *timestamp; 5168a94471fSTyler Baicar 5178a94471fSTyler Baicar if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 5188a94471fSTyler Baicar timestamp = (__u8 *)&(gdata->time_stamp); 5198a94471fSTyler Baicar sec = bcd2bin(timestamp[0]); 5208a94471fSTyler Baicar min = bcd2bin(timestamp[1]); 5218a94471fSTyler Baicar hour = bcd2bin(timestamp[2]); 5228a94471fSTyler Baicar day = bcd2bin(timestamp[4]); 5238a94471fSTyler Baicar mon = bcd2bin(timestamp[5]); 5248a94471fSTyler Baicar year = bcd2bin(timestamp[6]); 5258a94471fSTyler Baicar century = bcd2bin(timestamp[7]); 5268a94471fSTyler Baicar 5278a94471fSTyler Baicar printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 5288a94471fSTyler Baicar (timestamp[3] & 0x1 ? "precise " : "imprecise "), 5298a94471fSTyler Baicar century, year, mon, day, hour, min, sec); 5308a94471fSTyler Baicar } 5318a94471fSTyler Baicar } 5328a94471fSTyler Baicar 533bbcc2e7bSTyler Baicar static void 534bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 535bbcc2e7bSTyler Baicar int sec_no) 5367ea6c6c1SLuck, Tony { 5377ea6c6c1SLuck, Tony uuid_le *sec_type = (uuid_le *)gdata->section_type; 5387ea6c6c1SLuck, Tony __u16 severity; 5397ea6c6c1SLuck, Tony char newpfx[64]; 5407ea6c6c1SLuck, Tony 5418a94471fSTyler Baicar if (acpi_hest_get_version(gdata) >= 3) 5428a94471fSTyler Baicar cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 5438a94471fSTyler Baicar 5447ea6c6c1SLuck, Tony severity = gdata->error_severity; 5457ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 5467ea6c6c1SLuck, Tony cper_severity_str(severity)); 5477ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 5487ea6c6c1SLuck, Tony printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); 5497ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 5507ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 5517ea6c6c1SLuck, Tony 5527ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 5537ea6c6c1SLuck, Tony if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { 554bbcc2e7bSTyler Baicar struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 555bbcc2e7bSTyler Baicar 5567ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 5577ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 5587ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 5597ea6c6c1SLuck, Tony else 5607ea6c6c1SLuck, Tony goto err_section_too_small; 5617ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { 562bbcc2e7bSTyler Baicar struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 563bbcc2e7bSTyler Baicar 5647ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 5654c62360dSLuck, Tony if (gdata->error_data_length >= 5664c62360dSLuck, Tony sizeof(struct cper_sec_mem_err_old)) 5674c62360dSLuck, Tony cper_print_mem(newpfx, mem_err, 5684c62360dSLuck, Tony gdata->error_data_length); 5697ea6c6c1SLuck, Tony else 5707ea6c6c1SLuck, Tony goto err_section_too_small; 5717ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { 572bbcc2e7bSTyler Baicar struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 573bbcc2e7bSTyler Baicar 5747ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 5757ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 5767ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 5777ea6c6c1SLuck, Tony else 5787ea6c6c1SLuck, Tony goto err_section_too_small; 5792f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 5802f74f09bSTyler Baicar } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_ARM)) { 5812f74f09bSTyler Baicar struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 5822f74f09bSTyler Baicar 5832f74f09bSTyler Baicar printk("%ssection_type: ARM processor error\n", newpfx); 5842f74f09bSTyler Baicar if (gdata->error_data_length >= sizeof(*arm_err)) 5852f74f09bSTyler Baicar cper_print_proc_arm(newpfx, arm_err); 5862f74f09bSTyler Baicar else 5872f74f09bSTyler Baicar goto err_section_too_small; 5882f74f09bSTyler Baicar #endif 5890fc300f4STyler Baicar } else { 5900fc300f4STyler Baicar const void *err = acpi_hest_get_payload(gdata); 5910fc300f4STyler Baicar 5920fc300f4STyler Baicar printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 5930fc300f4STyler Baicar printk("%ssection length: %#x\n", newpfx, 5940fc300f4STyler Baicar gdata->error_data_length); 5950fc300f4STyler Baicar print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 5960fc300f4STyler Baicar gdata->error_data_length, true); 5970fc300f4STyler Baicar } 5987ea6c6c1SLuck, Tony 5997ea6c6c1SLuck, Tony return; 6007ea6c6c1SLuck, Tony 6017ea6c6c1SLuck, Tony err_section_too_small: 6027ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 6037ea6c6c1SLuck, Tony } 6047ea6c6c1SLuck, Tony 6057ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 6060a00fd5eSLv Zheng const struct acpi_hest_generic_status *estatus) 6077ea6c6c1SLuck, Tony { 6080a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 609bbcc2e7bSTyler Baicar unsigned int data_len; 6107ea6c6c1SLuck, Tony int sec_no = 0; 6117ea6c6c1SLuck, Tony char newpfx[64]; 6127ea6c6c1SLuck, Tony __u16 severity; 6137ea6c6c1SLuck, Tony 6147ea6c6c1SLuck, Tony severity = estatus->error_severity; 6157ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 6167ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 6177ea6c6c1SLuck, Tony "It has been corrected by h/w " 6187ea6c6c1SLuck, Tony "and requires no further action"); 6197ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 6207ea6c6c1SLuck, Tony data_len = estatus->data_length; 6210a00fd5eSLv Zheng gdata = (struct acpi_hest_generic_data *)(estatus + 1); 6227ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 623bbcc2e7bSTyler Baicar 624bbcc2e7bSTyler Baicar while (data_len >= acpi_hest_get_size(gdata)) { 6257ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 626bbcc2e7bSTyler Baicar data_len -= acpi_hest_get_record_size(gdata); 627bbcc2e7bSTyler Baicar gdata = acpi_hest_get_next(gdata); 6287ea6c6c1SLuck, Tony sec_no++; 6297ea6c6c1SLuck, Tony } 6307ea6c6c1SLuck, Tony } 6317ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 6327ea6c6c1SLuck, Tony 6330a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 6347ea6c6c1SLuck, Tony { 6357ea6c6c1SLuck, Tony if (estatus->data_length && 6360a00fd5eSLv Zheng estatus->data_length < sizeof(struct acpi_hest_generic_data)) 6377ea6c6c1SLuck, Tony return -EINVAL; 6387ea6c6c1SLuck, Tony if (estatus->raw_data_length && 6397ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 6407ea6c6c1SLuck, Tony return -EINVAL; 6417ea6c6c1SLuck, Tony 6427ea6c6c1SLuck, Tony return 0; 6437ea6c6c1SLuck, Tony } 6447ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 6457ea6c6c1SLuck, Tony 6460a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 6477ea6c6c1SLuck, Tony { 6480a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 6497ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 6507ea6c6c1SLuck, Tony int rc; 6517ea6c6c1SLuck, Tony 6527ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 6537ea6c6c1SLuck, Tony if (rc) 6547ea6c6c1SLuck, Tony return rc; 6557ea6c6c1SLuck, Tony data_len = estatus->data_length; 6560a00fd5eSLv Zheng gdata = (struct acpi_hest_generic_data *)(estatus + 1); 657bbcc2e7bSTyler Baicar 658bbcc2e7bSTyler Baicar while (data_len >= acpi_hest_get_size(gdata)) { 659bbcc2e7bSTyler Baicar gedata_len = acpi_hest_get_error_length(gdata); 660bbcc2e7bSTyler Baicar if (gedata_len > data_len - acpi_hest_get_size(gdata)) 6617ea6c6c1SLuck, Tony return -EINVAL; 662bbcc2e7bSTyler Baicar 663bbcc2e7bSTyler Baicar data_len -= acpi_hest_get_record_size(gdata); 664bbcc2e7bSTyler Baicar gdata = acpi_hest_get_next(gdata); 6657ea6c6c1SLuck, Tony } 6667ea6c6c1SLuck, Tony if (data_len) 6677ea6c6c1SLuck, Tony return -EINVAL; 6687ea6c6c1SLuck, Tony 6697ea6c6c1SLuck, Tony return 0; 6707ea6c6c1SLuck, Tony } 6717ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 672