blob: 1491dd4f08f9f1526718835a3e4ef63c86d1e659 [file] [log] [blame]
Huang Ying06d65de2010-05-18 14:35:19 +08001/*
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
Chen, Gong88f074f2013-10-18 14:28:59 -07008 * various tables, such as ERST, BERT and HEST etc.
Huang Ying06d65de2010-05-18 14:35:19 +08009 *
10 * For more information about CPER, please refer to Appendix N of UEFI
Chen, Gong147de142013-10-18 14:30:13 -070011 * Specification version 2.4.
Huang Ying06d65de2010-05-18 14:35:19 +080012 *
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>
Chen, Gongfbeef852013-10-18 14:30:21 -070031#include <linux/dmi.h>
Huang Ying06d65de2010-05-18 14:35:19 +080032#include <linux/acpi.h>
Lance Ortiz1d521002013-01-03 15:34:08 -070033#include <linux/pci.h>
Huang Yingc413d762011-02-21 13:54:43 +080034#include <linux/aer.h>
Huang Ying06d65de2010-05-18 14:35:19 +080035
Chen, Gongf6edea72013-10-18 14:30:29 -070036#define INDENT_SP " "
Huang Ying06d65de2010-05-18 14:35:19 +080037/*
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
Huang Yingf59c55d2010-12-07 10:22:30 +080053static 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 */
Huang Yingc413d762011-02-21 13:54:43 +080077void cper_print_bits(const char *pfx, unsigned int bits,
Chen, Gong88f074f2013-10-18 14:28:59 -070078 const char * const strs[], unsigned int strs_size)
Huang Yingf59c55d2010-12-07 10:22:30 +080079{
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];
Huang Yingc413d762011-02-21 13:54:43 +080088 if (!str)
89 continue;
Huang Yingf59c55d2010-12-07 10:22:30 +080090 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
Chen, Gong88f074f2013-10-18 14:28:59 -0700103static const char * const cper_proc_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800104 "IA32/X64",
105 "IA64",
106};
107
Chen, Gong88f074f2013-10-18 14:28:59 -0700108static const char * const cper_proc_isa_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800109 "IA32",
110 "IA64",
111 "X64",
112};
113
Chen, Gong88f074f2013-10-18 14:28:59 -0700114static const char * const cper_proc_error_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800115 "cache error",
116 "TLB error",
117 "bus error",
118 "micro-architectural error",
119};
120
Chen, Gong88f074f2013-10-18 14:28:59 -0700121static const char * const cper_proc_op_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800122 "unknown or generic",
123 "data read",
124 "data write",
125 "instruction execution",
126};
127
Chen, Gong88f074f2013-10-18 14:28:59 -0700128static const char * const cper_proc_flag_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800129 "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",
Chen, Gong147de142013-10-18 14:30:13 -0700196 "physical memory map-out event",
Huang Yingf59c55d2010-12-07 10:22:30 +0800197};
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);
Chen, Gong147de142013-10-18 14:30:13 -0700203 if (mem->validation_bits & CPER_MEM_VALID_PA)
Huang Yingf59c55d2010-12-07 10:22:30 +0800204 printk("%s""physical_address: 0x%016llx\n",
205 pfx, mem->physical_addr);
Chen, Gong147de142013-10-18 14:30:13 -0700206 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
Huang Yingf59c55d2010-12-07 10:22:30 +0800207 printk("%s""physical_address_mask: 0x%016llx\n",
208 pfx, mem->physical_addr_mask);
209 if (mem->validation_bits & CPER_MEM_VALID_NODE)
Chen, Gongf6edea72013-10-18 14:30:29 -0700210 pr_debug("node: %d\n", mem->node);
Huang Yingf59c55d2010-12-07 10:22:30 +0800211 if (mem->validation_bits & CPER_MEM_VALID_CARD)
Chen, Gongf6edea72013-10-18 14:30:29 -0700212 pr_debug("card: %d\n", mem->card);
Huang Yingf59c55d2010-12-07 10:22:30 +0800213 if (mem->validation_bits & CPER_MEM_VALID_MODULE)
Chen, Gongf6edea72013-10-18 14:30:29 -0700214 pr_debug("module: %d\n", mem->module);
Chen, Gongfbeef852013-10-18 14:30:21 -0700215 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
Chen, Gongf6edea72013-10-18 14:30:29 -0700216 pr_debug("rank: %d\n", mem->rank);
Huang Yingf59c55d2010-12-07 10:22:30 +0800217 if (mem->validation_bits & CPER_MEM_VALID_BANK)
Chen, Gongf6edea72013-10-18 14:30:29 -0700218 pr_debug("bank: %d\n", mem->bank);
Huang Yingf59c55d2010-12-07 10:22:30 +0800219 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
Chen, Gongf6edea72013-10-18 14:30:29 -0700220 pr_debug("device: %d\n", mem->device);
Huang Yingf59c55d2010-12-07 10:22:30 +0800221 if (mem->validation_bits & CPER_MEM_VALID_ROW)
Chen, Gongf6edea72013-10-18 14:30:29 -0700222 pr_debug("row: %d\n", mem->row);
Huang Yingf59c55d2010-12-07 10:22:30 +0800223 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
Chen, Gongf6edea72013-10-18 14:30:29 -0700224 pr_debug("column: %d\n", mem->column);
Huang Yingf59c55d2010-12-07 10:22:30 +0800225 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
Chen, Gongf6edea72013-10-18 14:30:29 -0700226 pr_debug("bit_position: %d\n", mem->bit_pos);
Huang Yingf59c55d2010-12-07 10:22:30 +0800227 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
Chen, Gongf6edea72013-10-18 14:30:29 -0700228 pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id);
Huang Yingf59c55d2010-12-07 10:22:30 +0800229 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
Chen, Gongf6edea72013-10-18 14:30:29 -0700230 pr_debug("responder_id: 0x%016llx\n", mem->responder_id);
Huang Yingf59c55d2010-12-07 10:22:30 +0800231 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
Chen, Gongf6edea72013-10-18 14:30:29 -0700232 pr_debug("target_id: 0x%016llx\n", mem->target_id);
Huang Yingf59c55d2010-12-07 10:22:30 +0800233 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 }
Chen, Gongfbeef852013-10-18 14:30:21 -0700239 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 }
Huang Yingf59c55d2010-12-07 10:22:30 +0800248}
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
Huang Yingc413d762011-02-21 13:54:43 +0800264static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
Chen, Gong88f074f2013-10-18 14:28:59 -0700265 const struct acpi_generic_data *gdata)
Huang Yingf59c55d2010-12-07 10:22:30 +0800266{
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
Chen, Gong88f074f2013-10-18 14:28:59 -0700300static void cper_estatus_print_section(
301 const char *pfx, const struct acpi_generic_data *gdata, int sec_no)
Huang Yingf59c55d2010-12-07 10:22:30 +0800302{
303 uuid_le *sec_type = (uuid_le *)gdata->section_type;
304 __u16 severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700305 char newpfx[64];
Huang Yingf59c55d2010-12-07 10:22:30 +0800306
307 severity = gdata->error_severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700308 printk("%s""Error %d, type: %s\n", pfx, sec_no,
Huang Yingf59c55d2010-12-07 10:22:30 +0800309 cper_severity_str(severity));
Huang Yingf59c55d2010-12-07 10:22:30 +0800310 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
Chen, Gongf6edea72013-10-18 14:30:29 -0700315 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
Huang Yingf59c55d2010-12-07 10:22:30 +0800316 if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) {
317 struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700318 printk("%s""section_type: general processor error\n", newpfx);
Huang Yingf59c55d2010-12-07 10:22:30 +0800319 if (gdata->error_data_length >= sizeof(*proc_err))
Chen, Gongf6edea72013-10-18 14:30:29 -0700320 cper_print_proc_generic(newpfx, proc_err);
Huang Yingf59c55d2010-12-07 10:22:30 +0800321 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);
Chen, Gongf6edea72013-10-18 14:30:29 -0700325 printk("%s""section_type: memory error\n", newpfx);
Huang Yingf59c55d2010-12-07 10:22:30 +0800326 if (gdata->error_data_length >= sizeof(*mem_err))
Chen, Gongf6edea72013-10-18 14:30:29 -0700327 cper_print_mem(newpfx, mem_err);
Huang Yingf59c55d2010-12-07 10:22:30 +0800328 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);
Chen, Gongf6edea72013-10-18 14:30:29 -0700332 printk("%s""section_type: PCIe error\n", newpfx);
Huang Yingf59c55d2010-12-07 10:22:30 +0800333 if (gdata->error_data_length >= sizeof(*pcie))
Chen, Gongf6edea72013-10-18 14:30:29 -0700334 cper_print_pcie(newpfx, pcie, gdata);
Huang Yingf59c55d2010-12-07 10:22:30 +0800335 else
336 goto err_section_too_small;
337 } else
Chen, Gongf6edea72013-10-18 14:30:29 -0700338 printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
Huang Yingf59c55d2010-12-07 10:22:30 +0800339
340 return;
341
342err_section_too_small:
343 pr_err(FW_WARN "error section length is too small\n");
344}
345
Chen, Gong88f074f2013-10-18 14:28:59 -0700346void cper_estatus_print(const char *pfx,
347 const struct acpi_generic_status *estatus)
Huang Yingf59c55d2010-12-07 10:22:30 +0800348{
Chen, Gong88f074f2013-10-18 14:28:59 -0700349 struct acpi_generic_data *gdata;
Huang Yingf59c55d2010-12-07 10:22:30 +0800350 unsigned int data_len, gedata_len;
351 int sec_no = 0;
Chen, Gongf6edea72013-10-18 14:30:29 -0700352 char newpfx[64];
Huang Yingf59c55d2010-12-07 10:22:30 +0800353 __u16 severity;
354
Huang Yingf59c55d2010-12-07 10:22:30 +0800355 severity = estatus->error_severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700356 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));
Huang Yingf59c55d2010-12-07 10:22:30 +0800361 data_len = estatus->data_length;
Chen, Gong88f074f2013-10-18 14:28:59 -0700362 gdata = (struct acpi_generic_data *)(estatus + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700363 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
Chen, Gong833ba4b2013-10-18 14:27:51 -0700364 while (data_len >= sizeof(*gdata)) {
Huang Yingf59c55d2010-12-07 10:22:30 +0800365 gedata_len = gdata->error_data_length;
Chen, Gongf6edea72013-10-18 14:30:29 -0700366 cper_estatus_print_section(newpfx, gdata, sec_no);
Huang Yingf59c55d2010-12-07 10:22:30 +0800367 data_len -= gedata_len + sizeof(*gdata);
Jiang Liu37d2a362012-02-15 00:01:44 +0800368 gdata = (void *)(gdata + 1) + gedata_len;
Huang Yingf59c55d2010-12-07 10:22:30 +0800369 sec_no++;
370 }
371}
Chen, Gong88f074f2013-10-18 14:28:59 -0700372EXPORT_SYMBOL_GPL(cper_estatus_print);
Huang Yingf59c55d2010-12-07 10:22:30 +0800373
Chen, Gong88f074f2013-10-18 14:28:59 -0700374int cper_estatus_check_header(const struct acpi_generic_status *estatus)
Huang Ying06d65de2010-05-18 14:35:19 +0800375{
376 if (estatus->data_length &&
Chen, Gong88f074f2013-10-18 14:28:59 -0700377 estatus->data_length < sizeof(struct acpi_generic_data))
Huang Ying06d65de2010-05-18 14:35:19 +0800378 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}
Chen, Gong88f074f2013-10-18 14:28:59 -0700385EXPORT_SYMBOL_GPL(cper_estatus_check_header);
Huang Ying06d65de2010-05-18 14:35:19 +0800386
Chen, Gong88f074f2013-10-18 14:28:59 -0700387int cper_estatus_check(const struct acpi_generic_status *estatus)
Huang Ying06d65de2010-05-18 14:35:19 +0800388{
Chen, Gong88f074f2013-10-18 14:28:59 -0700389 struct acpi_generic_data *gdata;
Huang Ying06d65de2010-05-18 14:35:19 +0800390 unsigned int data_len, gedata_len;
391 int rc;
392
Chen, Gong88f074f2013-10-18 14:28:59 -0700393 rc = cper_estatus_check_header(estatus);
Huang Ying06d65de2010-05-18 14:35:19 +0800394 if (rc)
395 return rc;
396 data_len = estatus->data_length;
Chen, Gong88f074f2013-10-18 14:28:59 -0700397 gdata = (struct acpi_generic_data *)(estatus + 1);
Chen Gongaaf9d932013-03-19 06:48:07 +0000398 while (data_len >= sizeof(*gdata)) {
Huang Ying06d65de2010-05-18 14:35:19 +0800399 gedata_len = gdata->error_data_length;
400 if (gedata_len > data_len - sizeof(*gdata))
401 return -EINVAL;
402 data_len -= gedata_len + sizeof(*gdata);
Jiang Liu37d2a362012-02-15 00:01:44 +0800403 gdata = (void *)(gdata + 1) + gedata_len;
Huang Ying06d65de2010-05-18 14:35:19 +0800404 }
405 if (data_len)
406 return -EINVAL;
407
408 return 0;
409}
Chen, Gong88f074f2013-10-18 14:28:59 -0700410EXPORT_SYMBOL_GPL(cper_estatus_check);