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