35c37f667781c7071c714aef274e68dbddca026b
[sfrench/cifs-2.6.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 #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  */
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
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  */
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
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
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
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
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
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
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
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
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
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         /* Fatal errors call __ghes_panic() before AER handler prints this */
438         if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
439             (gdata->error_severity & CPER_SEV_FATAL)) {
440                 struct aer_capability_regs *aer;
441
442                 aer = (struct aer_capability_regs *)pcie->aer_info;
443                 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
444                        pfx, aer->uncor_status, aer->uncor_mask);
445                 printk("%saer_uncor_severity: 0x%08x\n",
446                        pfx, aer->uncor_severity);
447                 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
448                        aer->header_log.dw0, aer->header_log.dw1,
449                        aer->header_log.dw2, aer->header_log.dw3);
450         }
451 }
452
453 static const char * const fw_err_rec_type_strs[] = {
454         "IPF SAL Error Record",
455         "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
456         "SOC Firmware Error Record Type2",
457 };
458
459 static void cper_print_fw_err(const char *pfx,
460                               struct acpi_hest_generic_data *gdata,
461                               const struct cper_sec_fw_err_rec_ref *fw_err)
462 {
463         void *buf = acpi_hest_get_payload(gdata);
464         u32 offset, length = gdata->error_data_length;
465
466         printk("%s""Firmware Error Record Type: %s\n", pfx,
467                fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
468                fw_err_rec_type_strs[fw_err->record_type] : "unknown");
469         printk("%s""Revision: %d\n", pfx, fw_err->revision);
470
471         /* Record Type based on UEFI 2.7 */
472         if (fw_err->revision == 0) {
473                 printk("%s""Record Identifier: %08llx\n", pfx,
474                        fw_err->record_identifier);
475         } else if (fw_err->revision == 2) {
476                 printk("%s""Record Identifier: %pUl\n", pfx,
477                        &fw_err->record_identifier_guid);
478         }
479
480         /*
481          * The FW error record may contain trailing data beyond the
482          * structure defined by the specification. As the fields
483          * defined (and hence the offset of any trailing data) vary
484          * with the revision, set the offset to account for this
485          * variation.
486          */
487         if (fw_err->revision == 0) {
488                 /* record_identifier_guid not defined */
489                 offset = offsetof(struct cper_sec_fw_err_rec_ref,
490                                   record_identifier_guid);
491         } else if (fw_err->revision == 1) {
492                 /* record_identifier not defined */
493                 offset = offsetof(struct cper_sec_fw_err_rec_ref,
494                                   record_identifier);
495         } else {
496                 offset = sizeof(*fw_err);
497         }
498
499         buf += offset;
500         length -= offset;
501
502         print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
503 }
504
505 static void cper_print_tstamp(const char *pfx,
506                                    struct acpi_hest_generic_data_v300 *gdata)
507 {
508         __u8 hour, min, sec, day, mon, year, century, *timestamp;
509
510         if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
511                 timestamp = (__u8 *)&(gdata->time_stamp);
512                 sec       = bcd2bin(timestamp[0]);
513                 min       = bcd2bin(timestamp[1]);
514                 hour      = bcd2bin(timestamp[2]);
515                 day       = bcd2bin(timestamp[4]);
516                 mon       = bcd2bin(timestamp[5]);
517                 year      = bcd2bin(timestamp[6]);
518                 century   = bcd2bin(timestamp[7]);
519
520                 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
521                        (timestamp[3] & 0x1 ? "precise " : "imprecise "),
522                        century, year, mon, day, hour, min, sec);
523         }
524 }
525
526 static void
527 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
528                            int sec_no)
529 {
530         guid_t *sec_type = (guid_t *)gdata->section_type;
531         __u16 severity;
532         char newpfx[64];
533
534         if (acpi_hest_get_version(gdata) >= 3)
535                 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
536
537         severity = gdata->error_severity;
538         printk("%s""Error %d, type: %s\n", pfx, sec_no,
539                cper_severity_str(severity));
540         if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
541                 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
542         if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
543                 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
544
545         snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
546         if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
547                 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
548
549                 printk("%s""section_type: general processor error\n", newpfx);
550                 if (gdata->error_data_length >= sizeof(*proc_err))
551                         cper_print_proc_generic(newpfx, proc_err);
552                 else
553                         goto err_section_too_small;
554         } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
555                 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
556
557                 printk("%s""section_type: memory error\n", newpfx);
558                 if (gdata->error_data_length >=
559                     sizeof(struct cper_sec_mem_err_old))
560                         cper_print_mem(newpfx, mem_err,
561                                        gdata->error_data_length);
562                 else
563                         goto err_section_too_small;
564         } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
565                 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
566
567                 printk("%s""section_type: PCIe error\n", newpfx);
568                 if (gdata->error_data_length >= sizeof(*pcie))
569                         cper_print_pcie(newpfx, pcie, gdata);
570                 else
571                         goto err_section_too_small;
572 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
573         } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
574                 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
575
576                 printk("%ssection_type: ARM processor error\n", newpfx);
577                 if (gdata->error_data_length >= sizeof(*arm_err))
578                         cper_print_proc_arm(newpfx, arm_err);
579                 else
580                         goto err_section_too_small;
581 #endif
582 #if defined(CONFIG_UEFI_CPER_X86)
583         } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
584                 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
585
586                 printk("%ssection_type: IA32/X64 processor error\n", newpfx);
587                 if (gdata->error_data_length >= sizeof(*ia_err))
588                         cper_print_proc_ia(newpfx, ia_err);
589                 else
590                         goto err_section_too_small;
591 #endif
592         } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
593                 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
594
595                 printk("%ssection_type: Firmware Error Record Reference\n",
596                        newpfx);
597                 /* The minimal FW Error Record contains 16 bytes */
598                 if (gdata->error_data_length >= SZ_16)
599                         cper_print_fw_err(newpfx, gdata, fw_err);
600                 else
601                         goto err_section_too_small;
602         } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
603                 struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
604
605                 printk("%ssection_type: CXL Protocol Error\n", newpfx);
606                 if (gdata->error_data_length >= sizeof(*prot_err))
607                         cper_print_prot_err(newpfx, prot_err);
608                 else
609                         goto err_section_too_small;
610         } else {
611                 const void *err = acpi_hest_get_payload(gdata);
612
613                 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
614                 printk("%ssection length: %#x\n", newpfx,
615                        gdata->error_data_length);
616                 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
617                                gdata->error_data_length, true);
618         }
619
620         return;
621
622 err_section_too_small:
623         pr_err(FW_WARN "error section length is too small\n");
624 }
625
626 void cper_estatus_print(const char *pfx,
627                         const struct acpi_hest_generic_status *estatus)
628 {
629         struct acpi_hest_generic_data *gdata;
630         int sec_no = 0;
631         char newpfx[64];
632         __u16 severity;
633
634         severity = estatus->error_severity;
635         if (severity == CPER_SEV_CORRECTED)
636                 printk("%s%s\n", pfx,
637                        "It has been corrected by h/w "
638                        "and requires no further action");
639         printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
640         snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
641
642         apei_estatus_for_each_section(estatus, gdata) {
643                 cper_estatus_print_section(newpfx, gdata, sec_no);
644                 sec_no++;
645         }
646 }
647 EXPORT_SYMBOL_GPL(cper_estatus_print);
648
649 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
650 {
651         if (estatus->data_length &&
652             estatus->data_length < sizeof(struct acpi_hest_generic_data))
653                 return -EINVAL;
654         if (estatus->raw_data_length &&
655             estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
656                 return -EINVAL;
657
658         return 0;
659 }
660 EXPORT_SYMBOL_GPL(cper_estatus_check_header);
661
662 int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
663 {
664         struct acpi_hest_generic_data *gdata;
665         unsigned int data_len, record_size;
666         int rc;
667
668         rc = cper_estatus_check_header(estatus);
669         if (rc)
670                 return rc;
671
672         data_len = estatus->data_length;
673
674         apei_estatus_for_each_section(estatus, gdata) {
675                 if (acpi_hest_get_size(gdata) > data_len)
676                         return -EINVAL;
677
678                 record_size = acpi_hest_get_record_size(gdata);
679                 if (record_size > data_len)
680                         return -EINVAL;
681
682                 data_len -= record_size;
683         }
684         if (data_len)
685                 return -EINVAL;
686
687         return 0;
688 }
689 EXPORT_SYMBOL_GPL(cper_estatus_check);