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