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