xref: /OK3568_Linux_fs/kernel/drivers/firmware/efi/cper.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * UEFI Common Platform Error Record (CPER) support
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2010, Intel Corp.
6*4882a593Smuzhiyun  *	Author: Huang Ying <ying.huang@intel.com>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * CPER is the format used to describe platform hardware error by
9*4882a593Smuzhiyun  * various tables, such as ERST, BERT and HEST etc.
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * For more information about CPER, please refer to Appendix N of UEFI
12*4882a593Smuzhiyun  * Specification version 2.4.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/time.h>
18*4882a593Smuzhiyun #include <linux/cper.h>
19*4882a593Smuzhiyun #include <linux/dmi.h>
20*4882a593Smuzhiyun #include <linux/acpi.h>
21*4882a593Smuzhiyun #include <linux/pci.h>
22*4882a593Smuzhiyun #include <linux/aer.h>
23*4882a593Smuzhiyun #include <linux/printk.h>
24*4882a593Smuzhiyun #include <linux/bcd.h>
25*4882a593Smuzhiyun #include <acpi/ghes.h>
26*4882a593Smuzhiyun #include <ras/ras_event.h>
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun  * CPER record ID need to be unique even after reboot, because record
30*4882a593Smuzhiyun  * ID is used as index for ERST storage, while CPER records from
31*4882a593Smuzhiyun  * multiple boot may co-exist in ERST.
32*4882a593Smuzhiyun  */
cper_next_record_id(void)33*4882a593Smuzhiyun u64 cper_next_record_id(void)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	static atomic64_t seq;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	if (!atomic64_read(&seq)) {
38*4882a593Smuzhiyun 		time64_t time = ktime_get_real_seconds();
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 		/*
41*4882a593Smuzhiyun 		 * This code is unlikely to still be needed in year 2106,
42*4882a593Smuzhiyun 		 * but just in case, let's use a few more bits for timestamps
43*4882a593Smuzhiyun 		 * after y2038 to be sure they keep increasing monotonically
44*4882a593Smuzhiyun 		 * for the next few hundred years...
45*4882a593Smuzhiyun 		 */
46*4882a593Smuzhiyun 		if (time < 0x80000000)
47*4882a593Smuzhiyun 			atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
48*4882a593Smuzhiyun 		else
49*4882a593Smuzhiyun 			atomic64_set(&seq, 0x8000000000000000ull |
50*4882a593Smuzhiyun 					   ktime_get_real_seconds() << 24);
51*4882a593Smuzhiyun 	}
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	return atomic64_inc_return(&seq);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_next_record_id);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun static const char * const severity_strs[] = {
58*4882a593Smuzhiyun 	"recoverable",
59*4882a593Smuzhiyun 	"fatal",
60*4882a593Smuzhiyun 	"corrected",
61*4882a593Smuzhiyun 	"info",
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun 
cper_severity_str(unsigned int severity)64*4882a593Smuzhiyun const char *cper_severity_str(unsigned int severity)
65*4882a593Smuzhiyun {
66*4882a593Smuzhiyun 	return severity < ARRAY_SIZE(severity_strs) ?
67*4882a593Smuzhiyun 		severity_strs[severity] : "unknown";
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_severity_str);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun /*
72*4882a593Smuzhiyun  * cper_print_bits - print strings for set bits
73*4882a593Smuzhiyun  * @pfx: prefix for each line, including log level and prefix string
74*4882a593Smuzhiyun  * @bits: bit mask
75*4882a593Smuzhiyun  * @strs: string array, indexed by bit position
76*4882a593Smuzhiyun  * @strs_size: size of the string array: @strs
77*4882a593Smuzhiyun  *
78*4882a593Smuzhiyun  * For each set bit in @bits, print the corresponding string in @strs.
79*4882a593Smuzhiyun  * If the output length is longer than 80, multiple line will be
80*4882a593Smuzhiyun  * printed, with @pfx is printed at the beginning of each line.
81*4882a593Smuzhiyun  */
cper_print_bits(const char * pfx,unsigned int bits,const char * const strs[],unsigned int strs_size)82*4882a593Smuzhiyun void cper_print_bits(const char *pfx, unsigned int bits,
83*4882a593Smuzhiyun 		     const char * const strs[], unsigned int strs_size)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun 	int i, len = 0;
86*4882a593Smuzhiyun 	const char *str;
87*4882a593Smuzhiyun 	char buf[84];
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	for (i = 0; i < strs_size; i++) {
90*4882a593Smuzhiyun 		if (!(bits & (1U << i)))
91*4882a593Smuzhiyun 			continue;
92*4882a593Smuzhiyun 		str = strs[i];
93*4882a593Smuzhiyun 		if (!str)
94*4882a593Smuzhiyun 			continue;
95*4882a593Smuzhiyun 		if (len && len + strlen(str) + 2 > 80) {
96*4882a593Smuzhiyun 			printk("%s\n", buf);
97*4882a593Smuzhiyun 			len = 0;
98*4882a593Smuzhiyun 		}
99*4882a593Smuzhiyun 		if (!len)
100*4882a593Smuzhiyun 			len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
101*4882a593Smuzhiyun 		else
102*4882a593Smuzhiyun 			len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun 	if (len)
105*4882a593Smuzhiyun 		printk("%s\n", buf);
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun static const char * const proc_type_strs[] = {
109*4882a593Smuzhiyun 	"IA32/X64",
110*4882a593Smuzhiyun 	"IA64",
111*4882a593Smuzhiyun 	"ARM",
112*4882a593Smuzhiyun };
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun static const char * const proc_isa_strs[] = {
115*4882a593Smuzhiyun 	"IA32",
116*4882a593Smuzhiyun 	"IA64",
117*4882a593Smuzhiyun 	"X64",
118*4882a593Smuzhiyun 	"ARM A32/T32",
119*4882a593Smuzhiyun 	"ARM A64",
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun const char * const cper_proc_error_type_strs[] = {
123*4882a593Smuzhiyun 	"cache error",
124*4882a593Smuzhiyun 	"TLB error",
125*4882a593Smuzhiyun 	"bus error",
126*4882a593Smuzhiyun 	"micro-architectural error",
127*4882a593Smuzhiyun };
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun static const char * const proc_op_strs[] = {
130*4882a593Smuzhiyun 	"unknown or generic",
131*4882a593Smuzhiyun 	"data read",
132*4882a593Smuzhiyun 	"data write",
133*4882a593Smuzhiyun 	"instruction execution",
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun static const char * const proc_flag_strs[] = {
137*4882a593Smuzhiyun 	"restartable",
138*4882a593Smuzhiyun 	"precise IP",
139*4882a593Smuzhiyun 	"overflow",
140*4882a593Smuzhiyun 	"corrected",
141*4882a593Smuzhiyun };
142*4882a593Smuzhiyun 
cper_print_proc_generic(const char * pfx,const struct cper_sec_proc_generic * proc)143*4882a593Smuzhiyun static void cper_print_proc_generic(const char *pfx,
144*4882a593Smuzhiyun 				    const struct cper_sec_proc_generic *proc)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_TYPE)
147*4882a593Smuzhiyun 		printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
148*4882a593Smuzhiyun 		       proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
149*4882a593Smuzhiyun 		       proc_type_strs[proc->proc_type] : "unknown");
150*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_ISA)
151*4882a593Smuzhiyun 		printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
152*4882a593Smuzhiyun 		       proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
153*4882a593Smuzhiyun 		       proc_isa_strs[proc->proc_isa] : "unknown");
154*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
155*4882a593Smuzhiyun 		printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
156*4882a593Smuzhiyun 		cper_print_bits(pfx, proc->proc_error_type,
157*4882a593Smuzhiyun 				cper_proc_error_type_strs,
158*4882a593Smuzhiyun 				ARRAY_SIZE(cper_proc_error_type_strs));
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
161*4882a593Smuzhiyun 		printk("%s""operation: %d, %s\n", pfx, proc->operation,
162*4882a593Smuzhiyun 		       proc->operation < ARRAY_SIZE(proc_op_strs) ?
163*4882a593Smuzhiyun 		       proc_op_strs[proc->operation] : "unknown");
164*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
165*4882a593Smuzhiyun 		printk("%s""flags: 0x%02x\n", pfx, proc->flags);
166*4882a593Smuzhiyun 		cper_print_bits(pfx, proc->flags, proc_flag_strs,
167*4882a593Smuzhiyun 				ARRAY_SIZE(proc_flag_strs));
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
170*4882a593Smuzhiyun 		printk("%s""level: %d\n", pfx, proc->level);
171*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_VERSION)
172*4882a593Smuzhiyun 		printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
173*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_ID)
174*4882a593Smuzhiyun 		printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
175*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
176*4882a593Smuzhiyun 		printk("%s""target_address: 0x%016llx\n",
177*4882a593Smuzhiyun 		       pfx, proc->target_addr);
178*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
179*4882a593Smuzhiyun 		printk("%s""requestor_id: 0x%016llx\n",
180*4882a593Smuzhiyun 		       pfx, proc->requestor_id);
181*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
182*4882a593Smuzhiyun 		printk("%s""responder_id: 0x%016llx\n",
183*4882a593Smuzhiyun 		       pfx, proc->responder_id);
184*4882a593Smuzhiyun 	if (proc->validation_bits & CPER_PROC_VALID_IP)
185*4882a593Smuzhiyun 		printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun static const char * const mem_err_type_strs[] = {
189*4882a593Smuzhiyun 	"unknown",
190*4882a593Smuzhiyun 	"no error",
191*4882a593Smuzhiyun 	"single-bit ECC",
192*4882a593Smuzhiyun 	"multi-bit ECC",
193*4882a593Smuzhiyun 	"single-symbol chipkill ECC",
194*4882a593Smuzhiyun 	"multi-symbol chipkill ECC",
195*4882a593Smuzhiyun 	"master abort",
196*4882a593Smuzhiyun 	"target abort",
197*4882a593Smuzhiyun 	"parity error",
198*4882a593Smuzhiyun 	"watchdog timeout",
199*4882a593Smuzhiyun 	"invalid address",
200*4882a593Smuzhiyun 	"mirror Broken",
201*4882a593Smuzhiyun 	"memory sparing",
202*4882a593Smuzhiyun 	"scrub corrected error",
203*4882a593Smuzhiyun 	"scrub uncorrected error",
204*4882a593Smuzhiyun 	"physical memory map-out event",
205*4882a593Smuzhiyun };
206*4882a593Smuzhiyun 
cper_mem_err_type_str(unsigned int etype)207*4882a593Smuzhiyun const char *cper_mem_err_type_str(unsigned int etype)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	return etype < ARRAY_SIZE(mem_err_type_strs) ?
210*4882a593Smuzhiyun 		mem_err_type_strs[etype] : "unknown";
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
213*4882a593Smuzhiyun 
cper_mem_err_location(struct cper_mem_err_compact * mem,char * msg)214*4882a593Smuzhiyun static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	u32 len, n;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	if (!msg)
219*4882a593Smuzhiyun 		return 0;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	n = 0;
222*4882a593Smuzhiyun 	len = CPER_REC_LEN - 1;
223*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_NODE)
224*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
225*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_CARD)
226*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
227*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_MODULE)
228*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
229*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
230*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
231*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_BANK)
232*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
233*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
234*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "bank_group: %d ",
235*4882a593Smuzhiyun 			       mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
236*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
237*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "bank_address: %d ",
238*4882a593Smuzhiyun 			       mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
239*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
240*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
241*4882a593Smuzhiyun 	if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
242*4882a593Smuzhiyun 		u32 row = mem->row;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 		row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
245*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "row: %d ", row);
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
248*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
249*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
250*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "bit_position: %d ",
251*4882a593Smuzhiyun 			       mem->bit_pos);
252*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
253*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
254*4882a593Smuzhiyun 			       mem->requestor_id);
255*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
256*4882a593Smuzhiyun 		n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
257*4882a593Smuzhiyun 			       mem->responder_id);
258*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
259*4882a593Smuzhiyun 		scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
260*4882a593Smuzhiyun 			  mem->target_id);
261*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
262*4882a593Smuzhiyun 		scnprintf(msg + n, len - n, "chip_id: %d ",
263*4882a593Smuzhiyun 			  mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	msg[n] = '\0';
266*4882a593Smuzhiyun 	return n;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
cper_dimm_err_location(struct cper_mem_err_compact * mem,char * msg)269*4882a593Smuzhiyun static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	u32 len, n;
272*4882a593Smuzhiyun 	const char *bank = NULL, *device = NULL;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
275*4882a593Smuzhiyun 		return 0;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	len = CPER_REC_LEN;
278*4882a593Smuzhiyun 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
279*4882a593Smuzhiyun 	if (bank && device)
280*4882a593Smuzhiyun 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
281*4882a593Smuzhiyun 	else
282*4882a593Smuzhiyun 		n = snprintf(msg, len,
283*4882a593Smuzhiyun 			     "DIMM location: not present. DMI handle: 0x%.4x ",
284*4882a593Smuzhiyun 			     mem->mem_dev_handle);
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	return n;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
cper_mem_err_pack(const struct cper_sec_mem_err * mem,struct cper_mem_err_compact * cmem)289*4882a593Smuzhiyun void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
290*4882a593Smuzhiyun 		       struct cper_mem_err_compact *cmem)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	cmem->validation_bits = mem->validation_bits;
293*4882a593Smuzhiyun 	cmem->node = mem->node;
294*4882a593Smuzhiyun 	cmem->card = mem->card;
295*4882a593Smuzhiyun 	cmem->module = mem->module;
296*4882a593Smuzhiyun 	cmem->bank = mem->bank;
297*4882a593Smuzhiyun 	cmem->device = mem->device;
298*4882a593Smuzhiyun 	cmem->row = mem->row;
299*4882a593Smuzhiyun 	cmem->column = mem->column;
300*4882a593Smuzhiyun 	cmem->bit_pos = mem->bit_pos;
301*4882a593Smuzhiyun 	cmem->requestor_id = mem->requestor_id;
302*4882a593Smuzhiyun 	cmem->responder_id = mem->responder_id;
303*4882a593Smuzhiyun 	cmem->target_id = mem->target_id;
304*4882a593Smuzhiyun 	cmem->extended = mem->extended;
305*4882a593Smuzhiyun 	cmem->rank = mem->rank;
306*4882a593Smuzhiyun 	cmem->mem_array_handle = mem->mem_array_handle;
307*4882a593Smuzhiyun 	cmem->mem_dev_handle = mem->mem_dev_handle;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun 
cper_mem_err_unpack(struct trace_seq * p,struct cper_mem_err_compact * cmem)310*4882a593Smuzhiyun const char *cper_mem_err_unpack(struct trace_seq *p,
311*4882a593Smuzhiyun 				struct cper_mem_err_compact *cmem)
312*4882a593Smuzhiyun {
313*4882a593Smuzhiyun 	const char *ret = trace_seq_buffer_ptr(p);
314*4882a593Smuzhiyun 	char rcd_decode_str[CPER_REC_LEN];
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	if (cper_mem_err_location(cmem, rcd_decode_str))
317*4882a593Smuzhiyun 		trace_seq_printf(p, "%s", rcd_decode_str);
318*4882a593Smuzhiyun 	if (cper_dimm_err_location(cmem, rcd_decode_str))
319*4882a593Smuzhiyun 		trace_seq_printf(p, "%s", rcd_decode_str);
320*4882a593Smuzhiyun 	trace_seq_putc(p, '\0');
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return ret;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
cper_print_mem(const char * pfx,const struct cper_sec_mem_err * mem,int len)325*4882a593Smuzhiyun static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
326*4882a593Smuzhiyun 	int len)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun 	struct cper_mem_err_compact cmem;
329*4882a593Smuzhiyun 	char rcd_decode_str[CPER_REC_LEN];
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
332*4882a593Smuzhiyun 	if (len == sizeof(struct cper_sec_mem_err_old) &&
333*4882a593Smuzhiyun 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
334*4882a593Smuzhiyun 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
335*4882a593Smuzhiyun 		return;
336*4882a593Smuzhiyun 	}
337*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
338*4882a593Smuzhiyun 		printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
339*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_PA)
340*4882a593Smuzhiyun 		printk("%s""physical_address: 0x%016llx\n",
341*4882a593Smuzhiyun 		       pfx, mem->physical_addr);
342*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
343*4882a593Smuzhiyun 		printk("%s""physical_address_mask: 0x%016llx\n",
344*4882a593Smuzhiyun 		       pfx, mem->physical_addr_mask);
345*4882a593Smuzhiyun 	cper_mem_err_pack(mem, &cmem);
346*4882a593Smuzhiyun 	if (cper_mem_err_location(&cmem, rcd_decode_str))
347*4882a593Smuzhiyun 		printk("%s%s\n", pfx, rcd_decode_str);
348*4882a593Smuzhiyun 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
349*4882a593Smuzhiyun 		u8 etype = mem->error_type;
350*4882a593Smuzhiyun 		printk("%s""error_type: %d, %s\n", pfx, etype,
351*4882a593Smuzhiyun 		       cper_mem_err_type_str(etype));
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
354*4882a593Smuzhiyun 		printk("%s%s\n", pfx, rcd_decode_str);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun static const char * const pcie_port_type_strs[] = {
358*4882a593Smuzhiyun 	"PCIe end point",
359*4882a593Smuzhiyun 	"legacy PCI end point",
360*4882a593Smuzhiyun 	"unknown",
361*4882a593Smuzhiyun 	"unknown",
362*4882a593Smuzhiyun 	"root port",
363*4882a593Smuzhiyun 	"upstream switch port",
364*4882a593Smuzhiyun 	"downstream switch port",
365*4882a593Smuzhiyun 	"PCIe to PCI/PCI-X bridge",
366*4882a593Smuzhiyun 	"PCI/PCI-X to PCIe bridge",
367*4882a593Smuzhiyun 	"root complex integrated endpoint device",
368*4882a593Smuzhiyun 	"root complex event collector",
369*4882a593Smuzhiyun };
370*4882a593Smuzhiyun 
cper_print_pcie(const char * pfx,const struct cper_sec_pcie * pcie,const struct acpi_hest_generic_data * gdata)371*4882a593Smuzhiyun static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
372*4882a593Smuzhiyun 			    const struct acpi_hest_generic_data *gdata)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
375*4882a593Smuzhiyun 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
376*4882a593Smuzhiyun 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
377*4882a593Smuzhiyun 		       pcie_port_type_strs[pcie->port_type] : "unknown");
378*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
379*4882a593Smuzhiyun 		printk("%s""version: %d.%d\n", pfx,
380*4882a593Smuzhiyun 		       pcie->version.major, pcie->version.minor);
381*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
382*4882a593Smuzhiyun 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
383*4882a593Smuzhiyun 		       pcie->command, pcie->status);
384*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
385*4882a593Smuzhiyun 		const __u8 *p;
386*4882a593Smuzhiyun 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
387*4882a593Smuzhiyun 		       pcie->device_id.segment, pcie->device_id.bus,
388*4882a593Smuzhiyun 		       pcie->device_id.device, pcie->device_id.function);
389*4882a593Smuzhiyun 		printk("%s""slot: %d\n", pfx,
390*4882a593Smuzhiyun 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
391*4882a593Smuzhiyun 		printk("%s""secondary_bus: 0x%02x\n", pfx,
392*4882a593Smuzhiyun 		       pcie->device_id.secondary_bus);
393*4882a593Smuzhiyun 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
394*4882a593Smuzhiyun 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
395*4882a593Smuzhiyun 		p = pcie->device_id.class_code;
396*4882a593Smuzhiyun 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
397*4882a593Smuzhiyun 	}
398*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
399*4882a593Smuzhiyun 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
400*4882a593Smuzhiyun 		       pcie->serial_number.lower, pcie->serial_number.upper);
401*4882a593Smuzhiyun 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
402*4882a593Smuzhiyun 		printk(
403*4882a593Smuzhiyun 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
404*4882a593Smuzhiyun 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
405*4882a593Smuzhiyun 
406*4882a593Smuzhiyun 	/* Fatal errors call __ghes_panic() before AER handler prints this */
407*4882a593Smuzhiyun 	if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
408*4882a593Smuzhiyun 	    (gdata->error_severity & CPER_SEV_FATAL)) {
409*4882a593Smuzhiyun 		struct aer_capability_regs *aer;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 		aer = (struct aer_capability_regs *)pcie->aer_info;
412*4882a593Smuzhiyun 		printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
413*4882a593Smuzhiyun 		       pfx, aer->uncor_status, aer->uncor_mask);
414*4882a593Smuzhiyun 		printk("%saer_uncor_severity: 0x%08x\n",
415*4882a593Smuzhiyun 		       pfx, aer->uncor_severity);
416*4882a593Smuzhiyun 		printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
417*4882a593Smuzhiyun 		       aer->header_log.dw0, aer->header_log.dw1,
418*4882a593Smuzhiyun 		       aer->header_log.dw2, aer->header_log.dw3);
419*4882a593Smuzhiyun 	}
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun static const char * const fw_err_rec_type_strs[] = {
423*4882a593Smuzhiyun 	"IPF SAL Error Record",
424*4882a593Smuzhiyun 	"SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
425*4882a593Smuzhiyun 	"SOC Firmware Error Record Type2",
426*4882a593Smuzhiyun };
427*4882a593Smuzhiyun 
cper_print_fw_err(const char * pfx,struct acpi_hest_generic_data * gdata,const struct cper_sec_fw_err_rec_ref * fw_err)428*4882a593Smuzhiyun static void cper_print_fw_err(const char *pfx,
429*4882a593Smuzhiyun 			      struct acpi_hest_generic_data *gdata,
430*4882a593Smuzhiyun 			      const struct cper_sec_fw_err_rec_ref *fw_err)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	void *buf = acpi_hest_get_payload(gdata);
433*4882a593Smuzhiyun 	u32 offset, length = gdata->error_data_length;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	printk("%s""Firmware Error Record Type: %s\n", pfx,
436*4882a593Smuzhiyun 	       fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
437*4882a593Smuzhiyun 	       fw_err_rec_type_strs[fw_err->record_type] : "unknown");
438*4882a593Smuzhiyun 	printk("%s""Revision: %d\n", pfx, fw_err->revision);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	/* Record Type based on UEFI 2.7 */
441*4882a593Smuzhiyun 	if (fw_err->revision == 0) {
442*4882a593Smuzhiyun 		printk("%s""Record Identifier: %08llx\n", pfx,
443*4882a593Smuzhiyun 		       fw_err->record_identifier);
444*4882a593Smuzhiyun 	} else if (fw_err->revision == 2) {
445*4882a593Smuzhiyun 		printk("%s""Record Identifier: %pUl\n", pfx,
446*4882a593Smuzhiyun 		       &fw_err->record_identifier_guid);
447*4882a593Smuzhiyun 	}
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun 	/*
450*4882a593Smuzhiyun 	 * The FW error record may contain trailing data beyond the
451*4882a593Smuzhiyun 	 * structure defined by the specification. As the fields
452*4882a593Smuzhiyun 	 * defined (and hence the offset of any trailing data) vary
453*4882a593Smuzhiyun 	 * with the revision, set the offset to account for this
454*4882a593Smuzhiyun 	 * variation.
455*4882a593Smuzhiyun 	 */
456*4882a593Smuzhiyun 	if (fw_err->revision == 0) {
457*4882a593Smuzhiyun 		/* record_identifier_guid not defined */
458*4882a593Smuzhiyun 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
459*4882a593Smuzhiyun 				  record_identifier_guid);
460*4882a593Smuzhiyun 	} else if (fw_err->revision == 1) {
461*4882a593Smuzhiyun 		/* record_identifier not defined */
462*4882a593Smuzhiyun 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
463*4882a593Smuzhiyun 				  record_identifier);
464*4882a593Smuzhiyun 	} else {
465*4882a593Smuzhiyun 		offset = sizeof(*fw_err);
466*4882a593Smuzhiyun 	}
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	buf += offset;
469*4882a593Smuzhiyun 	length -= offset;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun 
cper_print_tstamp(const char * pfx,struct acpi_hest_generic_data_v300 * gdata)474*4882a593Smuzhiyun static void cper_print_tstamp(const char *pfx,
475*4882a593Smuzhiyun 				   struct acpi_hest_generic_data_v300 *gdata)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
480*4882a593Smuzhiyun 		timestamp = (__u8 *)&(gdata->time_stamp);
481*4882a593Smuzhiyun 		sec       = bcd2bin(timestamp[0]);
482*4882a593Smuzhiyun 		min       = bcd2bin(timestamp[1]);
483*4882a593Smuzhiyun 		hour      = bcd2bin(timestamp[2]);
484*4882a593Smuzhiyun 		day       = bcd2bin(timestamp[4]);
485*4882a593Smuzhiyun 		mon       = bcd2bin(timestamp[5]);
486*4882a593Smuzhiyun 		year      = bcd2bin(timestamp[6]);
487*4882a593Smuzhiyun 		century   = bcd2bin(timestamp[7]);
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
490*4882a593Smuzhiyun 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
491*4882a593Smuzhiyun 		       century, year, mon, day, hour, min, sec);
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun 
495*4882a593Smuzhiyun static void
cper_estatus_print_section(const char * pfx,struct acpi_hest_generic_data * gdata,int sec_no)496*4882a593Smuzhiyun cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
497*4882a593Smuzhiyun 			   int sec_no)
498*4882a593Smuzhiyun {
499*4882a593Smuzhiyun 	guid_t *sec_type = (guid_t *)gdata->section_type;
500*4882a593Smuzhiyun 	__u16 severity;
501*4882a593Smuzhiyun 	char newpfx[64];
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	if (acpi_hest_get_version(gdata) >= 3)
504*4882a593Smuzhiyun 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun 	severity = gdata->error_severity;
507*4882a593Smuzhiyun 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
508*4882a593Smuzhiyun 	       cper_severity_str(severity));
509*4882a593Smuzhiyun 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
510*4882a593Smuzhiyun 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
511*4882a593Smuzhiyun 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
512*4882a593Smuzhiyun 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
515*4882a593Smuzhiyun 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
516*4882a593Smuzhiyun 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 		printk("%s""section_type: general processor error\n", newpfx);
519*4882a593Smuzhiyun 		if (gdata->error_data_length >= sizeof(*proc_err))
520*4882a593Smuzhiyun 			cper_print_proc_generic(newpfx, proc_err);
521*4882a593Smuzhiyun 		else
522*4882a593Smuzhiyun 			goto err_section_too_small;
523*4882a593Smuzhiyun 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
524*4882a593Smuzhiyun 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 		printk("%s""section_type: memory error\n", newpfx);
527*4882a593Smuzhiyun 		if (gdata->error_data_length >=
528*4882a593Smuzhiyun 		    sizeof(struct cper_sec_mem_err_old))
529*4882a593Smuzhiyun 			cper_print_mem(newpfx, mem_err,
530*4882a593Smuzhiyun 				       gdata->error_data_length);
531*4882a593Smuzhiyun 		else
532*4882a593Smuzhiyun 			goto err_section_too_small;
533*4882a593Smuzhiyun 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
534*4882a593Smuzhiyun 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 		printk("%s""section_type: PCIe error\n", newpfx);
537*4882a593Smuzhiyun 		if (gdata->error_data_length >= sizeof(*pcie))
538*4882a593Smuzhiyun 			cper_print_pcie(newpfx, pcie, gdata);
539*4882a593Smuzhiyun 		else
540*4882a593Smuzhiyun 			goto err_section_too_small;
541*4882a593Smuzhiyun #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
542*4882a593Smuzhiyun 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
543*4882a593Smuzhiyun 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 		printk("%ssection_type: ARM processor error\n", newpfx);
546*4882a593Smuzhiyun 		if (gdata->error_data_length >= sizeof(*arm_err))
547*4882a593Smuzhiyun 			cper_print_proc_arm(newpfx, arm_err);
548*4882a593Smuzhiyun 		else
549*4882a593Smuzhiyun 			goto err_section_too_small;
550*4882a593Smuzhiyun #endif
551*4882a593Smuzhiyun #if defined(CONFIG_UEFI_CPER_X86)
552*4882a593Smuzhiyun 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
553*4882a593Smuzhiyun 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
556*4882a593Smuzhiyun 		if (gdata->error_data_length >= sizeof(*ia_err))
557*4882a593Smuzhiyun 			cper_print_proc_ia(newpfx, ia_err);
558*4882a593Smuzhiyun 		else
559*4882a593Smuzhiyun 			goto err_section_too_small;
560*4882a593Smuzhiyun #endif
561*4882a593Smuzhiyun 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
562*4882a593Smuzhiyun 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 		printk("%ssection_type: Firmware Error Record Reference\n",
565*4882a593Smuzhiyun 		       newpfx);
566*4882a593Smuzhiyun 		/* The minimal FW Error Record contains 16 bytes */
567*4882a593Smuzhiyun 		if (gdata->error_data_length >= SZ_16)
568*4882a593Smuzhiyun 			cper_print_fw_err(newpfx, gdata, fw_err);
569*4882a593Smuzhiyun 		else
570*4882a593Smuzhiyun 			goto err_section_too_small;
571*4882a593Smuzhiyun 	} else {
572*4882a593Smuzhiyun 		const void *err = acpi_hest_get_payload(gdata);
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
575*4882a593Smuzhiyun 		printk("%ssection length: %#x\n", newpfx,
576*4882a593Smuzhiyun 		       gdata->error_data_length);
577*4882a593Smuzhiyun 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
578*4882a593Smuzhiyun 			       gdata->error_data_length, true);
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	return;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun err_section_too_small:
584*4882a593Smuzhiyun 	pr_err(FW_WARN "error section length is too small\n");
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun 
cper_estatus_print(const char * pfx,const struct acpi_hest_generic_status * estatus)587*4882a593Smuzhiyun void cper_estatus_print(const char *pfx,
588*4882a593Smuzhiyun 			const struct acpi_hest_generic_status *estatus)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun 	struct acpi_hest_generic_data *gdata;
591*4882a593Smuzhiyun 	int sec_no = 0;
592*4882a593Smuzhiyun 	char newpfx[64];
593*4882a593Smuzhiyun 	__u16 severity;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	severity = estatus->error_severity;
596*4882a593Smuzhiyun 	if (severity == CPER_SEV_CORRECTED)
597*4882a593Smuzhiyun 		printk("%s%s\n", pfx,
598*4882a593Smuzhiyun 		       "It has been corrected by h/w "
599*4882a593Smuzhiyun 		       "and requires no further action");
600*4882a593Smuzhiyun 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
601*4882a593Smuzhiyun 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	apei_estatus_for_each_section(estatus, gdata) {
604*4882a593Smuzhiyun 		cper_estatus_print_section(newpfx, gdata, sec_no);
605*4882a593Smuzhiyun 		sec_no++;
606*4882a593Smuzhiyun 	}
607*4882a593Smuzhiyun }
608*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_estatus_print);
609*4882a593Smuzhiyun 
cper_estatus_check_header(const struct acpi_hest_generic_status * estatus)610*4882a593Smuzhiyun int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
611*4882a593Smuzhiyun {
612*4882a593Smuzhiyun 	if (estatus->data_length &&
613*4882a593Smuzhiyun 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
614*4882a593Smuzhiyun 		return -EINVAL;
615*4882a593Smuzhiyun 	if (estatus->raw_data_length &&
616*4882a593Smuzhiyun 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
617*4882a593Smuzhiyun 		return -EINVAL;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	return 0;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_estatus_check_header);
622*4882a593Smuzhiyun 
cper_estatus_check(const struct acpi_hest_generic_status * estatus)623*4882a593Smuzhiyun int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
624*4882a593Smuzhiyun {
625*4882a593Smuzhiyun 	struct acpi_hest_generic_data *gdata;
626*4882a593Smuzhiyun 	unsigned int data_len, record_size;
627*4882a593Smuzhiyun 	int rc;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	rc = cper_estatus_check_header(estatus);
630*4882a593Smuzhiyun 	if (rc)
631*4882a593Smuzhiyun 		return rc;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	data_len = estatus->data_length;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	apei_estatus_for_each_section(estatus, gdata) {
636*4882a593Smuzhiyun 		if (sizeof(struct acpi_hest_generic_data) > data_len)
637*4882a593Smuzhiyun 			return -EINVAL;
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 		record_size = acpi_hest_get_record_size(gdata);
640*4882a593Smuzhiyun 		if (record_size > data_len)
641*4882a593Smuzhiyun 			return -EINVAL;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 		data_len -= record_size;
644*4882a593Smuzhiyun 	}
645*4882a593Smuzhiyun 	if (data_len)
646*4882a593Smuzhiyun 		return -EINVAL;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	return 0;
649*4882a593Smuzhiyun }
650*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(cper_estatus_check);
651