18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * UEFI Common Platform Error Record (CPER) support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010, Intel Corp.
68c2ecf20Sopenharmony_ci *	Author: Huang Ying <ying.huang@intel.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * CPER is the format used to describe platform hardware error by
98c2ecf20Sopenharmony_ci * various tables, such as ERST, BERT and HEST etc.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * For more information about CPER, please refer to Appendix N of UEFI
128c2ecf20Sopenharmony_ci * Specification version 2.4.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/time.h>
188c2ecf20Sopenharmony_ci#include <linux/cper.h>
198c2ecf20Sopenharmony_ci#include <linux/dmi.h>
208c2ecf20Sopenharmony_ci#include <linux/acpi.h>
218c2ecf20Sopenharmony_ci#include <linux/pci.h>
228c2ecf20Sopenharmony_ci#include <linux/aer.h>
238c2ecf20Sopenharmony_ci#include <linux/printk.h>
248c2ecf20Sopenharmony_ci#include <linux/bcd.h>
258c2ecf20Sopenharmony_ci#include <acpi/ghes.h>
268c2ecf20Sopenharmony_ci#include <ras/ras_event.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * CPER record ID need to be unique even after reboot, because record
308c2ecf20Sopenharmony_ci * ID is used as index for ERST storage, while CPER records from
318c2ecf20Sopenharmony_ci * multiple boot may co-exist in ERST.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ciu64 cper_next_record_id(void)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	static atomic64_t seq;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (!atomic64_read(&seq)) {
388c2ecf20Sopenharmony_ci		time64_t time = ktime_get_real_seconds();
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci		/*
418c2ecf20Sopenharmony_ci		 * This code is unlikely to still be needed in year 2106,
428c2ecf20Sopenharmony_ci		 * but just in case, let's use a few more bits for timestamps
438c2ecf20Sopenharmony_ci		 * after y2038 to be sure they keep increasing monotonically
448c2ecf20Sopenharmony_ci		 * for the next few hundred years...
458c2ecf20Sopenharmony_ci		 */
468c2ecf20Sopenharmony_ci		if (time < 0x80000000)
478c2ecf20Sopenharmony_ci			atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
488c2ecf20Sopenharmony_ci		else
498c2ecf20Sopenharmony_ci			atomic64_set(&seq, 0x8000000000000000ull |
508c2ecf20Sopenharmony_ci					   ktime_get_real_seconds() << 24);
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return atomic64_inc_return(&seq);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_next_record_id);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const char * const severity_strs[] = {
588c2ecf20Sopenharmony_ci	"recoverable",
598c2ecf20Sopenharmony_ci	"fatal",
608c2ecf20Sopenharmony_ci	"corrected",
618c2ecf20Sopenharmony_ci	"info",
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ciconst char *cper_severity_str(unsigned int severity)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	return severity < ARRAY_SIZE(severity_strs) ?
678c2ecf20Sopenharmony_ci		severity_strs[severity] : "unknown";
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_severity_str);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * cper_print_bits - print strings for set bits
738c2ecf20Sopenharmony_ci * @pfx: prefix for each line, including log level and prefix string
748c2ecf20Sopenharmony_ci * @bits: bit mask
758c2ecf20Sopenharmony_ci * @strs: string array, indexed by bit position
768c2ecf20Sopenharmony_ci * @strs_size: size of the string array: @strs
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * For each set bit in @bits, print the corresponding string in @strs.
798c2ecf20Sopenharmony_ci * If the output length is longer than 80, multiple line will be
808c2ecf20Sopenharmony_ci * printed, with @pfx is printed at the beginning of each line.
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_civoid cper_print_bits(const char *pfx, unsigned int bits,
838c2ecf20Sopenharmony_ci		     const char * const strs[], unsigned int strs_size)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int i, len = 0;
868c2ecf20Sopenharmony_ci	const char *str;
878c2ecf20Sopenharmony_ci	char buf[84];
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	for (i = 0; i < strs_size; i++) {
908c2ecf20Sopenharmony_ci		if (!(bits & (1U << i)))
918c2ecf20Sopenharmony_ci			continue;
928c2ecf20Sopenharmony_ci		str = strs[i];
938c2ecf20Sopenharmony_ci		if (!str)
948c2ecf20Sopenharmony_ci			continue;
958c2ecf20Sopenharmony_ci		if (len && len + strlen(str) + 2 > 80) {
968c2ecf20Sopenharmony_ci			printk("%s\n", buf);
978c2ecf20Sopenharmony_ci			len = 0;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci		if (!len)
1008c2ecf20Sopenharmony_ci			len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
1018c2ecf20Sopenharmony_ci		else
1028c2ecf20Sopenharmony_ci			len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	if (len)
1058c2ecf20Sopenharmony_ci		printk("%s\n", buf);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic const char * const proc_type_strs[] = {
1098c2ecf20Sopenharmony_ci	"IA32/X64",
1108c2ecf20Sopenharmony_ci	"IA64",
1118c2ecf20Sopenharmony_ci	"ARM",
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic const char * const proc_isa_strs[] = {
1158c2ecf20Sopenharmony_ci	"IA32",
1168c2ecf20Sopenharmony_ci	"IA64",
1178c2ecf20Sopenharmony_ci	"X64",
1188c2ecf20Sopenharmony_ci	"ARM A32/T32",
1198c2ecf20Sopenharmony_ci	"ARM A64",
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ciconst char * const cper_proc_error_type_strs[] = {
1238c2ecf20Sopenharmony_ci	"cache error",
1248c2ecf20Sopenharmony_ci	"TLB error",
1258c2ecf20Sopenharmony_ci	"bus error",
1268c2ecf20Sopenharmony_ci	"micro-architectural error",
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const char * const proc_op_strs[] = {
1308c2ecf20Sopenharmony_ci	"unknown or generic",
1318c2ecf20Sopenharmony_ci	"data read",
1328c2ecf20Sopenharmony_ci	"data write",
1338c2ecf20Sopenharmony_ci	"instruction execution",
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const char * const proc_flag_strs[] = {
1378c2ecf20Sopenharmony_ci	"restartable",
1388c2ecf20Sopenharmony_ci	"precise IP",
1398c2ecf20Sopenharmony_ci	"overflow",
1408c2ecf20Sopenharmony_ci	"corrected",
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void cper_print_proc_generic(const char *pfx,
1448c2ecf20Sopenharmony_ci				    const struct cper_sec_proc_generic *proc)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_TYPE)
1478c2ecf20Sopenharmony_ci		printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
1488c2ecf20Sopenharmony_ci		       proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
1498c2ecf20Sopenharmony_ci		       proc_type_strs[proc->proc_type] : "unknown");
1508c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_ISA)
1518c2ecf20Sopenharmony_ci		printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
1528c2ecf20Sopenharmony_ci		       proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
1538c2ecf20Sopenharmony_ci		       proc_isa_strs[proc->proc_isa] : "unknown");
1548c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
1558c2ecf20Sopenharmony_ci		printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
1568c2ecf20Sopenharmony_ci		cper_print_bits(pfx, proc->proc_error_type,
1578c2ecf20Sopenharmony_ci				cper_proc_error_type_strs,
1588c2ecf20Sopenharmony_ci				ARRAY_SIZE(cper_proc_error_type_strs));
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
1618c2ecf20Sopenharmony_ci		printk("%s""operation: %d, %s\n", pfx, proc->operation,
1628c2ecf20Sopenharmony_ci		       proc->operation < ARRAY_SIZE(proc_op_strs) ?
1638c2ecf20Sopenharmony_ci		       proc_op_strs[proc->operation] : "unknown");
1648c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
1658c2ecf20Sopenharmony_ci		printk("%s""flags: 0x%02x\n", pfx, proc->flags);
1668c2ecf20Sopenharmony_ci		cper_print_bits(pfx, proc->flags, proc_flag_strs,
1678c2ecf20Sopenharmony_ci				ARRAY_SIZE(proc_flag_strs));
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
1708c2ecf20Sopenharmony_ci		printk("%s""level: %d\n", pfx, proc->level);
1718c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_VERSION)
1728c2ecf20Sopenharmony_ci		printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
1738c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_ID)
1748c2ecf20Sopenharmony_ci		printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
1758c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
1768c2ecf20Sopenharmony_ci		printk("%s""target_address: 0x%016llx\n",
1778c2ecf20Sopenharmony_ci		       pfx, proc->target_addr);
1788c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
1798c2ecf20Sopenharmony_ci		printk("%s""requestor_id: 0x%016llx\n",
1808c2ecf20Sopenharmony_ci		       pfx, proc->requestor_id);
1818c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
1828c2ecf20Sopenharmony_ci		printk("%s""responder_id: 0x%016llx\n",
1838c2ecf20Sopenharmony_ci		       pfx, proc->responder_id);
1848c2ecf20Sopenharmony_ci	if (proc->validation_bits & CPER_PROC_VALID_IP)
1858c2ecf20Sopenharmony_ci		printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic const char * const mem_err_type_strs[] = {
1898c2ecf20Sopenharmony_ci	"unknown",
1908c2ecf20Sopenharmony_ci	"no error",
1918c2ecf20Sopenharmony_ci	"single-bit ECC",
1928c2ecf20Sopenharmony_ci	"multi-bit ECC",
1938c2ecf20Sopenharmony_ci	"single-symbol chipkill ECC",
1948c2ecf20Sopenharmony_ci	"multi-symbol chipkill ECC",
1958c2ecf20Sopenharmony_ci	"master abort",
1968c2ecf20Sopenharmony_ci	"target abort",
1978c2ecf20Sopenharmony_ci	"parity error",
1988c2ecf20Sopenharmony_ci	"watchdog timeout",
1998c2ecf20Sopenharmony_ci	"invalid address",
2008c2ecf20Sopenharmony_ci	"mirror Broken",
2018c2ecf20Sopenharmony_ci	"memory sparing",
2028c2ecf20Sopenharmony_ci	"scrub corrected error",
2038c2ecf20Sopenharmony_ci	"scrub uncorrected error",
2048c2ecf20Sopenharmony_ci	"physical memory map-out event",
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ciconst char *cper_mem_err_type_str(unsigned int etype)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return etype < ARRAY_SIZE(mem_err_type_strs) ?
2108c2ecf20Sopenharmony_ci		mem_err_type_strs[etype] : "unknown";
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_mem_err_type_str);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	u32 len, n;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (!msg)
2198c2ecf20Sopenharmony_ci		return 0;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	n = 0;
2228c2ecf20Sopenharmony_ci	len = CPER_REC_LEN - 1;
2238c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_NODE)
2248c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
2258c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_CARD)
2268c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
2278c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_MODULE)
2288c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
2298c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
2308c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
2318c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_BANK)
2328c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
2338c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
2348c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "bank_group: %d ",
2358c2ecf20Sopenharmony_ci			       mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
2368c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
2378c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "bank_address: %d ",
2388c2ecf20Sopenharmony_ci			       mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
2398c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
2408c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
2418c2ecf20Sopenharmony_ci	if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
2428c2ecf20Sopenharmony_ci		u32 row = mem->row;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
2458c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "row: %d ", row);
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
2488c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
2498c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
2508c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "bit_position: %d ",
2518c2ecf20Sopenharmony_ci			       mem->bit_pos);
2528c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
2538c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
2548c2ecf20Sopenharmony_ci			       mem->requestor_id);
2558c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
2568c2ecf20Sopenharmony_ci		n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
2578c2ecf20Sopenharmony_ci			       mem->responder_id);
2588c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
2598c2ecf20Sopenharmony_ci		scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
2608c2ecf20Sopenharmony_ci			  mem->target_id);
2618c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
2628c2ecf20Sopenharmony_ci		scnprintf(msg + n, len - n, "chip_id: %d ",
2638c2ecf20Sopenharmony_ci			  mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	msg[n] = '\0';
2668c2ecf20Sopenharmony_ci	return n;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	u32 len, n;
2728c2ecf20Sopenharmony_ci	const char *bank = NULL, *device = NULL;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
2758c2ecf20Sopenharmony_ci		return 0;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	len = CPER_REC_LEN;
2788c2ecf20Sopenharmony_ci	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
2798c2ecf20Sopenharmony_ci	if (bank && device)
2808c2ecf20Sopenharmony_ci		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
2818c2ecf20Sopenharmony_ci	else
2828c2ecf20Sopenharmony_ci		n = snprintf(msg, len,
2838c2ecf20Sopenharmony_ci			     "DIMM location: not present. DMI handle: 0x%.4x ",
2848c2ecf20Sopenharmony_ci			     mem->mem_dev_handle);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return n;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_civoid cper_mem_err_pack(const struct cper_sec_mem_err *mem,
2908c2ecf20Sopenharmony_ci		       struct cper_mem_err_compact *cmem)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	cmem->validation_bits = mem->validation_bits;
2938c2ecf20Sopenharmony_ci	cmem->node = mem->node;
2948c2ecf20Sopenharmony_ci	cmem->card = mem->card;
2958c2ecf20Sopenharmony_ci	cmem->module = mem->module;
2968c2ecf20Sopenharmony_ci	cmem->bank = mem->bank;
2978c2ecf20Sopenharmony_ci	cmem->device = mem->device;
2988c2ecf20Sopenharmony_ci	cmem->row = mem->row;
2998c2ecf20Sopenharmony_ci	cmem->column = mem->column;
3008c2ecf20Sopenharmony_ci	cmem->bit_pos = mem->bit_pos;
3018c2ecf20Sopenharmony_ci	cmem->requestor_id = mem->requestor_id;
3028c2ecf20Sopenharmony_ci	cmem->responder_id = mem->responder_id;
3038c2ecf20Sopenharmony_ci	cmem->target_id = mem->target_id;
3048c2ecf20Sopenharmony_ci	cmem->extended = mem->extended;
3058c2ecf20Sopenharmony_ci	cmem->rank = mem->rank;
3068c2ecf20Sopenharmony_ci	cmem->mem_array_handle = mem->mem_array_handle;
3078c2ecf20Sopenharmony_ci	cmem->mem_dev_handle = mem->mem_dev_handle;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ciconst char *cper_mem_err_unpack(struct trace_seq *p,
3118c2ecf20Sopenharmony_ci				struct cper_mem_err_compact *cmem)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	const char *ret = trace_seq_buffer_ptr(p);
3148c2ecf20Sopenharmony_ci	char rcd_decode_str[CPER_REC_LEN];
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (cper_mem_err_location(cmem, rcd_decode_str))
3178c2ecf20Sopenharmony_ci		trace_seq_printf(p, "%s", rcd_decode_str);
3188c2ecf20Sopenharmony_ci	if (cper_dimm_err_location(cmem, rcd_decode_str))
3198c2ecf20Sopenharmony_ci		trace_seq_printf(p, "%s", rcd_decode_str);
3208c2ecf20Sopenharmony_ci	trace_seq_putc(p, '\0');
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return ret;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
3268c2ecf20Sopenharmony_ci	int len)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct cper_mem_err_compact cmem;
3298c2ecf20Sopenharmony_ci	char rcd_decode_str[CPER_REC_LEN];
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
3328c2ecf20Sopenharmony_ci	if (len == sizeof(struct cper_sec_mem_err_old) &&
3338c2ecf20Sopenharmony_ci	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
3348c2ecf20Sopenharmony_ci		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
3388c2ecf20Sopenharmony_ci		printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
3398c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_PA)
3408c2ecf20Sopenharmony_ci		printk("%s""physical_address: 0x%016llx\n",
3418c2ecf20Sopenharmony_ci		       pfx, mem->physical_addr);
3428c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
3438c2ecf20Sopenharmony_ci		printk("%s""physical_address_mask: 0x%016llx\n",
3448c2ecf20Sopenharmony_ci		       pfx, mem->physical_addr_mask);
3458c2ecf20Sopenharmony_ci	cper_mem_err_pack(mem, &cmem);
3468c2ecf20Sopenharmony_ci	if (cper_mem_err_location(&cmem, rcd_decode_str))
3478c2ecf20Sopenharmony_ci		printk("%s%s\n", pfx, rcd_decode_str);
3488c2ecf20Sopenharmony_ci	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
3498c2ecf20Sopenharmony_ci		u8 etype = mem->error_type;
3508c2ecf20Sopenharmony_ci		printk("%s""error_type: %d, %s\n", pfx, etype,
3518c2ecf20Sopenharmony_ci		       cper_mem_err_type_str(etype));
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	if (cper_dimm_err_location(&cmem, rcd_decode_str))
3548c2ecf20Sopenharmony_ci		printk("%s%s\n", pfx, rcd_decode_str);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic const char * const pcie_port_type_strs[] = {
3588c2ecf20Sopenharmony_ci	"PCIe end point",
3598c2ecf20Sopenharmony_ci	"legacy PCI end point",
3608c2ecf20Sopenharmony_ci	"unknown",
3618c2ecf20Sopenharmony_ci	"unknown",
3628c2ecf20Sopenharmony_ci	"root port",
3638c2ecf20Sopenharmony_ci	"upstream switch port",
3648c2ecf20Sopenharmony_ci	"downstream switch port",
3658c2ecf20Sopenharmony_ci	"PCIe to PCI/PCI-X bridge",
3668c2ecf20Sopenharmony_ci	"PCI/PCI-X to PCIe bridge",
3678c2ecf20Sopenharmony_ci	"root complex integrated endpoint device",
3688c2ecf20Sopenharmony_ci	"root complex event collector",
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
3728c2ecf20Sopenharmony_ci			    const struct acpi_hest_generic_data *gdata)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
3758c2ecf20Sopenharmony_ci		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
3768c2ecf20Sopenharmony_ci		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
3778c2ecf20Sopenharmony_ci		       pcie_port_type_strs[pcie->port_type] : "unknown");
3788c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
3798c2ecf20Sopenharmony_ci		printk("%s""version: %d.%d\n", pfx,
3808c2ecf20Sopenharmony_ci		       pcie->version.major, pcie->version.minor);
3818c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
3828c2ecf20Sopenharmony_ci		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
3838c2ecf20Sopenharmony_ci		       pcie->command, pcie->status);
3848c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
3858c2ecf20Sopenharmony_ci		const __u8 *p;
3868c2ecf20Sopenharmony_ci		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
3878c2ecf20Sopenharmony_ci		       pcie->device_id.segment, pcie->device_id.bus,
3888c2ecf20Sopenharmony_ci		       pcie->device_id.device, pcie->device_id.function);
3898c2ecf20Sopenharmony_ci		printk("%s""slot: %d\n", pfx,
3908c2ecf20Sopenharmony_ci		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
3918c2ecf20Sopenharmony_ci		printk("%s""secondary_bus: 0x%02x\n", pfx,
3928c2ecf20Sopenharmony_ci		       pcie->device_id.secondary_bus);
3938c2ecf20Sopenharmony_ci		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
3948c2ecf20Sopenharmony_ci		       pcie->device_id.vendor_id, pcie->device_id.device_id);
3958c2ecf20Sopenharmony_ci		p = pcie->device_id.class_code;
3968c2ecf20Sopenharmony_ci		printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
3998c2ecf20Sopenharmony_ci		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
4008c2ecf20Sopenharmony_ci		       pcie->serial_number.lower, pcie->serial_number.upper);
4018c2ecf20Sopenharmony_ci	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
4028c2ecf20Sopenharmony_ci		printk(
4038c2ecf20Sopenharmony_ci	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
4048c2ecf20Sopenharmony_ci	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* Fatal errors call __ghes_panic() before AER handler prints this */
4078c2ecf20Sopenharmony_ci	if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
4088c2ecf20Sopenharmony_ci	    (gdata->error_severity & CPER_SEV_FATAL)) {
4098c2ecf20Sopenharmony_ci		struct aer_capability_regs *aer;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		aer = (struct aer_capability_regs *)pcie->aer_info;
4128c2ecf20Sopenharmony_ci		printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
4138c2ecf20Sopenharmony_ci		       pfx, aer->uncor_status, aer->uncor_mask);
4148c2ecf20Sopenharmony_ci		printk("%saer_uncor_severity: 0x%08x\n",
4158c2ecf20Sopenharmony_ci		       pfx, aer->uncor_severity);
4168c2ecf20Sopenharmony_ci		printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
4178c2ecf20Sopenharmony_ci		       aer->header_log.dw0, aer->header_log.dw1,
4188c2ecf20Sopenharmony_ci		       aer->header_log.dw2, aer->header_log.dw3);
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic const char * const fw_err_rec_type_strs[] = {
4238c2ecf20Sopenharmony_ci	"IPF SAL Error Record",
4248c2ecf20Sopenharmony_ci	"SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
4258c2ecf20Sopenharmony_ci	"SOC Firmware Error Record Type2",
4268c2ecf20Sopenharmony_ci};
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void cper_print_fw_err(const char *pfx,
4298c2ecf20Sopenharmony_ci			      struct acpi_hest_generic_data *gdata,
4308c2ecf20Sopenharmony_ci			      const struct cper_sec_fw_err_rec_ref *fw_err)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	void *buf = acpi_hest_get_payload(gdata);
4338c2ecf20Sopenharmony_ci	u32 offset, length = gdata->error_data_length;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	printk("%s""Firmware Error Record Type: %s\n", pfx,
4368c2ecf20Sopenharmony_ci	       fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
4378c2ecf20Sopenharmony_ci	       fw_err_rec_type_strs[fw_err->record_type] : "unknown");
4388c2ecf20Sopenharmony_ci	printk("%s""Revision: %d\n", pfx, fw_err->revision);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* Record Type based on UEFI 2.7 */
4418c2ecf20Sopenharmony_ci	if (fw_err->revision == 0) {
4428c2ecf20Sopenharmony_ci		printk("%s""Record Identifier: %08llx\n", pfx,
4438c2ecf20Sopenharmony_ci		       fw_err->record_identifier);
4448c2ecf20Sopenharmony_ci	} else if (fw_err->revision == 2) {
4458c2ecf20Sopenharmony_ci		printk("%s""Record Identifier: %pUl\n", pfx,
4468c2ecf20Sopenharmony_ci		       &fw_err->record_identifier_guid);
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/*
4508c2ecf20Sopenharmony_ci	 * The FW error record may contain trailing data beyond the
4518c2ecf20Sopenharmony_ci	 * structure defined by the specification. As the fields
4528c2ecf20Sopenharmony_ci	 * defined (and hence the offset of any trailing data) vary
4538c2ecf20Sopenharmony_ci	 * with the revision, set the offset to account for this
4548c2ecf20Sopenharmony_ci	 * variation.
4558c2ecf20Sopenharmony_ci	 */
4568c2ecf20Sopenharmony_ci	if (fw_err->revision == 0) {
4578c2ecf20Sopenharmony_ci		/* record_identifier_guid not defined */
4588c2ecf20Sopenharmony_ci		offset = offsetof(struct cper_sec_fw_err_rec_ref,
4598c2ecf20Sopenharmony_ci				  record_identifier_guid);
4608c2ecf20Sopenharmony_ci	} else if (fw_err->revision == 1) {
4618c2ecf20Sopenharmony_ci		/* record_identifier not defined */
4628c2ecf20Sopenharmony_ci		offset = offsetof(struct cper_sec_fw_err_rec_ref,
4638c2ecf20Sopenharmony_ci				  record_identifier);
4648c2ecf20Sopenharmony_ci	} else {
4658c2ecf20Sopenharmony_ci		offset = sizeof(*fw_err);
4668c2ecf20Sopenharmony_ci	}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	buf += offset;
4698c2ecf20Sopenharmony_ci	length -= offset;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cistatic void cper_print_tstamp(const char *pfx,
4758c2ecf20Sopenharmony_ci				   struct acpi_hest_generic_data_v300 *gdata)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	__u8 hour, min, sec, day, mon, year, century, *timestamp;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
4808c2ecf20Sopenharmony_ci		timestamp = (__u8 *)&(gdata->time_stamp);
4818c2ecf20Sopenharmony_ci		sec       = bcd2bin(timestamp[0]);
4828c2ecf20Sopenharmony_ci		min       = bcd2bin(timestamp[1]);
4838c2ecf20Sopenharmony_ci		hour      = bcd2bin(timestamp[2]);
4848c2ecf20Sopenharmony_ci		day       = bcd2bin(timestamp[4]);
4858c2ecf20Sopenharmony_ci		mon       = bcd2bin(timestamp[5]);
4868c2ecf20Sopenharmony_ci		year      = bcd2bin(timestamp[6]);
4878c2ecf20Sopenharmony_ci		century   = bcd2bin(timestamp[7]);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
4908c2ecf20Sopenharmony_ci		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
4918c2ecf20Sopenharmony_ci		       century, year, mon, day, hour, min, sec);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void
4968c2ecf20Sopenharmony_cicper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
4978c2ecf20Sopenharmony_ci			   int sec_no)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	guid_t *sec_type = (guid_t *)gdata->section_type;
5008c2ecf20Sopenharmony_ci	__u16 severity;
5018c2ecf20Sopenharmony_ci	char newpfx[64];
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (acpi_hest_get_version(gdata) >= 3)
5048c2ecf20Sopenharmony_ci		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	severity = gdata->error_severity;
5078c2ecf20Sopenharmony_ci	printk("%s""Error %d, type: %s\n", pfx, sec_no,
5088c2ecf20Sopenharmony_ci	       cper_severity_str(severity));
5098c2ecf20Sopenharmony_ci	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
5108c2ecf20Sopenharmony_ci		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
5118c2ecf20Sopenharmony_ci	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
5128c2ecf20Sopenharmony_ci		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
5158c2ecf20Sopenharmony_ci	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
5168c2ecf20Sopenharmony_ci		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		printk("%s""section_type: general processor error\n", newpfx);
5198c2ecf20Sopenharmony_ci		if (gdata->error_data_length >= sizeof(*proc_err))
5208c2ecf20Sopenharmony_ci			cper_print_proc_generic(newpfx, proc_err);
5218c2ecf20Sopenharmony_ci		else
5228c2ecf20Sopenharmony_ci			goto err_section_too_small;
5238c2ecf20Sopenharmony_ci	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
5248c2ecf20Sopenharmony_ci		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		printk("%s""section_type: memory error\n", newpfx);
5278c2ecf20Sopenharmony_ci		if (gdata->error_data_length >=
5288c2ecf20Sopenharmony_ci		    sizeof(struct cper_sec_mem_err_old))
5298c2ecf20Sopenharmony_ci			cper_print_mem(newpfx, mem_err,
5308c2ecf20Sopenharmony_ci				       gdata->error_data_length);
5318c2ecf20Sopenharmony_ci		else
5328c2ecf20Sopenharmony_ci			goto err_section_too_small;
5338c2ecf20Sopenharmony_ci	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
5348c2ecf20Sopenharmony_ci		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		printk("%s""section_type: PCIe error\n", newpfx);
5378c2ecf20Sopenharmony_ci		if (gdata->error_data_length >= sizeof(*pcie))
5388c2ecf20Sopenharmony_ci			cper_print_pcie(newpfx, pcie, gdata);
5398c2ecf20Sopenharmony_ci		else
5408c2ecf20Sopenharmony_ci			goto err_section_too_small;
5418c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
5428c2ecf20Sopenharmony_ci	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
5438c2ecf20Sopenharmony_ci		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		printk("%ssection_type: ARM processor error\n", newpfx);
5468c2ecf20Sopenharmony_ci		if (gdata->error_data_length >= sizeof(*arm_err))
5478c2ecf20Sopenharmony_ci			cper_print_proc_arm(newpfx, arm_err);
5488c2ecf20Sopenharmony_ci		else
5498c2ecf20Sopenharmony_ci			goto err_section_too_small;
5508c2ecf20Sopenharmony_ci#endif
5518c2ecf20Sopenharmony_ci#if defined(CONFIG_UEFI_CPER_X86)
5528c2ecf20Sopenharmony_ci	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
5538c2ecf20Sopenharmony_ci		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
5568c2ecf20Sopenharmony_ci		if (gdata->error_data_length >= sizeof(*ia_err))
5578c2ecf20Sopenharmony_ci			cper_print_proc_ia(newpfx, ia_err);
5588c2ecf20Sopenharmony_ci		else
5598c2ecf20Sopenharmony_ci			goto err_section_too_small;
5608c2ecf20Sopenharmony_ci#endif
5618c2ecf20Sopenharmony_ci	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
5628c2ecf20Sopenharmony_ci		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci		printk("%ssection_type: Firmware Error Record Reference\n",
5658c2ecf20Sopenharmony_ci		       newpfx);
5668c2ecf20Sopenharmony_ci		/* The minimal FW Error Record contains 16 bytes */
5678c2ecf20Sopenharmony_ci		if (gdata->error_data_length >= SZ_16)
5688c2ecf20Sopenharmony_ci			cper_print_fw_err(newpfx, gdata, fw_err);
5698c2ecf20Sopenharmony_ci		else
5708c2ecf20Sopenharmony_ci			goto err_section_too_small;
5718c2ecf20Sopenharmony_ci	} else {
5728c2ecf20Sopenharmony_ci		const void *err = acpi_hest_get_payload(gdata);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
5758c2ecf20Sopenharmony_ci		printk("%ssection length: %#x\n", newpfx,
5768c2ecf20Sopenharmony_ci		       gdata->error_data_length);
5778c2ecf20Sopenharmony_ci		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
5788c2ecf20Sopenharmony_ci			       gdata->error_data_length, true);
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	return;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cierr_section_too_small:
5848c2ecf20Sopenharmony_ci	pr_err(FW_WARN "error section length is too small\n");
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_civoid cper_estatus_print(const char *pfx,
5888c2ecf20Sopenharmony_ci			const struct acpi_hest_generic_status *estatus)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct acpi_hest_generic_data *gdata;
5918c2ecf20Sopenharmony_ci	int sec_no = 0;
5928c2ecf20Sopenharmony_ci	char newpfx[64];
5938c2ecf20Sopenharmony_ci	__u16 severity;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	severity = estatus->error_severity;
5968c2ecf20Sopenharmony_ci	if (severity == CPER_SEV_CORRECTED)
5978c2ecf20Sopenharmony_ci		printk("%s%s\n", pfx,
5988c2ecf20Sopenharmony_ci		       "It has been corrected by h/w "
5998c2ecf20Sopenharmony_ci		       "and requires no further action");
6008c2ecf20Sopenharmony_ci	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
6018c2ecf20Sopenharmony_ci	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	apei_estatus_for_each_section(estatus, gdata) {
6048c2ecf20Sopenharmony_ci		cper_estatus_print_section(newpfx, gdata, sec_no);
6058c2ecf20Sopenharmony_ci		sec_no++;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_print);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ciint cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	if (estatus->data_length &&
6138c2ecf20Sopenharmony_ci	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
6148c2ecf20Sopenharmony_ci		return -EINVAL;
6158c2ecf20Sopenharmony_ci	if (estatus->raw_data_length &&
6168c2ecf20Sopenharmony_ci	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
6178c2ecf20Sopenharmony_ci		return -EINVAL;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_check_header);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ciint cper_estatus_check(const struct acpi_hest_generic_status *estatus)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct acpi_hest_generic_data *gdata;
6268c2ecf20Sopenharmony_ci	unsigned int data_len, record_size;
6278c2ecf20Sopenharmony_ci	int rc;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	rc = cper_estatus_check_header(estatus);
6308c2ecf20Sopenharmony_ci	if (rc)
6318c2ecf20Sopenharmony_ci		return rc;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	data_len = estatus->data_length;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	apei_estatus_for_each_section(estatus, gdata) {
6368c2ecf20Sopenharmony_ci		if (sizeof(struct acpi_hest_generic_data) > data_len)
6378c2ecf20Sopenharmony_ci			return -EINVAL;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		record_size = acpi_hest_get_record_size(gdata);
6408c2ecf20Sopenharmony_ci		if (record_size > data_len)
6418c2ecf20Sopenharmony_ci			return -EINVAL;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci		data_len -= record_size;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	if (data_len)
6468c2ecf20Sopenharmony_ci		return -EINVAL;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return 0;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_check);
651