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