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