blob: d4253742543841d6d261dfea22156259b0a13183 [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 " "
Chen, Gong3760cd22014-06-11 13:59:45 -070037
38static char rcd_decode_str[CPER_REC_LEN];
39
Huang Ying06d65de2010-05-18 14:35:19 +080040/*
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 */
45u64 cper_next_record_id(void)
46{
47 static atomic64_t seq;
48
49 if (!atomic64_read(&seq))
50 atomic64_set(&seq, ((u64)get_seconds()) << 32);
51
52 return atomic64_inc_return(&seq);
53}
54EXPORT_SYMBOL_GPL(cper_next_record_id);
55
Chen, Gong3760cd22014-06-11 13:59:45 -070056static const char * const severity_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +080057 "recoverable",
58 "fatal",
59 "corrected",
60 "info",
61};
62
Chen, Gong3760cd22014-06-11 13:59:45 -070063const char *cper_severity_str(unsigned int severity)
Huang Yingf59c55d2010-12-07 10:22:30 +080064{
Chen, Gong3760cd22014-06-11 13:59:45 -070065 return severity < ARRAY_SIZE(severity_strs) ?
66 severity_strs[severity] : "unknown";
Huang Yingf59c55d2010-12-07 10:22:30 +080067}
Chen, Gong3760cd22014-06-11 13:59:45 -070068EXPORT_SYMBOL_GPL(cper_severity_str);
Huang Yingf59c55d2010-12-07 10:22:30 +080069
70/*
71 * cper_print_bits - print strings for set bits
72 * @pfx: prefix for each line, including log level and prefix string
73 * @bits: bit mask
74 * @strs: string array, indexed by bit position
75 * @strs_size: size of the string array: @strs
76 *
77 * For each set bit in @bits, print the corresponding string in @strs.
78 * If the output length is longer than 80, multiple line will be
79 * printed, with @pfx is printed at the beginning of each line.
80 */
Huang Yingc413d762011-02-21 13:54:43 +080081void cper_print_bits(const char *pfx, unsigned int bits,
Chen, Gong88f074f2013-10-18 14:28:59 -070082 const char * const strs[], unsigned int strs_size)
Huang Yingf59c55d2010-12-07 10:22:30 +080083{
84 int i, len = 0;
85 const char *str;
86 char buf[84];
87
88 for (i = 0; i < strs_size; i++) {
89 if (!(bits & (1U << i)))
90 continue;
91 str = strs[i];
Huang Yingc413d762011-02-21 13:54:43 +080092 if (!str)
93 continue;
Huang Yingf59c55d2010-12-07 10:22:30 +080094 if (len && len + strlen(str) + 2 > 80) {
95 printk("%s\n", buf);
96 len = 0;
97 }
98 if (!len)
99 len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
100 else
101 len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
102 }
103 if (len)
104 printk("%s\n", buf);
105}
106
Chen, Gong3760cd22014-06-11 13:59:45 -0700107static const char * const proc_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800108 "IA32/X64",
109 "IA64",
110};
111
Chen, Gong3760cd22014-06-11 13:59:45 -0700112static const char * const proc_isa_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800113 "IA32",
114 "IA64",
115 "X64",
116};
117
Chen, Gong3760cd22014-06-11 13:59:45 -0700118static const char * const proc_error_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800119 "cache error",
120 "TLB error",
121 "bus error",
122 "micro-architectural error",
123};
124
Chen, Gong3760cd22014-06-11 13:59:45 -0700125static const char * const proc_op_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800126 "unknown or generic",
127 "data read",
128 "data write",
129 "instruction execution",
130};
131
Chen, Gong3760cd22014-06-11 13:59:45 -0700132static const char * const proc_flag_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800133 "restartable",
134 "precise IP",
135 "overflow",
136 "corrected",
137};
138
139static void cper_print_proc_generic(const char *pfx,
140 const struct cper_sec_proc_generic *proc)
141{
142 if (proc->validation_bits & CPER_PROC_VALID_TYPE)
143 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
Chen, Gong3760cd22014-06-11 13:59:45 -0700144 proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
145 proc_type_strs[proc->proc_type] : "unknown");
Huang Yingf59c55d2010-12-07 10:22:30 +0800146 if (proc->validation_bits & CPER_PROC_VALID_ISA)
147 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
Chen, Gong3760cd22014-06-11 13:59:45 -0700148 proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
149 proc_isa_strs[proc->proc_isa] : "unknown");
Huang Yingf59c55d2010-12-07 10:22:30 +0800150 if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
151 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
152 cper_print_bits(pfx, proc->proc_error_type,
Chen, Gong3760cd22014-06-11 13:59:45 -0700153 proc_error_type_strs,
154 ARRAY_SIZE(proc_error_type_strs));
Huang Yingf59c55d2010-12-07 10:22:30 +0800155 }
156 if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
157 printk("%s""operation: %d, %s\n", pfx, proc->operation,
Chen, Gong3760cd22014-06-11 13:59:45 -0700158 proc->operation < ARRAY_SIZE(proc_op_strs) ?
159 proc_op_strs[proc->operation] : "unknown");
Huang Yingf59c55d2010-12-07 10:22:30 +0800160 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
161 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
Chen, Gong3760cd22014-06-11 13:59:45 -0700162 cper_print_bits(pfx, proc->flags, proc_flag_strs,
163 ARRAY_SIZE(proc_flag_strs));
Huang Yingf59c55d2010-12-07 10:22:30 +0800164 }
165 if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
166 printk("%s""level: %d\n", pfx, proc->level);
167 if (proc->validation_bits & CPER_PROC_VALID_VERSION)
168 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
169 if (proc->validation_bits & CPER_PROC_VALID_ID)
170 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
171 if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
172 printk("%s""target_address: 0x%016llx\n",
173 pfx, proc->target_addr);
174 if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
175 printk("%s""requestor_id: 0x%016llx\n",
176 pfx, proc->requestor_id);
177 if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
178 printk("%s""responder_id: 0x%016llx\n",
179 pfx, proc->responder_id);
180 if (proc->validation_bits & CPER_PROC_VALID_IP)
181 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
182}
183
Chen, Gong3760cd22014-06-11 13:59:45 -0700184static const char * const mem_err_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800185 "unknown",
186 "no error",
187 "single-bit ECC",
188 "multi-bit ECC",
189 "single-symbol chipkill ECC",
190 "multi-symbol chipkill ECC",
191 "master abort",
192 "target abort",
193 "parity error",
194 "watchdog timeout",
195 "invalid address",
196 "mirror Broken",
197 "memory sparing",
198 "scrub corrected error",
199 "scrub uncorrected error",
Chen, Gong147de142013-10-18 14:30:13 -0700200 "physical memory map-out event",
Huang Yingf59c55d2010-12-07 10:22:30 +0800201};
202
Chen, Gong3760cd22014-06-11 13:59:45 -0700203const char *cper_mem_err_type_str(unsigned int etype)
204{
205 return etype < ARRAY_SIZE(mem_err_type_strs) ?
206 mem_err_type_strs[etype] : "unknown";
207}
208EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
209
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400210static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
Chen, Gong3760cd22014-06-11 13:59:45 -0700211{
212 u32 len, n;
213
214 if (!msg)
215 return 0;
216
217 n = 0;
218 len = CPER_REC_LEN - 1;
219 if (mem->validation_bits & CPER_MEM_VALID_NODE)
220 n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
221 if (mem->validation_bits & CPER_MEM_VALID_CARD)
222 n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
223 if (mem->validation_bits & CPER_MEM_VALID_MODULE)
224 n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
225 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
226 n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
227 if (mem->validation_bits & CPER_MEM_VALID_BANK)
228 n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
229 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
230 n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
231 if (mem->validation_bits & CPER_MEM_VALID_ROW)
232 n += scnprintf(msg + n, len - n, "row: %d ", mem->row);
233 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
234 n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
235 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
236 n += scnprintf(msg + n, len - n, "bit_position: %d ",
237 mem->bit_pos);
238 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
239 n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
240 mem->requestor_id);
241 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
242 n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
243 mem->responder_id);
244 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
245 scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
246 mem->target_id);
247
248 msg[n] = '\0';
249 return n;
250}
251
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400252static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
Chen, Gong3760cd22014-06-11 13:59:45 -0700253{
254 u32 len, n;
255 const char *bank = NULL, *device = NULL;
256
257 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
258 return 0;
259
260 n = 0;
261 len = CPER_REC_LEN - 1;
262 dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
263 if (bank && device)
264 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
265 else
266 n = snprintf(msg, len,
267 "DIMM location: not present. DMI handle: 0x%.4x ",
268 mem->mem_dev_handle);
269
270 msg[n] = '\0';
271 return n;
272}
273
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400274void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
275 struct cper_mem_err_compact *cmem)
276{
277 cmem->validation_bits = mem->validation_bits;
278 cmem->node = mem->node;
279 cmem->card = mem->card;
280 cmem->module = mem->module;
281 cmem->bank = mem->bank;
282 cmem->device = mem->device;
283 cmem->row = mem->row;
284 cmem->column = mem->column;
285 cmem->bit_pos = mem->bit_pos;
286 cmem->requestor_id = mem->requestor_id;
287 cmem->responder_id = mem->responder_id;
288 cmem->target_id = mem->target_id;
289 cmem->rank = mem->rank;
290 cmem->mem_array_handle = mem->mem_array_handle;
291 cmem->mem_dev_handle = mem->mem_dev_handle;
292}
293
294const char *cper_mem_err_unpack(struct trace_seq *p,
295 struct cper_mem_err_compact *cmem)
296{
Steven Rostedt (Red Hat)dbcf3e02014-10-31 19:43:08 -0400297 const char *ret = trace_seq_buffer_ptr(p);
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400298
299 if (cper_mem_err_location(cmem, rcd_decode_str))
300 trace_seq_printf(p, "%s", rcd_decode_str);
301 if (cper_dimm_err_location(cmem, rcd_decode_str))
302 trace_seq_printf(p, "%s", rcd_decode_str);
303 trace_seq_putc(p, '\0');
304
305 return ret;
306}
307
Luck, Tony4c623602015-06-30 15:57:51 -0700308static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
309 int len)
Huang Yingf59c55d2010-12-07 10:22:30 +0800310{
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400311 struct cper_mem_err_compact cmem;
312
Luck, Tony4c623602015-06-30 15:57:51 -0700313 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
314 if (len == sizeof(struct cper_sec_mem_err_old) &&
315 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
316 pr_err(FW_WARN "valid bits set for fields beyond structure\n");
317 return;
318 }
Huang Yingf59c55d2010-12-07 10:22:30 +0800319 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
320 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
Chen, Gong147de142013-10-18 14:30:13 -0700321 if (mem->validation_bits & CPER_MEM_VALID_PA)
Huang Yingf59c55d2010-12-07 10:22:30 +0800322 printk("%s""physical_address: 0x%016llx\n",
323 pfx, mem->physical_addr);
Chen, Gong147de142013-10-18 14:30:13 -0700324 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
Huang Yingf59c55d2010-12-07 10:22:30 +0800325 printk("%s""physical_address_mask: 0x%016llx\n",
326 pfx, mem->physical_addr_mask);
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400327 cper_mem_err_pack(mem, &cmem);
328 if (cper_mem_err_location(&cmem, rcd_decode_str))
Chen, Gong3760cd22014-06-11 13:59:45 -0700329 printk("%s%s\n", pfx, rcd_decode_str);
Huang Yingf59c55d2010-12-07 10:22:30 +0800330 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
331 u8 etype = mem->error_type;
332 printk("%s""error_type: %d, %s\n", pfx, etype,
Chen, Gong3760cd22014-06-11 13:59:45 -0700333 cper_mem_err_type_str(etype));
Huang Yingf59c55d2010-12-07 10:22:30 +0800334 }
Chen, Gong2dfb7d52014-06-17 22:33:07 -0400335 if (cper_dimm_err_location(&cmem, rcd_decode_str))
Chen, Gong3760cd22014-06-11 13:59:45 -0700336 printk("%s%s\n", pfx, rcd_decode_str);
Huang Yingf59c55d2010-12-07 10:22:30 +0800337}
338
Chen, Gong3760cd22014-06-11 13:59:45 -0700339static const char * const pcie_port_type_strs[] = {
Huang Yingf59c55d2010-12-07 10:22:30 +0800340 "PCIe end point",
341 "legacy PCI end point",
342 "unknown",
343 "unknown",
344 "root port",
345 "upstream switch port",
346 "downstream switch port",
347 "PCIe to PCI/PCI-X bridge",
348 "PCI/PCI-X to PCIe bridge",
349 "root complex integrated endpoint device",
350 "root complex event collector",
351};
352
Huang Yingc413d762011-02-21 13:54:43 +0800353static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
Lv Zheng0a00fd52014-06-03 16:32:53 +0800354 const struct acpi_hest_generic_data *gdata)
Huang Yingf59c55d2010-12-07 10:22:30 +0800355{
356 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
357 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
Chen, Gong3760cd22014-06-11 13:59:45 -0700358 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
359 pcie_port_type_strs[pcie->port_type] : "unknown");
Huang Yingf59c55d2010-12-07 10:22:30 +0800360 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
361 printk("%s""version: %d.%d\n", pfx,
362 pcie->version.major, pcie->version.minor);
363 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
364 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
365 pcie->command, pcie->status);
366 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
367 const __u8 *p;
368 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
369 pcie->device_id.segment, pcie->device_id.bus,
370 pcie->device_id.device, pcie->device_id.function);
371 printk("%s""slot: %d\n", pfx,
372 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
373 printk("%s""secondary_bus: 0x%02x\n", pfx,
374 pcie->device_id.secondary_bus);
375 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
376 pcie->device_id.vendor_id, pcie->device_id.device_id);
377 p = pcie->device_id.class_code;
378 printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]);
379 }
380 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
381 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
382 pcie->serial_number.lower, pcie->serial_number.upper);
383 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
384 printk(
385 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
386 pfx, pcie->bridge.secondary_status, pcie->bridge.control);
387}
388
Chen, Gong88f074f2013-10-18 14:28:59 -0700389static void cper_estatus_print_section(
Lv Zheng0a00fd52014-06-03 16:32:53 +0800390 const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no)
Huang Yingf59c55d2010-12-07 10:22:30 +0800391{
392 uuid_le *sec_type = (uuid_le *)gdata->section_type;
393 __u16 severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700394 char newpfx[64];
Huang Yingf59c55d2010-12-07 10:22:30 +0800395
396 severity = gdata->error_severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700397 printk("%s""Error %d, type: %s\n", pfx, sec_no,
Huang Yingf59c55d2010-12-07 10:22:30 +0800398 cper_severity_str(severity));
Huang Yingf59c55d2010-12-07 10:22:30 +0800399 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
400 printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id);
401 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
402 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
403
Chen, Gongf6edea72013-10-18 14:30:29 -0700404 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
Huang Yingf59c55d2010-12-07 10:22:30 +0800405 if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) {
406 struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700407 printk("%s""section_type: general processor error\n", newpfx);
Huang Yingf59c55d2010-12-07 10:22:30 +0800408 if (gdata->error_data_length >= sizeof(*proc_err))
Chen, Gongf6edea72013-10-18 14:30:29 -0700409 cper_print_proc_generic(newpfx, proc_err);
Huang Yingf59c55d2010-12-07 10:22:30 +0800410 else
411 goto err_section_too_small;
412 } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) {
413 struct cper_sec_mem_err *mem_err = (void *)(gdata + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700414 printk("%s""section_type: memory error\n", newpfx);
Luck, Tony4c623602015-06-30 15:57:51 -0700415 if (gdata->error_data_length >=
416 sizeof(struct cper_sec_mem_err_old))
417 cper_print_mem(newpfx, mem_err,
418 gdata->error_data_length);
Huang Yingf59c55d2010-12-07 10:22:30 +0800419 else
420 goto err_section_too_small;
421 } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) {
422 struct cper_sec_pcie *pcie = (void *)(gdata + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700423 printk("%s""section_type: PCIe error\n", newpfx);
Huang Yingf59c55d2010-12-07 10:22:30 +0800424 if (gdata->error_data_length >= sizeof(*pcie))
Chen, Gongf6edea72013-10-18 14:30:29 -0700425 cper_print_pcie(newpfx, pcie, gdata);
Huang Yingf59c55d2010-12-07 10:22:30 +0800426 else
427 goto err_section_too_small;
428 } else
Chen, Gongf6edea72013-10-18 14:30:29 -0700429 printk("%s""section type: unknown, %pUl\n", newpfx, sec_type);
Huang Yingf59c55d2010-12-07 10:22:30 +0800430
431 return;
432
433err_section_too_small:
434 pr_err(FW_WARN "error section length is too small\n");
435}
436
Chen, Gong88f074f2013-10-18 14:28:59 -0700437void cper_estatus_print(const char *pfx,
Lv Zheng0a00fd52014-06-03 16:32:53 +0800438 const struct acpi_hest_generic_status *estatus)
Huang Yingf59c55d2010-12-07 10:22:30 +0800439{
Lv Zheng0a00fd52014-06-03 16:32:53 +0800440 struct acpi_hest_generic_data *gdata;
Huang Yingf59c55d2010-12-07 10:22:30 +0800441 unsigned int data_len, gedata_len;
442 int sec_no = 0;
Chen, Gongf6edea72013-10-18 14:30:29 -0700443 char newpfx[64];
Huang Yingf59c55d2010-12-07 10:22:30 +0800444 __u16 severity;
445
Huang Yingf59c55d2010-12-07 10:22:30 +0800446 severity = estatus->error_severity;
Chen, Gongf6edea72013-10-18 14:30:29 -0700447 if (severity == CPER_SEV_CORRECTED)
448 printk("%s%s\n", pfx,
449 "It has been corrected by h/w "
450 "and requires no further action");
451 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
Huang Yingf59c55d2010-12-07 10:22:30 +0800452 data_len = estatus->data_length;
Lv Zheng0a00fd52014-06-03 16:32:53 +0800453 gdata = (struct acpi_hest_generic_data *)(estatus + 1);
Chen, Gongf6edea72013-10-18 14:30:29 -0700454 snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP);
Chen, Gong833ba4b2013-10-18 14:27:51 -0700455 while (data_len >= sizeof(*gdata)) {
Huang Yingf59c55d2010-12-07 10:22:30 +0800456 gedata_len = gdata->error_data_length;
Chen, Gongf6edea72013-10-18 14:30:29 -0700457 cper_estatus_print_section(newpfx, gdata, sec_no);
Huang Yingf59c55d2010-12-07 10:22:30 +0800458 data_len -= gedata_len + sizeof(*gdata);
Jiang Liu37d2a362012-02-15 00:01:44 +0800459 gdata = (void *)(gdata + 1) + gedata_len;
Huang Yingf59c55d2010-12-07 10:22:30 +0800460 sec_no++;
461 }
462}
Chen, Gong88f074f2013-10-18 14:28:59 -0700463EXPORT_SYMBOL_GPL(cper_estatus_print);
Huang Yingf59c55d2010-12-07 10:22:30 +0800464
Lv Zheng0a00fd52014-06-03 16:32:53 +0800465int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
Huang Ying06d65de2010-05-18 14:35:19 +0800466{
467 if (estatus->data_length &&
Lv Zheng0a00fd52014-06-03 16:32:53 +0800468 estatus->data_length < sizeof(struct acpi_hest_generic_data))
Huang Ying06d65de2010-05-18 14:35:19 +0800469 return -EINVAL;
470 if (estatus->raw_data_length &&
471 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
472 return -EINVAL;
473
474 return 0;
475}
Chen, Gong88f074f2013-10-18 14:28:59 -0700476EXPORT_SYMBOL_GPL(cper_estatus_check_header);
Huang Ying06d65de2010-05-18 14:35:19 +0800477
Lv Zheng0a00fd52014-06-03 16:32:53 +0800478int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
Huang Ying06d65de2010-05-18 14:35:19 +0800479{
Lv Zheng0a00fd52014-06-03 16:32:53 +0800480 struct acpi_hest_generic_data *gdata;
Huang Ying06d65de2010-05-18 14:35:19 +0800481 unsigned int data_len, gedata_len;
482 int rc;
483
Chen, Gong88f074f2013-10-18 14:28:59 -0700484 rc = cper_estatus_check_header(estatus);
Huang Ying06d65de2010-05-18 14:35:19 +0800485 if (rc)
486 return rc;
487 data_len = estatus->data_length;
Lv Zheng0a00fd52014-06-03 16:32:53 +0800488 gdata = (struct acpi_hest_generic_data *)(estatus + 1);
Chen Gongaaf9d932013-03-19 06:48:07 +0000489 while (data_len >= sizeof(*gdata)) {
Huang Ying06d65de2010-05-18 14:35:19 +0800490 gedata_len = gdata->error_data_length;
491 if (gedata_len > data_len - sizeof(*gdata))
492 return -EINVAL;
493 data_len -= gedata_len + sizeof(*gdata);
Jiang Liu37d2a362012-02-15 00:01:44 +0800494 gdata = (void *)(gdata + 1) + gedata_len;
Huang Ying06d65de2010-05-18 14:35:19 +0800495 }
496 if (data_len)
497 return -EINVAL;
498
499 return 0;
500}
Chen, Gong88f074f2013-10-18 14:28:59 -0700501EXPORT_SYMBOL_GPL(cper_estatus_check);