GNU Linux-libre 5.10.153-gnu1
[releases.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 /*
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  */
33 u64 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 }
55 EXPORT_SYMBOL_GPL(cper_next_record_id);
56
57 static const char * const severity_strs[] = {
58         "recoverable",
59         "fatal",
60         "corrected",
61         "info",
62 };
63
64 const char *cper_severity_str(unsigned int severity)
65 {
66         return severity < ARRAY_SIZE(severity_strs) ?
67                 severity_strs[severity] : "unknown";
68 }
69 EXPORT_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  */
82 void 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
108 static const char * const proc_type_strs[] = {
109         "IA32/X64",
110         "IA64",
111         "ARM",
112 };
113
114 static const char * const proc_isa_strs[] = {
115         "IA32",
116         "IA64",
117         "X64",
118         "ARM A32/T32",
119         "ARM A64",
120 };
121
122 const char * const cper_proc_error_type_strs[] = {
123         "cache error",
124         "TLB error",
125         "bus error",
126         "micro-architectural error",
127 };
128
129 static const char * const proc_op_strs[] = {
130         "unknown or generic",
131         "data read",
132         "data write",
133         "instruction execution",
134 };
135
136 static const char * const proc_flag_strs[] = {
137         "restartable",
138         "precise IP",
139         "overflow",
140         "corrected",
141 };
142
143 static 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
188 static 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
207 const 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 }
212 EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
213
214 static 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
269 static 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
289 void 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
310 const 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
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         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
357 static 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
371 static 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
422 static 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
428 static 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
474 static 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
495 static void
496 cper_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
583 err_section_too_small:
584         pr_err(FW_WARN "error section length is too small\n");
585 }
586
587 void 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 }
608 EXPORT_SYMBOL_GPL(cper_estatus_print);
609
610 int 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 }
621 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
622
623 int 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 }
650 EXPORT_SYMBOL_GPL(cper_estatus_check);