aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-11-11 21:16:44 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-11 21:16:44 -0500
commit340286cd4e9aff841affb897f6d2535ed27605cf (patch)
tree6eb32c966f59bd2958b9d7e83b1199deddee6d5b /drivers/firmware
parent339a4b72c83dc7d8b29f43f7417add791e201ad3 (diff)
parent9ebddac7ea2a1f4b4ce3335a78312a58dfaadb4d (diff)
Merge branch 'x86-mce-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 RAS changes from Ingo Molnar: "The biggest change adds support for Intel 'CPER' (UEFI Common Platform Error Record) error logging, which builds upon an enhanced error logging mechanism available on Xeon processors. Full description is here: http://www.intel.com/content/www/us/en/architecture-and-technology/enhanced-mca-logging-xeon-paper.html This change provides a module (and support code) to check for an extended error log and prints extra details about the error on the console" * 'x86-mce-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: ACPI, x86: Fix extended error log driver to depend on CONFIG_X86_LOCAL_APIC dmi: Avoid unaligned memory access in save_mem_devices() Move cper.c from drivers/acpi/apei to drivers/firmware/efi EDAC, GHES: Update ghes error record info ACPI, APEI, CPER: Cleanup CPER memory error output format ACPI, APEI, CPER: Enhance memory reporting capability ACPI, APEI, CPER: Add UEFI 2.4 support for memory error DMI: Parse memory device (type 17) in SMBIOS ACPI, x86: Extended error log driver for x86 platform bitops: Introduce a more generic BITMASK macro ACPI, CPER: Update cper info ACPI, APEI, CPER: Fix status check during error printing
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/dmi_scan.c61
-rw-r--r--drivers/firmware/efi/Kconfig3
-rw-r--r--drivers/firmware/efi/Makefile1
-rw-r--r--drivers/firmware/efi/cper.c410
4 files changed, 475 insertions, 0 deletions
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index fa0affb699b4..c7e81ff8f3ef 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -8,6 +8,7 @@
8#include <linux/bootmem.h> 8#include <linux/bootmem.h>
9#include <linux/random.h> 9#include <linux/random.h>
10#include <asm/dmi.h> 10#include <asm/dmi.h>
11#include <asm/unaligned.h>
11 12
12/* 13/*
13 * DMI stands for "Desktop Management Interface". It is part 14 * DMI stands for "Desktop Management Interface". It is part
@@ -25,6 +26,13 @@ static int dmi_initialized;
25/* DMI system identification string used during boot */ 26/* DMI system identification string used during boot */
26static char dmi_ids_string[128] __initdata; 27static char dmi_ids_string[128] __initdata;
27 28
29static struct dmi_memdev_info {
30 const char *device;
31 const char *bank;
32 u16 handle;
33} *dmi_memdev;
34static int dmi_memdev_nr;
35
28static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s) 36static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
29{ 37{
30 const u8 *bp = ((u8 *) dm) + dm->length; 38 const u8 *bp = ((u8 *) dm) + dm->length;
@@ -322,6 +330,42 @@ static void __init dmi_save_extended_devices(const struct dmi_header *dm)
322 dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1))); 330 dmi_save_one_device(*d & 0x7f, dmi_string_nosave(dm, *(d - 1)));
323} 331}
324 332
333static void __init count_mem_devices(const struct dmi_header *dm, void *v)
334{
335 if (dm->type != DMI_ENTRY_MEM_DEVICE)
336 return;
337 dmi_memdev_nr++;
338}
339
340static void __init save_mem_devices(const struct dmi_header *dm, void *v)
341{
342 const char *d = (const char *)dm;
343 static int nr;
344
345 if (dm->type != DMI_ENTRY_MEM_DEVICE)
346 return;
347 if (nr >= dmi_memdev_nr) {
348 pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n");
349 return;
350 }
351 dmi_memdev[nr].handle = get_unaligned(&dm->handle);
352 dmi_memdev[nr].device = dmi_string(dm, d[0x10]);
353 dmi_memdev[nr].bank = dmi_string(dm, d[0x11]);
354 nr++;
355}
356
357void __init dmi_memdev_walk(void)
358{
359 if (!dmi_available)
360 return;
361
362 if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) {
363 dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr);
364 if (dmi_memdev)
365 dmi_walk_early(save_mem_devices);
366 }
367}
368
325/* 369/*
326 * Process a DMI table entry. Right now all we care about are the BIOS 370 * Process a DMI table entry. Right now all we care about are the BIOS
327 * and machine entries. For 2.5 we should pull the smbus controller info 371 * and machine entries. For 2.5 we should pull the smbus controller info
@@ -815,3 +859,20 @@ bool dmi_match(enum dmi_field f, const char *str)
815 return !strcmp(info, str); 859 return !strcmp(info, str);
816} 860}
817EXPORT_SYMBOL_GPL(dmi_match); 861EXPORT_SYMBOL_GPL(dmi_match);
862
863void dmi_memdev_name(u16 handle, const char **bank, const char **device)
864{
865 int n;
866
867 if (dmi_memdev == NULL)
868 return;
869
870 for (n = 0; n < dmi_memdev_nr; n++) {
871 if (handle == dmi_memdev[n].handle) {
872 *bank = dmi_memdev[n].bank;
873 *device = dmi_memdev[n].device;
874 break;
875 }
876 }
877}
878EXPORT_SYMBOL_GPL(dmi_memdev_name);
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index b0fc7c79dfbb..3150aa4874e8 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -36,4 +36,7 @@ config EFI_VARS_PSTORE_DEFAULT_DISABLE
36 backend for pstore by default. This setting can be overridden 36 backend for pstore by default. This setting can be overridden
37 using the efivars module's pstore_disable parameter. 37 using the efivars module's pstore_disable parameter.
38 38
39config UEFI_CPER
40 def_bool n
41
39endmenu 42endmenu
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index 99245ab5a79c..9ba156d3c775 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -4,3 +4,4 @@
4obj-y += efi.o vars.o 4obj-y += efi.o vars.o
5obj-$(CONFIG_EFI_VARS) += efivars.o 5obj-$(CONFIG_EFI_VARS) += efivars.o
6obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o 6obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
7obj-$(CONFIG_UEFI_CPER) += cper.o
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
new file mode 100644
index 000000000000..1491dd4f08f9
--- /dev/null
+++ b/drivers/firmware/efi/cper.c
@@ -0,0 +1,410 @@
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
36#define INDENT_SP " "
37/*
38 * CPER record ID need to be unique even after reboot, because record
39 * ID is used as index for ERST storage, while CPER records from
40 * multiple boot may co-exist in ERST.
41 */
42u64 cper_next_record_id(void)
43{
44 static atomic64_t seq;
45
46 if (!atomic64_read(&seq))
47 atomic64_set(&seq, ((u64)get_seconds()) << 32);
48
49 return atomic64_inc_return(&seq);
50}
51EXPORT_SYMBOL_GPL(cper_next_record_id);
52
53static const char *cper_severity_strs[] = {
54 "recoverable",
55 "fatal",
56 "corrected",
57 "info",
58};
59
60static const char *cper_severity_str(unsigned int severity)
61{
62 return severity < ARRAY_SIZE(cper_severity_strs) ?
63 cper_severity_strs[severity] : "unknown";
64}
65
66/*
67 * cper_print_bits - print strings for set bits
68 * @pfx: prefix for each line, including log level and prefix string
69 * @bits: bit mask
70 * @strs: string array, indexed by bit position
71 * @strs_size: size of the string array: @strs
72 *
73 * For each set bit in @bits, print the corresponding string in @strs.
74 * If the output length is longer than 80, multiple line will be
75 * printed, with @pfx is printed at the beginning of each line.
76 */
77void cper_print_bits(const char *pfx, unsigned int bits,
78 const char * const strs[], unsigned int strs_size)
79{
80 int i, len = 0;
81 const char *str;
82 char buf[84];
83
84 for (i = 0; i < strs_size; i++) {
85 if (!(bits & (1U << i)))
86 continue;
87 str = strs[i];
88 if (!str)
89 continue;
90 if (len && len + strlen(str) + 2 > 80) {
91 printk("%s\n", buf);
92 len = 0;
93 }
94 if (!len)
95 len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
96 else
97 len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
98 }
99 if (len)
100 printk("%s\n", buf);
101}
102
103static const char * const cper_proc_type_strs[] = {
104 "IA32/X64",
105 "IA64",
106};
107
108static const char * const cper_proc_isa_strs[] = {
109 "IA32",
110 "IA64",
111 "X64",
112};
113
114static const char * const cper_proc_error_type_strs[] = {
115 "cache error",
116 "TLB error",
117 "bus error",
118 "micro-architectural error",
119};
120
121static const char * const cper_proc_op_strs[] = {
122 "unknown or generic",
123 "data read",
124 "data write",
125 "instruction execution",
126};
127
128static const char * const cper_proc_flag_strs[] = {
129 "restartable",
130 "precise IP",
131 "overflow",
132 "corrected",
133};
134
135static void cper_print_proc_generic(const char *pfx,
136 const struct cper_sec_proc_generic *proc)
137{
138 if (proc->validation_bits & CPER_PROC_VALID_TYPE)
139 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
140 proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ?
141 cper_proc_type_strs[proc->proc_type] : "unknown");
142 if (proc->validation_bits & CPER_PROC_VALID_ISA)
143 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
144 proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ?
145 cper_proc_isa_strs[proc->proc_isa] : "unknown");
146 if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
147 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
148 cper_print_bits(pfx, proc->proc_error_type,
149 cper_proc_error_type_strs,
150 ARRAY_SIZE(cper_proc_error_type_strs));
151 }
152 if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
153 printk("%s""operation: %d, %s\n", pfx, proc->operation,
154 proc->operation < ARRAY_SIZE(cper_proc_op_strs) ?
155 cper_proc_op_strs[proc->operation] : "unknown");
156 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
157 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
158 cper_print_bits(pfx, proc->flags, cper_proc_flag_strs,
159 ARRAY_SIZE(cper_proc_flag_strs));
160 }
161 if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
162 printk("%s""level: %d\n", pfx, proc->level);
163 if (proc->validation_bits & CPER_PROC_VALID_VERSION)
164 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
165 if (proc->validation_bits & CPER_PROC_VALID_ID)
166 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
167 if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
168 printk("%s""target_address: 0x%016llx\n",
169 pfx, proc->target_addr);
170 if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
171 printk("%s""requestor_id: 0x%016llx\n",
172 pfx, proc->requestor_id);
173 if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
174 printk("%s""responder_id: 0x%016llx\n",
175 pfx, proc->responder_id);
176 if (proc->validation_bits & CPER_PROC_VALID_IP)
177 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
178}
179
180static const char *cper_mem_err_type_strs[] = {
181 "unknown",
182 "no error",
183 "single-bit ECC",
184 "multi-bit ECC",
185 "single-symbol chipkill ECC",
186 "multi-symbol chipkill ECC",
187 "master abort",
188 "target abort",
189 "parity error",
190 "watchdog timeout",
191 "invalid address",
192 "mirror Broken",
193 "memory sparing",
194 "scrub corrected error",
195 "scrub uncorrected error",
196 "physical memory map-out event",
197};
198
199static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem)
200{
201 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
202 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
203 if (mem->validation_bits & CPER_MEM_VALID_PA)
204 printk("%s""physical_address: 0x%016llx\n",
205 pfx, mem->physical_addr);
206 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
207 printk("%s""physical_address_mask: 0x%016llx\n",
208 pfx, mem->physical_addr_mask);
209 if (mem->validation_bits & CPER_MEM_VALID_NODE)
210 pr_debug("node: %d\n", mem->node);
211 if (mem->validation_bits & CPER_MEM_VALID_CARD)
212 pr_debug("card: %d\n", mem->card);
213 if (mem->validation_bits & CPER_MEM_VALID_MODULE)
214 pr_debug("module: %d\n", mem->module);
215 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
216 pr_debug("rank: %d\n", mem->rank);
217 if (mem->validation_bits & CPER_MEM_VALID_BANK)
218 pr_debug("bank: %d\n", mem->bank);
219 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
220 pr_debug("device: %d\n", mem->device);
221 if (mem->validation_bits & CPER_MEM_VALID_ROW)
222 pr_debug("row: %d\n", mem->row);
223 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
224 pr_debug("column: %d\n", mem->column);
225 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
226 pr_debug("bit_position: %d\n", mem->bit_pos);
227 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
228 pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id);
229 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
230 pr_debug("responder_id: 0x%016llx\n", mem->responder_id);
231 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
232 pr_debug("target_id: 0x%016llx\n", mem->target_id);
233 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
234 u8 etype = mem->error_type;
235 printk("%s""error_type: %d, %s\n", pfx, etype,
236 etype < ARRAY_SIZE(cper_mem_err_type_strs) ?
237 cper_mem_err_type_strs[etype] : "unknown");
238 }
239 if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) {
240 const char *bank = NULL, *device = NULL;
241 dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
242 if (bank != NULL && device != NULL)
243 printk("%s""DIMM location: %s %s", pfx, bank, device);
244 else
245 printk("%s""DIMM DMI handle: 0x%.4x",
246 pfx, mem->mem_dev_handle);
247 }
248}
249
250static const char *cper_pcie_port_type_strs[] = {
251 "PCIe end point",
252 "legacy PCI end point",
253 "unknown",
254 "unknown",
255 "root port",
256 "upstream switch port",
257 "downstream switch port",
258 "PCIe to PCI/PCI-X bridge",
259 "PCI/PCI-X to PCIe bridge",
260 "root complex integrated endpoint device",
261 "root complex event collector",
262};
263
264static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
265 const struct acpi_generic_data *gdata)
266{
267 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
268 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
269 pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ?
270 cper_pcie_port_type_strs[pcie->port_type] : "unknown");
271 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
272 printk("%s""version: %d.%d\n", pfx,
273 pcie->version.major, pcie->version.minor);
274 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
275 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
276 pcie->command, pcie->status);
277 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
278 const __u8 *p;
279 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
280 pcie->device_id.segment, pcie->device_id.bus,
281 pcie->device_id.device, pcie->device_id.function);
282 printk("%s""slot: %d\n", pfx,
283 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
284 printk("%s""secondary_bus: 0x%02x\n", pfx,
285 pcie->device_id.secondary_bus);
286 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
287 pcie->device_id.vendor_id, pcie->device_id.device_id);
288 p = pcie->device_id.class_code;
289 printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]);
290 }
291 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
292 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
293 pcie->serial_number.lower, pcie->serial_number.upper);
294 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
295 printk(
296 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
297 pfx, pcie->bridge.secondary_status, pcie->bridge.control);
298}
299
300static void cper_estatus_print_section(
301 const char *pfx, const struct acpi_generic_data *gdata, int sec_no)
302{
303 uuid_le *sec_type = (uuid_le *)gdata->section_type;
304 __u16 severity;
305 char newpfx[64];
306
307 severity = gdata->error_severity;
308 printk("%s""Error %d, type: %s\n", pfx, sec_no,
309 cper_severity_str(severity));
310 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
311 printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id);
312 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
313 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
314
315 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
316 if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) {
317 struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1);
318 printk("%s""section_type: general processor error\n", newpfx);
319 if (gdata->error_data_length >= sizeof(*proc_err))
320 cper_print_proc_generic(newpfx, proc_err);
321 else
322 goto err_section_too_small;
323 } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) {
324 struct cper_sec_mem_err *mem_err = (void *)(gdata + 1);
325 printk("%s""section_type: memory error\n", newpfx);
326 if (gdata->error_data_length >= sizeof(*mem_err))
327 cper_print_mem(newpfx, mem_err);
328 else
329 goto err_section_too_small;
330 } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) {
331 struct cper_sec_pcie *pcie = (void *)(gdata + 1);
332 printk("%s""section_type: PCIe error\n", newpfx);
333 if (gdata->error_data_length >= sizeof(*pcie))
334 cper_print_pcie(newpfx, pcie, gdata);
335 else
336 goto err_section_too_small;
337 } else
338 printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
339
340 return;
341
342err_section_too_small:
343 pr_err(FW_WARN "error section length is too small\n");
344}
345
346void cper_estatus_print(const char *pfx,
347 const struct acpi_generic_status *estatus)
348{
349 struct acpi_generic_data *gdata;
350 unsigned int data_len, gedata_len;
351 int sec_no = 0;
352 char newpfx[64];
353 __u16 severity;
354
355 severity = estatus->error_severity;
356 if (severity == CPER_SEV_CORRECTED)
357 printk("%s%s\n", pfx,
358 "It has been corrected by h/w "
359 "and requires no further action");
360 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
361 data_len = estatus->data_length;
362 gdata = (struct acpi_generic_data *)(estatus + 1);
363 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
364 while (data_len >= sizeof(*gdata)) {
365 gedata_len = gdata->error_data_length;
366 cper_estatus_print_section(newpfx, gdata, sec_no);
367 data_len -= gedata_len + sizeof(*gdata);
368 gdata = (void *)(gdata + 1) + gedata_len;
369 sec_no++;
370 }
371}
372EXPORT_SYMBOL_GPL(cper_estatus_print);
373
374int cper_estatus_check_header(const struct acpi_generic_status *estatus)
375{
376 if (estatus->data_length &&
377 estatus->data_length < sizeof(struct acpi_generic_data))
378 return -EINVAL;
379 if (estatus->raw_data_length &&
380 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
381 return -EINVAL;
382
383 return 0;
384}
385EXPORT_SYMBOL_GPL(cper_estatus_check_header);
386
387int cper_estatus_check(const struct acpi_generic_status *estatus)
388{
389 struct acpi_generic_data *gdata;
390 unsigned int data_len, gedata_len;
391 int rc;
392
393 rc = cper_estatus_check_header(estatus);
394 if (rc)
395 return rc;
396 data_len = estatus->data_length;
397 gdata = (struct acpi_generic_data *)(estatus + 1);
398 while (data_len >= sizeof(*gdata)) {
399 gedata_len = gdata->error_data_length;
400 if (gedata_len > data_len - sizeof(*gdata))
401 return -EINVAL;
402 data_len -= gedata_len + sizeof(*gdata);
403 gdata = (void *)(gdata + 1) + gedata_len;
404 }
405 if (data_len)
406 return -EINVAL;
407
408 return 0;
409}
410EXPORT_SYMBOL_GPL(cper_estatus_check);