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