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