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