xref: /openbmc/linux/drivers/firmware/efi/cper.c (revision abdbf1a2)
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 
295 int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
296 {
297 	u32 len, n;
298 	const char *bank = NULL, *device = NULL;
299 
300 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
301 		return 0;
302 
303 	len = CPER_REC_LEN;
304 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
305 	if (bank && device)
306 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
307 	else
308 		n = snprintf(msg, len,
309 			     "DIMM location: not present. DMI handle: 0x%.4x ",
310 			     mem->mem_dev_handle);
311 
312 	return n;
313 }
314 
315 void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
316 		       struct cper_mem_err_compact *cmem)
317 {
318 	cmem->validation_bits = mem->validation_bits;
319 	cmem->node = mem->node;
320 	cmem->card = mem->card;
321 	cmem->module = mem->module;
322 	cmem->bank = mem->bank;
323 	cmem->device = mem->device;
324 	cmem->row = mem->row;
325 	cmem->column = mem->column;
326 	cmem->bit_pos = mem->bit_pos;
327 	cmem->requestor_id = mem->requestor_id;
328 	cmem->responder_id = mem->responder_id;
329 	cmem->target_id = mem->target_id;
330 	cmem->extended = mem->extended;
331 	cmem->rank = mem->rank;
332 	cmem->mem_array_handle = mem->mem_array_handle;
333 	cmem->mem_dev_handle = mem->mem_dev_handle;
334 }
335 
336 const char *cper_mem_err_unpack(struct trace_seq *p,
337 				struct cper_mem_err_compact *cmem)
338 {
339 	const char *ret = trace_seq_buffer_ptr(p);
340 	char rcd_decode_str[CPER_REC_LEN];
341 
342 	if (cper_mem_err_location(cmem, rcd_decode_str))
343 		trace_seq_printf(p, "%s", rcd_decode_str);
344 	if (cper_dimm_err_location(cmem, rcd_decode_str))
345 		trace_seq_printf(p, "%s", rcd_decode_str);
346 	trace_seq_putc(p, '\0');
347 
348 	return ret;
349 }
350 
351 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
352 	int len)
353 {
354 	struct cper_mem_err_compact cmem;
355 	char rcd_decode_str[CPER_REC_LEN];
356 
357 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
358 	if (len == sizeof(struct cper_sec_mem_err_old) &&
359 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
360 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
361 		return;
362 	}
363 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
364 		printk("%s error_status: %s (0x%016llx)\n",
365 		       pfx, cper_mem_err_status_str(mem->error_status),
366 		       mem->error_status);
367 	if (mem->validation_bits & CPER_MEM_VALID_PA)
368 		printk("%s""physical_address: 0x%016llx\n",
369 		       pfx, mem->physical_addr);
370 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
371 		printk("%s""physical_address_mask: 0x%016llx\n",
372 		       pfx, mem->physical_addr_mask);
373 	cper_mem_err_pack(mem, &cmem);
374 	if (cper_mem_err_location(&cmem, rcd_decode_str))
375 		printk("%s%s\n", pfx, rcd_decode_str);
376 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
377 		u8 etype = mem->error_type;
378 		printk("%s""error_type: %d, %s\n", pfx, etype,
379 		       cper_mem_err_type_str(etype));
380 	}
381 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
382 		printk("%s%s\n", pfx, rcd_decode_str);
383 }
384 
385 static const char * const pcie_port_type_strs[] = {
386 	"PCIe end point",
387 	"legacy PCI end point",
388 	"unknown",
389 	"unknown",
390 	"root port",
391 	"upstream switch port",
392 	"downstream switch port",
393 	"PCIe to PCI/PCI-X bridge",
394 	"PCI/PCI-X to PCIe bridge",
395 	"root complex integrated endpoint device",
396 	"root complex event collector",
397 };
398 
399 static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
400 			    const struct acpi_hest_generic_data *gdata)
401 {
402 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
403 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
404 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
405 		       pcie_port_type_strs[pcie->port_type] : "unknown");
406 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
407 		printk("%s""version: %d.%d\n", pfx,
408 		       pcie->version.major, pcie->version.minor);
409 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
410 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
411 		       pcie->command, pcie->status);
412 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
413 		const __u8 *p;
414 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
415 		       pcie->device_id.segment, pcie->device_id.bus,
416 		       pcie->device_id.device, pcie->device_id.function);
417 		printk("%s""slot: %d\n", pfx,
418 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
419 		printk("%s""secondary_bus: 0x%02x\n", pfx,
420 		       pcie->device_id.secondary_bus);
421 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
422 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
423 		p = pcie->device_id.class_code;
424 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
425 	}
426 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
427 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
428 		       pcie->serial_number.lower, pcie->serial_number.upper);
429 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
430 		printk(
431 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
432 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
433 
434 	/* Fatal errors call __ghes_panic() before AER handler prints this */
435 	if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
436 	    (gdata->error_severity & CPER_SEV_FATAL)) {
437 		struct aer_capability_regs *aer;
438 
439 		aer = (struct aer_capability_regs *)pcie->aer_info;
440 		printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
441 		       pfx, aer->uncor_status, aer->uncor_mask);
442 		printk("%saer_uncor_severity: 0x%08x\n",
443 		       pfx, aer->uncor_severity);
444 		printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
445 		       aer->header_log.dw0, aer->header_log.dw1,
446 		       aer->header_log.dw2, aer->header_log.dw3);
447 	}
448 }
449 
450 static const char * const fw_err_rec_type_strs[] = {
451 	"IPF SAL Error Record",
452 	"SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
453 	"SOC Firmware Error Record Type2",
454 };
455 
456 static void cper_print_fw_err(const char *pfx,
457 			      struct acpi_hest_generic_data *gdata,
458 			      const struct cper_sec_fw_err_rec_ref *fw_err)
459 {
460 	void *buf = acpi_hest_get_payload(gdata);
461 	u32 offset, length = gdata->error_data_length;
462 
463 	printk("%s""Firmware Error Record Type: %s\n", pfx,
464 	       fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
465 	       fw_err_rec_type_strs[fw_err->record_type] : "unknown");
466 	printk("%s""Revision: %d\n", pfx, fw_err->revision);
467 
468 	/* Record Type based on UEFI 2.7 */
469 	if (fw_err->revision == 0) {
470 		printk("%s""Record Identifier: %08llx\n", pfx,
471 		       fw_err->record_identifier);
472 	} else if (fw_err->revision == 2) {
473 		printk("%s""Record Identifier: %pUl\n", pfx,
474 		       &fw_err->record_identifier_guid);
475 	}
476 
477 	/*
478 	 * The FW error record may contain trailing data beyond the
479 	 * structure defined by the specification. As the fields
480 	 * defined (and hence the offset of any trailing data) vary
481 	 * with the revision, set the offset to account for this
482 	 * variation.
483 	 */
484 	if (fw_err->revision == 0) {
485 		/* record_identifier_guid not defined */
486 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
487 				  record_identifier_guid);
488 	} else if (fw_err->revision == 1) {
489 		/* record_identifier not defined */
490 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
491 				  record_identifier);
492 	} else {
493 		offset = sizeof(*fw_err);
494 	}
495 
496 	buf += offset;
497 	length -= offset;
498 
499 	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
500 }
501 
502 static void cper_print_tstamp(const char *pfx,
503 				   struct acpi_hest_generic_data_v300 *gdata)
504 {
505 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
506 
507 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
508 		timestamp = (__u8 *)&(gdata->time_stamp);
509 		sec       = bcd2bin(timestamp[0]);
510 		min       = bcd2bin(timestamp[1]);
511 		hour      = bcd2bin(timestamp[2]);
512 		day       = bcd2bin(timestamp[4]);
513 		mon       = bcd2bin(timestamp[5]);
514 		year      = bcd2bin(timestamp[6]);
515 		century   = bcd2bin(timestamp[7]);
516 
517 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
518 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
519 		       century, year, mon, day, hour, min, sec);
520 	}
521 }
522 
523 static void
524 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
525 			   int sec_no)
526 {
527 	guid_t *sec_type = (guid_t *)gdata->section_type;
528 	__u16 severity;
529 	char newpfx[64];
530 
531 	if (acpi_hest_get_version(gdata) >= 3)
532 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
533 
534 	severity = gdata->error_severity;
535 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
536 	       cper_severity_str(severity));
537 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
538 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
539 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
540 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
541 
542 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
543 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
544 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
545 
546 		printk("%s""section_type: general processor error\n", newpfx);
547 		if (gdata->error_data_length >= sizeof(*proc_err))
548 			cper_print_proc_generic(newpfx, proc_err);
549 		else
550 			goto err_section_too_small;
551 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
552 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
553 
554 		printk("%s""section_type: memory error\n", newpfx);
555 		if (gdata->error_data_length >=
556 		    sizeof(struct cper_sec_mem_err_old))
557 			cper_print_mem(newpfx, mem_err,
558 				       gdata->error_data_length);
559 		else
560 			goto err_section_too_small;
561 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
562 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
563 
564 		printk("%s""section_type: PCIe error\n", newpfx);
565 		if (gdata->error_data_length >= sizeof(*pcie))
566 			cper_print_pcie(newpfx, pcie, gdata);
567 		else
568 			goto err_section_too_small;
569 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
570 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
571 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
572 
573 		printk("%ssection_type: ARM processor error\n", newpfx);
574 		if (gdata->error_data_length >= sizeof(*arm_err))
575 			cper_print_proc_arm(newpfx, arm_err);
576 		else
577 			goto err_section_too_small;
578 #endif
579 #if defined(CONFIG_UEFI_CPER_X86)
580 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
581 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
582 
583 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
584 		if (gdata->error_data_length >= sizeof(*ia_err))
585 			cper_print_proc_ia(newpfx, ia_err);
586 		else
587 			goto err_section_too_small;
588 #endif
589 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
590 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
591 
592 		printk("%ssection_type: Firmware Error Record Reference\n",
593 		       newpfx);
594 		/* The minimal FW Error Record contains 16 bytes */
595 		if (gdata->error_data_length >= SZ_16)
596 			cper_print_fw_err(newpfx, gdata, fw_err);
597 		else
598 			goto err_section_too_small;
599 	} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
600 		struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
601 
602 		printk("%ssection_type: CXL Protocol Error\n", newpfx);
603 		if (gdata->error_data_length >= sizeof(*prot_err))
604 			cper_print_prot_err(newpfx, prot_err);
605 		else
606 			goto err_section_too_small;
607 	} else {
608 		const void *err = acpi_hest_get_payload(gdata);
609 
610 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
611 		printk("%ssection length: %#x\n", newpfx,
612 		       gdata->error_data_length);
613 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
614 			       gdata->error_data_length, true);
615 	}
616 
617 	return;
618 
619 err_section_too_small:
620 	pr_err(FW_WARN "error section length is too small\n");
621 }
622 
623 void cper_estatus_print(const char *pfx,
624 			const struct acpi_hest_generic_status *estatus)
625 {
626 	struct acpi_hest_generic_data *gdata;
627 	int sec_no = 0;
628 	char newpfx[64];
629 	__u16 severity;
630 
631 	severity = estatus->error_severity;
632 	if (severity == CPER_SEV_CORRECTED)
633 		printk("%s%s\n", pfx,
634 		       "It has been corrected by h/w "
635 		       "and requires no further action");
636 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
637 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
638 
639 	apei_estatus_for_each_section(estatus, gdata) {
640 		cper_estatus_print_section(newpfx, gdata, sec_no);
641 		sec_no++;
642 	}
643 }
644 EXPORT_SYMBOL_GPL(cper_estatus_print);
645 
646 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
647 {
648 	if (estatus->data_length &&
649 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
650 		return -EINVAL;
651 	if (estatus->raw_data_length &&
652 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
653 		return -EINVAL;
654 
655 	return 0;
656 }
657 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
658 
659 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
660 {
661 	struct acpi_hest_generic_data *gdata;
662 	unsigned int data_len, record_size;
663 	int rc;
664 
665 	rc = cper_estatus_check_header(estatus);
666 	if (rc)
667 		return rc;
668 
669 	data_len = estatus->data_length;
670 
671 	apei_estatus_for_each_section(estatus, gdata) {
672 		if (acpi_hest_get_size(gdata) > data_len)
673 			return -EINVAL;
674 
675 		record_size = acpi_hest_get_record_size(gdata);
676 		if (record_size > data_len)
677 			return -EINVAL;
678 
679 		data_len -= record_size;
680 	}
681 	if (data_len)
682 		return -EINVAL;
683 
684 	return 0;
685 }
686 EXPORT_SYMBOL_GPL(cper_estatus_check);
687