Merge tag 'dt-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / drivers / firmware / efi / cper.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/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 static char rcd_decode_str[CPER_REC_LEN];
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 static const char * const proc_type_strs[] = {
111         "IA32/X64",
112         "IA64",
113         "ARM",
114 };
115
116 static const char * const proc_isa_strs[] = {
117         "IA32",
118         "IA64",
119         "X64",
120         "ARM A32/T32",
121         "ARM A64",
122 };
123
124 const char * const cper_proc_error_type_strs[] = {
125         "cache error",
126         "TLB error",
127         "bus error",
128         "micro-architectural error",
129 };
130
131 static const char * const proc_op_strs[] = {
132         "unknown or generic",
133         "data read",
134         "data write",
135         "instruction execution",
136 };
137
138 static const char * const proc_flag_strs[] = {
139         "restartable",
140         "precise IP",
141         "overflow",
142         "corrected",
143 };
144
145 static void cper_print_proc_generic(const char *pfx,
146                                     const struct cper_sec_proc_generic *proc)
147 {
148         if (proc->validation_bits & CPER_PROC_VALID_TYPE)
149                 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
150                        proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
151                        proc_type_strs[proc->proc_type] : "unknown");
152         if (proc->validation_bits & CPER_PROC_VALID_ISA)
153                 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
154                        proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
155                        proc_isa_strs[proc->proc_isa] : "unknown");
156         if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
157                 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
158                 cper_print_bits(pfx, proc->proc_error_type,
159                                 cper_proc_error_type_strs,
160                                 ARRAY_SIZE(cper_proc_error_type_strs));
161         }
162         if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
163                 printk("%s""operation: %d, %s\n", pfx, proc->operation,
164                        proc->operation < ARRAY_SIZE(proc_op_strs) ?
165                        proc_op_strs[proc->operation] : "unknown");
166         if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
167                 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
168                 cper_print_bits(pfx, proc->flags, proc_flag_strs,
169                                 ARRAY_SIZE(proc_flag_strs));
170         }
171         if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
172                 printk("%s""level: %d\n", pfx, proc->level);
173         if (proc->validation_bits & CPER_PROC_VALID_VERSION)
174                 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
175         if (proc->validation_bits & CPER_PROC_VALID_ID)
176                 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
177         if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
178                 printk("%s""target_address: 0x%016llx\n",
179                        pfx, proc->target_addr);
180         if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
181                 printk("%s""requestor_id: 0x%016llx\n",
182                        pfx, proc->requestor_id);
183         if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
184                 printk("%s""responder_id: 0x%016llx\n",
185                        pfx, proc->responder_id);
186         if (proc->validation_bits & CPER_PROC_VALID_IP)
187                 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
188 }
189
190 static const char * const mem_err_type_strs[] = {
191         "unknown",
192         "no error",
193         "single-bit ECC",
194         "multi-bit ECC",
195         "single-symbol chipkill ECC",
196         "multi-symbol chipkill ECC",
197         "master abort",
198         "target abort",
199         "parity error",
200         "watchdog timeout",
201         "invalid address",
202         "mirror Broken",
203         "memory sparing",
204         "scrub corrected error",
205         "scrub uncorrected error",
206         "physical memory map-out event",
207 };
208
209 const char *cper_mem_err_type_str(unsigned int etype)
210 {
211         return etype < ARRAY_SIZE(mem_err_type_strs) ?
212                 mem_err_type_strs[etype] : "unknown";
213 }
214 EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
215
216 static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
217 {
218         u32 len, n;
219
220         if (!msg)
221                 return 0;
222
223         n = 0;
224         len = CPER_REC_LEN;
225         if (mem->validation_bits & CPER_MEM_VALID_NODE)
226                 n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
227         if (mem->validation_bits & CPER_MEM_VALID_CARD)
228                 n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
229         if (mem->validation_bits & CPER_MEM_VALID_MODULE)
230                 n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
231         if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
232                 n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
233         if (mem->validation_bits & CPER_MEM_VALID_BANK)
234                 n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
235         if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
236                 n += scnprintf(msg + n, len - n, "bank_group: %d ",
237                                mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
238         if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
239                 n += scnprintf(msg + n, len - n, "bank_address: %d ",
240                                mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
241         if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
242                 n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
243         if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
244                 u32 row = mem->row;
245
246                 row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
247                 n += scnprintf(msg + n, len - n, "row: %d ", row);
248         }
249         if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
250                 n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
251         if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
252                 n += scnprintf(msg + n, len - n, "bit_position: %d ",
253                                mem->bit_pos);
254         if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
255                 n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
256                                mem->requestor_id);
257         if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
258                 n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
259                                mem->responder_id);
260         if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
261                 n += scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
262                                mem->target_id);
263         if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
264                 n += scnprintf(msg + n, len - n, "chip_id: %d ",
265                                mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
266
267         return n;
268 }
269
270 static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
271 {
272         u32 len, n;
273         const char *bank = NULL, *device = NULL;
274
275         if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
276                 return 0;
277
278         len = CPER_REC_LEN;
279         dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
280         if (bank && device)
281                 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
282         else
283                 n = snprintf(msg, len,
284                              "DIMM location: not present. DMI handle: 0x%.4x ",
285                              mem->mem_dev_handle);
286
287         return n;
288 }
289
290 void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
291                        struct cper_mem_err_compact *cmem)
292 {
293         cmem->validation_bits = mem->validation_bits;
294         cmem->node = mem->node;
295         cmem->card = mem->card;
296         cmem->module = mem->module;
297         cmem->bank = mem->bank;
298         cmem->device = mem->device;
299         cmem->row = mem->row;
300         cmem->column = mem->column;
301         cmem->bit_pos = mem->bit_pos;
302         cmem->requestor_id = mem->requestor_id;
303         cmem->responder_id = mem->responder_id;
304         cmem->target_id = mem->target_id;
305         cmem->extended = mem->extended;
306         cmem->rank = mem->rank;
307         cmem->mem_array_handle = mem->mem_array_handle;
308         cmem->mem_dev_handle = mem->mem_dev_handle;
309 }
310
311 const char *cper_mem_err_unpack(struct trace_seq *p,
312                                 struct cper_mem_err_compact *cmem)
313 {
314         const char *ret = trace_seq_buffer_ptr(p);
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
325 static 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
330         /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
331         if (len == sizeof(struct cper_sec_mem_err_old) &&
332             (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
333                 pr_err(FW_WARN "valid bits set for fields beyond structure\n");
334                 return;
335         }
336         if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
337                 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
338         if (mem->validation_bits & CPER_MEM_VALID_PA)
339                 printk("%s""physical_address: 0x%016llx\n",
340                        pfx, mem->physical_addr);
341         if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
342                 printk("%s""physical_address_mask: 0x%016llx\n",
343                        pfx, mem->physical_addr_mask);
344         cper_mem_err_pack(mem, &cmem);
345         if (cper_mem_err_location(&cmem, rcd_decode_str))
346                 printk("%s%s\n", pfx, rcd_decode_str);
347         if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
348                 u8 etype = mem->error_type;
349                 printk("%s""error_type: %d, %s\n", pfx, etype,
350                        cper_mem_err_type_str(etype));
351         }
352         if (cper_dimm_err_location(&cmem, rcd_decode_str))
353                 printk("%s%s\n", pfx, rcd_decode_str);
354 }
355
356 static const char * const pcie_port_type_strs[] = {
357         "PCIe end point",
358         "legacy PCI end point",
359         "unknown",
360         "unknown",
361         "root port",
362         "upstream switch port",
363         "downstream switch port",
364         "PCIe to PCI/PCI-X bridge",
365         "PCI/PCI-X to PCIe bridge",
366         "root complex integrated endpoint device",
367         "root complex event collector",
368 };
369
370 static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
371                             const struct acpi_hest_generic_data *gdata)
372 {
373         if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
374                 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
375                        pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
376                        pcie_port_type_strs[pcie->port_type] : "unknown");
377         if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
378                 printk("%s""version: %d.%d\n", pfx,
379                        pcie->version.major, pcie->version.minor);
380         if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
381                 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
382                        pcie->command, pcie->status);
383         if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
384                 const __u8 *p;
385                 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
386                        pcie->device_id.segment, pcie->device_id.bus,
387                        pcie->device_id.device, pcie->device_id.function);
388                 printk("%s""slot: %d\n", pfx,
389                        pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
390                 printk("%s""secondary_bus: 0x%02x\n", pfx,
391                        pcie->device_id.secondary_bus);
392                 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
393                        pcie->device_id.vendor_id, pcie->device_id.device_id);
394                 p = pcie->device_id.class_code;
395                 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
396         }
397         if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
398                 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
399                        pcie->serial_number.lower, pcie->serial_number.upper);
400         if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
401                 printk(
402         "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
403         pfx, pcie->bridge.secondary_status, pcie->bridge.control);
404
405         /* Fatal errors call __ghes_panic() before AER handler prints this */
406         if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
407             (gdata->error_severity & CPER_SEV_FATAL)) {
408                 struct aer_capability_regs *aer;
409
410                 aer = (struct aer_capability_regs *)pcie->aer_info;
411                 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
412                        pfx, aer->uncor_status, aer->uncor_mask);
413                 printk("%saer_uncor_severity: 0x%08x\n",
414                        pfx, aer->uncor_severity);
415                 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
416                        aer->header_log.dw0, aer->header_log.dw1,
417                        aer->header_log.dw2, aer->header_log.dw3);
418         }
419 }
420
421 static const char * const fw_err_rec_type_strs[] = {
422         "IPF SAL Error Record",
423         "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
424         "SOC Firmware Error Record Type2",
425 };
426
427 static void cper_print_fw_err(const char *pfx,
428                               struct acpi_hest_generic_data *gdata,
429                               const struct cper_sec_fw_err_rec_ref *fw_err)
430 {
431         void *buf = acpi_hest_get_payload(gdata);
432         u32 offset, length = gdata->error_data_length;
433
434         printk("%s""Firmware Error Record Type: %s\n", pfx,
435                fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
436                fw_err_rec_type_strs[fw_err->record_type] : "unknown");
437         printk("%s""Revision: %d\n", pfx, fw_err->revision);
438
439         /* Record Type based on UEFI 2.7 */
440         if (fw_err->revision == 0) {
441                 printk("%s""Record Identifier: %08llx\n", pfx,
442                        fw_err->record_identifier);
443         } else if (fw_err->revision == 2) {
444                 printk("%s""Record Identifier: %pUl\n", pfx,
445                        &fw_err->record_identifier_guid);
446         }
447
448         /*
449          * The FW error record may contain trailing data beyond the
450          * structure defined by the specification. As the fields
451          * defined (and hence the offset of any trailing data) vary
452          * with the revision, set the offset to account for this
453          * variation.
454          */
455         if (fw_err->revision == 0) {
456                 /* record_identifier_guid not defined */
457                 offset = offsetof(struct cper_sec_fw_err_rec_ref,
458                                   record_identifier_guid);
459         } else if (fw_err->revision == 1) {
460                 /* record_identifier not defined */
461                 offset = offsetof(struct cper_sec_fw_err_rec_ref,
462                                   record_identifier);
463         } else {
464                 offset = sizeof(*fw_err);
465         }
466
467         buf += offset;
468         length -= offset;
469
470         print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
471 }
472
473 static void cper_print_tstamp(const char *pfx,
474                                    struct acpi_hest_generic_data_v300 *gdata)
475 {
476         __u8 hour, min, sec, day, mon, year, century, *timestamp;
477
478         if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
479                 timestamp = (__u8 *)&(gdata->time_stamp);
480                 sec       = bcd2bin(timestamp[0]);
481                 min       = bcd2bin(timestamp[1]);
482                 hour      = bcd2bin(timestamp[2]);
483                 day       = bcd2bin(timestamp[4]);
484                 mon       = bcd2bin(timestamp[5]);
485                 year      = bcd2bin(timestamp[6]);
486                 century   = bcd2bin(timestamp[7]);
487
488                 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
489                        (timestamp[3] & 0x1 ? "precise " : "imprecise "),
490                        century, year, mon, day, hour, min, sec);
491         }
492 }
493
494 static void
495 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
496                            int sec_no)
497 {
498         guid_t *sec_type = (guid_t *)gdata->section_type;
499         __u16 severity;
500         char newpfx[64];
501
502         if (acpi_hest_get_version(gdata) >= 3)
503                 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
504
505         severity = gdata->error_severity;
506         printk("%s""Error %d, type: %s\n", pfx, sec_no,
507                cper_severity_str(severity));
508         if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
509                 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
510         if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
511                 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
512
513         snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
514         if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
515                 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
516
517                 printk("%s""section_type: general processor error\n", newpfx);
518                 if (gdata->error_data_length >= sizeof(*proc_err))
519                         cper_print_proc_generic(newpfx, proc_err);
520                 else
521                         goto err_section_too_small;
522         } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
523                 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
524
525                 printk("%s""section_type: memory error\n", newpfx);
526                 if (gdata->error_data_length >=
527                     sizeof(struct cper_sec_mem_err_old))
528                         cper_print_mem(newpfx, mem_err,
529                                        gdata->error_data_length);
530                 else
531                         goto err_section_too_small;
532         } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
533                 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
534
535                 printk("%s""section_type: PCIe error\n", newpfx);
536                 if (gdata->error_data_length >= sizeof(*pcie))
537                         cper_print_pcie(newpfx, pcie, gdata);
538                 else
539                         goto err_section_too_small;
540 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
541         } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
542                 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
543
544                 printk("%ssection_type: ARM processor error\n", newpfx);
545                 if (gdata->error_data_length >= sizeof(*arm_err))
546                         cper_print_proc_arm(newpfx, arm_err);
547                 else
548                         goto err_section_too_small;
549 #endif
550 #if defined(CONFIG_UEFI_CPER_X86)
551         } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
552                 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
553
554                 printk("%ssection_type: IA32/X64 processor error\n", newpfx);
555                 if (gdata->error_data_length >= sizeof(*ia_err))
556                         cper_print_proc_ia(newpfx, ia_err);
557                 else
558                         goto err_section_too_small;
559 #endif
560         } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
561                 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
562
563                 printk("%ssection_type: Firmware Error Record Reference\n",
564                        newpfx);
565                 /* The minimal FW Error Record contains 16 bytes */
566                 if (gdata->error_data_length >= SZ_16)
567                         cper_print_fw_err(newpfx, gdata, fw_err);
568                 else
569                         goto err_section_too_small;
570         } else {
571                 const void *err = acpi_hest_get_payload(gdata);
572
573                 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
574                 printk("%ssection length: %#x\n", newpfx,
575                        gdata->error_data_length);
576                 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
577                                gdata->error_data_length, true);
578         }
579
580         return;
581
582 err_section_too_small:
583         pr_err(FW_WARN "error section length is too small\n");
584 }
585
586 void cper_estatus_print(const char *pfx,
587                         const struct acpi_hest_generic_status *estatus)
588 {
589         struct acpi_hest_generic_data *gdata;
590         int sec_no = 0;
591         char newpfx[64];
592         __u16 severity;
593
594         severity = estatus->error_severity;
595         if (severity == CPER_SEV_CORRECTED)
596                 printk("%s%s\n", pfx,
597                        "It has been corrected by h/w "
598                        "and requires no further action");
599         printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
600         snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
601
602         apei_estatus_for_each_section(estatus, gdata) {
603                 cper_estatus_print_section(newpfx, gdata, sec_no);
604                 sec_no++;
605         }
606 }
607 EXPORT_SYMBOL_GPL(cper_estatus_print);
608
609 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
610 {
611         if (estatus->data_length &&
612             estatus->data_length < sizeof(struct acpi_hest_generic_data))
613                 return -EINVAL;
614         if (estatus->raw_data_length &&
615             estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
616                 return -EINVAL;
617
618         return 0;
619 }
620 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
621
622 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
623 {
624         struct acpi_hest_generic_data *gdata;
625         unsigned int data_len, record_size;
626         int rc;
627
628         rc = cper_estatus_check_header(estatus);
629         if (rc)
630                 return rc;
631
632         data_len = estatus->data_length;
633
634         apei_estatus_for_each_section(estatus, gdata) {
635                 if (acpi_hest_get_size(gdata) > data_len)
636                         return -EINVAL;
637
638                 record_size = acpi_hest_get_record_size(gdata);
639                 if (record_size > data_len)
640                         return -EINVAL;
641
642                 data_len -= record_size;
643         }
644         if (data_len)
645                 return -EINVAL;
646
647         return 0;
648 }
649 EXPORT_SYMBOL_GPL(cper_estatus_check);