Commit | Line | Data |
---|---|---|
06d65dea HY |
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 | |
88f074f4 | 8 | * various tables, such as ERST, BERT and HEST etc. |
06d65dea HY |
9 | * |
10 | * For more information about CPER, please refer to Appendix N of UEFI | |
147de147 | 11 | * Specification version 2.4. |
06d65dea HY |
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> | |
fbeef85f | 31 | #include <linux/dmi.h> |
06d65dea | 32 | #include <linux/acpi.h> |
1d521000 | 33 | #include <linux/pci.h> |
c413d768 | 34 | #include <linux/aer.h> |
06d65dea | 35 | |
f6edea77 | 36 | #define INDENT_SP " " |
3760cd20 CG |
37 | |
38 | static char rcd_decode_str[CPER_REC_LEN]; | |
39 | ||
06d65dea HY |
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 | atomic64_set(&seq, ((u64)get_seconds()) << 32); | |
51 | ||
52 | return atomic64_inc_return(&seq); | |
53 | } | |
54 | EXPORT_SYMBOL_GPL(cper_next_record_id); | |
55 | ||
3760cd20 | 56 | static const char * const severity_strs[] = { |
f59c55d0 HY |
57 | "recoverable", |
58 | "fatal", | |
59 | "corrected", | |
60 | "info", | |
61 | }; | |
62 | ||
3760cd20 | 63 | const char *cper_severity_str(unsigned int severity) |
f59c55d0 | 64 | { |
3760cd20 CG |
65 | return severity < ARRAY_SIZE(severity_strs) ? |
66 | severity_strs[severity] : "unknown"; | |
f59c55d0 | 67 | } |
3760cd20 | 68 | EXPORT_SYMBOL_GPL(cper_severity_str); |
f59c55d0 HY |
69 | |
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 | */ | |
c413d768 | 81 | void cper_print_bits(const char *pfx, unsigned int bits, |
88f074f4 | 82 | const char * const strs[], unsigned int strs_size) |
f59c55d0 HY |
83 | { |
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]; | |
c413d768 HY |
92 | if (!str) |
93 | continue; | |
f59c55d0 HY |
94 | 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 | ||
3760cd20 | 107 | static const char * const proc_type_strs[] = { |
f59c55d0 HY |
108 | "IA32/X64", |
109 | "IA64", | |
110 | }; | |
111 | ||
3760cd20 | 112 | static const char * const proc_isa_strs[] = { |
f59c55d0 HY |
113 | "IA32", |
114 | "IA64", | |
115 | "X64", | |
116 | }; | |
117 | ||
3760cd20 | 118 | static const char * const proc_error_type_strs[] = { |
f59c55d0 HY |
119 | "cache error", |
120 | "TLB error", | |
121 | "bus error", | |
122 | "micro-architectural error", | |
123 | }; | |
124 | ||
3760cd20 | 125 | static const char * const proc_op_strs[] = { |
f59c55d0 HY |
126 | "unknown or generic", |
127 | "data read", | |
128 | "data write", | |
129 | "instruction execution", | |
130 | }; | |
131 | ||
3760cd20 | 132 | static const char * const proc_flag_strs[] = { |
f59c55d0 HY |
133 | "restartable", |
134 | "precise IP", | |
135 | "overflow", | |
136 | "corrected", | |
137 | }; | |
138 | ||
139 | static 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, | |
3760cd20 CG |
144 | proc->proc_type < ARRAY_SIZE(proc_type_strs) ? |
145 | proc_type_strs[proc->proc_type] : "unknown"); | |
f59c55d0 HY |
146 | if (proc->validation_bits & CPER_PROC_VALID_ISA) |
147 | printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, | |
3760cd20 CG |
148 | proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? |
149 | proc_isa_strs[proc->proc_isa] : "unknown"); | |
f59c55d0 HY |
150 | 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, | |
3760cd20 CG |
153 | proc_error_type_strs, |
154 | ARRAY_SIZE(proc_error_type_strs)); | |
f59c55d0 HY |
155 | } |
156 | if (proc->validation_bits & CPER_PROC_VALID_OPERATION) | |
157 | printk("%s""operation: %d, %s\n", pfx, proc->operation, | |
3760cd20 CG |
158 | proc->operation < ARRAY_SIZE(proc_op_strs) ? |
159 | proc_op_strs[proc->operation] : "unknown"); | |
f59c55d0 HY |
160 | if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { |
161 | printk("%s""flags: 0x%02x\n", pfx, proc->flags); | |
3760cd20 CG |
162 | cper_print_bits(pfx, proc->flags, proc_flag_strs, |
163 | ARRAY_SIZE(proc_flag_strs)); | |
f59c55d0 HY |
164 | } |
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 | ||
3760cd20 | 184 | static const char * const mem_err_type_strs[] = { |
f59c55d0 HY |
185 | "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", | |
147de147 | 200 | "physical memory map-out event", |
f59c55d0 HY |
201 | }; |
202 | ||
3760cd20 | 203 | const char *cper_mem_err_type_str(unsigned int etype) |
f59c55d0 | 204 | { |
3760cd20 CG |
205 | return etype < ARRAY_SIZE(mem_err_type_strs) ? |
206 | mem_err_type_strs[etype] : "unknown"; | |
207 | } | |
208 | EXPORT_SYMBOL_GPL(cper_mem_err_type_str); | |
209 | ||
2dfb7d51 | 210 | static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) |
3760cd20 CG |
211 | { |
212 | u32 len, n; | |
213 | ||
214 | if (!msg) | |
215 | return 0; | |
216 | ||
217 | n = 0; | |
218 | len = CPER_REC_LEN - 1; | |
f59c55d0 | 219 | if (mem->validation_bits & CPER_MEM_VALID_NODE) |
3760cd20 | 220 | n += scnprintf(msg + n, len - n, "node: %d ", mem->node); |
f59c55d0 | 221 | if (mem->validation_bits & CPER_MEM_VALID_CARD) |
3760cd20 | 222 | n += scnprintf(msg + n, len - n, "card: %d ", mem->card); |
f59c55d0 | 223 | if (mem->validation_bits & CPER_MEM_VALID_MODULE) |
3760cd20 | 224 | n += scnprintf(msg + n, len - n, "module: %d ", mem->module); |
fbeef85f | 225 | if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) |
3760cd20 | 226 | n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); |
f59c55d0 | 227 | if (mem->validation_bits & CPER_MEM_VALID_BANK) |
3760cd20 | 228 | n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); |
f59c55d0 | 229 | if (mem->validation_bits & CPER_MEM_VALID_DEVICE) |
3760cd20 | 230 | n += scnprintf(msg + n, len - n, "device: %d ", mem->device); |
f59c55d0 | 231 | if (mem->validation_bits & CPER_MEM_VALID_ROW) |
3760cd20 | 232 | n += scnprintf(msg + n, len - n, "row: %d ", mem->row); |
f59c55d0 | 233 | if (mem->validation_bits & CPER_MEM_VALID_COLUMN) |
3760cd20 | 234 | n += scnprintf(msg + n, len - n, "column: %d ", mem->column); |
f59c55d0 | 235 | if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) |
3760cd20 CG |
236 | n += scnprintf(msg + n, len - n, "bit_position: %d ", |
237 | mem->bit_pos); | |
f59c55d0 | 238 | if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) |
3760cd20 CG |
239 | n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", |
240 | mem->requestor_id); | |
f59c55d0 | 241 | if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) |
3760cd20 CG |
242 | n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", |
243 | mem->responder_id); | |
f59c55d0 | 244 | if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) |
3760cd20 CG |
245 | scnprintf(msg + n, len - n, "target_id: 0x%016llx ", |
246 | mem->target_id); | |
247 | ||
248 | msg[n] = '\0'; | |
249 | return n; | |
250 | } | |
251 | ||
2dfb7d51 | 252 | static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) |
3760cd20 CG |
253 | { |
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 | ||
2dfb7d51 CG |
274 | void 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 | ||
294 | const char *cper_mem_err_unpack(struct trace_seq *p, | |
295 | struct cper_mem_err_compact *cmem) | |
296 | { | |
dbcf3e06 | 297 | const char *ret = trace_seq_buffer_ptr(p); |
2dfb7d51 CG |
298 | |
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 | ||
3760cd20 CG |
308 | static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) |
309 | { | |
2dfb7d51 CG |
310 | struct cper_mem_err_compact cmem; |
311 | ||
3760cd20 CG |
312 | if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) |
313 | printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); | |
314 | if (mem->validation_bits & CPER_MEM_VALID_PA) | |
315 | printk("%s""physical_address: 0x%016llx\n", | |
316 | pfx, mem->physical_addr); | |
317 | if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) | |
318 | printk("%s""physical_address_mask: 0x%016llx\n", | |
319 | pfx, mem->physical_addr_mask); | |
2dfb7d51 CG |
320 | cper_mem_err_pack(mem, &cmem); |
321 | if (cper_mem_err_location(&cmem, rcd_decode_str)) | |
3760cd20 | 322 | printk("%s%s\n", pfx, rcd_decode_str); |
f59c55d0 HY |
323 | if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { |
324 | u8 etype = mem->error_type; | |
325 | printk("%s""error_type: %d, %s\n", pfx, etype, | |
3760cd20 | 326 | cper_mem_err_type_str(etype)); |
fbeef85f | 327 | } |
2dfb7d51 | 328 | if (cper_dimm_err_location(&cmem, rcd_decode_str)) |
3760cd20 | 329 | printk("%s%s\n", pfx, rcd_decode_str); |
f59c55d0 HY |
330 | } |
331 | ||
3760cd20 | 332 | static const char * const pcie_port_type_strs[] = { |
f59c55d0 HY |
333 | "PCIe end point", |
334 | "legacy PCI end point", | |
335 | "unknown", | |
336 | "unknown", | |
337 | "root port", | |
338 | "upstream switch port", | |
339 | "downstream switch port", | |
340 | "PCIe to PCI/PCI-X bridge", | |
341 | "PCI/PCI-X to PCIe bridge", | |
342 | "root complex integrated endpoint device", | |
343 | "root complex event collector", | |
344 | }; | |
345 | ||
c413d768 | 346 | static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, |
0a00fd5e | 347 | const struct acpi_hest_generic_data *gdata) |
f59c55d0 HY |
348 | { |
349 | if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) | |
350 | printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, | |
3760cd20 CG |
351 | pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? |
352 | pcie_port_type_strs[pcie->port_type] : "unknown"); | |
f59c55d0 HY |
353 | if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) |
354 | printk("%s""version: %d.%d\n", pfx, | |
355 | pcie->version.major, pcie->version.minor); | |
356 | if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) | |
357 | printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, | |
358 | pcie->command, pcie->status); | |
359 | if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { | |
360 | const __u8 *p; | |
361 | printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, | |
362 | pcie->device_id.segment, pcie->device_id.bus, | |
363 | pcie->device_id.device, pcie->device_id.function); | |
364 | printk("%s""slot: %d\n", pfx, | |
365 | pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); | |
366 | printk("%s""secondary_bus: 0x%02x\n", pfx, | |
367 | pcie->device_id.secondary_bus); | |
368 | printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, | |
369 | pcie->device_id.vendor_id, pcie->device_id.device_id); | |
370 | p = pcie->device_id.class_code; | |
371 | printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); | |
372 | } | |
373 | if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) | |
374 | printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, | |
375 | pcie->serial_number.lower, pcie->serial_number.upper); | |
376 | if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) | |
377 | printk( | |
378 | "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", | |
379 | pfx, pcie->bridge.secondary_status, pcie->bridge.control); | |
380 | } | |
381 | ||
88f074f4 | 382 | static void cper_estatus_print_section( |
0a00fd5e | 383 | const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no) |
f59c55d0 HY |
384 | { |
385 | uuid_le *sec_type = (uuid_le *)gdata->section_type; | |
386 | __u16 severity; | |
f6edea77 | 387 | char newpfx[64]; |
f59c55d0 HY |
388 | |
389 | severity = gdata->error_severity; | |
f6edea77 | 390 | printk("%s""Error %d, type: %s\n", pfx, sec_no, |
f59c55d0 | 391 | cper_severity_str(severity)); |
f59c55d0 HY |
392 | if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) |
393 | printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); | |
394 | if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) | |
395 | printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); | |
396 | ||
f6edea77 | 397 | snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); |
f59c55d0 HY |
398 | if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { |
399 | struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); | |
f6edea77 | 400 | printk("%s""section_type: general processor error\n", newpfx); |
f59c55d0 | 401 | if (gdata->error_data_length >= sizeof(*proc_err)) |
f6edea77 | 402 | cper_print_proc_generic(newpfx, proc_err); |
f59c55d0 HY |
403 | else |
404 | goto err_section_too_small; | |
405 | } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { | |
406 | struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); | |
f6edea77 | 407 | printk("%s""section_type: memory error\n", newpfx); |
f59c55d0 | 408 | if (gdata->error_data_length >= sizeof(*mem_err)) |
f6edea77 | 409 | cper_print_mem(newpfx, mem_err); |
f59c55d0 HY |
410 | else |
411 | goto err_section_too_small; | |
412 | } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { | |
413 | struct cper_sec_pcie *pcie = (void *)(gdata + 1); | |
f6edea77 | 414 | printk("%s""section_type: PCIe error\n", newpfx); |
f59c55d0 | 415 | if (gdata->error_data_length >= sizeof(*pcie)) |
f6edea77 | 416 | cper_print_pcie(newpfx, pcie, gdata); |
f59c55d0 HY |
417 | else |
418 | goto err_section_too_small; | |
419 | } else | |
f6edea77 | 420 | printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); |
f59c55d0 HY |
421 | |
422 | return; | |
423 | ||
424 | err_section_too_small: | |
425 | pr_err(FW_WARN "error section length is too small\n"); | |
426 | } | |
427 | ||
88f074f4 | 428 | void cper_estatus_print(const char *pfx, |
0a00fd5e | 429 | const struct acpi_hest_generic_status *estatus) |
f59c55d0 | 430 | { |
0a00fd5e | 431 | struct acpi_hest_generic_data *gdata; |
f59c55d0 HY |
432 | unsigned int data_len, gedata_len; |
433 | int sec_no = 0; | |
f6edea77 | 434 | char newpfx[64]; |
f59c55d0 HY |
435 | __u16 severity; |
436 | ||
f59c55d0 | 437 | severity = estatus->error_severity; |
f6edea77 CG |
438 | if (severity == CPER_SEV_CORRECTED) |
439 | printk("%s%s\n", pfx, | |
440 | "It has been corrected by h/w " | |
441 | "and requires no further action"); | |
442 | printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); | |
f59c55d0 | 443 | data_len = estatus->data_length; |
0a00fd5e | 444 | gdata = (struct acpi_hest_generic_data *)(estatus + 1); |
f6edea77 | 445 | snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); |
833ba4b1 | 446 | while (data_len >= sizeof(*gdata)) { |
f59c55d0 | 447 | gedata_len = gdata->error_data_length; |
f6edea77 | 448 | cper_estatus_print_section(newpfx, gdata, sec_no); |
f59c55d0 | 449 | data_len -= gedata_len + sizeof(*gdata); |
37d2a362 | 450 | gdata = (void *)(gdata + 1) + gedata_len; |
f59c55d0 HY |
451 | sec_no++; |
452 | } | |
453 | } | |
88f074f4 | 454 | EXPORT_SYMBOL_GPL(cper_estatus_print); |
f59c55d0 | 455 | |
0a00fd5e | 456 | int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) |
06d65dea HY |
457 | { |
458 | if (estatus->data_length && | |
0a00fd5e | 459 | estatus->data_length < sizeof(struct acpi_hest_generic_data)) |
06d65dea HY |
460 | return -EINVAL; |
461 | if (estatus->raw_data_length && | |
462 | estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) | |
463 | return -EINVAL; | |
464 | ||
465 | return 0; | |
466 | } | |
88f074f4 | 467 | EXPORT_SYMBOL_GPL(cper_estatus_check_header); |
06d65dea | 468 | |
0a00fd5e | 469 | int cper_estatus_check(const struct acpi_hest_generic_status *estatus) |
06d65dea | 470 | { |
0a00fd5e | 471 | struct acpi_hest_generic_data *gdata; |
06d65dea HY |
472 | unsigned int data_len, gedata_len; |
473 | int rc; | |
474 | ||
88f074f4 | 475 | rc = cper_estatus_check_header(estatus); |
06d65dea HY |
476 | if (rc) |
477 | return rc; | |
478 | data_len = estatus->data_length; | |
0a00fd5e | 479 | gdata = (struct acpi_hest_generic_data *)(estatus + 1); |
aaf9d93b | 480 | while (data_len >= sizeof(*gdata)) { |
06d65dea HY |
481 | gedata_len = gdata->error_data_length; |
482 | if (gedata_len > data_len - sizeof(*gdata)) | |
483 | return -EINVAL; | |
484 | data_len -= gedata_len + sizeof(*gdata); | |
37d2a362 | 485 | gdata = (void *)(gdata + 1) + gedata_len; |
06d65dea HY |
486 | } |
487 | if (data_len) | |
488 | return -EINVAL; | |
489 | ||
490 | return 0; | |
491 | } | |
88f074f4 | 492 | EXPORT_SYMBOL_GPL(cper_estatus_check); |