xref: /linux/drivers/firmware/efi/cper.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
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.dw0, aer->header_log.dw1,
449 		       aer->header_log.dw2, aer->header_log.dw3);
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 static void
527 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
528 			   int sec_no)
529 {
530 	guid_t *sec_type = (guid_t *)gdata->section_type;
531 	__u16 severity;
532 	char newpfx[64];
533 
534 	if (acpi_hest_get_version(gdata) >= 3)
535 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
536 
537 	severity = gdata->error_severity;
538 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
539 	       cper_severity_str(severity));
540 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
541 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
542 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
543 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
544 
545 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
546 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
547 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
548 
549 		printk("%s""section_type: general processor error\n", newpfx);
550 		if (gdata->error_data_length >= sizeof(*proc_err))
551 			cper_print_proc_generic(newpfx, proc_err);
552 		else
553 			goto err_section_too_small;
554 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
555 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
556 
557 		printk("%s""section_type: memory error\n", newpfx);
558 		if (gdata->error_data_length >=
559 		    sizeof(struct cper_sec_mem_err_old))
560 			cper_print_mem(newpfx, mem_err,
561 				       gdata->error_data_length);
562 		else
563 			goto err_section_too_small;
564 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
565 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
566 
567 		printk("%s""section_type: PCIe error\n", newpfx);
568 		if (gdata->error_data_length >= sizeof(*pcie))
569 			cper_print_pcie(newpfx, pcie, gdata);
570 		else
571 			goto err_section_too_small;
572 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
573 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
574 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
575 
576 		printk("%ssection_type: ARM processor error\n", newpfx);
577 		if (gdata->error_data_length >= sizeof(*arm_err))
578 			cper_print_proc_arm(newpfx, arm_err);
579 		else
580 			goto err_section_too_small;
581 #endif
582 #if defined(CONFIG_UEFI_CPER_X86)
583 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
584 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
585 
586 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
587 		if (gdata->error_data_length >= sizeof(*ia_err))
588 			cper_print_proc_ia(newpfx, ia_err);
589 		else
590 			goto err_section_too_small;
591 #endif
592 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
593 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
594 
595 		printk("%ssection_type: Firmware Error Record Reference\n",
596 		       newpfx);
597 		/* The minimal FW Error Record contains 16 bytes */
598 		if (gdata->error_data_length >= SZ_16)
599 			cper_print_fw_err(newpfx, gdata, fw_err);
600 		else
601 			goto err_section_too_small;
602 	} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
603 		struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
604 
605 		printk("%ssection_type: CXL Protocol Error\n", newpfx);
606 		if (gdata->error_data_length >= sizeof(*prot_err))
607 			cper_print_prot_err(newpfx, prot_err);
608 		else
609 			goto err_section_too_small;
610 	} else {
611 		const void *err = acpi_hest_get_payload(gdata);
612 
613 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
614 		printk("%ssection length: %#x\n", newpfx,
615 		       gdata->error_data_length);
616 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
617 			       gdata->error_data_length, true);
618 	}
619 
620 	return;
621 
622 err_section_too_small:
623 	pr_err(FW_WARN "error section length is too small\n");
624 }
625 
626 void cper_estatus_print(const char *pfx,
627 			const struct acpi_hest_generic_status *estatus)
628 {
629 	struct acpi_hest_generic_data *gdata;
630 	int sec_no = 0;
631 	char newpfx[64];
632 	__u16 severity;
633 
634 	severity = estatus->error_severity;
635 	if (severity == CPER_SEV_CORRECTED)
636 		printk("%s%s\n", pfx,
637 		       "It has been corrected by h/w "
638 		       "and requires no further action");
639 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
640 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
641 
642 	apei_estatus_for_each_section(estatus, gdata) {
643 		cper_estatus_print_section(newpfx, gdata, sec_no);
644 		sec_no++;
645 	}
646 }
647 EXPORT_SYMBOL_GPL(cper_estatus_print);
648 
649 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
650 {
651 	if (estatus->data_length &&
652 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
653 		return -EINVAL;
654 	if (estatus->raw_data_length &&
655 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
656 		return -EINVAL;
657 
658 	return 0;
659 }
660 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
661 
662 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
663 {
664 	struct acpi_hest_generic_data *gdata;
665 	unsigned int data_len, record_size;
666 	int rc;
667 
668 	rc = cper_estatus_check_header(estatus);
669 	if (rc)
670 		return rc;
671 
672 	data_len = estatus->data_length;
673 
674 	apei_estatus_for_each_section(estatus, gdata) {
675 		if (acpi_hest_get_size(gdata) > data_len)
676 			return -EINVAL;
677 
678 		record_size = acpi_hest_get_record_size(gdata);
679 		if (record_size > data_len)
680 			return -EINVAL;
681 
682 		data_len -= record_size;
683 	}
684 	if (data_len)
685 		return -EINVAL;
686 
687 	return 0;
688 }
689 EXPORT_SYMBOL_GPL(cper_estatus_check);
690