xref: /openbmc/linux/drivers/firmware/efi/cper.c (revision c0020756315eebec58310aca42cf9fb73e1322eb)
17ea6c6c1SLuck, Tony /*
27ea6c6c1SLuck, Tony  * UEFI Common Platform Error Record (CPER) support
37ea6c6c1SLuck, Tony  *
47ea6c6c1SLuck, Tony  * Copyright (C) 2010, Intel Corp.
57ea6c6c1SLuck, Tony  *	Author: Huang Ying <ying.huang@intel.com>
67ea6c6c1SLuck, Tony  *
77ea6c6c1SLuck, Tony  * CPER is the format used to describe platform hardware error by
87ea6c6c1SLuck, Tony  * various tables, such as ERST, BERT and HEST etc.
97ea6c6c1SLuck, Tony  *
107ea6c6c1SLuck, Tony  * For more information about CPER, please refer to Appendix N of UEFI
117ea6c6c1SLuck, Tony  * Specification version 2.4.
127ea6c6c1SLuck, Tony  *
137ea6c6c1SLuck, Tony  * This program is free software; you can redistribute it and/or
147ea6c6c1SLuck, Tony  * modify it under the terms of the GNU General Public License version
157ea6c6c1SLuck, Tony  * 2 as published by the Free Software Foundation.
167ea6c6c1SLuck, Tony  *
177ea6c6c1SLuck, Tony  * This program is distributed in the hope that it will be useful,
187ea6c6c1SLuck, Tony  * but WITHOUT ANY WARRANTY; without even the implied warranty of
197ea6c6c1SLuck, Tony  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
207ea6c6c1SLuck, Tony  * GNU General Public License for more details.
217ea6c6c1SLuck, Tony  *
227ea6c6c1SLuck, Tony  * You should have received a copy of the GNU General Public License
237ea6c6c1SLuck, Tony  * along with this program; if not, write to the Free Software
247ea6c6c1SLuck, Tony  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
257ea6c6c1SLuck, Tony  */
267ea6c6c1SLuck, Tony 
277ea6c6c1SLuck, Tony #include <linux/kernel.h>
287ea6c6c1SLuck, Tony #include <linux/module.h>
297ea6c6c1SLuck, Tony #include <linux/time.h>
307ea6c6c1SLuck, Tony #include <linux/cper.h>
317ea6c6c1SLuck, Tony #include <linux/dmi.h>
327ea6c6c1SLuck, Tony #include <linux/acpi.h>
337ea6c6c1SLuck, Tony #include <linux/pci.h>
347ea6c6c1SLuck, Tony #include <linux/aer.h>
358a94471fSTyler Baicar #include <linux/printk.h>
368a94471fSTyler Baicar #include <linux/bcd.h>
37bbcc2e7bSTyler Baicar #include <acpi/ghes.h>
38e9279e83STyler Baicar #include <ras/ras_event.h>
397ea6c6c1SLuck, Tony 
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 {
537*c0020756SAndy Shevchenko 	guid_t *sec_type = (guid_t *)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)
548*c0020756SAndy Shevchenko 		printk("%s""fru_id: %pUl\n", pfx, 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);
553*c0020756SAndy Shevchenko 	if (guid_equal(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;
561*c0020756SAndy Shevchenko 	} else if (guid_equal(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;
571*c0020756SAndy Shevchenko 	} else if (guid_equal(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