GNU Linux-libre 4.19.264-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 /*
41  * CPER record ID need to be unique even after reboot, because record
42  * ID is used as index for ERST storage, while CPER records from
43  * multiple boot may co-exist in ERST.
44  */
45 u64 cper_next_record_id(void)
46 {
47         static atomic64_t seq;
48
49         if (!atomic64_read(&seq)) {
50                 time64_t time = ktime_get_real_seconds();
51
52                 /*
53                  * This code is unlikely to still be needed in year 2106,
54                  * but just in case, let's use a few more bits for timestamps
55                  * after y2038 to be sure they keep increasing monotonically
56                  * for the next few hundred years...
57                  */
58                 if (time < 0x80000000)
59                         atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
60                 else
61                         atomic64_set(&seq, 0x8000000000000000ull |
62                                            ktime_get_real_seconds() << 24);
63         }
64
65         return atomic64_inc_return(&seq);
66 }
67 EXPORT_SYMBOL_GPL(cper_next_record_id);
68
69 static const char * const severity_strs[] = {
70         "recoverable",
71         "fatal",
72         "corrected",
73         "info",
74 };
75
76 const char *cper_severity_str(unsigned int severity)
77 {
78         return severity < ARRAY_SIZE(severity_strs) ?
79                 severity_strs[severity] : "unknown";
80 }
81 EXPORT_SYMBOL_GPL(cper_severity_str);
82
83 /*
84  * cper_print_bits - print strings for set bits
85  * @pfx: prefix for each line, including log level and prefix string
86  * @bits: bit mask
87  * @strs: string array, indexed by bit position
88  * @strs_size: size of the string array: @strs
89  *
90  * For each set bit in @bits, print the corresponding string in @strs.
91  * If the output length is longer than 80, multiple line will be
92  * printed, with @pfx is printed at the beginning of each line.
93  */
94 void cper_print_bits(const char *pfx, unsigned int bits,
95                      const char * const strs[], unsigned int strs_size)
96 {
97         int i, len = 0;
98         const char *str;
99         char buf[84];
100
101         for (i = 0; i < strs_size; i++) {
102                 if (!(bits & (1U << i)))
103                         continue;
104                 str = strs[i];
105                 if (!str)
106                         continue;
107                 if (len && len + strlen(str) + 2 > 80) {
108                         printk("%s\n", buf);
109                         len = 0;
110                 }
111                 if (!len)
112                         len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
113                 else
114                         len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
115         }
116         if (len)
117                 printk("%s\n", buf);
118 }
119
120 static const char * const proc_type_strs[] = {
121         "IA32/X64",
122         "IA64",
123         "ARM",
124 };
125
126 static const char * const proc_isa_strs[] = {
127         "IA32",
128         "IA64",
129         "X64",
130         "ARM A32/T32",
131         "ARM A64",
132 };
133
134 const char * const cper_proc_error_type_strs[] = {
135         "cache error",
136         "TLB error",
137         "bus error",
138         "micro-architectural error",
139 };
140
141 static const char * const proc_op_strs[] = {
142         "unknown or generic",
143         "data read",
144         "data write",
145         "instruction execution",
146 };
147
148 static const char * const proc_flag_strs[] = {
149         "restartable",
150         "precise IP",
151         "overflow",
152         "corrected",
153 };
154
155 static void cper_print_proc_generic(const char *pfx,
156                                     const struct cper_sec_proc_generic *proc)
157 {
158         if (proc->validation_bits & CPER_PROC_VALID_TYPE)
159                 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
160                        proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
161                        proc_type_strs[proc->proc_type] : "unknown");
162         if (proc->validation_bits & CPER_PROC_VALID_ISA)
163                 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
164                        proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
165                        proc_isa_strs[proc->proc_isa] : "unknown");
166         if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
167                 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
168                 cper_print_bits(pfx, proc->proc_error_type,
169                                 cper_proc_error_type_strs,
170                                 ARRAY_SIZE(cper_proc_error_type_strs));
171         }
172         if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
173                 printk("%s""operation: %d, %s\n", pfx, proc->operation,
174                        proc->operation < ARRAY_SIZE(proc_op_strs) ?
175                        proc_op_strs[proc->operation] : "unknown");
176         if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
177                 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
178                 cper_print_bits(pfx, proc->flags, proc_flag_strs,
179                                 ARRAY_SIZE(proc_flag_strs));
180         }
181         if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
182                 printk("%s""level: %d\n", pfx, proc->level);
183         if (proc->validation_bits & CPER_PROC_VALID_VERSION)
184                 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
185         if (proc->validation_bits & CPER_PROC_VALID_ID)
186                 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
187         if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
188                 printk("%s""target_address: 0x%016llx\n",
189                        pfx, proc->target_addr);
190         if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
191                 printk("%s""requestor_id: 0x%016llx\n",
192                        pfx, proc->requestor_id);
193         if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
194                 printk("%s""responder_id: 0x%016llx\n",
195                        pfx, proc->responder_id);
196         if (proc->validation_bits & CPER_PROC_VALID_IP)
197                 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
198 }
199
200 static const char * const mem_err_type_strs[] = {
201         "unknown",
202         "no error",
203         "single-bit ECC",
204         "multi-bit ECC",
205         "single-symbol chipkill ECC",
206         "multi-symbol chipkill ECC",
207         "master abort",
208         "target abort",
209         "parity error",
210         "watchdog timeout",
211         "invalid address",
212         "mirror Broken",
213         "memory sparing",
214         "scrub corrected error",
215         "scrub uncorrected error",
216         "physical memory map-out event",
217 };
218
219 const char *cper_mem_err_type_str(unsigned int etype)
220 {
221         return etype < ARRAY_SIZE(mem_err_type_strs) ?
222                 mem_err_type_strs[etype] : "unknown";
223 }
224 EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
225
226 static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
227 {
228         u32 len, n;
229
230         if (!msg)
231                 return 0;
232
233         n = 0;
234         len = CPER_REC_LEN - 1;
235         if (mem->validation_bits & CPER_MEM_VALID_NODE)
236                 n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
237         if (mem->validation_bits & CPER_MEM_VALID_CARD)
238                 n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
239         if (mem->validation_bits & CPER_MEM_VALID_MODULE)
240                 n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
241         if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
242                 n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
243         if (mem->validation_bits & CPER_MEM_VALID_BANK)
244                 n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
245         if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
246                 n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
247         if (mem->validation_bits & CPER_MEM_VALID_ROW)
248                 n += scnprintf(msg + n, len - n, "row: %d ", mem->row);
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                 scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
262                           mem->target_id);
263
264         msg[n] = '\0';
265         return n;
266 }
267
268 static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
269 {
270         u32 len, n;
271         const char *bank = NULL, *device = NULL;
272
273         if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
274                 return 0;
275
276         len = CPER_REC_LEN;
277         dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
278         if (bank && device)
279                 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
280         else
281                 n = snprintf(msg, len,
282                              "DIMM location: not present. DMI handle: 0x%.4x ",
283                              mem->mem_dev_handle);
284
285         return n;
286 }
287
288 void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
289                        struct cper_mem_err_compact *cmem)
290 {
291         cmem->validation_bits = mem->validation_bits;
292         cmem->node = mem->node;
293         cmem->card = mem->card;
294         cmem->module = mem->module;
295         cmem->bank = mem->bank;
296         cmem->device = mem->device;
297         cmem->row = mem->row;
298         cmem->column = mem->column;
299         cmem->bit_pos = mem->bit_pos;
300         cmem->requestor_id = mem->requestor_id;
301         cmem->responder_id = mem->responder_id;
302         cmem->target_id = mem->target_id;
303         cmem->rank = mem->rank;
304         cmem->mem_array_handle = mem->mem_array_handle;
305         cmem->mem_dev_handle = mem->mem_dev_handle;
306 }
307
308 const char *cper_mem_err_unpack(struct trace_seq *p,
309                                 struct cper_mem_err_compact *cmem)
310 {
311         const char *ret = trace_seq_buffer_ptr(p);
312         char rcd_decode_str[CPER_REC_LEN];
313
314         if (cper_mem_err_location(cmem, rcd_decode_str))
315                 trace_seq_printf(p, "%s", rcd_decode_str);
316         if (cper_dimm_err_location(cmem, rcd_decode_str))
317                 trace_seq_printf(p, "%s", rcd_decode_str);
318         trace_seq_putc(p, '\0');
319
320         return ret;
321 }
322
323 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
324         int len)
325 {
326         struct cper_mem_err_compact cmem;
327         char rcd_decode_str[CPER_REC_LEN];
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);