xref: /linux/drivers/firmware/efi/cper.c (revision dfa5dc3ad3b15a519101f134ed76c068526004e4)
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/bitmap.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/time.h>
19 #include <linux/cper.h>
20 #include <linux/dmi.h>
21 #include <linux/acpi.h>
22 #include <linux/pci.h>
23 #include <linux/aer.h>
24 #include <linux/printk.h>
25 #include <linux/bcd.h>
26 #include <acpi/ghes.h>
27 #include <ras/ras_event.h>
28 #include <cxl/event.h>
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 /**
111  * cper_bits_to_str - return a string for set bits
112  * @buf: buffer to store the output string
113  * @buf_size: size of the output string buffer
114  * @bits: bit mask
115  * @strs: string array, indexed by bit position
116  * @strs_size: size of the string array: @strs
117  *
118  * Add to @buf the bitmask in hexadecimal. Then, for each set bit in @bits,
119  * add the corresponding string describing the bit in @strs to @buf.
120  *
121  * A typical example is::
122  *
123  *	const char * const bits[] = {
124  *		"bit 3 name",
125  *		"bit 4 name",
126  *		"bit 5 name",
127  *	};
128  *	char str[120];
129  *	unsigned int bitmask = BIT(3) | BIT(5);
130  *	#define MASK GENMASK(5,3)
131  *
132  *	cper_bits_to_str(str, sizeof(str), FIELD_GET(MASK, bitmask),
133  *			 bits, ARRAY_SIZE(bits));
134  *
135  * The above code fills the string ``str`` with ``bit 3 name|bit 5 name``.
136  *
137  * Return: number of bytes stored or an error code if lower than zero.
138  */
139 int cper_bits_to_str(char *buf, int buf_size, unsigned long bits,
140 		     const char * const strs[], unsigned int strs_size)
141 {
142 	int len = buf_size;
143 	char *str = buf;
144 	int i, size;
145 
146 	*buf = '\0';
147 
148 	for_each_set_bit(i, &bits, strs_size) {
149 		if (!(bits & BIT_ULL(i)))
150 			continue;
151 
152 		if (*buf && len > 0) {
153 			*str = '|';
154 			len--;
155 			str++;
156 		}
157 
158 		size = strscpy(str, strs[i], len);
159 		if (size < 0)
160 			return size;
161 
162 		len -= size;
163 		str += size;
164 	}
165 	return buf_size - len;
166 }
167 EXPORT_SYMBOL_GPL(cper_bits_to_str);
168 
169 static const char * const proc_type_strs[] = {
170 	"IA32/X64",
171 	"IA64",
172 	"ARM",
173 };
174 
175 static const char * const proc_isa_strs[] = {
176 	"IA32",
177 	"IA64",
178 	"X64",
179 	"ARM A32/T32",
180 	"ARM A64",
181 };
182 
183 const char * const cper_proc_error_type_strs[] = {
184 	"cache error",
185 	"TLB error",
186 	"bus error",
187 	"micro-architectural error",
188 };
189 
190 static const char * const proc_op_strs[] = {
191 	"unknown or generic",
192 	"data read",
193 	"data write",
194 	"instruction execution",
195 };
196 
197 static const char * const proc_flag_strs[] = {
198 	"restartable",
199 	"precise IP",
200 	"overflow",
201 	"corrected",
202 };
203 
204 static void cper_print_proc_generic(const char *pfx,
205 				    const struct cper_sec_proc_generic *proc)
206 {
207 	if (proc->validation_bits & CPER_PROC_VALID_TYPE)
208 		printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
209 		       proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
210 		       proc_type_strs[proc->proc_type] : "unknown");
211 	if (proc->validation_bits & CPER_PROC_VALID_ISA)
212 		printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
213 		       proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
214 		       proc_isa_strs[proc->proc_isa] : "unknown");
215 	if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
216 		printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
217 		cper_print_bits(pfx, proc->proc_error_type,
218 				cper_proc_error_type_strs,
219 				ARRAY_SIZE(cper_proc_error_type_strs));
220 	}
221 	if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
222 		printk("%s""operation: %d, %s\n", pfx, proc->operation,
223 		       proc->operation < ARRAY_SIZE(proc_op_strs) ?
224 		       proc_op_strs[proc->operation] : "unknown");
225 	if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
226 		printk("%s""flags: 0x%02x\n", pfx, proc->flags);
227 		cper_print_bits(pfx, proc->flags, proc_flag_strs,
228 				ARRAY_SIZE(proc_flag_strs));
229 	}
230 	if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
231 		printk("%s""level: %d\n", pfx, proc->level);
232 	if (proc->validation_bits & CPER_PROC_VALID_VERSION)
233 		printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
234 	if (proc->validation_bits & CPER_PROC_VALID_ID)
235 		printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
236 	if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
237 		printk("%s""target_address: 0x%016llx\n",
238 		       pfx, proc->target_addr);
239 	if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
240 		printk("%s""requestor_id: 0x%016llx\n",
241 		       pfx, proc->requestor_id);
242 	if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
243 		printk("%s""responder_id: 0x%016llx\n",
244 		       pfx, proc->responder_id);
245 	if (proc->validation_bits & CPER_PROC_VALID_IP)
246 		printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
247 }
248 
249 static const char * const mem_err_type_strs[] = {
250 	"unknown",
251 	"no error",
252 	"single-bit ECC",
253 	"multi-bit ECC",
254 	"single-symbol chipkill ECC",
255 	"multi-symbol chipkill ECC",
256 	"master abort",
257 	"target abort",
258 	"parity error",
259 	"watchdog timeout",
260 	"invalid address",
261 	"mirror Broken",
262 	"memory sparing",
263 	"scrub corrected error",
264 	"scrub uncorrected error",
265 	"physical memory map-out event",
266 };
267 
268 const char *cper_mem_err_type_str(unsigned int etype)
269 {
270 	return etype < ARRAY_SIZE(mem_err_type_strs) ?
271 		mem_err_type_strs[etype] : "unknown";
272 }
273 EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
274 
275 const char *cper_mem_err_status_str(u64 status)
276 {
277 	switch ((status >> 8) & 0xff) {
278 	case  1:	return "Error detected internal to the component";
279 	case  4:	return "Storage error in DRAM memory";
280 	case  5:	return "Storage error in TLB";
281 	case  6:	return "Storage error in cache";
282 	case  7:	return "Error in one or more functional units";
283 	case  8:	return "Component failed self test";
284 	case  9:	return "Overflow or undervalue of internal queue";
285 	case 16:	return "Error detected in the bus";
286 	case 17:	return "Virtual address not found on IO-TLB or IO-PDIR";
287 	case 18:	return "Improper access error";
288 	case 19:	return "Access to a memory address which is not mapped to any component";
289 	case 20:	return "Loss of Lockstep";
290 	case 21:	return "Response not associated with a request";
291 	case 22:	return "Bus parity error - must also set the A, C, or D Bits";
292 	case 23:	return "Detection of a protocol error";
293 	case 24:	return "Detection of a PATH_ERROR";
294 	case 25:	return "Bus operation timeout";
295 	case 26:	return "A read was issued to data that has been poisoned";
296 	default:	return "Reserved";
297 	}
298 }
299 EXPORT_SYMBOL_GPL(cper_mem_err_status_str);
300 
301 int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
302 {
303 	u32 len, n;
304 
305 	if (!msg)
306 		return 0;
307 
308 	n = 0;
309 	len = CPER_REC_LEN;
310 	if (mem->validation_bits & CPER_MEM_VALID_NODE)
311 		n += scnprintf(msg + n, len - n, "node:%d ", mem->node);
312 	if (mem->validation_bits & CPER_MEM_VALID_CARD)
313 		n += scnprintf(msg + n, len - n, "card:%d ", mem->card);
314 	if (mem->validation_bits & CPER_MEM_VALID_MODULE)
315 		n += scnprintf(msg + n, len - n, "module:%d ", mem->module);
316 	if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
317 		n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank);
318 	if (mem->validation_bits & CPER_MEM_VALID_BANK)
319 		n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank);
320 	if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
321 		n += scnprintf(msg + n, len - n, "bank_group:%d ",
322 			       mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
323 	if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
324 		n += scnprintf(msg + n, len - n, "bank_address:%d ",
325 			       mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
326 	if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
327 		n += scnprintf(msg + n, len - n, "device:%d ", mem->device);
328 	if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
329 		u32 row = mem->row;
330 
331 		row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
332 		n += scnprintf(msg + n, len - n, "row:%d ", row);
333 	}
334 	if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
335 		n += scnprintf(msg + n, len - n, "column:%d ", mem->column);
336 	if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
337 		n += scnprintf(msg + n, len - n, "bit_position:%d ",
338 			       mem->bit_pos);
339 	if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
340 		n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ",
341 			       mem->requestor_id);
342 	if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
343 		n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ",
344 			       mem->responder_id);
345 	if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
346 		n += scnprintf(msg + n, len - n, "target_id:0x%016llx ",
347 			       mem->target_id);
348 	if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
349 		n += scnprintf(msg + n, len - n, "chip_id:%d ",
350 			       mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
351 
352 	return n;
353 }
354 EXPORT_SYMBOL_GPL(cper_mem_err_location);
355 
356 int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
357 {
358 	u32 len, n;
359 	const char *bank = NULL, *device = NULL;
360 
361 	if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
362 		return 0;
363 
364 	len = CPER_REC_LEN;
365 	dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
366 	if (bank && device)
367 		n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
368 	else
369 		n = snprintf(msg, len,
370 			     "DIMM location: not present. DMI handle: 0x%.4x ",
371 			     mem->mem_dev_handle);
372 
373 	return n;
374 }
375 EXPORT_SYMBOL_GPL(cper_dimm_err_location);
376 
377 void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
378 		       struct cper_mem_err_compact *cmem)
379 {
380 	cmem->validation_bits = mem->validation_bits;
381 	cmem->node = mem->node;
382 	cmem->card = mem->card;
383 	cmem->module = mem->module;
384 	cmem->bank = mem->bank;
385 	cmem->device = mem->device;
386 	cmem->row = mem->row;
387 	cmem->column = mem->column;
388 	cmem->bit_pos = mem->bit_pos;
389 	cmem->requestor_id = mem->requestor_id;
390 	cmem->responder_id = mem->responder_id;
391 	cmem->target_id = mem->target_id;
392 	cmem->extended = mem->extended;
393 	cmem->rank = mem->rank;
394 	cmem->mem_array_handle = mem->mem_array_handle;
395 	cmem->mem_dev_handle = mem->mem_dev_handle;
396 }
397 EXPORT_SYMBOL_GPL(cper_mem_err_pack);
398 
399 const char *cper_mem_err_unpack(struct trace_seq *p,
400 				struct cper_mem_err_compact *cmem)
401 {
402 	const char *ret = trace_seq_buffer_ptr(p);
403 	char rcd_decode_str[CPER_REC_LEN];
404 
405 	if (cper_mem_err_location(cmem, rcd_decode_str))
406 		trace_seq_printf(p, "%s", rcd_decode_str);
407 	if (cper_dimm_err_location(cmem, rcd_decode_str))
408 		trace_seq_printf(p, "%s", rcd_decode_str);
409 	trace_seq_putc(p, '\0');
410 
411 	return ret;
412 }
413 
414 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
415 	int len)
416 {
417 	struct cper_mem_err_compact cmem;
418 	char rcd_decode_str[CPER_REC_LEN];
419 
420 	/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
421 	if (len == sizeof(struct cper_sec_mem_err_old) &&
422 	    (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
423 		pr_err(FW_WARN "valid bits set for fields beyond structure\n");
424 		return;
425 	}
426 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
427 		printk("%s error_status: %s (0x%016llx)\n",
428 		       pfx, cper_mem_err_status_str(mem->error_status),
429 		       mem->error_status);
430 	if (mem->validation_bits & CPER_MEM_VALID_PA)
431 		printk("%s""physical_address: 0x%016llx\n",
432 		       pfx, mem->physical_addr);
433 	if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
434 		printk("%s""physical_address_mask: 0x%016llx\n",
435 		       pfx, mem->physical_addr_mask);
436 	cper_mem_err_pack(mem, &cmem);
437 	if (cper_mem_err_location(&cmem, rcd_decode_str))
438 		printk("%s%s\n", pfx, rcd_decode_str);
439 	if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
440 		u8 etype = mem->error_type;
441 		printk("%s""error_type: %d, %s\n", pfx, etype,
442 		       cper_mem_err_type_str(etype));
443 	}
444 	if (cper_dimm_err_location(&cmem, rcd_decode_str))
445 		printk("%s%s\n", pfx, rcd_decode_str);
446 }
447 
448 static const char * const pcie_port_type_strs[] = {
449 	"PCIe end point",
450 	"legacy PCI end point",
451 	"unknown",
452 	"unknown",
453 	"root port",
454 	"upstream switch port",
455 	"downstream switch port",
456 	"PCIe to PCI/PCI-X bridge",
457 	"PCI/PCI-X to PCIe bridge",
458 	"root complex integrated endpoint device",
459 	"root complex event collector",
460 };
461 
462 static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
463 			    const struct acpi_hest_generic_data *gdata)
464 {
465 	if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
466 		printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
467 		       pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
468 		       pcie_port_type_strs[pcie->port_type] : "unknown");
469 	if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
470 		printk("%s""version: %d.%d\n", pfx,
471 		       pcie->version.major, pcie->version.minor);
472 	if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
473 		printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
474 		       pcie->command, pcie->status);
475 	if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
476 		const __u8 *p;
477 		printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
478 		       pcie->device_id.segment, pcie->device_id.bus,
479 		       pcie->device_id.device, pcie->device_id.function);
480 		printk("%s""slot: %d\n", pfx,
481 		       pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
482 		printk("%s""secondary_bus: 0x%02x\n", pfx,
483 		       pcie->device_id.secondary_bus);
484 		printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
485 		       pcie->device_id.vendor_id, pcie->device_id.device_id);
486 		p = pcie->device_id.class_code;
487 		printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
488 	}
489 	if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
490 		printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
491 		       pcie->serial_number.lower, pcie->serial_number.upper);
492 	if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
493 		printk(
494 	"%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
495 	pfx, pcie->bridge.secondary_status, pcie->bridge.control);
496 
497 	/*
498 	 * Print all valid AER info. Record may be from BERT (boot-time) or GHES (run-time).
499 	 *
500 	 * Fatal errors call __ghes_panic() before AER handler prints this.
501 	 */
502 	if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) {
503 		struct aer_capability_regs *aer;
504 
505 		aer = (struct aer_capability_regs *)pcie->aer_info;
506 		printk("%saer_cor_status: 0x%08x, aer_cor_mask: 0x%08x\n",
507 		       pfx, aer->cor_status, aer->cor_mask);
508 		printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
509 		       pfx, aer->uncor_status, aer->uncor_mask);
510 		printk("%saer_uncor_severity: 0x%08x\n",
511 		       pfx, aer->uncor_severity);
512 		printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
513 		       aer->header_log.dw[0], aer->header_log.dw[1],
514 		       aer->header_log.dw[2], aer->header_log.dw[3]);
515 	}
516 }
517 
518 static const char * const fw_err_rec_type_strs[] = {
519 	"IPF SAL Error Record",
520 	"SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
521 	"SOC Firmware Error Record Type2",
522 };
523 
524 static void cper_print_fw_err(const char *pfx,
525 			      struct acpi_hest_generic_data *gdata,
526 			      const struct cper_sec_fw_err_rec_ref *fw_err)
527 {
528 	void *buf = acpi_hest_get_payload(gdata);
529 	u32 offset, length = gdata->error_data_length;
530 
531 	printk("%s""Firmware Error Record Type: %s\n", pfx,
532 	       fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
533 	       fw_err_rec_type_strs[fw_err->record_type] : "unknown");
534 	printk("%s""Revision: %d\n", pfx, fw_err->revision);
535 
536 	/* Record Type based on UEFI 2.7 */
537 	if (fw_err->revision == 0) {
538 		printk("%s""Record Identifier: %08llx\n", pfx,
539 		       fw_err->record_identifier);
540 	} else if (fw_err->revision == 2) {
541 		printk("%s""Record Identifier: %pUl\n", pfx,
542 		       &fw_err->record_identifier_guid);
543 	}
544 
545 	/*
546 	 * The FW error record may contain trailing data beyond the
547 	 * structure defined by the specification. As the fields
548 	 * defined (and hence the offset of any trailing data) vary
549 	 * with the revision, set the offset to account for this
550 	 * variation.
551 	 */
552 	if (fw_err->revision == 0) {
553 		/* record_identifier_guid not defined */
554 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
555 				  record_identifier_guid);
556 	} else if (fw_err->revision == 1) {
557 		/* record_identifier not defined */
558 		offset = offsetof(struct cper_sec_fw_err_rec_ref,
559 				  record_identifier);
560 	} else {
561 		offset = sizeof(*fw_err);
562 	}
563 	if (offset > length) {
564 		printk("%s""error section length is too small: offset=%d, length=%d\n",
565 		       pfx, offset, length);
566 		return;
567 	}
568 
569 	buf += offset;
570 	length -= offset;
571 
572 	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
573 }
574 
575 static void cper_print_tstamp(const char *pfx,
576 				   struct acpi_hest_generic_data_v300 *gdata)
577 {
578 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
579 
580 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
581 		timestamp = (__u8 *)&(gdata->time_stamp);
582 		sec       = bcd2bin(timestamp[0]);
583 		min       = bcd2bin(timestamp[1]);
584 		hour      = bcd2bin(timestamp[2]);
585 		day       = bcd2bin(timestamp[4]);
586 		mon       = bcd2bin(timestamp[5]);
587 		year      = bcd2bin(timestamp[6]);
588 		century   = bcd2bin(timestamp[7]);
589 
590 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
591 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
592 		       century, year, mon, day, hour, min, sec);
593 	}
594 }
595 
596 struct ignore_section {
597 	guid_t guid;
598 	const char *name;
599 };
600 
601 static const struct ignore_section ignore_sections[] = {
602 	{ .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" },
603 	{ .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" },
604 	{ .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" },
605 };
606 
607 static void
608 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
609 			   int sec_no)
610 {
611 	guid_t *sec_type = (guid_t *)gdata->section_type;
612 	__u16 severity;
613 	char newpfx[64];
614 
615 	if (acpi_hest_get_version(gdata) >= 3)
616 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
617 
618 	severity = gdata->error_severity;
619 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
620 	       cper_severity_str(severity));
621 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
622 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
623 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
624 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
625 
626 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
627 
628 	for (int i = 0; i < ARRAY_SIZE(ignore_sections); i++) {
629 		if (guid_equal(sec_type, &ignore_sections[i].guid)) {
630 			printk("%ssection_type: %s\n", newpfx, ignore_sections[i].name);
631 			return;
632 		}
633 	}
634 
635 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
636 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
637 
638 		printk("%s""section_type: general processor error\n", newpfx);
639 		if (gdata->error_data_length >= sizeof(*proc_err))
640 			cper_print_proc_generic(newpfx, proc_err);
641 		else
642 			goto err_section_too_small;
643 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
644 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
645 
646 		printk("%s""section_type: memory error\n", newpfx);
647 		if (gdata->error_data_length >=
648 		    sizeof(struct cper_sec_mem_err_old))
649 			cper_print_mem(newpfx, mem_err,
650 				       gdata->error_data_length);
651 		else
652 			goto err_section_too_small;
653 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
654 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
655 
656 		printk("%s""section_type: PCIe error\n", newpfx);
657 		if (gdata->error_data_length >= sizeof(*pcie))
658 			cper_print_pcie(newpfx, pcie, gdata);
659 		else
660 			goto err_section_too_small;
661 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
662 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
663 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
664 
665 		printk("%ssection_type: ARM processor error\n", newpfx);
666 		if (gdata->error_data_length >= sizeof(*arm_err))
667 			cper_print_proc_arm(newpfx, arm_err,
668 					    gdata->error_data_length);
669 		else
670 			goto err_section_too_small;
671 #endif
672 #if defined(CONFIG_UEFI_CPER_X86)
673 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
674 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
675 
676 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
677 		if (gdata->error_data_length >= sizeof(*ia_err))
678 			cper_print_proc_ia(newpfx, ia_err);
679 		else
680 			goto err_section_too_small;
681 #endif
682 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
683 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
684 
685 		printk("%ssection_type: Firmware Error Record Reference\n",
686 		       newpfx);
687 		/* The minimal FW Error Record contains 16 bytes */
688 		if (gdata->error_data_length >= SZ_16)
689 			cper_print_fw_err(newpfx, gdata, fw_err);
690 		else
691 			goto err_section_too_small;
692 	} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
693 		struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
694 
695 		printk("%ssection_type: CXL Protocol Error\n", newpfx);
696 		if (gdata->error_data_length >= sizeof(*prot_err))
697 			cxl_cper_print_prot_err(newpfx, prot_err);
698 		else
699 			goto err_section_too_small;
700 	} else {
701 		const void *err = acpi_hest_get_payload(gdata);
702 
703 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
704 		printk("%ssection length: %#x\n", newpfx,
705 		       gdata->error_data_length);
706 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
707 			       gdata->error_data_length, true);
708 	}
709 
710 	return;
711 
712 err_section_too_small:
713 	pr_err(FW_WARN "error section length is too small\n");
714 }
715 
716 void cper_estatus_print(const char *pfx,
717 			const struct acpi_hest_generic_status *estatus)
718 {
719 	struct acpi_hest_generic_data *gdata;
720 	int sec_no = 0;
721 	char newpfx[64];
722 	__u16 severity;
723 
724 	severity = estatus->error_severity;
725 	if (severity == CPER_SEV_CORRECTED)
726 		printk("%s%s\n", pfx,
727 		       "It has been corrected by h/w "
728 		       "and requires no further action");
729 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
730 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
731 
732 	apei_estatus_for_each_section(estatus, gdata) {
733 		cper_estatus_print_section(newpfx, gdata, sec_no);
734 		sec_no++;
735 	}
736 }
737 EXPORT_SYMBOL_GPL(cper_estatus_print);
738 
739 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
740 {
741 	if (estatus->data_length &&
742 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
743 		return -EINVAL;
744 	if (estatus->raw_data_length &&
745 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
746 		return -EINVAL;
747 
748 	return 0;
749 }
750 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
751 
752 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
753 {
754 	struct acpi_hest_generic_data *gdata;
755 	unsigned int data_len, record_size;
756 	int rc;
757 
758 	rc = cper_estatus_check_header(estatus);
759 	if (rc)
760 		return rc;
761 
762 	data_len = estatus->data_length;
763 
764 	apei_estatus_for_each_section(estatus, gdata) {
765 		if (acpi_hest_get_size(gdata) > data_len)
766 			return -EINVAL;
767 
768 		record_size = acpi_hest_get_record_size(gdata);
769 		if (record_size > data_len)
770 			return -EINVAL;
771 
772 		data_len -= record_size;
773 	}
774 	if (data_len)
775 		return -EINVAL;
776 
777 	return 0;
778 }
779 EXPORT_SYMBOL_GPL(cper_estatus_check);
780