xref: /linux/drivers/firmware/efi/cper.c (revision b1dd1e2f3e4ed970949ab4bb982bb0165f3e979d)
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 len - buf_size;
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 
564 	buf += offset;
565 	length -= offset;
566 
567 	print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
568 }
569 
570 static void cper_print_tstamp(const char *pfx,
571 				   struct acpi_hest_generic_data_v300 *gdata)
572 {
573 	__u8 hour, min, sec, day, mon, year, century, *timestamp;
574 
575 	if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
576 		timestamp = (__u8 *)&(gdata->time_stamp);
577 		sec       = bcd2bin(timestamp[0]);
578 		min       = bcd2bin(timestamp[1]);
579 		hour      = bcd2bin(timestamp[2]);
580 		day       = bcd2bin(timestamp[4]);
581 		mon       = bcd2bin(timestamp[5]);
582 		year      = bcd2bin(timestamp[6]);
583 		century   = bcd2bin(timestamp[7]);
584 
585 		printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
586 		       (timestamp[3] & 0x1 ? "precise " : "imprecise "),
587 		       century, year, mon, day, hour, min, sec);
588 	}
589 }
590 
591 struct ignore_section {
592 	guid_t guid;
593 	const char *name;
594 };
595 
596 static const struct ignore_section ignore_sections[] = {
597 	{ .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" },
598 	{ .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" },
599 	{ .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" },
600 };
601 
602 static void
603 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
604 			   int sec_no)
605 {
606 	guid_t *sec_type = (guid_t *)gdata->section_type;
607 	__u16 severity;
608 	char newpfx[64];
609 
610 	if (acpi_hest_get_version(gdata) >= 3)
611 		cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
612 
613 	severity = gdata->error_severity;
614 	printk("%s""Error %d, type: %s\n", pfx, sec_no,
615 	       cper_severity_str(severity));
616 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
617 		printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
618 	if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
619 		printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
620 
621 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
622 
623 	for (int i = 0; i < ARRAY_SIZE(ignore_sections); i++) {
624 		if (guid_equal(sec_type, &ignore_sections[i].guid)) {
625 			printk("%ssection_type: %s\n", newpfx, ignore_sections[i].name);
626 			return;
627 		}
628 	}
629 
630 	if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
631 		struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
632 
633 		printk("%s""section_type: general processor error\n", newpfx);
634 		if (gdata->error_data_length >= sizeof(*proc_err))
635 			cper_print_proc_generic(newpfx, proc_err);
636 		else
637 			goto err_section_too_small;
638 	} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
639 		struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
640 
641 		printk("%s""section_type: memory error\n", newpfx);
642 		if (gdata->error_data_length >=
643 		    sizeof(struct cper_sec_mem_err_old))
644 			cper_print_mem(newpfx, mem_err,
645 				       gdata->error_data_length);
646 		else
647 			goto err_section_too_small;
648 	} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
649 		struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
650 
651 		printk("%s""section_type: PCIe error\n", newpfx);
652 		if (gdata->error_data_length >= sizeof(*pcie))
653 			cper_print_pcie(newpfx, pcie, gdata);
654 		else
655 			goto err_section_too_small;
656 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
657 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
658 		struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
659 
660 		printk("%ssection_type: ARM processor error\n", newpfx);
661 		if (gdata->error_data_length >= sizeof(*arm_err))
662 			cper_print_proc_arm(newpfx, arm_err);
663 		else
664 			goto err_section_too_small;
665 #endif
666 #if defined(CONFIG_UEFI_CPER_X86)
667 	} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
668 		struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
669 
670 		printk("%ssection_type: IA32/X64 processor error\n", newpfx);
671 		if (gdata->error_data_length >= sizeof(*ia_err))
672 			cper_print_proc_ia(newpfx, ia_err);
673 		else
674 			goto err_section_too_small;
675 #endif
676 	} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
677 		struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
678 
679 		printk("%ssection_type: Firmware Error Record Reference\n",
680 		       newpfx);
681 		/* The minimal FW Error Record contains 16 bytes */
682 		if (gdata->error_data_length >= SZ_16)
683 			cper_print_fw_err(newpfx, gdata, fw_err);
684 		else
685 			goto err_section_too_small;
686 	} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
687 		struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
688 
689 		printk("%ssection_type: CXL Protocol Error\n", newpfx);
690 		if (gdata->error_data_length >= sizeof(*prot_err))
691 			cxl_cper_print_prot_err(newpfx, prot_err);
692 		else
693 			goto err_section_too_small;
694 	} else {
695 		const void *err = acpi_hest_get_payload(gdata);
696 
697 		printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
698 		printk("%ssection length: %#x\n", newpfx,
699 		       gdata->error_data_length);
700 		print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
701 			       gdata->error_data_length, true);
702 	}
703 
704 	return;
705 
706 err_section_too_small:
707 	pr_err(FW_WARN "error section length is too small\n");
708 }
709 
710 void cper_estatus_print(const char *pfx,
711 			const struct acpi_hest_generic_status *estatus)
712 {
713 	struct acpi_hest_generic_data *gdata;
714 	int sec_no = 0;
715 	char newpfx[64];
716 	__u16 severity;
717 
718 	severity = estatus->error_severity;
719 	if (severity == CPER_SEV_CORRECTED)
720 		printk("%s%s\n", pfx,
721 		       "It has been corrected by h/w "
722 		       "and requires no further action");
723 	printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
724 	snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
725 
726 	apei_estatus_for_each_section(estatus, gdata) {
727 		cper_estatus_print_section(newpfx, gdata, sec_no);
728 		sec_no++;
729 	}
730 }
731 EXPORT_SYMBOL_GPL(cper_estatus_print);
732 
733 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
734 {
735 	if (estatus->data_length &&
736 	    estatus->data_length < sizeof(struct acpi_hest_generic_data))
737 		return -EINVAL;
738 	if (estatus->raw_data_length &&
739 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
740 		return -EINVAL;
741 
742 	return 0;
743 }
744 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
745 
746 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
747 {
748 	struct acpi_hest_generic_data *gdata;
749 	unsigned int data_len, record_size;
750 	int rc;
751 
752 	rc = cper_estatus_check_header(estatus);
753 	if (rc)
754 		return rc;
755 
756 	data_len = estatus->data_length;
757 
758 	apei_estatus_for_each_section(estatus, gdata) {
759 		if (acpi_hest_get_size(gdata) > data_len)
760 			return -EINVAL;
761 
762 		record_size = acpi_hest_get_record_size(gdata);
763 		if (record_size > data_len)
764 			return -EINVAL;
765 
766 		data_len -= record_size;
767 	}
768 	if (data_len)
769 		return -EINVAL;
770 
771 	return 0;
772 }
773 EXPORT_SYMBOL_GPL(cper_estatus_check);
774