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