xref: /openbmc/linux/drivers/firmware/efi/cper.c (revision 2f74f09bce4f8d0236f20174a6daae63e10fe733)
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>
387ea6c6c1SLuck, Tony 
397ea6c6c1SLuck, Tony #define INDENT_SP	" "
403760cd20SChen, Gong 
413760cd20SChen, Gong static char rcd_decode_str[CPER_REC_LEN];
423760cd20SChen, Gong 
437ea6c6c1SLuck, Tony /*
447ea6c6c1SLuck, Tony  * CPER record ID need to be unique even after reboot, because record
457ea6c6c1SLuck, Tony  * ID is used as index for ERST storage, while CPER records from
467ea6c6c1SLuck, Tony  * multiple boot may co-exist in ERST.
477ea6c6c1SLuck, Tony  */
487ea6c6c1SLuck, Tony u64 cper_next_record_id(void)
497ea6c6c1SLuck, Tony {
507ea6c6c1SLuck, Tony 	static atomic64_t seq;
517ea6c6c1SLuck, Tony 
527ea6c6c1SLuck, Tony 	if (!atomic64_read(&seq))
537ea6c6c1SLuck, Tony 		atomic64_set(&seq, ((u64)get_seconds()) << 32);
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
1047ea6c6c1SLuck, Tony 			len += snprintf(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",
113*2f74f09bSTyler Baicar 	"ARM",
1147ea6c6c1SLuck, Tony };
1157ea6c6c1SLuck, Tony 
1163760cd20SChen, Gong static const char * const proc_isa_strs[] = {
1177ea6c6c1SLuck, Tony 	"IA32",
1187ea6c6c1SLuck, Tony 	"IA64",
1197ea6c6c1SLuck, Tony 	"X64",
120*2f74f09bSTyler Baicar 	"ARM A32/T32",
121*2f74f09bSTyler Baicar 	"ARM A64",
1227ea6c6c1SLuck, Tony };
1237ea6c6c1SLuck, Tony 
1243760cd20SChen, Gong static const char * const 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,
1593760cd20SChen, Gong 				proc_error_type_strs,
1603760cd20SChen, Gong 				ARRAY_SIZE(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 
190*2f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
191*2f74f09bSTyler Baicar static const char * const arm_reg_ctx_strs[] = {
192*2f74f09bSTyler Baicar 	"AArch32 general purpose registers",
193*2f74f09bSTyler Baicar 	"AArch32 EL1 context registers",
194*2f74f09bSTyler Baicar 	"AArch32 EL2 context registers",
195*2f74f09bSTyler Baicar 	"AArch32 secure context registers",
196*2f74f09bSTyler Baicar 	"AArch64 general purpose registers",
197*2f74f09bSTyler Baicar 	"AArch64 EL1 context registers",
198*2f74f09bSTyler Baicar 	"AArch64 EL2 context registers",
199*2f74f09bSTyler Baicar 	"AArch64 EL3 context registers",
200*2f74f09bSTyler Baicar 	"Misc. system register structure",
201*2f74f09bSTyler Baicar };
202*2f74f09bSTyler Baicar 
203*2f74f09bSTyler Baicar static void cper_print_proc_arm(const char *pfx,
204*2f74f09bSTyler Baicar 				const struct cper_sec_proc_arm *proc)
205*2f74f09bSTyler Baicar {
206*2f74f09bSTyler Baicar 	int i, len, max_ctx_type;
207*2f74f09bSTyler Baicar 	struct cper_arm_err_info *err_info;
208*2f74f09bSTyler Baicar 	struct cper_arm_ctx_info *ctx_info;
209*2f74f09bSTyler Baicar 	char newpfx[64];
210*2f74f09bSTyler Baicar 
211*2f74f09bSTyler Baicar 	printk("%sMIDR: 0x%016llx\n", pfx, proc->midr);
212*2f74f09bSTyler Baicar 
213*2f74f09bSTyler Baicar 	len = proc->section_length - (sizeof(*proc) +
214*2f74f09bSTyler Baicar 		proc->err_info_num * (sizeof(*err_info)));
215*2f74f09bSTyler Baicar 	if (len < 0) {
216*2f74f09bSTyler Baicar 		printk("%ssection length: %d\n", pfx, proc->section_length);
217*2f74f09bSTyler Baicar 		printk("%ssection length is too small\n", pfx);
218*2f74f09bSTyler Baicar 		printk("%sfirmware-generated error record is incorrect\n", pfx);
219*2f74f09bSTyler Baicar 		printk("%sERR_INFO_NUM is %d\n", pfx, proc->err_info_num);
220*2f74f09bSTyler Baicar 		return;
221*2f74f09bSTyler Baicar 	}
222*2f74f09bSTyler Baicar 
223*2f74f09bSTyler Baicar 	if (proc->validation_bits & CPER_ARM_VALID_MPIDR)
224*2f74f09bSTyler Baicar 		printk("%sMultiprocessor Affinity Register (MPIDR): 0x%016llx\n",
225*2f74f09bSTyler Baicar 			pfx, proc->mpidr);
226*2f74f09bSTyler Baicar 
227*2f74f09bSTyler Baicar 	if (proc->validation_bits & CPER_ARM_VALID_AFFINITY_LEVEL)
228*2f74f09bSTyler Baicar 		printk("%serror affinity level: %d\n", pfx,
229*2f74f09bSTyler Baicar 			proc->affinity_level);
230*2f74f09bSTyler Baicar 
231*2f74f09bSTyler Baicar 	if (proc->validation_bits & CPER_ARM_VALID_RUNNING_STATE) {
232*2f74f09bSTyler Baicar 		printk("%srunning state: 0x%x\n", pfx, proc->running_state);
233*2f74f09bSTyler Baicar 		printk("%sPower State Coordination Interface state: %d\n",
234*2f74f09bSTyler Baicar 			pfx, proc->psci_state);
235*2f74f09bSTyler Baicar 	}
236*2f74f09bSTyler Baicar 
237*2f74f09bSTyler Baicar 	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
238*2f74f09bSTyler Baicar 
239*2f74f09bSTyler Baicar 	err_info = (struct cper_arm_err_info *)(proc + 1);
240*2f74f09bSTyler Baicar 	for (i = 0; i < proc->err_info_num; i++) {
241*2f74f09bSTyler Baicar 		printk("%sError info structure %d:\n", pfx, i);
242*2f74f09bSTyler Baicar 
243*2f74f09bSTyler Baicar 		printk("%snum errors: %d\n", pfx, err_info->multiple_error + 1);
244*2f74f09bSTyler Baicar 
245*2f74f09bSTyler Baicar 		if (err_info->validation_bits & CPER_ARM_INFO_VALID_FLAGS) {
246*2f74f09bSTyler Baicar 			if (err_info->flags & CPER_ARM_INFO_FLAGS_FIRST)
247*2f74f09bSTyler Baicar 				printk("%sfirst error captured\n", newpfx);
248*2f74f09bSTyler Baicar 			if (err_info->flags & CPER_ARM_INFO_FLAGS_LAST)
249*2f74f09bSTyler Baicar 				printk("%slast error captured\n", newpfx);
250*2f74f09bSTyler Baicar 			if (err_info->flags & CPER_ARM_INFO_FLAGS_PROPAGATED)
251*2f74f09bSTyler Baicar 				printk("%spropagated error captured\n",
252*2f74f09bSTyler Baicar 				       newpfx);
253*2f74f09bSTyler Baicar 			if (err_info->flags & CPER_ARM_INFO_FLAGS_OVERFLOW)
254*2f74f09bSTyler Baicar 				printk("%soverflow occurred, error info is incomplete\n",
255*2f74f09bSTyler Baicar 				       newpfx);
256*2f74f09bSTyler Baicar 		}
257*2f74f09bSTyler Baicar 
258*2f74f09bSTyler Baicar 		printk("%serror_type: %d, %s\n", newpfx, err_info->type,
259*2f74f09bSTyler Baicar 			err_info->type < ARRAY_SIZE(proc_error_type_strs) ?
260*2f74f09bSTyler Baicar 			proc_error_type_strs[err_info->type] : "unknown");
261*2f74f09bSTyler Baicar 		if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO)
262*2f74f09bSTyler Baicar 			printk("%serror_info: 0x%016llx\n", newpfx,
263*2f74f09bSTyler Baicar 			       err_info->error_info);
264*2f74f09bSTyler Baicar 		if (err_info->validation_bits & CPER_ARM_INFO_VALID_VIRT_ADDR)
265*2f74f09bSTyler Baicar 			printk("%svirtual fault address: 0x%016llx\n",
266*2f74f09bSTyler Baicar 				newpfx, err_info->virt_fault_addr);
267*2f74f09bSTyler Baicar 		if (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR)
268*2f74f09bSTyler Baicar 			printk("%sphysical fault address: 0x%016llx\n",
269*2f74f09bSTyler Baicar 				newpfx, err_info->physical_fault_addr);
270*2f74f09bSTyler Baicar 		err_info += 1;
271*2f74f09bSTyler Baicar 	}
272*2f74f09bSTyler Baicar 
273*2f74f09bSTyler Baicar 	ctx_info = (struct cper_arm_ctx_info *)err_info;
274*2f74f09bSTyler Baicar 	max_ctx_type = ARRAY_SIZE(arm_reg_ctx_strs) - 1;
275*2f74f09bSTyler Baicar 	for (i = 0; i < proc->context_info_num; i++) {
276*2f74f09bSTyler Baicar 		int size = sizeof(*ctx_info) + ctx_info->size;
277*2f74f09bSTyler Baicar 
278*2f74f09bSTyler Baicar 		printk("%sContext info structure %d:\n", pfx, i);
279*2f74f09bSTyler Baicar 		if (len < size) {
280*2f74f09bSTyler Baicar 			printk("%ssection length is too small\n", newpfx);
281*2f74f09bSTyler Baicar 			printk("%sfirmware-generated error record is incorrect\n", pfx);
282*2f74f09bSTyler Baicar 			return;
283*2f74f09bSTyler Baicar 		}
284*2f74f09bSTyler Baicar 		if (ctx_info->type > max_ctx_type) {
285*2f74f09bSTyler Baicar 			printk("%sInvalid context type: %d (max: %d)\n",
286*2f74f09bSTyler Baicar 				newpfx, ctx_info->type, max_ctx_type);
287*2f74f09bSTyler Baicar 			return;
288*2f74f09bSTyler Baicar 		}
289*2f74f09bSTyler Baicar 		printk("%sregister context type: %s\n", newpfx,
290*2f74f09bSTyler Baicar 			arm_reg_ctx_strs[ctx_info->type]);
291*2f74f09bSTyler Baicar 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4,
292*2f74f09bSTyler Baicar 				(ctx_info + 1), ctx_info->size, 0);
293*2f74f09bSTyler Baicar 		len -= size;
294*2f74f09bSTyler Baicar 		ctx_info = (struct cper_arm_ctx_info *)((long)ctx_info + size);
295*2f74f09bSTyler Baicar 	}
296*2f74f09bSTyler Baicar 
297*2f74f09bSTyler Baicar 	if (len > 0) {
298*2f74f09bSTyler Baicar 		printk("%sVendor specific error info has %u bytes:\n", pfx,
299*2f74f09bSTyler Baicar 		       len);
300*2f74f09bSTyler Baicar 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, ctx_info,
301*2f74f09bSTyler Baicar 				len, true);
302*2f74f09bSTyler Baicar 	}
303*2f74f09bSTyler Baicar }
304*2f74f09bSTyler Baicar #endif
305*2f74f09bSTyler Baicar 
3063760cd20SChen, Gong static const char * const mem_err_type_strs[] = {
3077ea6c6c1SLuck, Tony 	"unknown",
3087ea6c6c1SLuck, Tony 	"no error",
3097ea6c6c1SLuck, Tony 	"single-bit ECC",
3107ea6c6c1SLuck, Tony 	"multi-bit ECC",
3117ea6c6c1SLuck, Tony 	"single-symbol chipkill ECC",
3127ea6c6c1SLuck, Tony 	"multi-symbol chipkill ECC",
3137ea6c6c1SLuck, Tony 	"master abort",
3147ea6c6c1SLuck, Tony 	"target abort",
3157ea6c6c1SLuck, Tony 	"parity error",
3167ea6c6c1SLuck, Tony 	"watchdog timeout",
3177ea6c6c1SLuck, Tony 	"invalid address",
3187ea6c6c1SLuck, Tony 	"mirror Broken",
3197ea6c6c1SLuck, Tony 	"memory sparing",
3207ea6c6c1SLuck, Tony 	"scrub corrected error",
3217ea6c6c1SLuck, Tony 	"scrub uncorrected error",
3227ea6c6c1SLuck, Tony 	"physical memory map-out event",
3237ea6c6c1SLuck, Tony };
3247ea6c6c1SLuck, Tony 
3253760cd20SChen, Gong const char *cper_mem_err_type_str(unsigned int etype)
3263760cd20SChen, Gong {
3273760cd20SChen, Gong 	return etype < ARRAY_SIZE(mem_err_type_strs) ?
3283760cd20SChen, Gong 		mem_err_type_strs[etype] : "unknown";
3293760cd20SChen, Gong }
3303760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
3313760cd20SChen, Gong 
3322dfb7d51SChen, Gong static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
3333760cd20SChen, Gong {
3343760cd20SChen, Gong 	u32 len, n;
3353760cd20SChen, Gong 
3363760cd20SChen, Gong 	if (!msg)
3373760cd20SChen, Gong 		return 0;
3383760cd20SChen, Gong 
3393760cd20SChen, Gong 	n = 0;
3403760cd20SChen, Gong 	len = CPER_REC_LEN - 1;
3413760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_NODE)
3423760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
3433760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_CARD)
3443760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
3453760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_MODULE)
3463760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
3473760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
3483760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
3493760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_BANK)
3503760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
3513760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
3523760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
3533760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_ROW)
3543760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "row: %d ", mem->row);
3553760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
3563760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
3573760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
3583760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "bit_position: %d ",
3593760cd20SChen, Gong 			       mem->bit_pos);
3603760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
3613760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
3623760cd20SChen, Gong 			       mem->requestor_id);
3633760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
3643760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
3653760cd20SChen, Gong 			       mem->responder_id);
3663760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
3673760cd20SChen, Gong 		scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
3683760cd20SChen, Gong 			  mem->target_id);
3693760cd20SChen, Gong 
3703760cd20SChen, Gong 	msg[n] = '\0';
3713760cd20SChen, Gong 	return n;
3723760cd20SChen, Gong }
3733760cd20SChen, Gong 
3742dfb7d51SChen, Gong static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
3753760cd20SChen, Gong {
3763760cd20SChen, Gong 	u32 len, n;
3773760cd20SChen, Gong 	const char *bank = NULL, *device = NULL;
3783760cd20SChen, Gong 
3793760cd20SChen, Gong 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
3803760cd20SChen, Gong 		return 0;
3813760cd20SChen, Gong 
3823760cd20SChen, Gong 	n = 0;
3833760cd20SChen, Gong 	len = CPER_REC_LEN - 1;
3843760cd20SChen, Gong 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
3853760cd20SChen, Gong 	if (bank && device)
3863760cd20SChen, Gong 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
3873760cd20SChen, Gong 	else
3883760cd20SChen, Gong 		n = snprintf(msg, len,
3893760cd20SChen, Gong 			     "DIMM location: not present. DMI handle: 0x%.4x ",
3903760cd20SChen, Gong 			     mem->mem_dev_handle);
3913760cd20SChen, Gong 
3923760cd20SChen, Gong 	msg[n] = '\0';
3933760cd20SChen, Gong 	return n;
3943760cd20SChen, Gong }
3953760cd20SChen, Gong 
3962dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
3972dfb7d51SChen, Gong 		       struct cper_mem_err_compact *cmem)
3982dfb7d51SChen, Gong {
3992dfb7d51SChen, Gong 	cmem->validation_bits = mem->validation_bits;
4002dfb7d51SChen, Gong 	cmem->node = mem->node;
4012dfb7d51SChen, Gong 	cmem->card = mem->card;
4022dfb7d51SChen, Gong 	cmem->module = mem->module;
4032dfb7d51SChen, Gong 	cmem->bank = mem->bank;
4042dfb7d51SChen, Gong 	cmem->device = mem->device;
4052dfb7d51SChen, Gong 	cmem->row = mem->row;
4062dfb7d51SChen, Gong 	cmem->column = mem->column;
4072dfb7d51SChen, Gong 	cmem->bit_pos = mem->bit_pos;
4082dfb7d51SChen, Gong 	cmem->requestor_id = mem->requestor_id;
4092dfb7d51SChen, Gong 	cmem->responder_id = mem->responder_id;
4102dfb7d51SChen, Gong 	cmem->target_id = mem->target_id;
4112dfb7d51SChen, Gong 	cmem->rank = mem->rank;
4122dfb7d51SChen, Gong 	cmem->mem_array_handle = mem->mem_array_handle;
4132dfb7d51SChen, Gong 	cmem->mem_dev_handle = mem->mem_dev_handle;
4142dfb7d51SChen, Gong }
4152dfb7d51SChen, Gong 
4162dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p,
4172dfb7d51SChen, Gong 				struct cper_mem_err_compact *cmem)
4182dfb7d51SChen, Gong {
419dbcf3e06SSteven Rostedt (Red Hat) 	const char *ret = trace_seq_buffer_ptr(p);
4202dfb7d51SChen, Gong 
4212dfb7d51SChen, Gong 	if (cper_mem_err_location(cmem, rcd_decode_str))
4222dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
4232dfb7d51SChen, Gong 	if (cper_dimm_err_location(cmem, rcd_decode_str))
4242dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
4252dfb7d51SChen, Gong 	trace_seq_putc(p, '\0');
4262dfb7d51SChen, Gong 
4272dfb7d51SChen, Gong 	return ret;
4282dfb7d51SChen, Gong }
4292dfb7d51SChen, Gong 
4304c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
4314c62360dSLuck, Tony 	int len)
4327ea6c6c1SLuck, Tony {
4332dfb7d51SChen, Gong 	struct cper_mem_err_compact cmem;
4342dfb7d51SChen, Gong 
4354c62360dSLuck, Tony 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
4364c62360dSLuck, Tony 	if (len == sizeof(struct cper_sec_mem_err_old) &&
4374c62360dSLuck, Tony 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
4384c62360dSLuck, Tony 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
4394c62360dSLuck, Tony 		return;
4404c62360dSLuck, Tony 	}
4417ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
4427ea6c6c1SLuck, Tony 		printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
4437ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA)
4447ea6c6c1SLuck, Tony 		printk("%s""physical_address: 0x%016llx\n",
4457ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr);
4467ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
4477ea6c6c1SLuck, Tony 		printk("%s""physical_address_mask: 0x%016llx\n",
4487ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr_mask);
4492dfb7d51SChen, Gong 	cper_mem_err_pack(mem, &cmem);
4502dfb7d51SChen, Gong 	if (cper_mem_err_location(&cmem, rcd_decode_str))
4513760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
4527ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
4537ea6c6c1SLuck, Tony 		u8 etype = mem->error_type;
4547ea6c6c1SLuck, Tony 		printk("%s""error_type: %d, %s\n", pfx, etype,
4553760cd20SChen, Gong 		       cper_mem_err_type_str(etype));
4567ea6c6c1SLuck, Tony 	}
4572dfb7d51SChen, Gong 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
4583760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
4597ea6c6c1SLuck, Tony }
4607ea6c6c1SLuck, Tony 
4613760cd20SChen, Gong static const char * const pcie_port_type_strs[] = {
4627ea6c6c1SLuck, Tony 	"PCIe end point",
4637ea6c6c1SLuck, Tony 	"legacy PCI end point",
4647ea6c6c1SLuck, Tony 	"unknown",
4657ea6c6c1SLuck, Tony 	"unknown",
4667ea6c6c1SLuck, Tony 	"root port",
4677ea6c6c1SLuck, Tony 	"upstream switch port",
4687ea6c6c1SLuck, Tony 	"downstream switch port",
4697ea6c6c1SLuck, Tony 	"PCIe to PCI/PCI-X bridge",
4707ea6c6c1SLuck, Tony 	"PCI/PCI-X to PCIe bridge",
4717ea6c6c1SLuck, Tony 	"root complex integrated endpoint device",
4727ea6c6c1SLuck, Tony 	"root complex event collector",
4737ea6c6c1SLuck, Tony };
4747ea6c6c1SLuck, Tony 
4757ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
4760a00fd5eSLv Zheng 			    const struct acpi_hest_generic_data *gdata)
4777ea6c6c1SLuck, Tony {
4787ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
4797ea6c6c1SLuck, Tony 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
4803760cd20SChen, Gong 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
4813760cd20SChen, Gong 		       pcie_port_type_strs[pcie->port_type] : "unknown");
4827ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
4837ea6c6c1SLuck, Tony 		printk("%s""version: %d.%d\n", pfx,
4847ea6c6c1SLuck, Tony 		       pcie->version.major, pcie->version.minor);
4857ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
4867ea6c6c1SLuck, Tony 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
4877ea6c6c1SLuck, Tony 		       pcie->command, pcie->status);
4887ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
4897ea6c6c1SLuck, Tony 		const __u8 *p;
4907ea6c6c1SLuck, Tony 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
4917ea6c6c1SLuck, Tony 		       pcie->device_id.segment, pcie->device_id.bus,
4927ea6c6c1SLuck, Tony 		       pcie->device_id.device, pcie->device_id.function);
4937ea6c6c1SLuck, Tony 		printk("%s""slot: %d\n", pfx,
4947ea6c6c1SLuck, Tony 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
4957ea6c6c1SLuck, Tony 		printk("%s""secondary_bus: 0x%02x\n", pfx,
4967ea6c6c1SLuck, Tony 		       pcie->device_id.secondary_bus);
4977ea6c6c1SLuck, Tony 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
4987ea6c6c1SLuck, Tony 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
4997ea6c6c1SLuck, Tony 		p = pcie->device_id.class_code;
5007ea6c6c1SLuck, Tony 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]);
5017ea6c6c1SLuck, Tony 	}
5027ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
5037ea6c6c1SLuck, Tony 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
5047ea6c6c1SLuck, Tony 		       pcie->serial_number.lower, pcie->serial_number.upper);
5057ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
5067ea6c6c1SLuck, Tony 		printk(
5077ea6c6c1SLuck, Tony 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
5087ea6c6c1SLuck, Tony 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
5097ea6c6c1SLuck, Tony }
5107ea6c6c1SLuck, Tony 
5118a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx,
5128a94471fSTyler Baicar 				   struct acpi_hest_generic_data_v300 *gdata)
5138a94471fSTyler Baicar {
5148a94471fSTyler Baicar 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
5158a94471fSTyler Baicar 
5168a94471fSTyler Baicar 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
5178a94471fSTyler Baicar 		timestamp = (__u8 *)&(gdata->time_stamp);
5188a94471fSTyler Baicar 		sec       = bcd2bin(timestamp[0]);
5198a94471fSTyler Baicar 		min       = bcd2bin(timestamp[1]);
5208a94471fSTyler Baicar 		hour      = bcd2bin(timestamp[2]);
5218a94471fSTyler Baicar 		day       = bcd2bin(timestamp[4]);
5228a94471fSTyler Baicar 		mon       = bcd2bin(timestamp[5]);
5238a94471fSTyler Baicar 		year      = bcd2bin(timestamp[6]);
5248a94471fSTyler Baicar 		century   = bcd2bin(timestamp[7]);
5258a94471fSTyler Baicar 
5268a94471fSTyler Baicar 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
5278a94471fSTyler Baicar 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
5288a94471fSTyler Baicar 		       century, year, mon, day, hour, min, sec);
5298a94471fSTyler Baicar 	}
5308a94471fSTyler Baicar }
5318a94471fSTyler Baicar 
532bbcc2e7bSTyler Baicar static void
533bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
534bbcc2e7bSTyler Baicar 			   int sec_no)
5357ea6c6c1SLuck, Tony {
5367ea6c6c1SLuck, Tony 	uuid_le *sec_type = (uuid_le *)gdata->section_type;
5377ea6c6c1SLuck, Tony 	__u16 severity;
5387ea6c6c1SLuck, Tony 	char newpfx[64];
5397ea6c6c1SLuck, Tony 
5408a94471fSTyler Baicar 	if (acpi_hest_get_version(gdata) >= 3)
5418a94471fSTyler Baicar 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
5428a94471fSTyler Baicar 
5437ea6c6c1SLuck, Tony 	severity = gdata->error_severity;
5447ea6c6c1SLuck, Tony 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
5457ea6c6c1SLuck, Tony 	       cper_severity_str(severity));
5467ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
5477ea6c6c1SLuck, Tony 		printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id);
5487ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
5497ea6c6c1SLuck, Tony 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
5507ea6c6c1SLuck, Tony 
5517ea6c6c1SLuck, Tony 	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
5527ea6c6c1SLuck, Tony 	if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) {
553bbcc2e7bSTyler Baicar 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
554bbcc2e7bSTyler Baicar 
5557ea6c6c1SLuck, Tony 		printk("%s""section_type: general processor error\n", newpfx);
5567ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*proc_err))
5577ea6c6c1SLuck, Tony 			cper_print_proc_generic(newpfx, proc_err);
5587ea6c6c1SLuck, Tony 		else
5597ea6c6c1SLuck, Tony 			goto err_section_too_small;
5607ea6c6c1SLuck, Tony 	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) {
561bbcc2e7bSTyler Baicar 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
562bbcc2e7bSTyler Baicar 
5637ea6c6c1SLuck, Tony 		printk("%s""section_type: memory error\n", newpfx);
5644c62360dSLuck, Tony 		if (gdata->error_data_length >=
5654c62360dSLuck, Tony 		    sizeof(struct cper_sec_mem_err_old))
5664c62360dSLuck, Tony 			cper_print_mem(newpfx, mem_err,
5674c62360dSLuck, Tony 				       gdata->error_data_length);
5687ea6c6c1SLuck, Tony 		else
5697ea6c6c1SLuck, Tony 			goto err_section_too_small;
5707ea6c6c1SLuck, Tony 	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) {
571bbcc2e7bSTyler Baicar 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
572bbcc2e7bSTyler Baicar 
5737ea6c6c1SLuck, Tony 		printk("%s""section_type: PCIe error\n", newpfx);
5747ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*pcie))
5757ea6c6c1SLuck, Tony 			cper_print_pcie(newpfx, pcie, gdata);
5767ea6c6c1SLuck, Tony 		else
5777ea6c6c1SLuck, Tony 			goto err_section_too_small;
578*2f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
579*2f74f09bSTyler Baicar 	} else if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_ARM)) {
580*2f74f09bSTyler Baicar 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
581*2f74f09bSTyler Baicar 
582*2f74f09bSTyler Baicar 		printk("%ssection_type: ARM processor error\n", newpfx);
583*2f74f09bSTyler Baicar 		if (gdata->error_data_length >= sizeof(*arm_err))
584*2f74f09bSTyler Baicar 			cper_print_proc_arm(newpfx, arm_err);
585*2f74f09bSTyler Baicar 		else
586*2f74f09bSTyler Baicar 			goto err_section_too_small;
587*2f74f09bSTyler Baicar #endif
5887ea6c6c1SLuck, Tony 	} else
5897ea6c6c1SLuck, Tony 		printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
5907ea6c6c1SLuck, Tony 
5917ea6c6c1SLuck, Tony 	return;
5927ea6c6c1SLuck, Tony 
5937ea6c6c1SLuck, Tony err_section_too_small:
5947ea6c6c1SLuck, Tony 	pr_err(FW_WARN "error section length is too small\n");
5957ea6c6c1SLuck, Tony }
5967ea6c6c1SLuck, Tony 
5977ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx,
5980a00fd5eSLv Zheng 			const struct acpi_hest_generic_status *estatus)
5997ea6c6c1SLuck, Tony {
6000a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
601bbcc2e7bSTyler Baicar 	unsigned int data_len;
6027ea6c6c1SLuck, Tony 	int sec_no = 0;
6037ea6c6c1SLuck, Tony 	char newpfx[64];
6047ea6c6c1SLuck, Tony 	__u16 severity;
6057ea6c6c1SLuck, Tony 
6067ea6c6c1SLuck, Tony 	severity = estatus->error_severity;
6077ea6c6c1SLuck, Tony 	if (severity == CPER_SEV_CORRECTED)
6087ea6c6c1SLuck, Tony 		printk("%s%s\n", pfx,
6097ea6c6c1SLuck, Tony 		       "It has been corrected by h/w "
6107ea6c6c1SLuck, Tony 		       "and requires no further action");
6117ea6c6c1SLuck, Tony 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
6127ea6c6c1SLuck, Tony 	data_len = estatus->data_length;
6130a00fd5eSLv Zheng 	gdata = (struct acpi_hest_generic_data *)(estatus + 1);
6147ea6c6c1SLuck, Tony 	snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
615bbcc2e7bSTyler Baicar 
616bbcc2e7bSTyler Baicar 	while (data_len >= acpi_hest_get_size(gdata)) {
6177ea6c6c1SLuck, Tony 		cper_estatus_print_section(newpfx, gdata, sec_no);
618bbcc2e7bSTyler Baicar 		data_len -= acpi_hest_get_record_size(gdata);
619bbcc2e7bSTyler Baicar 		gdata = acpi_hest_get_next(gdata);
6207ea6c6c1SLuck, Tony 		sec_no++;
6217ea6c6c1SLuck, Tony 	}
6227ea6c6c1SLuck, Tony }
6237ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print);
6247ea6c6c1SLuck, Tony 
6250a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
6267ea6c6c1SLuck, Tony {
6277ea6c6c1SLuck, Tony 	if (estatus->data_length &&
6280a00fd5eSLv Zheng 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
6297ea6c6c1SLuck, Tony 		return -EINVAL;
6307ea6c6c1SLuck, Tony 	if (estatus->raw_data_length &&
6317ea6c6c1SLuck, Tony 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
6327ea6c6c1SLuck, Tony 		return -EINVAL;
6337ea6c6c1SLuck, Tony 
6347ea6c6c1SLuck, Tony 	return 0;
6357ea6c6c1SLuck, Tony }
6367ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header);
6377ea6c6c1SLuck, Tony 
6380a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
6397ea6c6c1SLuck, Tony {
6400a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
6417ea6c6c1SLuck, Tony 	unsigned int data_len, gedata_len;
6427ea6c6c1SLuck, Tony 	int rc;
6437ea6c6c1SLuck, Tony 
6447ea6c6c1SLuck, Tony 	rc = cper_estatus_check_header(estatus);
6457ea6c6c1SLuck, Tony 	if (rc)
6467ea6c6c1SLuck, Tony 		return rc;
6477ea6c6c1SLuck, Tony 	data_len = estatus->data_length;
6480a00fd5eSLv Zheng 	gdata = (struct acpi_hest_generic_data *)(estatus + 1);
649bbcc2e7bSTyler Baicar 
650bbcc2e7bSTyler Baicar 	while (data_len >= acpi_hest_get_size(gdata)) {
651bbcc2e7bSTyler Baicar 		gedata_len = acpi_hest_get_error_length(gdata);
652bbcc2e7bSTyler Baicar 		if (gedata_len > data_len - acpi_hest_get_size(gdata))
6537ea6c6c1SLuck, Tony 			return -EINVAL;
654bbcc2e7bSTyler Baicar 
655bbcc2e7bSTyler Baicar 		data_len -= acpi_hest_get_record_size(gdata);
656bbcc2e7bSTyler Baicar 		gdata = acpi_hest_get_next(gdata);
6577ea6c6c1SLuck, Tony 	}
6587ea6c6c1SLuck, Tony 	if (data_len)
6597ea6c6c1SLuck, Tony 		return -EINVAL;
6607ea6c6c1SLuck, Tony 
6617ea6c6c1SLuck, Tony 	return 0;
6627ea6c6c1SLuck, Tony }
6637ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check);
664