]>
Commit | Line | Data |
---|---|---|
4febfb8d | 1 | // SPDX-License-Identifier: GPL-2.0 |
06d65dea HY |
2 | /* |
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 | |
88f074f4 | 9 | * various tables, such as ERST, BERT and HEST etc. |
06d65dea HY |
10 | * |
11 | * For more information about CPER, please refer to Appendix N of UEFI | |
147de147 | 12 | * Specification version 2.4. |
06d65dea HY |
13 | */ |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/time.h> | |
18 | #include <linux/cper.h> | |
fbeef85f | 19 | #include <linux/dmi.h> |
06d65dea | 20 | #include <linux/acpi.h> |
1d521000 | 21 | #include <linux/pci.h> |
c413d768 | 22 | #include <linux/aer.h> |
8a94471f TB |
23 | #include <linux/printk.h> |
24 | #include <linux/bcd.h> | |
bbcc2e7b | 25 | #include <acpi/ghes.h> |
e9279e83 | 26 | #include <ras/ras_event.h> |
06d65dea | 27 | |
3760cd20 CG |
28 | static char rcd_decode_str[CPER_REC_LEN]; |
29 | ||
06d65dea HY |
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 | */ | |
35 | u64 cper_next_record_id(void) | |
36 | { | |
37 | static atomic64_t seq; | |
38 | ||
7bb49709 AB |
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 | } | |
06d65dea HY |
54 | |
55 | return atomic64_inc_return(&seq); | |
56 | } | |
57 | EXPORT_SYMBOL_GPL(cper_next_record_id); | |
58 | ||
3760cd20 | 59 | static const char * const severity_strs[] = { |
f59c55d0 HY |
60 | "recoverable", |
61 | "fatal", | |
62 | "corrected", | |
63 | "info", | |
64 | }; | |
65 | ||
3760cd20 | 66 | const char *cper_severity_str(unsigned int severity) |
f59c55d0 | 67 | { |
3760cd20 CG |
68 | return severity < ARRAY_SIZE(severity_strs) ? |
69 | severity_strs[severity] : "unknown"; | |
f59c55d0 | 70 | } |
3760cd20 | 71 | EXPORT_SYMBOL_GPL(cper_severity_str); |
f59c55d0 HY |
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 | */ | |
c413d768 | 84 | void cper_print_bits(const char *pfx, unsigned int bits, |
88f074f4 | 85 | const char * const strs[], unsigned int strs_size) |
f59c55d0 HY |
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]; | |
c413d768 HY |
95 | if (!str) |
96 | continue; | |
f59c55d0 HY |
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 | |
b450b30b | 104 | len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str); |
f59c55d0 HY |
105 | } |
106 | if (len) | |
107 | printk("%s\n", buf); | |
108 | } | |
109 | ||
3760cd20 | 110 | static const char * const proc_type_strs[] = { |
f59c55d0 HY |
111 | "IA32/X64", |
112 | "IA64", | |
2f74f09b | 113 | "ARM", |
f59c55d0 HY |
114 | }; |
115 | ||
3760cd20 | 116 | static const char * const proc_isa_strs[] = { |
f59c55d0 HY |
117 | "IA32", |
118 | "IA64", | |
119 | "X64", | |
2f74f09b TB |
120 | "ARM A32/T32", |
121 | "ARM A64", | |
f59c55d0 HY |
122 | }; |
123 | ||
c6d8c8ef | 124 | const char * const cper_proc_error_type_strs[] = { |
f59c55d0 HY |
125 | "cache error", |
126 | "TLB error", | |
127 | "bus error", | |
128 | "micro-architectural error", | |
129 | }; | |
130 | ||
3760cd20 | 131 | static const char * const proc_op_strs[] = { |
f59c55d0 HY |
132 | "unknown or generic", |
133 | "data read", | |
134 | "data write", | |
135 | "instruction execution", | |
136 | }; | |
137 | ||
3760cd20 | 138 | static const char * const proc_flag_strs[] = { |
f59c55d0 HY |
139 | "restartable", |
140 | "precise IP", | |
141 | "overflow", | |
142 | "corrected", | |
143 | }; | |
144 | ||
145 | static 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, | |
3760cd20 CG |
150 | proc->proc_type < ARRAY_SIZE(proc_type_strs) ? |
151 | proc_type_strs[proc->proc_type] : "unknown"); | |
f59c55d0 HY |
152 | if (proc->validation_bits & CPER_PROC_VALID_ISA) |
153 | printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, | |
3760cd20 CG |
154 | proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? |
155 | proc_isa_strs[proc->proc_isa] : "unknown"); | |
f59c55d0 HY |
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, | |
c6d8c8ef TB |
159 | cper_proc_error_type_strs, |
160 | ARRAY_SIZE(cper_proc_error_type_strs)); | |
f59c55d0 HY |
161 | } |
162 | if (proc->validation_bits & CPER_PROC_VALID_OPERATION) | |
163 | printk("%s""operation: %d, %s\n", pfx, proc->operation, | |
3760cd20 CG |
164 | proc->operation < ARRAY_SIZE(proc_op_strs) ? |
165 | proc_op_strs[proc->operation] : "unknown"); | |
f59c55d0 HY |
166 | if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { |
167 | printk("%s""flags: 0x%02x\n", pfx, proc->flags); | |
3760cd20 CG |
168 | cper_print_bits(pfx, proc->flags, proc_flag_strs, |
169 | ARRAY_SIZE(proc_flag_strs)); | |
f59c55d0 HY |
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 | ||
3760cd20 | 190 | static const char * const mem_err_type_strs[] = { |
f59c55d0 HY |
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", | |
147de147 | 206 | "physical memory map-out event", |
f59c55d0 HY |
207 | }; |
208 | ||
3760cd20 | 209 | const char *cper_mem_err_type_str(unsigned int etype) |
f59c55d0 | 210 | { |
3760cd20 CG |
211 | return etype < ARRAY_SIZE(mem_err_type_strs) ? |
212 | mem_err_type_strs[etype] : "unknown"; | |
213 | } | |
214 | EXPORT_SYMBOL_GPL(cper_mem_err_type_str); | |
215 | ||
2dfb7d51 | 216 | static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) |
3760cd20 CG |
217 | { |
218 | u32 len, n; | |
219 | ||
220 | if (!msg) | |
221 | return 0; | |
222 | ||
223 | n = 0; | |
224 | len = CPER_REC_LEN - 1; | |
f59c55d0 | 225 | if (mem->validation_bits & CPER_MEM_VALID_NODE) |
3760cd20 | 226 | n += scnprintf(msg + n, len - n, "node: %d ", mem->node); |
f59c55d0 | 227 | if (mem->validation_bits & CPER_MEM_VALID_CARD) |
3760cd20 | 228 | n += scnprintf(msg + n, len - n, "card: %d ", mem->card); |
f59c55d0 | 229 | if (mem->validation_bits & CPER_MEM_VALID_MODULE) |
3760cd20 | 230 | n += scnprintf(msg + n, len - n, "module: %d ", mem->module); |
fbeef85f | 231 | if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) |
3760cd20 | 232 | n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); |
f59c55d0 | 233 | if (mem->validation_bits & CPER_MEM_VALID_BANK) |
3760cd20 | 234 | n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); |
f59c55d0 | 235 | if (mem->validation_bits & CPER_MEM_VALID_DEVICE) |
3760cd20 | 236 | n += scnprintf(msg + n, len - n, "device: %d ", mem->device); |
f59c55d0 | 237 | if (mem->validation_bits & CPER_MEM_VALID_ROW) |
3760cd20 | 238 | n += scnprintf(msg + n, len - n, "row: %d ", mem->row); |
f59c55d0 | 239 | if (mem->validation_bits & CPER_MEM_VALID_COLUMN) |
3760cd20 | 240 | n += scnprintf(msg + n, len - n, "column: %d ", mem->column); |
f59c55d0 | 241 | if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) |
3760cd20 CG |
242 | n += scnprintf(msg + n, len - n, "bit_position: %d ", |
243 | mem->bit_pos); | |
f59c55d0 | 244 | if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) |
3760cd20 CG |
245 | n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", |
246 | mem->requestor_id); | |
f59c55d0 | 247 | if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) |
3760cd20 CG |
248 | n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", |
249 | mem->responder_id); | |
f59c55d0 | 250 | if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) |
3760cd20 CG |
251 | scnprintf(msg + n, len - n, "target_id: 0x%016llx ", |
252 | mem->target_id); | |
253 | ||
254 | msg[n] = '\0'; | |
255 | return n; | |
256 | } | |
257 | ||
2dfb7d51 | 258 | static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) |
3760cd20 CG |
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 | ||
266 | n = 0; | |
267 | len = CPER_REC_LEN - 1; | |
268 | dmi_memdev_name(mem->mem_dev_handle, &bank, &device); | |
269 | if (bank && device) | |
270 | n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); | |
271 | else | |
272 | n = snprintf(msg, len, | |
273 | "DIMM location: not present. DMI handle: 0x%.4x ", | |
274 | mem->mem_dev_handle); | |
275 | ||
276 | msg[n] = '\0'; | |
277 | return n; | |
278 | } | |
279 | ||
2dfb7d51 CG |
280 | void cper_mem_err_pack(const struct cper_sec_mem_err *mem, |
281 | struct cper_mem_err_compact *cmem) | |
282 | { | |
283 | cmem->validation_bits = mem->validation_bits; | |
284 | cmem->node = mem->node; | |
285 | cmem->card = mem->card; | |
286 | cmem->module = mem->module; | |
287 | cmem->bank = mem->bank; | |
288 | cmem->device = mem->device; | |
289 | cmem->row = mem->row; | |
290 | cmem->column = mem->column; | |
291 | cmem->bit_pos = mem->bit_pos; | |
292 | cmem->requestor_id = mem->requestor_id; | |
293 | cmem->responder_id = mem->responder_id; | |
294 | cmem->target_id = mem->target_id; | |
295 | cmem->rank = mem->rank; | |
296 | cmem->mem_array_handle = mem->mem_array_handle; | |
297 | cmem->mem_dev_handle = mem->mem_dev_handle; | |
298 | } | |
299 | ||
300 | const char *cper_mem_err_unpack(struct trace_seq *p, | |
301 | struct cper_mem_err_compact *cmem) | |
302 | { | |
dbcf3e06 | 303 | const char *ret = trace_seq_buffer_ptr(p); |
2dfb7d51 CG |
304 | |
305 | if (cper_mem_err_location(cmem, rcd_decode_str)) | |
306 | trace_seq_printf(p, "%s", rcd_decode_str); | |
307 | if (cper_dimm_err_location(cmem, rcd_decode_str)) | |
308 | trace_seq_printf(p, "%s", rcd_decode_str); | |
309 | trace_seq_putc(p, '\0'); | |
310 | ||
311 | return ret; | |
312 | } | |
313 | ||
4c62360d TL |
314 | static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, |
315 | int len) | |
3760cd20 | 316 | { |
2dfb7d51 CG |
317 | struct cper_mem_err_compact cmem; |
318 | ||
4c62360d TL |
319 | /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ |
320 | if (len == sizeof(struct cper_sec_mem_err_old) && | |
321 | (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { | |
322 | pr_err(FW_WARN "valid bits set for fields beyond structure\n"); | |
323 | return; | |
324 | } | |
3760cd20 CG |
325 | if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) |
326 | printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); | |
327 | if (mem->validation_bits & CPER_MEM_VALID_PA) | |
328 | printk("%s""physical_address: 0x%016llx\n", | |
329 | pfx, mem->physical_addr); | |
330 | if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) | |
331 | printk("%s""physical_address_mask: 0x%016llx\n", | |
332 | pfx, mem->physical_addr_mask); | |
2dfb7d51 CG |
333 | cper_mem_err_pack(mem, &cmem); |
334 | if (cper_mem_err_location(&cmem, rcd_decode_str)) | |
3760cd20 | 335 | printk("%s%s\n", pfx, rcd_decode_str); |
f59c55d0 HY |
336 | if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { |
337 | u8 etype = mem->error_type; | |
338 | printk("%s""error_type: %d, %s\n", pfx, etype, | |
3760cd20 | 339 | cper_mem_err_type_str(etype)); |
fbeef85f | 340 | } |
2dfb7d51 | 341 | if (cper_dimm_err_location(&cmem, rcd_decode_str)) |
3760cd20 | 342 | printk("%s%s\n", pfx, rcd_decode_str); |
f59c55d0 HY |
343 | } |
344 | ||
3760cd20 | 345 | static const char * const pcie_port_type_strs[] = { |
f59c55d0 HY |
346 | "PCIe end point", |
347 | "legacy PCI end point", | |
348 | "unknown", | |
349 | "unknown", | |
350 | "root port", | |
351 | "upstream switch port", | |
352 | "downstream switch port", | |
353 | "PCIe to PCI/PCI-X bridge", | |
354 | "PCI/PCI-X to PCIe bridge", | |
355 | "root complex integrated endpoint device", | |
356 | "root complex event collector", | |
357 | }; | |
358 | ||
c413d768 | 359 | static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, |
0a00fd5e | 360 | const struct acpi_hest_generic_data *gdata) |
f59c55d0 HY |
361 | { |
362 | if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) | |
363 | printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, | |
3760cd20 CG |
364 | pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? |
365 | pcie_port_type_strs[pcie->port_type] : "unknown"); | |
f59c55d0 HY |
366 | if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) |
367 | printk("%s""version: %d.%d\n", pfx, | |
368 | pcie->version.major, pcie->version.minor); | |
369 | if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) | |
370 | printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, | |
371 | pcie->command, pcie->status); | |
372 | if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { | |
373 | const __u8 *p; | |
374 | printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, | |
375 | pcie->device_id.segment, pcie->device_id.bus, | |
376 | pcie->device_id.device, pcie->device_id.function); | |
377 | printk("%s""slot: %d\n", pfx, | |
378 | pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); | |
379 | printk("%s""secondary_bus: 0x%02x\n", pfx, | |
380 | pcie->device_id.secondary_bus); | |
381 | printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, | |
382 | pcie->device_id.vendor_id, pcie->device_id.device_id); | |
383 | p = pcie->device_id.class_code; | |
6fb9367a | 384 | printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); |
f59c55d0 HY |
385 | } |
386 | if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) | |
387 | printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, | |
388 | pcie->serial_number.lower, pcie->serial_number.upper); | |
389 | if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) | |
390 | printk( | |
391 | "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", | |
392 | pfx, pcie->bridge.secondary_status, pcie->bridge.control); | |
b194a77f XT |
393 | |
394 | /* Fatal errors call __ghes_panic() before AER handler prints this */ | |
395 | if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && | |
396 | (gdata->error_severity & CPER_SEV_FATAL)) { | |
397 | struct aer_capability_regs *aer; | |
398 | ||
399 | aer = (struct aer_capability_regs *)pcie->aer_info; | |
400 | printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", | |
401 | pfx, aer->uncor_status, aer->uncor_mask); | |
402 | printk("%saer_uncor_severity: 0x%08x\n", | |
403 | pfx, aer->uncor_severity); | |
404 | printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, | |
405 | aer->header_log.dw0, aer->header_log.dw1, | |
406 | aer->header_log.dw2, aer->header_log.dw3); | |
407 | } | |
f59c55d0 HY |
408 | } |
409 | ||
8a94471f TB |
410 | static void cper_print_tstamp(const char *pfx, |
411 | struct acpi_hest_generic_data_v300 *gdata) | |
412 | { | |
413 | __u8 hour, min, sec, day, mon, year, century, *timestamp; | |
414 | ||
415 | if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { | |
416 | timestamp = (__u8 *)&(gdata->time_stamp); | |
417 | sec = bcd2bin(timestamp[0]); | |
418 | min = bcd2bin(timestamp[1]); | |
419 | hour = bcd2bin(timestamp[2]); | |
420 | day = bcd2bin(timestamp[4]); | |
421 | mon = bcd2bin(timestamp[5]); | |
422 | year = bcd2bin(timestamp[6]); | |
423 | century = bcd2bin(timestamp[7]); | |
424 | ||
425 | printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, | |
426 | (timestamp[3] & 0x1 ? "precise " : "imprecise "), | |
427 | century, year, mon, day, hour, min, sec); | |
428 | } | |
429 | } | |
430 | ||
bbcc2e7b TB |
431 | static void |
432 | cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, | |
433 | int sec_no) | |
f59c55d0 | 434 | { |
c0020756 | 435 | guid_t *sec_type = (guid_t *)gdata->section_type; |
f59c55d0 | 436 | __u16 severity; |
f6edea77 | 437 | char newpfx[64]; |
f59c55d0 | 438 | |
8a94471f TB |
439 | if (acpi_hest_get_version(gdata) >= 3) |
440 | cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); | |
441 | ||
f59c55d0 | 442 | severity = gdata->error_severity; |
f6edea77 | 443 | printk("%s""Error %d, type: %s\n", pfx, sec_no, |
f59c55d0 | 444 | cper_severity_str(severity)); |
f59c55d0 | 445 | if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) |
c0020756 | 446 | printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); |
f59c55d0 HY |
447 | if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) |
448 | printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); | |
449 | ||
75e4fd31 | 450 | snprintf(newpfx, sizeof(newpfx), "%s ", pfx); |
c0020756 | 451 | if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { |
bbcc2e7b TB |
452 | struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); |
453 | ||
f6edea77 | 454 | printk("%s""section_type: general processor error\n", newpfx); |
f59c55d0 | 455 | if (gdata->error_data_length >= sizeof(*proc_err)) |
f6edea77 | 456 | cper_print_proc_generic(newpfx, proc_err); |
f59c55d0 HY |
457 | else |
458 | goto err_section_too_small; | |
c0020756 | 459 | } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { |
bbcc2e7b TB |
460 | struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); |
461 | ||
f6edea77 | 462 | printk("%s""section_type: memory error\n", newpfx); |
4c62360d TL |
463 | if (gdata->error_data_length >= |
464 | sizeof(struct cper_sec_mem_err_old)) | |
465 | cper_print_mem(newpfx, mem_err, | |
466 | gdata->error_data_length); | |
f59c55d0 HY |
467 | else |
468 | goto err_section_too_small; | |
c0020756 | 469 | } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { |
bbcc2e7b TB |
470 | struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); |
471 | ||
f6edea77 | 472 | printk("%s""section_type: PCIe error\n", newpfx); |
f59c55d0 | 473 | if (gdata->error_data_length >= sizeof(*pcie)) |
f6edea77 | 474 | cper_print_pcie(newpfx, pcie, gdata); |
f59c55d0 HY |
475 | else |
476 | goto err_section_too_small; | |
2f74f09b | 477 | #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) |
e8f4194d | 478 | } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { |
2f74f09b TB |
479 | struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); |
480 | ||
481 | printk("%ssection_type: ARM processor error\n", newpfx); | |
482 | if (gdata->error_data_length >= sizeof(*arm_err)) | |
483 | cper_print_proc_arm(newpfx, arm_err); | |
f9e1bdb9 YG |
484 | else |
485 | goto err_section_too_small; | |
486 | #endif | |
487 | #if defined(CONFIG_UEFI_CPER_X86) | |
488 | } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { | |
489 | struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); | |
490 | ||
491 | printk("%ssection_type: IA32/X64 processor error\n", newpfx); | |
492 | if (gdata->error_data_length >= sizeof(*ia_err)) | |
493 | cper_print_proc_ia(newpfx, ia_err); | |
2f74f09b TB |
494 | else |
495 | goto err_section_too_small; | |
496 | #endif | |
0fc300f4 TB |
497 | } else { |
498 | const void *err = acpi_hest_get_payload(gdata); | |
499 | ||
500 | printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); | |
501 | printk("%ssection length: %#x\n", newpfx, | |
502 | gdata->error_data_length); | |
503 | print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, | |
504 | gdata->error_data_length, true); | |
505 | } | |
f59c55d0 HY |
506 | |
507 | return; | |
508 | ||
509 | err_section_too_small: | |
510 | pr_err(FW_WARN "error section length is too small\n"); | |
511 | } | |
512 | ||
88f074f4 | 513 | void cper_estatus_print(const char *pfx, |
0a00fd5e | 514 | const struct acpi_hest_generic_status *estatus) |
f59c55d0 | 515 | { |
0a00fd5e | 516 | struct acpi_hest_generic_data *gdata; |
f59c55d0 | 517 | int sec_no = 0; |
f6edea77 | 518 | char newpfx[64]; |
f59c55d0 HY |
519 | __u16 severity; |
520 | ||
f59c55d0 | 521 | severity = estatus->error_severity; |
f6edea77 CG |
522 | if (severity == CPER_SEV_CORRECTED) |
523 | printk("%s%s\n", pfx, | |
524 | "It has been corrected by h/w " | |
525 | "and requires no further action"); | |
526 | printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); | |
75e4fd31 | 527 | snprintf(newpfx, sizeof(newpfx), "%s ", pfx); |
bbcc2e7b | 528 | |
c4335fdd | 529 | apei_estatus_for_each_section(estatus, gdata) { |
f6edea77 | 530 | cper_estatus_print_section(newpfx, gdata, sec_no); |
f59c55d0 HY |
531 | sec_no++; |
532 | } | |
533 | } | |
88f074f4 | 534 | EXPORT_SYMBOL_GPL(cper_estatus_print); |
f59c55d0 | 535 | |
0a00fd5e | 536 | int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) |
06d65dea HY |
537 | { |
538 | if (estatus->data_length && | |
0a00fd5e | 539 | estatus->data_length < sizeof(struct acpi_hest_generic_data)) |
06d65dea HY |
540 | return -EINVAL; |
541 | if (estatus->raw_data_length && | |
542 | estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) | |
543 | return -EINVAL; | |
544 | ||
545 | return 0; | |
546 | } | |
88f074f4 | 547 | EXPORT_SYMBOL_GPL(cper_estatus_check_header); |
06d65dea | 548 | |
0a00fd5e | 549 | int cper_estatus_check(const struct acpi_hest_generic_status *estatus) |
06d65dea | 550 | { |
0a00fd5e | 551 | struct acpi_hest_generic_data *gdata; |
45b14a4f | 552 | unsigned int data_len, record_size; |
06d65dea HY |
553 | int rc; |
554 | ||
88f074f4 | 555 | rc = cper_estatus_check_header(estatus); |
06d65dea HY |
556 | if (rc) |
557 | return rc; | |
45b14a4f | 558 | |
06d65dea | 559 | data_len = estatus->data_length; |
bbcc2e7b | 560 | |
c4335fdd | 561 | apei_estatus_for_each_section(estatus, gdata) { |
45b14a4f RL |
562 | if (sizeof(struct acpi_hest_generic_data) > data_len) |
563 | return -EINVAL; | |
564 | ||
565 | record_size = acpi_hest_get_record_size(gdata); | |
566 | if (record_size > data_len) | |
06d65dea | 567 | return -EINVAL; |
45b14a4f RL |
568 | |
569 | data_len -= record_size; | |
06d65dea HY |
570 | } |
571 | if (data_len) | |
572 | return -EINVAL; | |
573 | ||
574 | return 0; | |
575 | } | |
88f074f4 | 576 | EXPORT_SYMBOL_GPL(cper_estatus_check); |