xref: /linux/drivers/firmware/efi/cper.c (revision d7171eb4)
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>
27abdbf1a2SSmita 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  */
cper_next_record_id(void)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 
cper_severity_str(unsigned int severity)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  */
cper_print_bits(const char * pfx,unsigned int bits,const char * const strs[],unsigned int strs_size)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 
cper_print_proc_generic(const char * pfx,const struct cper_sec_proc_generic * proc)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 
cper_mem_err_type_str(unsigned int etype)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 
cper_mem_err_status_str(u64 status)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 
cper_mem_err_location(struct cper_mem_err_compact * mem,char * msg)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 }
2945012524eSJia He EXPORT_SYMBOL_GPL(cper_mem_err_location);
2953760cd20SChen, Gong 
cper_dimm_err_location(struct cper_mem_err_compact * mem,char * msg)296ed27b5dfSShuai Xue int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
2973760cd20SChen, Gong {
2983760cd20SChen, Gong 	u32 len, n;
2993760cd20SChen, Gong 	const char *bank = NULL, *device = NULL;
3003760cd20SChen, Gong 
3013760cd20SChen, Gong 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
3023760cd20SChen, Gong 		return 0;
3033760cd20SChen, Gong 
304942859d9SRasmus Villemoes 	len = CPER_REC_LEN;
3053760cd20SChen, Gong 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
3063760cd20SChen, Gong 	if (bank && device)
3073760cd20SChen, Gong 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
3083760cd20SChen, Gong 	else
3093760cd20SChen, Gong 		n = snprintf(msg, len,
3103760cd20SChen, Gong 			     "DIMM location: not present. DMI handle: 0x%.4x ",
3113760cd20SChen, Gong 			     mem->mem_dev_handle);
3123760cd20SChen, Gong 
3133760cd20SChen, Gong 	return n;
3143760cd20SChen, Gong }
3155012524eSJia He EXPORT_SYMBOL_GPL(cper_dimm_err_location);
3163760cd20SChen, Gong 
cper_mem_err_pack(const struct cper_sec_mem_err * mem,struct cper_mem_err_compact * cmem)3172dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
3182dfb7d51SChen, Gong 		       struct cper_mem_err_compact *cmem)
3192dfb7d51SChen, Gong {
3202dfb7d51SChen, Gong 	cmem->validation_bits = mem->validation_bits;
3212dfb7d51SChen, Gong 	cmem->node = mem->node;
3222dfb7d51SChen, Gong 	cmem->card = mem->card;
3232dfb7d51SChen, Gong 	cmem->module = mem->module;
3242dfb7d51SChen, Gong 	cmem->bank = mem->bank;
3252dfb7d51SChen, Gong 	cmem->device = mem->device;
3262dfb7d51SChen, Gong 	cmem->row = mem->row;
3272dfb7d51SChen, Gong 	cmem->column = mem->column;
3282dfb7d51SChen, Gong 	cmem->bit_pos = mem->bit_pos;
3292dfb7d51SChen, Gong 	cmem->requestor_id = mem->requestor_id;
3302dfb7d51SChen, Gong 	cmem->responder_id = mem->responder_id;
3312dfb7d51SChen, Gong 	cmem->target_id = mem->target_id;
3329baf68ccSAlex Kluver 	cmem->extended = mem->extended;
3332dfb7d51SChen, Gong 	cmem->rank = mem->rank;
3342dfb7d51SChen, Gong 	cmem->mem_array_handle = mem->mem_array_handle;
3352dfb7d51SChen, Gong 	cmem->mem_dev_handle = mem->mem_dev_handle;
3362dfb7d51SChen, Gong }
3375012524eSJia He EXPORT_SYMBOL_GPL(cper_mem_err_pack);
3382dfb7d51SChen, Gong 
cper_mem_err_unpack(struct trace_seq * p,struct cper_mem_err_compact * cmem)3392dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p,
3402dfb7d51SChen, Gong 				struct cper_mem_err_compact *cmem)
3412dfb7d51SChen, Gong {
342dbcf3e06SSteven Rostedt (Red Hat) 	const char *ret = trace_seq_buffer_ptr(p);
343b3a72ca8SArd Biesheuvel 	char rcd_decode_str[CPER_REC_LEN];
3442dfb7d51SChen, Gong 
3452dfb7d51SChen, Gong 	if (cper_mem_err_location(cmem, rcd_decode_str))
3462dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
3472dfb7d51SChen, Gong 	if (cper_dimm_err_location(cmem, rcd_decode_str))
3482dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
3492dfb7d51SChen, Gong 	trace_seq_putc(p, '\0');
3502dfb7d51SChen, Gong 
3512dfb7d51SChen, Gong 	return ret;
3522dfb7d51SChen, Gong }
3532dfb7d51SChen, Gong 
cper_print_mem(const char * pfx,const struct cper_sec_mem_err * mem,int len)3544c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
3554c62360dSLuck, Tony 	int len)
3567ea6c6c1SLuck, Tony {
3572dfb7d51SChen, Gong 	struct cper_mem_err_compact cmem;
358b3a72ca8SArd Biesheuvel 	char rcd_decode_str[CPER_REC_LEN];
3592dfb7d51SChen, Gong 
3604c62360dSLuck, Tony 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
3614c62360dSLuck, Tony 	if (len == sizeof(struct cper_sec_mem_err_old) &&
3624c62360dSLuck, Tony 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
3634c62360dSLuck, Tony 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
3644c62360dSLuck, Tony 		return;
3654c62360dSLuck, Tony 	}
3667ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
367bdae7965SShuai Xue 		printk("%s error_status: %s (0x%016llx)\n",
368bdae7965SShuai Xue 		       pfx, cper_mem_err_status_str(mem->error_status),
369bdae7965SShuai Xue 		       mem->error_status);
3707ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA)
3717ea6c6c1SLuck, Tony 		printk("%s""physical_address: 0x%016llx\n",
3727ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr);
3737ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
3747ea6c6c1SLuck, Tony 		printk("%s""physical_address_mask: 0x%016llx\n",
3757ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr_mask);
3762dfb7d51SChen, Gong 	cper_mem_err_pack(mem, &cmem);
3772dfb7d51SChen, Gong 	if (cper_mem_err_location(&cmem, rcd_decode_str))
3783760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
3797ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
3807ea6c6c1SLuck, Tony 		u8 etype = mem->error_type;
3817ea6c6c1SLuck, Tony 		printk("%s""error_type: %d, %s\n", pfx, etype,
3823760cd20SChen, Gong 		       cper_mem_err_type_str(etype));
3837ea6c6c1SLuck, Tony 	}
3842dfb7d51SChen, Gong 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
3853760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
3867ea6c6c1SLuck, Tony }
3877ea6c6c1SLuck, Tony 
3883760cd20SChen, Gong static const char * const pcie_port_type_strs[] = {
3897ea6c6c1SLuck, Tony 	"PCIe end point",
3907ea6c6c1SLuck, Tony 	"legacy PCI end point",
3917ea6c6c1SLuck, Tony 	"unknown",
3927ea6c6c1SLuck, Tony 	"unknown",
3937ea6c6c1SLuck, Tony 	"root port",
3947ea6c6c1SLuck, Tony 	"upstream switch port",
3957ea6c6c1SLuck, Tony 	"downstream switch port",
3967ea6c6c1SLuck, Tony 	"PCIe to PCI/PCI-X bridge",
3977ea6c6c1SLuck, Tony 	"PCI/PCI-X to PCIe bridge",
3987ea6c6c1SLuck, Tony 	"root complex integrated endpoint device",
3997ea6c6c1SLuck, Tony 	"root complex event collector",
4007ea6c6c1SLuck, Tony };
4017ea6c6c1SLuck, Tony 
cper_print_pcie(const char * pfx,const struct cper_sec_pcie * pcie,const struct acpi_hest_generic_data * gdata)4027ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
4030a00fd5eSLv Zheng 			    const struct acpi_hest_generic_data *gdata)
4047ea6c6c1SLuck, Tony {
4057ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
4067ea6c6c1SLuck, Tony 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
4073760cd20SChen, Gong 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
4083760cd20SChen, Gong 		       pcie_port_type_strs[pcie->port_type] : "unknown");
4097ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
4107ea6c6c1SLuck, Tony 		printk("%s""version: %d.%d\n", pfx,
4117ea6c6c1SLuck, Tony 		       pcie->version.major, pcie->version.minor);
4127ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
4137ea6c6c1SLuck, Tony 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
4147ea6c6c1SLuck, Tony 		       pcie->command, pcie->status);
4157ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
4167ea6c6c1SLuck, Tony 		const __u8 *p;
4177ea6c6c1SLuck, Tony 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
4187ea6c6c1SLuck, Tony 		       pcie->device_id.segment, pcie->device_id.bus,
4197ea6c6c1SLuck, Tony 		       pcie->device_id.device, pcie->device_id.function);
4207ea6c6c1SLuck, Tony 		printk("%s""slot: %d\n", pfx,
4217ea6c6c1SLuck, Tony 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
4227ea6c6c1SLuck, Tony 		printk("%s""secondary_bus: 0x%02x\n", pfx,
4237ea6c6c1SLuck, Tony 		       pcie->device_id.secondary_bus);
4247ea6c6c1SLuck, Tony 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
4257ea6c6c1SLuck, Tony 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
4267ea6c6c1SLuck, Tony 		p = pcie->device_id.class_code;
4276fb9367aSLukas Wunner 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
4287ea6c6c1SLuck, Tony 	}
4297ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
4307ea6c6c1SLuck, Tony 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
4317ea6c6c1SLuck, Tony 		       pcie->serial_number.lower, pcie->serial_number.upper);
4327ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
4337ea6c6c1SLuck, Tony 		printk(
4347ea6c6c1SLuck, Tony 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
4357ea6c6c1SLuck, Tony 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
436b194a77fSXiaofei Tan 
437*d7171eb4SYazen Ghannam 	/*
438*d7171eb4SYazen Ghannam 	 * Print all valid AER info. Record may be from BERT (boot-time) or GHES (run-time).
439*d7171eb4SYazen Ghannam 	 *
440*d7171eb4SYazen Ghannam 	 * Fatal errors call __ghes_panic() before AER handler prints this.
441*d7171eb4SYazen Ghannam 	 */
442*d7171eb4SYazen Ghannam 	if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) {
443b194a77fSXiaofei Tan 		struct aer_capability_regs *aer;
444b194a77fSXiaofei Tan 
445b194a77fSXiaofei Tan 		aer = (struct aer_capability_regs *)pcie->aer_info;
446*d7171eb4SYazen Ghannam 		printk("%saer_cor_status: 0x%08x, aer_cor_mask: 0x%08x\n",
447*d7171eb4SYazen Ghannam 		       pfx, aer->cor_status, aer->cor_mask);
448b194a77fSXiaofei Tan 		printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
449b194a77fSXiaofei Tan 		       pfx, aer->uncor_status, aer->uncor_mask);
450b194a77fSXiaofei Tan 		printk("%saer_uncor_severity: 0x%08x\n",
451b194a77fSXiaofei Tan 		       pfx, aer->uncor_severity);
452b194a77fSXiaofei Tan 		printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
4530a5a46a6SIlpo Järvinen 		       aer->header_log.dw[0], aer->header_log.dw[1],
4540a5a46a6SIlpo Järvinen 		       aer->header_log.dw[2], aer->header_log.dw[3]);
455b194a77fSXiaofei Tan 	}
4567ea6c6c1SLuck, Tony }
4577ea6c6c1SLuck, Tony 
4583d8c11efSPunit Agrawal static const char * const fw_err_rec_type_strs[] = {
4593d8c11efSPunit Agrawal 	"IPF SAL Error Record",
4603d8c11efSPunit Agrawal 	"SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
4613d8c11efSPunit Agrawal 	"SOC Firmware Error Record Type2",
4623d8c11efSPunit Agrawal };
4633d8c11efSPunit Agrawal 
cper_print_fw_err(const char * pfx,struct acpi_hest_generic_data * gdata,const struct cper_sec_fw_err_rec_ref * fw_err)4643d8c11efSPunit Agrawal static void cper_print_fw_err(const char *pfx,
4653d8c11efSPunit Agrawal 			      struct acpi_hest_generic_data *gdata,
4663d8c11efSPunit Agrawal 			      const struct cper_sec_fw_err_rec_ref *fw_err)
4673d8c11efSPunit Agrawal {
4683d8c11efSPunit Agrawal 	void *buf = acpi_hest_get_payload(gdata);
4693d8c11efSPunit Agrawal 	u32 offset, length = gdata->error_data_length;
4703d8c11efSPunit Agrawal 
4713d8c11efSPunit Agrawal 	printk("%s""Firmware Error Record Type: %s\n", pfx,
4723d8c11efSPunit Agrawal 	       fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
4733d8c11efSPunit Agrawal 	       fw_err_rec_type_strs[fw_err->record_type] : "unknown");
4743d8c11efSPunit Agrawal 	printk("%s""Revision: %d\n", pfx, fw_err->revision);
4753d8c11efSPunit Agrawal 
4763d8c11efSPunit Agrawal 	/* Record Type based on UEFI 2.7 */
4773d8c11efSPunit Agrawal 	if (fw_err->revision == 0) {
4783d8c11efSPunit Agrawal 		printk("%s""Record Identifier: %08llx\n", pfx,
4793d8c11efSPunit Agrawal 		       fw_err->record_identifier);
4803d8c11efSPunit Agrawal 	} else if (fw_err->revision == 2) {
4813d8c11efSPunit Agrawal 		printk("%s""Record Identifier: %pUl\n", pfx,
4823d8c11efSPunit Agrawal 		       &fw_err->record_identifier_guid);
4833d8c11efSPunit Agrawal 	}
4843d8c11efSPunit Agrawal 
4853d8c11efSPunit Agrawal 	/*
4863d8c11efSPunit Agrawal 	 * The FW error record may contain trailing data beyond the
4873d8c11efSPunit Agrawal 	 * structure defined by the specification. As the fields
4883d8c11efSPunit Agrawal 	 * defined (and hence the offset of any trailing data) vary
4893d8c11efSPunit Agrawal 	 * with the revision, set the offset to account for this
4903d8c11efSPunit Agrawal 	 * variation.
4913d8c11efSPunit Agrawal 	 */
4923d8c11efSPunit Agrawal 	if (fw_err->revision == 0) {
4933d8c11efSPunit Agrawal 		/* record_identifier_guid not defined */
4943d8c11efSPunit Agrawal 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
4953d8c11efSPunit Agrawal 				  record_identifier_guid);
4963d8c11efSPunit Agrawal 	} else if (fw_err->revision == 1) {
4973d8c11efSPunit Agrawal 		/* record_identifier not defined */
4983d8c11efSPunit Agrawal 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
4993d8c11efSPunit Agrawal 				  record_identifier);
5003d8c11efSPunit Agrawal 	} else {
5013d8c11efSPunit Agrawal 		offset = sizeof(*fw_err);
5023d8c11efSPunit Agrawal 	}
5033d8c11efSPunit Agrawal 
5043d8c11efSPunit Agrawal 	buf += offset;
5053d8c11efSPunit Agrawal 	length -= offset;
5063d8c11efSPunit Agrawal 
5073d8c11efSPunit Agrawal 	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
5083d8c11efSPunit Agrawal }
5093d8c11efSPunit Agrawal 
cper_print_tstamp(const char * pfx,struct acpi_hest_generic_data_v300 * gdata)5108a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx,
5118a94471fSTyler Baicar 				   struct acpi_hest_generic_data_v300 *gdata)
5128a94471fSTyler Baicar {
5138a94471fSTyler Baicar 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
5148a94471fSTyler Baicar 
5158a94471fSTyler Baicar 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
5168a94471fSTyler Baicar 		timestamp = (__u8 *)&(gdata->time_stamp);
5178a94471fSTyler Baicar 		sec       = bcd2bin(timestamp[0]);
5188a94471fSTyler Baicar 		min       = bcd2bin(timestamp[1]);
5198a94471fSTyler Baicar 		hour      = bcd2bin(timestamp[2]);
5208a94471fSTyler Baicar 		day       = bcd2bin(timestamp[4]);
5218a94471fSTyler Baicar 		mon       = bcd2bin(timestamp[5]);
5228a94471fSTyler Baicar 		year      = bcd2bin(timestamp[6]);
5238a94471fSTyler Baicar 		century   = bcd2bin(timestamp[7]);
5248a94471fSTyler Baicar 
5258a94471fSTyler Baicar 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
5268a94471fSTyler Baicar 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
5278a94471fSTyler Baicar 		       century, year, mon, day, hour, min, sec);
5288a94471fSTyler Baicar 	}
5298a94471fSTyler Baicar }
5308a94471fSTyler Baicar 
53154ce1927SIra Weiny struct ignore_section {
53254ce1927SIra Weiny 	guid_t guid;
53354ce1927SIra Weiny 	const char *name;
53454ce1927SIra Weiny };
53554ce1927SIra Weiny 
53654ce1927SIra Weiny static const struct ignore_section ignore_sections[] = {
53754ce1927SIra Weiny 	{ .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" },
53854ce1927SIra Weiny 	{ .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" },
53954ce1927SIra Weiny 	{ .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" },
54054ce1927SIra Weiny };
54154ce1927SIra Weiny 
542bbcc2e7bSTyler Baicar static void
cper_estatus_print_section(const char * pfx,struct acpi_hest_generic_data * gdata,int sec_no)543bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
544bbcc2e7bSTyler Baicar 			   int sec_no)
5457ea6c6c1SLuck, Tony {
546c0020756SAndy Shevchenko 	guid_t *sec_type = (guid_t *)gdata->section_type;
5477ea6c6c1SLuck, Tony 	__u16 severity;
5487ea6c6c1SLuck, Tony 	char newpfx[64];
5497ea6c6c1SLuck, Tony 
5508a94471fSTyler Baicar 	if (acpi_hest_get_version(gdata) >= 3)
5518a94471fSTyler Baicar 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
5528a94471fSTyler Baicar 
5537ea6c6c1SLuck, Tony 	severity = gdata->error_severity;
5547ea6c6c1SLuck, Tony 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
5557ea6c6c1SLuck, Tony 	       cper_severity_str(severity));
5567ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
557c0020756SAndy Shevchenko 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
5587ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
5597ea6c6c1SLuck, Tony 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
5607ea6c6c1SLuck, Tony 
56175e4fd31SBorislav Petkov 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
56254ce1927SIra Weiny 
56354ce1927SIra Weiny 	for (int i = 0; i < ARRAY_SIZE(ignore_sections); i++) {
56454ce1927SIra Weiny 		if (guid_equal(sec_type, &ignore_sections[i].guid)) {
56554ce1927SIra Weiny 			printk("%ssection_type: %s\n", newpfx, ignore_sections[i].name);
56654ce1927SIra Weiny 			return;
56754ce1927SIra Weiny 		}
56854ce1927SIra Weiny 	}
56954ce1927SIra Weiny 
570c0020756SAndy Shevchenko 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
571bbcc2e7bSTyler Baicar 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
572bbcc2e7bSTyler Baicar 
5737ea6c6c1SLuck, Tony 		printk("%s""section_type: general processor error\n", newpfx);
5747ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*proc_err))
5757ea6c6c1SLuck, Tony 			cper_print_proc_generic(newpfx, proc_err);
5767ea6c6c1SLuck, Tony 		else
5777ea6c6c1SLuck, Tony 			goto err_section_too_small;
578c0020756SAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
579bbcc2e7bSTyler Baicar 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
580bbcc2e7bSTyler Baicar 
5817ea6c6c1SLuck, Tony 		printk("%s""section_type: memory error\n", newpfx);
5824c62360dSLuck, Tony 		if (gdata->error_data_length >=
5834c62360dSLuck, Tony 		    sizeof(struct cper_sec_mem_err_old))
5844c62360dSLuck, Tony 			cper_print_mem(newpfx, mem_err,
5854c62360dSLuck, Tony 				       gdata->error_data_length);
5867ea6c6c1SLuck, Tony 		else
5877ea6c6c1SLuck, Tony 			goto err_section_too_small;
588c0020756SAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
589bbcc2e7bSTyler Baicar 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
590bbcc2e7bSTyler Baicar 
5917ea6c6c1SLuck, Tony 		printk("%s""section_type: PCIe error\n", newpfx);
5927ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*pcie))
5937ea6c6c1SLuck, Tony 			cper_print_pcie(newpfx, pcie, gdata);
5947ea6c6c1SLuck, Tony 		else
5957ea6c6c1SLuck, Tony 			goto err_section_too_small;
5962f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
597e8f4194dSAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
5982f74f09bSTyler Baicar 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
5992f74f09bSTyler Baicar 
6002f74f09bSTyler Baicar 		printk("%ssection_type: ARM processor error\n", newpfx);
6012f74f09bSTyler Baicar 		if (gdata->error_data_length >= sizeof(*arm_err))
6022f74f09bSTyler Baicar 			cper_print_proc_arm(newpfx, arm_err);
6032f74f09bSTyler Baicar 		else
6042f74f09bSTyler Baicar 			goto err_section_too_small;
6052f74f09bSTyler Baicar #endif
606f9e1bdb9SYazen Ghannam #if defined(CONFIG_UEFI_CPER_X86)
607f9e1bdb9SYazen Ghannam 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
608f9e1bdb9SYazen Ghannam 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
609f9e1bdb9SYazen Ghannam 
610f9e1bdb9SYazen Ghannam 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
611f9e1bdb9SYazen Ghannam 		if (gdata->error_data_length >= sizeof(*ia_err))
612f9e1bdb9SYazen Ghannam 			cper_print_proc_ia(newpfx, ia_err);
613f9e1bdb9SYazen Ghannam 		else
614f9e1bdb9SYazen Ghannam 			goto err_section_too_small;
615f9e1bdb9SYazen Ghannam #endif
6163d8c11efSPunit Agrawal 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
6173d8c11efSPunit Agrawal 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
6183d8c11efSPunit Agrawal 
6193d8c11efSPunit Agrawal 		printk("%ssection_type: Firmware Error Record Reference\n",
6203d8c11efSPunit Agrawal 		       newpfx);
6213d8c11efSPunit Agrawal 		/* The minimal FW Error Record contains 16 bytes */
6223d8c11efSPunit Agrawal 		if (gdata->error_data_length >= SZ_16)
6233d8c11efSPunit Agrawal 			cper_print_fw_err(newpfx, gdata, fw_err);
6243d8c11efSPunit Agrawal 		else
6253d8c11efSPunit Agrawal 			goto err_section_too_small;
626abdbf1a2SSmita Koralahalli 	} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
627abdbf1a2SSmita Koralahalli 		struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
628abdbf1a2SSmita Koralahalli 
629abdbf1a2SSmita Koralahalli 		printk("%ssection_type: CXL Protocol Error\n", newpfx);
630abdbf1a2SSmita Koralahalli 		if (gdata->error_data_length >= sizeof(*prot_err))
631abdbf1a2SSmita Koralahalli 			cper_print_prot_err(newpfx, prot_err);
632abdbf1a2SSmita Koralahalli 		else
633abdbf1a2SSmita Koralahalli 			goto err_section_too_small;
6340fc300f4STyler Baicar 	} else {
6350fc300f4STyler Baicar 		const void *err = acpi_hest_get_payload(gdata);
6360fc300f4STyler Baicar 
6370fc300f4STyler Baicar 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
6380fc300f4STyler Baicar 		printk("%ssection length: %#x\n", newpfx,
6390fc300f4STyler Baicar 		       gdata->error_data_length);
6400fc300f4STyler Baicar 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
6410fc300f4STyler Baicar 			       gdata->error_data_length, true);
6420fc300f4STyler Baicar 	}
6437ea6c6c1SLuck, Tony 
6447ea6c6c1SLuck, Tony 	return;
6457ea6c6c1SLuck, Tony 
6467ea6c6c1SLuck, Tony err_section_too_small:
6477ea6c6c1SLuck, Tony 	pr_err(FW_WARN "error section length is too small\n");
6487ea6c6c1SLuck, Tony }
6497ea6c6c1SLuck, Tony 
cper_estatus_print(const char * pfx,const struct acpi_hest_generic_status * estatus)6507ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx,
6510a00fd5eSLv Zheng 			const struct acpi_hest_generic_status *estatus)
6527ea6c6c1SLuck, Tony {
6530a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
6547ea6c6c1SLuck, Tony 	int sec_no = 0;
6557ea6c6c1SLuck, Tony 	char newpfx[64];
6567ea6c6c1SLuck, Tony 	__u16 severity;
6577ea6c6c1SLuck, Tony 
6587ea6c6c1SLuck, Tony 	severity = estatus->error_severity;
6597ea6c6c1SLuck, Tony 	if (severity == CPER_SEV_CORRECTED)
6607ea6c6c1SLuck, Tony 		printk("%s%s\n", pfx,
6617ea6c6c1SLuck, Tony 		       "It has been corrected by h/w "
6627ea6c6c1SLuck, Tony 		       "and requires no further action");
6637ea6c6c1SLuck, Tony 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
66475e4fd31SBorislav Petkov 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
665bbcc2e7bSTyler Baicar 
666c4335fddSgengdongjiu 	apei_estatus_for_each_section(estatus, gdata) {
6677ea6c6c1SLuck, Tony 		cper_estatus_print_section(newpfx, gdata, sec_no);
6687ea6c6c1SLuck, Tony 		sec_no++;
6697ea6c6c1SLuck, Tony 	}
6707ea6c6c1SLuck, Tony }
6717ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print);
6727ea6c6c1SLuck, Tony 
cper_estatus_check_header(const struct acpi_hest_generic_status * estatus)6730a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
6747ea6c6c1SLuck, Tony {
6757ea6c6c1SLuck, Tony 	if (estatus->data_length &&
6760a00fd5eSLv Zheng 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
6777ea6c6c1SLuck, Tony 		return -EINVAL;
6787ea6c6c1SLuck, Tony 	if (estatus->raw_data_length &&
6797ea6c6c1SLuck, Tony 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
6807ea6c6c1SLuck, Tony 		return -EINVAL;
6817ea6c6c1SLuck, Tony 
6827ea6c6c1SLuck, Tony 	return 0;
6837ea6c6c1SLuck, Tony }
6847ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header);
6857ea6c6c1SLuck, Tony 
cper_estatus_check(const struct acpi_hest_generic_status * estatus)6860a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
6877ea6c6c1SLuck, Tony {
6880a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
68945b14a4fSRoss Lagerwall 	unsigned int data_len, record_size;
6907ea6c6c1SLuck, Tony 	int rc;
6917ea6c6c1SLuck, Tony 
6927ea6c6c1SLuck, Tony 	rc = cper_estatus_check_header(estatus);
6937ea6c6c1SLuck, Tony 	if (rc)
6947ea6c6c1SLuck, Tony 		return rc;
69545b14a4fSRoss Lagerwall 
6967ea6c6c1SLuck, Tony 	data_len = estatus->data_length;
697bbcc2e7bSTyler Baicar 
698c4335fddSgengdongjiu 	apei_estatus_for_each_section(estatus, gdata) {
6991be72c8eSShuai Xue 		if (acpi_hest_get_size(gdata) > data_len)
7007ea6c6c1SLuck, Tony 			return -EINVAL;
70145b14a4fSRoss Lagerwall 
70245b14a4fSRoss Lagerwall 		record_size = acpi_hest_get_record_size(gdata);
70345b14a4fSRoss Lagerwall 		if (record_size > data_len)
70445b14a4fSRoss Lagerwall 			return -EINVAL;
70545b14a4fSRoss Lagerwall 
70645b14a4fSRoss Lagerwall 		data_len -= record_size;
7077ea6c6c1SLuck, Tony 	}
7087ea6c6c1SLuck, Tony 	if (data_len)
7097ea6c6c1SLuck, Tony 		return -EINVAL;
7107ea6c6c1SLuck, Tony 
7117ea6c6c1SLuck, Tony 	return 0;
7127ea6c6c1SLuck, Tony }
7137ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check);
714