xref: /openbmc/linux/drivers/firmware/efi/cper.c (revision e8f4194d9b98aa13f9f567a0056bbf683d2b1ab8)
17ea6c6c1SLuck, Tony /*
27ea6c6c1SLuck, Tony  * UEFI Common Platform Error Record (CPER) support
37ea6c6c1SLuck, Tony  *
47ea6c6c1SLuck, Tony  * Copyright (C) 2010, Intel Corp.
57ea6c6c1SLuck, Tony  *	Author: Huang Ying <ying.huang@intel.com>
67ea6c6c1SLuck, Tony  *
77ea6c6c1SLuck, Tony  * CPER is the format used to describe platform hardware error by
87ea6c6c1SLuck, Tony  * various tables, such as ERST, BERT and HEST etc.
97ea6c6c1SLuck, Tony  *
107ea6c6c1SLuck, Tony  * For more information about CPER, please refer to Appendix N of UEFI
117ea6c6c1SLuck, Tony  * Specification version 2.4.
127ea6c6c1SLuck, Tony  *
137ea6c6c1SLuck, Tony  * This program is free software; you can redistribute it and/or
147ea6c6c1SLuck, Tony  * modify it under the terms of the GNU General Public License version
157ea6c6c1SLuck, Tony  * 2 as published by the Free Software Foundation.
167ea6c6c1SLuck, Tony  *
177ea6c6c1SLuck, Tony  * This program is distributed in the hope that it will be useful,
187ea6c6c1SLuck, Tony  * but WITHOUT ANY WARRANTY; without even the implied warranty of
197ea6c6c1SLuck, Tony  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
207ea6c6c1SLuck, Tony  * GNU General Public License for more details.
217ea6c6c1SLuck, Tony  *
227ea6c6c1SLuck, Tony  * You should have received a copy of the GNU General Public License
237ea6c6c1SLuck, Tony  * along with this program; if not, write to the Free Software
247ea6c6c1SLuck, Tony  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
257ea6c6c1SLuck, Tony  */
267ea6c6c1SLuck, Tony 
277ea6c6c1SLuck, Tony #include <linux/kernel.h>
287ea6c6c1SLuck, Tony #include <linux/module.h>
297ea6c6c1SLuck, Tony #include <linux/time.h>
307ea6c6c1SLuck, Tony #include <linux/cper.h>
317ea6c6c1SLuck, Tony #include <linux/dmi.h>
327ea6c6c1SLuck, Tony #include <linux/acpi.h>
337ea6c6c1SLuck, Tony #include <linux/pci.h>
347ea6c6c1SLuck, Tony #include <linux/aer.h>
358a94471fSTyler Baicar #include <linux/printk.h>
368a94471fSTyler Baicar #include <linux/bcd.h>
37bbcc2e7bSTyler Baicar #include <acpi/ghes.h>
38e9279e83STyler Baicar #include <ras/ras_event.h>
397ea6c6c1SLuck, Tony 
403760cd20SChen, Gong static char rcd_decode_str[CPER_REC_LEN];
413760cd20SChen, Gong 
427ea6c6c1SLuck, Tony /*
437ea6c6c1SLuck, Tony  * CPER record ID need to be unique even after reboot, because record
447ea6c6c1SLuck, Tony  * ID is used as index for ERST storage, while CPER records from
457ea6c6c1SLuck, Tony  * multiple boot may co-exist in ERST.
467ea6c6c1SLuck, Tony  */
477ea6c6c1SLuck, Tony u64 cper_next_record_id(void)
487ea6c6c1SLuck, Tony {
497ea6c6c1SLuck, Tony 	static atomic64_t seq;
507ea6c6c1SLuck, Tony 
517bb49709SArnd Bergmann 	if (!atomic64_read(&seq)) {
527bb49709SArnd Bergmann 		time64_t time = ktime_get_real_seconds();
537bb49709SArnd Bergmann 
547bb49709SArnd Bergmann 		/*
557bb49709SArnd Bergmann 		 * This code is unlikely to still be needed in year 2106,
567bb49709SArnd Bergmann 		 * but just in case, let's use a few more bits for timestamps
577bb49709SArnd Bergmann 		 * after y2038 to be sure they keep increasing monotonically
587bb49709SArnd Bergmann 		 * for the next few hundred years...
597bb49709SArnd Bergmann 		 */
607bb49709SArnd Bergmann 		if (time < 0x80000000)
617bb49709SArnd Bergmann 			atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
627bb49709SArnd Bergmann 		else
637bb49709SArnd Bergmann 			atomic64_set(&seq, 0x8000000000000000ull |
647bb49709SArnd Bergmann 					   ktime_get_real_seconds() << 24);
657bb49709SArnd Bergmann 	}
667ea6c6c1SLuck, Tony 
677ea6c6c1SLuck, Tony 	return atomic64_inc_return(&seq);
687ea6c6c1SLuck, Tony }
697ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id);
707ea6c6c1SLuck, Tony 
713760cd20SChen, Gong static const char * const severity_strs[] = {
727ea6c6c1SLuck, Tony 	"recoverable",
737ea6c6c1SLuck, Tony 	"fatal",
747ea6c6c1SLuck, Tony 	"corrected",
757ea6c6c1SLuck, Tony 	"info",
767ea6c6c1SLuck, Tony };
777ea6c6c1SLuck, Tony 
783760cd20SChen, Gong const char *cper_severity_str(unsigned int severity)
797ea6c6c1SLuck, Tony {
803760cd20SChen, Gong 	return severity < ARRAY_SIZE(severity_strs) ?
813760cd20SChen, Gong 		severity_strs[severity] : "unknown";
827ea6c6c1SLuck, Tony }
833760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_severity_str);
847ea6c6c1SLuck, Tony 
857ea6c6c1SLuck, Tony /*
867ea6c6c1SLuck, Tony  * cper_print_bits - print strings for set bits
877ea6c6c1SLuck, Tony  * @pfx: prefix for each line, including log level and prefix string
887ea6c6c1SLuck, Tony  * @bits: bit mask
897ea6c6c1SLuck, Tony  * @strs: string array, indexed by bit position
907ea6c6c1SLuck, Tony  * @strs_size: size of the string array: @strs
917ea6c6c1SLuck, Tony  *
927ea6c6c1SLuck, Tony  * For each set bit in @bits, print the corresponding string in @strs.
937ea6c6c1SLuck, Tony  * If the output length is longer than 80, multiple line will be
947ea6c6c1SLuck, Tony  * printed, with @pfx is printed at the beginning of each line.
957ea6c6c1SLuck, Tony  */
967ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits,
977ea6c6c1SLuck, Tony 		     const char * const strs[], unsigned int strs_size)
987ea6c6c1SLuck, Tony {
997ea6c6c1SLuck, Tony 	int i, len = 0;
1007ea6c6c1SLuck, Tony 	const char *str;
1017ea6c6c1SLuck, Tony 	char buf[84];
1027ea6c6c1SLuck, Tony 
1037ea6c6c1SLuck, Tony 	for (i = 0; i < strs_size; i++) {
1047ea6c6c1SLuck, Tony 		if (!(bits & (1U << i)))
1057ea6c6c1SLuck, Tony 			continue;
1067ea6c6c1SLuck, Tony 		str = strs[i];
1077ea6c6c1SLuck, Tony 		if (!str)
1087ea6c6c1SLuck, Tony 			continue;
1097ea6c6c1SLuck, Tony 		if (len && len + strlen(str) + 2 > 80) {
1107ea6c6c1SLuck, Tony 			printk("%s\n", buf);
1117ea6c6c1SLuck, Tony 			len = 0;
1127ea6c6c1SLuck, Tony 		}
1137ea6c6c1SLuck, Tony 		if (!len)
1147ea6c6c1SLuck, Tony 			len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
1157ea6c6c1SLuck, Tony 		else
1167ea6c6c1SLuck, Tony 			len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
1177ea6c6c1SLuck, Tony 	}
1187ea6c6c1SLuck, Tony 	if (len)
1197ea6c6c1SLuck, Tony 		printk("%s\n", buf);
1207ea6c6c1SLuck, Tony }
1217ea6c6c1SLuck, Tony 
1223760cd20SChen, Gong static const char * const proc_type_strs[] = {
1237ea6c6c1SLuck, Tony 	"IA32/X64",
1247ea6c6c1SLuck, Tony 	"IA64",
1252f74f09bSTyler Baicar 	"ARM",
1267ea6c6c1SLuck, Tony };
1277ea6c6c1SLuck, Tony 
1283760cd20SChen, Gong static const char * const proc_isa_strs[] = {
1297ea6c6c1SLuck, Tony 	"IA32",
1307ea6c6c1SLuck, Tony 	"IA64",
1317ea6c6c1SLuck, Tony 	"X64",
1322f74f09bSTyler Baicar 	"ARM A32/T32",
1332f74f09bSTyler Baicar 	"ARM A64",
1347ea6c6c1SLuck, Tony };
1357ea6c6c1SLuck, Tony 
136c6d8c8efSTyler Baicar const char * const cper_proc_error_type_strs[] = {
1377ea6c6c1SLuck, Tony 	"cache error",
1387ea6c6c1SLuck, Tony 	"TLB error",
1397ea6c6c1SLuck, Tony 	"bus error",
1407ea6c6c1SLuck, Tony 	"micro-architectural error",
1417ea6c6c1SLuck, Tony };
1427ea6c6c1SLuck, Tony 
1433760cd20SChen, Gong static const char * const proc_op_strs[] = {
1447ea6c6c1SLuck, Tony 	"unknown or generic",
1457ea6c6c1SLuck, Tony 	"data read",
1467ea6c6c1SLuck, Tony 	"data write",
1477ea6c6c1SLuck, Tony 	"instruction execution",
1487ea6c6c1SLuck, Tony };
1497ea6c6c1SLuck, Tony 
1503760cd20SChen, Gong static const char * const proc_flag_strs[] = {
1517ea6c6c1SLuck, Tony 	"restartable",
1527ea6c6c1SLuck, Tony 	"precise IP",
1537ea6c6c1SLuck, Tony 	"overflow",
1547ea6c6c1SLuck, Tony 	"corrected",
1557ea6c6c1SLuck, Tony };
1567ea6c6c1SLuck, Tony 
1577ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx,
1587ea6c6c1SLuck, Tony 				    const struct cper_sec_proc_generic *proc)
1597ea6c6c1SLuck, Tony {
1607ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_TYPE)
1617ea6c6c1SLuck, Tony 		printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
1623760cd20SChen, Gong 		       proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
1633760cd20SChen, Gong 		       proc_type_strs[proc->proc_type] : "unknown");
1647ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_ISA)
1657ea6c6c1SLuck, Tony 		printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
1663760cd20SChen, Gong 		       proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
1673760cd20SChen, Gong 		       proc_isa_strs[proc->proc_isa] : "unknown");
1687ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
1697ea6c6c1SLuck, Tony 		printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
1707ea6c6c1SLuck, Tony 		cper_print_bits(pfx, proc->proc_error_type,
171c6d8c8efSTyler Baicar 				cper_proc_error_type_strs,
172c6d8c8efSTyler Baicar 				ARRAY_SIZE(cper_proc_error_type_strs));
1737ea6c6c1SLuck, Tony 	}
1747ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
1757ea6c6c1SLuck, Tony 		printk("%s""operation: %d, %s\n", pfx, proc->operation,
1763760cd20SChen, Gong 		       proc->operation < ARRAY_SIZE(proc_op_strs) ?
1773760cd20SChen, Gong 		       proc_op_strs[proc->operation] : "unknown");
1787ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
1797ea6c6c1SLuck, Tony 		printk("%s""flags: 0x%02x\n", pfx, proc->flags);
1803760cd20SChen, Gong 		cper_print_bits(pfx, proc->flags, proc_flag_strs,
1813760cd20SChen, Gong 				ARRAY_SIZE(proc_flag_strs));
1827ea6c6c1SLuck, Tony 	}
1837ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
1847ea6c6c1SLuck, Tony 		printk("%s""level: %d\n", pfx, proc->level);
1857ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_VERSION)
1867ea6c6c1SLuck, Tony 		printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
1877ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_ID)
1887ea6c6c1SLuck, Tony 		printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
1897ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
1907ea6c6c1SLuck, Tony 		printk("%s""target_address: 0x%016llx\n",
1917ea6c6c1SLuck, Tony 		       pfx, proc->target_addr);
1927ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
1937ea6c6c1SLuck, Tony 		printk("%s""requestor_id: 0x%016llx\n",
1947ea6c6c1SLuck, Tony 		       pfx, proc->requestor_id);
1957ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
1967ea6c6c1SLuck, Tony 		printk("%s""responder_id: 0x%016llx\n",
1977ea6c6c1SLuck, Tony 		       pfx, proc->responder_id);
1987ea6c6c1SLuck, Tony 	if (proc->validation_bits & CPER_PROC_VALID_IP)
1997ea6c6c1SLuck, Tony 		printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
2007ea6c6c1SLuck, Tony }
2017ea6c6c1SLuck, Tony 
2023760cd20SChen, Gong static const char * const mem_err_type_strs[] = {
2037ea6c6c1SLuck, Tony 	"unknown",
2047ea6c6c1SLuck, Tony 	"no error",
2057ea6c6c1SLuck, Tony 	"single-bit ECC",
2067ea6c6c1SLuck, Tony 	"multi-bit ECC",
2077ea6c6c1SLuck, Tony 	"single-symbol chipkill ECC",
2087ea6c6c1SLuck, Tony 	"multi-symbol chipkill ECC",
2097ea6c6c1SLuck, Tony 	"master abort",
2107ea6c6c1SLuck, Tony 	"target abort",
2117ea6c6c1SLuck, Tony 	"parity error",
2127ea6c6c1SLuck, Tony 	"watchdog timeout",
2137ea6c6c1SLuck, Tony 	"invalid address",
2147ea6c6c1SLuck, Tony 	"mirror Broken",
2157ea6c6c1SLuck, Tony 	"memory sparing",
2167ea6c6c1SLuck, Tony 	"scrub corrected error",
2177ea6c6c1SLuck, Tony 	"scrub uncorrected error",
2187ea6c6c1SLuck, Tony 	"physical memory map-out event",
2197ea6c6c1SLuck, Tony };
2207ea6c6c1SLuck, Tony 
2213760cd20SChen, Gong const char *cper_mem_err_type_str(unsigned int etype)
2223760cd20SChen, Gong {
2233760cd20SChen, Gong 	return etype < ARRAY_SIZE(mem_err_type_strs) ?
2243760cd20SChen, Gong 		mem_err_type_strs[etype] : "unknown";
2253760cd20SChen, Gong }
2263760cd20SChen, Gong EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
2273760cd20SChen, Gong 
2282dfb7d51SChen, Gong static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
2293760cd20SChen, Gong {
2303760cd20SChen, Gong 	u32 len, n;
2313760cd20SChen, Gong 
2323760cd20SChen, Gong 	if (!msg)
2333760cd20SChen, Gong 		return 0;
2343760cd20SChen, Gong 
2353760cd20SChen, Gong 	n = 0;
2363760cd20SChen, Gong 	len = CPER_REC_LEN - 1;
2373760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_NODE)
2383760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
2393760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_CARD)
2403760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
2413760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_MODULE)
2423760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
2433760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
2443760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
2453760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_BANK)
2463760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
2473760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
2483760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
2493760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_ROW)
2503760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "row: %d ", mem->row);
2513760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
2523760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
2533760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
2543760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "bit_position: %d ",
2553760cd20SChen, Gong 			       mem->bit_pos);
2563760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
2573760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
2583760cd20SChen, Gong 			       mem->requestor_id);
2593760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
2603760cd20SChen, Gong 		n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
2613760cd20SChen, Gong 			       mem->responder_id);
2623760cd20SChen, Gong 	if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
2633760cd20SChen, Gong 		scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
2643760cd20SChen, Gong 			  mem->target_id);
2653760cd20SChen, Gong 
2663760cd20SChen, Gong 	msg[n] = '\0';
2673760cd20SChen, Gong 	return n;
2683760cd20SChen, Gong }
2693760cd20SChen, Gong 
2702dfb7d51SChen, Gong static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
2713760cd20SChen, Gong {
2723760cd20SChen, Gong 	u32 len, n;
2733760cd20SChen, Gong 	const char *bank = NULL, *device = NULL;
2743760cd20SChen, Gong 
2753760cd20SChen, Gong 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
2763760cd20SChen, Gong 		return 0;
2773760cd20SChen, Gong 
2783760cd20SChen, Gong 	n = 0;
2793760cd20SChen, Gong 	len = CPER_REC_LEN - 1;
2803760cd20SChen, Gong 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
2813760cd20SChen, Gong 	if (bank && device)
2823760cd20SChen, Gong 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
2833760cd20SChen, Gong 	else
2843760cd20SChen, Gong 		n = snprintf(msg, len,
2853760cd20SChen, Gong 			     "DIMM location: not present. DMI handle: 0x%.4x ",
2863760cd20SChen, Gong 			     mem->mem_dev_handle);
2873760cd20SChen, Gong 
2883760cd20SChen, Gong 	msg[n] = '\0';
2893760cd20SChen, Gong 	return n;
2903760cd20SChen, Gong }
2913760cd20SChen, Gong 
2922dfb7d51SChen, Gong void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
2932dfb7d51SChen, Gong 		       struct cper_mem_err_compact *cmem)
2942dfb7d51SChen, Gong {
2952dfb7d51SChen, Gong 	cmem->validation_bits = mem->validation_bits;
2962dfb7d51SChen, Gong 	cmem->node = mem->node;
2972dfb7d51SChen, Gong 	cmem->card = mem->card;
2982dfb7d51SChen, Gong 	cmem->module = mem->module;
2992dfb7d51SChen, Gong 	cmem->bank = mem->bank;
3002dfb7d51SChen, Gong 	cmem->device = mem->device;
3012dfb7d51SChen, Gong 	cmem->row = mem->row;
3022dfb7d51SChen, Gong 	cmem->column = mem->column;
3032dfb7d51SChen, Gong 	cmem->bit_pos = mem->bit_pos;
3042dfb7d51SChen, Gong 	cmem->requestor_id = mem->requestor_id;
3052dfb7d51SChen, Gong 	cmem->responder_id = mem->responder_id;
3062dfb7d51SChen, Gong 	cmem->target_id = mem->target_id;
3072dfb7d51SChen, Gong 	cmem->rank = mem->rank;
3082dfb7d51SChen, Gong 	cmem->mem_array_handle = mem->mem_array_handle;
3092dfb7d51SChen, Gong 	cmem->mem_dev_handle = mem->mem_dev_handle;
3102dfb7d51SChen, Gong }
3112dfb7d51SChen, Gong 
3122dfb7d51SChen, Gong const char *cper_mem_err_unpack(struct trace_seq *p,
3132dfb7d51SChen, Gong 				struct cper_mem_err_compact *cmem)
3142dfb7d51SChen, Gong {
315dbcf3e06SSteven Rostedt (Red Hat) 	const char *ret = trace_seq_buffer_ptr(p);
3162dfb7d51SChen, Gong 
3172dfb7d51SChen, Gong 	if (cper_mem_err_location(cmem, rcd_decode_str))
3182dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
3192dfb7d51SChen, Gong 	if (cper_dimm_err_location(cmem, rcd_decode_str))
3202dfb7d51SChen, Gong 		trace_seq_printf(p, "%s", rcd_decode_str);
3212dfb7d51SChen, Gong 	trace_seq_putc(p, '\0');
3222dfb7d51SChen, Gong 
3232dfb7d51SChen, Gong 	return ret;
3242dfb7d51SChen, Gong }
3252dfb7d51SChen, Gong 
3264c62360dSLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
3274c62360dSLuck, Tony 	int len)
3287ea6c6c1SLuck, Tony {
3292dfb7d51SChen, Gong 	struct cper_mem_err_compact cmem;
3302dfb7d51SChen, Gong 
3314c62360dSLuck, Tony 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
3324c62360dSLuck, Tony 	if (len == sizeof(struct cper_sec_mem_err_old) &&
3334c62360dSLuck, Tony 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
3344c62360dSLuck, Tony 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
3354c62360dSLuck, Tony 		return;
3364c62360dSLuck, Tony 	}
3377ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
3387ea6c6c1SLuck, Tony 		printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
3397ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA)
3407ea6c6c1SLuck, Tony 		printk("%s""physical_address: 0x%016llx\n",
3417ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr);
3427ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
3437ea6c6c1SLuck, Tony 		printk("%s""physical_address_mask: 0x%016llx\n",
3447ea6c6c1SLuck, Tony 		       pfx, mem->physical_addr_mask);
3452dfb7d51SChen, Gong 	cper_mem_err_pack(mem, &cmem);
3462dfb7d51SChen, Gong 	if (cper_mem_err_location(&cmem, rcd_decode_str))
3473760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
3487ea6c6c1SLuck, Tony 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
3497ea6c6c1SLuck, Tony 		u8 etype = mem->error_type;
3507ea6c6c1SLuck, Tony 		printk("%s""error_type: %d, %s\n", pfx, etype,
3513760cd20SChen, Gong 		       cper_mem_err_type_str(etype));
3527ea6c6c1SLuck, Tony 	}
3532dfb7d51SChen, Gong 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
3543760cd20SChen, Gong 		printk("%s%s\n", pfx, rcd_decode_str);
3557ea6c6c1SLuck, Tony }
3567ea6c6c1SLuck, Tony 
3573760cd20SChen, Gong static const char * const pcie_port_type_strs[] = {
3587ea6c6c1SLuck, Tony 	"PCIe end point",
3597ea6c6c1SLuck, Tony 	"legacy PCI end point",
3607ea6c6c1SLuck, Tony 	"unknown",
3617ea6c6c1SLuck, Tony 	"unknown",
3627ea6c6c1SLuck, Tony 	"root port",
3637ea6c6c1SLuck, Tony 	"upstream switch port",
3647ea6c6c1SLuck, Tony 	"downstream switch port",
3657ea6c6c1SLuck, Tony 	"PCIe to PCI/PCI-X bridge",
3667ea6c6c1SLuck, Tony 	"PCI/PCI-X to PCIe bridge",
3677ea6c6c1SLuck, Tony 	"root complex integrated endpoint device",
3687ea6c6c1SLuck, Tony 	"root complex event collector",
3697ea6c6c1SLuck, Tony };
3707ea6c6c1SLuck, Tony 
3717ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
3720a00fd5eSLv Zheng 			    const struct acpi_hest_generic_data *gdata)
3737ea6c6c1SLuck, Tony {
3747ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
3757ea6c6c1SLuck, Tony 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
3763760cd20SChen, Gong 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
3773760cd20SChen, Gong 		       pcie_port_type_strs[pcie->port_type] : "unknown");
3787ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
3797ea6c6c1SLuck, Tony 		printk("%s""version: %d.%d\n", pfx,
3807ea6c6c1SLuck, Tony 		       pcie->version.major, pcie->version.minor);
3817ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
3827ea6c6c1SLuck, Tony 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
3837ea6c6c1SLuck, Tony 		       pcie->command, pcie->status);
3847ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
3857ea6c6c1SLuck, Tony 		const __u8 *p;
3867ea6c6c1SLuck, Tony 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
3877ea6c6c1SLuck, Tony 		       pcie->device_id.segment, pcie->device_id.bus,
3887ea6c6c1SLuck, Tony 		       pcie->device_id.device, pcie->device_id.function);
3897ea6c6c1SLuck, Tony 		printk("%s""slot: %d\n", pfx,
3907ea6c6c1SLuck, Tony 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
3917ea6c6c1SLuck, Tony 		printk("%s""secondary_bus: 0x%02x\n", pfx,
3927ea6c6c1SLuck, Tony 		       pcie->device_id.secondary_bus);
3937ea6c6c1SLuck, Tony 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
3947ea6c6c1SLuck, Tony 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
3957ea6c6c1SLuck, Tony 		p = pcie->device_id.class_code;
3967ea6c6c1SLuck, Tony 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]);
3977ea6c6c1SLuck, Tony 	}
3987ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
3997ea6c6c1SLuck, Tony 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
4007ea6c6c1SLuck, Tony 		       pcie->serial_number.lower, pcie->serial_number.upper);
4017ea6c6c1SLuck, Tony 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
4027ea6c6c1SLuck, Tony 		printk(
4037ea6c6c1SLuck, Tony 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
4047ea6c6c1SLuck, Tony 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
4057ea6c6c1SLuck, Tony }
4067ea6c6c1SLuck, Tony 
4078a94471fSTyler Baicar static void cper_print_tstamp(const char *pfx,
4088a94471fSTyler Baicar 				   struct acpi_hest_generic_data_v300 *gdata)
4098a94471fSTyler Baicar {
4108a94471fSTyler Baicar 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
4118a94471fSTyler Baicar 
4128a94471fSTyler Baicar 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
4138a94471fSTyler Baicar 		timestamp = (__u8 *)&(gdata->time_stamp);
4148a94471fSTyler Baicar 		sec       = bcd2bin(timestamp[0]);
4158a94471fSTyler Baicar 		min       = bcd2bin(timestamp[1]);
4168a94471fSTyler Baicar 		hour      = bcd2bin(timestamp[2]);
4178a94471fSTyler Baicar 		day       = bcd2bin(timestamp[4]);
4188a94471fSTyler Baicar 		mon       = bcd2bin(timestamp[5]);
4198a94471fSTyler Baicar 		year      = bcd2bin(timestamp[6]);
4208a94471fSTyler Baicar 		century   = bcd2bin(timestamp[7]);
4218a94471fSTyler Baicar 
4228a94471fSTyler Baicar 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
4238a94471fSTyler Baicar 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
4248a94471fSTyler Baicar 		       century, year, mon, day, hour, min, sec);
4258a94471fSTyler Baicar 	}
4268a94471fSTyler Baicar }
4278a94471fSTyler Baicar 
428bbcc2e7bSTyler Baicar static void
429bbcc2e7bSTyler Baicar cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
430bbcc2e7bSTyler Baicar 			   int sec_no)
4317ea6c6c1SLuck, Tony {
432c0020756SAndy Shevchenko 	guid_t *sec_type = (guid_t *)gdata->section_type;
4337ea6c6c1SLuck, Tony 	__u16 severity;
4347ea6c6c1SLuck, Tony 	char newpfx[64];
4357ea6c6c1SLuck, Tony 
4368a94471fSTyler Baicar 	if (acpi_hest_get_version(gdata) >= 3)
4378a94471fSTyler Baicar 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
4388a94471fSTyler Baicar 
4397ea6c6c1SLuck, Tony 	severity = gdata->error_severity;
4407ea6c6c1SLuck, Tony 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
4417ea6c6c1SLuck, Tony 	       cper_severity_str(severity));
4427ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
443c0020756SAndy Shevchenko 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
4447ea6c6c1SLuck, Tony 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
4457ea6c6c1SLuck, Tony 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
4467ea6c6c1SLuck, Tony 
44775e4fd31SBorislav Petkov 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
448c0020756SAndy Shevchenko 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
449bbcc2e7bSTyler Baicar 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
450bbcc2e7bSTyler Baicar 
4517ea6c6c1SLuck, Tony 		printk("%s""section_type: general processor error\n", newpfx);
4527ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*proc_err))
4537ea6c6c1SLuck, Tony 			cper_print_proc_generic(newpfx, proc_err);
4547ea6c6c1SLuck, Tony 		else
4557ea6c6c1SLuck, Tony 			goto err_section_too_small;
456c0020756SAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
457bbcc2e7bSTyler Baicar 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
458bbcc2e7bSTyler Baicar 
4597ea6c6c1SLuck, Tony 		printk("%s""section_type: memory error\n", newpfx);
4604c62360dSLuck, Tony 		if (gdata->error_data_length >=
4614c62360dSLuck, Tony 		    sizeof(struct cper_sec_mem_err_old))
4624c62360dSLuck, Tony 			cper_print_mem(newpfx, mem_err,
4634c62360dSLuck, Tony 				       gdata->error_data_length);
4647ea6c6c1SLuck, Tony 		else
4657ea6c6c1SLuck, Tony 			goto err_section_too_small;
466c0020756SAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
467bbcc2e7bSTyler Baicar 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
468bbcc2e7bSTyler Baicar 
4697ea6c6c1SLuck, Tony 		printk("%s""section_type: PCIe error\n", newpfx);
4707ea6c6c1SLuck, Tony 		if (gdata->error_data_length >= sizeof(*pcie))
4717ea6c6c1SLuck, Tony 			cper_print_pcie(newpfx, pcie, gdata);
4727ea6c6c1SLuck, Tony 		else
4737ea6c6c1SLuck, Tony 			goto err_section_too_small;
4742f74f09bSTyler Baicar #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
475*e8f4194dSAndy Shevchenko 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
4762f74f09bSTyler Baicar 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
4772f74f09bSTyler Baicar 
4782f74f09bSTyler Baicar 		printk("%ssection_type: ARM processor error\n", newpfx);
4792f74f09bSTyler Baicar 		if (gdata->error_data_length >= sizeof(*arm_err))
4802f74f09bSTyler Baicar 			cper_print_proc_arm(newpfx, arm_err);
4812f74f09bSTyler Baicar 		else
4822f74f09bSTyler Baicar 			goto err_section_too_small;
4832f74f09bSTyler Baicar #endif
484f9e1bdb9SYazen Ghannam #if defined(CONFIG_UEFI_CPER_X86)
485f9e1bdb9SYazen Ghannam 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
486f9e1bdb9SYazen Ghannam 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
487f9e1bdb9SYazen Ghannam 
488f9e1bdb9SYazen Ghannam 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
489f9e1bdb9SYazen Ghannam 		if (gdata->error_data_length >= sizeof(*ia_err))
490f9e1bdb9SYazen Ghannam 			cper_print_proc_ia(newpfx, ia_err);
491f9e1bdb9SYazen Ghannam 		else
492f9e1bdb9SYazen Ghannam 			goto err_section_too_small;
493f9e1bdb9SYazen Ghannam #endif
4940fc300f4STyler Baicar 	} else {
4950fc300f4STyler Baicar 		const void *err = acpi_hest_get_payload(gdata);
4960fc300f4STyler Baicar 
4970fc300f4STyler Baicar 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
4980fc300f4STyler Baicar 		printk("%ssection length: %#x\n", newpfx,
4990fc300f4STyler Baicar 		       gdata->error_data_length);
5000fc300f4STyler Baicar 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
5010fc300f4STyler Baicar 			       gdata->error_data_length, true);
5020fc300f4STyler Baicar 	}
5037ea6c6c1SLuck, Tony 
5047ea6c6c1SLuck, Tony 	return;
5057ea6c6c1SLuck, Tony 
5067ea6c6c1SLuck, Tony err_section_too_small:
5077ea6c6c1SLuck, Tony 	pr_err(FW_WARN "error section length is too small\n");
5087ea6c6c1SLuck, Tony }
5097ea6c6c1SLuck, Tony 
5107ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx,
5110a00fd5eSLv Zheng 			const struct acpi_hest_generic_status *estatus)
5127ea6c6c1SLuck, Tony {
5130a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
5147ea6c6c1SLuck, Tony 	int sec_no = 0;
5157ea6c6c1SLuck, Tony 	char newpfx[64];
5167ea6c6c1SLuck, Tony 	__u16 severity;
5177ea6c6c1SLuck, Tony 
5187ea6c6c1SLuck, Tony 	severity = estatus->error_severity;
5197ea6c6c1SLuck, Tony 	if (severity == CPER_SEV_CORRECTED)
5207ea6c6c1SLuck, Tony 		printk("%s%s\n", pfx,
5217ea6c6c1SLuck, Tony 		       "It has been corrected by h/w "
5227ea6c6c1SLuck, Tony 		       "and requires no further action");
5237ea6c6c1SLuck, Tony 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
52475e4fd31SBorislav Petkov 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
525bbcc2e7bSTyler Baicar 
526c4335fddSgengdongjiu 	apei_estatus_for_each_section(estatus, gdata) {
5277ea6c6c1SLuck, Tony 		cper_estatus_print_section(newpfx, gdata, sec_no);
5287ea6c6c1SLuck, Tony 		sec_no++;
5297ea6c6c1SLuck, Tony 	}
5307ea6c6c1SLuck, Tony }
5317ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print);
5327ea6c6c1SLuck, Tony 
5330a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
5347ea6c6c1SLuck, Tony {
5357ea6c6c1SLuck, Tony 	if (estatus->data_length &&
5360a00fd5eSLv Zheng 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
5377ea6c6c1SLuck, Tony 		return -EINVAL;
5387ea6c6c1SLuck, Tony 	if (estatus->raw_data_length &&
5397ea6c6c1SLuck, Tony 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
5407ea6c6c1SLuck, Tony 		return -EINVAL;
5417ea6c6c1SLuck, Tony 
5427ea6c6c1SLuck, Tony 	return 0;
5437ea6c6c1SLuck, Tony }
5447ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header);
5457ea6c6c1SLuck, Tony 
5460a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
5477ea6c6c1SLuck, Tony {
5480a00fd5eSLv Zheng 	struct acpi_hest_generic_data *gdata;
5497ea6c6c1SLuck, Tony 	unsigned int data_len, gedata_len;
5507ea6c6c1SLuck, Tony 	int rc;
5517ea6c6c1SLuck, Tony 
5527ea6c6c1SLuck, Tony 	rc = cper_estatus_check_header(estatus);
5537ea6c6c1SLuck, Tony 	if (rc)
5547ea6c6c1SLuck, Tony 		return rc;
5557ea6c6c1SLuck, Tony 	data_len = estatus->data_length;
556bbcc2e7bSTyler Baicar 
557c4335fddSgengdongjiu 	apei_estatus_for_each_section(estatus, gdata) {
558bbcc2e7bSTyler Baicar 		gedata_len = acpi_hest_get_error_length(gdata);
559bbcc2e7bSTyler Baicar 		if (gedata_len > data_len - acpi_hest_get_size(gdata))
5607ea6c6c1SLuck, Tony 			return -EINVAL;
561bbcc2e7bSTyler Baicar 		data_len -= acpi_hest_get_record_size(gdata);
5627ea6c6c1SLuck, Tony 	}
5637ea6c6c1SLuck, Tony 	if (data_len)
5647ea6c6c1SLuck, Tony 		return -EINVAL;
5657ea6c6c1SLuck, Tony 
5667ea6c6c1SLuck, Tony 	return 0;
5677ea6c6c1SLuck, Tony }
5687ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check);
569