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