File Index
Files and directories
Sign in
FILE

cper.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/efi/cper.c C
  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 */
 35u64 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}
 57EXPORT_SYMBOL_GPL(cper_next_record_id);
 58
 59static const char * const severity_strs[] = {
 60	"recoverable",
 61	"fatal",
 62	"corrected",
 63	"info",
 64};
 65
 66const char *cper_severity_str(unsigned int severity)
 67{
 68	return severity < ARRAY_SIZE(severity_strs) ?
 69		severity_strs[severity] : "unknown";
 70}
 71EXPORT_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 */
 84void 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 */
139int 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}
167EXPORT_SYMBOL_GPL(cper_bits_to_str);
168
169static const char * const proc_type_strs[] = {
170	"IA32/X64",
171	"IA64",
172	"ARM",
173};
174
175static const char * const proc_isa_strs[] = {
176	"IA32",
177	"IA64",
178	"X64",
179	"ARM A32/T32",
180	"ARM A64",
181};
182
183const char * const cper_proc_error_type_strs[] = {
184	"cache error",
185	"TLB error",
186	"bus error",
187	"micro-architectural error",
188};
189
190static const char * const proc_op_strs[] = {
191	"unknown or generic",
192	"data read",
193	"data write",
194	"instruction execution",
195};
196
197static const char * const proc_flag_strs[] = {
198	"restartable",
199	"precise IP",
200	"overflow",
201	"corrected",
202};
203
204static 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
249static 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
268const 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}
273EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
274
275const 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}
299EXPORT_SYMBOL_GPL(cper_mem_err_status_str);
300
301int 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}
354EXPORT_SYMBOL_GPL(cper_mem_err_location);
355
356int 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}
375EXPORT_SYMBOL_GPL(cper_dimm_err_location);
376
377void 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}
397EXPORT_SYMBOL_GPL(cper_mem_err_pack);
398
399const 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
414static 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
448static 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
462static 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
518static 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
524static 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
575static 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
596struct ignore_section {
597	guid_t guid;
598	const char *name;
599};
600
601static 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
607static void
608cper_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
712err_section_too_small:
713	pr_err(FW_WARN "error section length is too small\n");
714}
715
716void 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}
737EXPORT_SYMBOL_GPL(cper_estatus_print);
738
739int 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}
750EXPORT_SYMBOL_GPL(cper_estatus_check_header);
751
752int 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}
779EXPORT_SYMBOL_GPL(cper_estatus_check);
~/ Read-only file preview.