Loading...
1// SPDX-License-Identifier: GPL-2.0
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
9 * various tables, such as ERST, BERT and HEST etc.
10 *
11 * For more information about CPER, please refer to Appendix N of UEFI
12 * Specification version 2.4.
13 */
14
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/time.h>
18#include <linux/cper.h>
19#include <linux/dmi.h>
20#include <linux/acpi.h>
21#include <linux/pci.h>
22#include <linux/aer.h>
23#include <linux/printk.h>
24#include <linux/bcd.h>
25#include <acpi/ghes.h>
26#include <ras/ras_event.h>
27#include "cper_cxl.h"
28
29/*
30 * CPER record ID need to be unique even after reboot, because record
31 * ID is used as index for ERST storage, while CPER records from
32 * multiple boot may co-exist in ERST.
33 */
34u64 cper_next_record_id(void)
35{
36 static atomic64_t seq;
37
38 if (!atomic64_read(&seq)) {
39 time64_t time = ktime_get_real_seconds();
40
41 /*
42 * This code is unlikely to still be needed in year 2106,
43 * but just in case, let's use a few more bits for timestamps
44 * after y2038 to be sure they keep increasing monotonically
45 * for the next few hundred years...
46 */
47 if (time < 0x80000000)
48 atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
49 else
50 atomic64_set(&seq, 0x8000000000000000ull |
51 ktime_get_real_seconds() << 24);
52 }
53
54 return atomic64_inc_return(&seq);
55}
56EXPORT_SYMBOL_GPL(cper_next_record_id);
57
58static const char * const severity_strs[] = {
59 "recoverable",
60 "fatal",
61 "corrected",
62 "info",
63};
64
65const char *cper_severity_str(unsigned int severity)
66{
67 return severity < ARRAY_SIZE(severity_strs) ?
68 severity_strs[severity] : "unknown";
69}
70EXPORT_SYMBOL_GPL(cper_severity_str);
71
72/*
73 * cper_print_bits - print strings for set bits
74 * @pfx: prefix for each line, including log level and prefix string
75 * @bits: bit mask
76 * @strs: string array, indexed by bit position
77 * @strs_size: size of the string array: @strs
78 *
79 * For each set bit in @bits, print the corresponding string in @strs.
80 * If the output length is longer than 80, multiple line will be
81 * printed, with @pfx is printed at the beginning of each line.
82 */
83void cper_print_bits(const char *pfx, unsigned int bits,
84 const char * const strs[], unsigned int strs_size)
85{
86 int i, len = 0;
87 const char *str;
88 char buf[84];
89
90 for (i = 0; i < strs_size; i++) {
91 if (!(bits & (1U << i)))
92 continue;
93 str = strs[i];
94 if (!str)
95 continue;
96 if (len && len + strlen(str) + 2 > 80) {
97 printk("%s\n", buf);
98 len = 0;
99 }
100 if (!len)
101 len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
102 else
103 len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
104 }
105 if (len)
106 printk("%s\n", buf);
107}
108
109static const char * const proc_type_strs[] = {
110 "IA32/X64",
111 "IA64",
112 "ARM",
113};
114
115static const char * const proc_isa_strs[] = {
116 "IA32",
117 "IA64",
118 "X64",
119 "ARM A32/T32",
120 "ARM A64",
121};
122
123const char * const cper_proc_error_type_strs[] = {
124 "cache error",
125 "TLB error",
126 "bus error",
127 "micro-architectural error",
128};
129
130static const char * const proc_op_strs[] = {
131 "unknown or generic",
132 "data read",
133 "data write",
134 "instruction execution",
135};
136
137static const char * const proc_flag_strs[] = {
138 "restartable",
139 "precise IP",
140 "overflow",
141 "corrected",
142};
143
144static void cper_print_proc_generic(const char *pfx,
145 const struct cper_sec_proc_generic *proc)
146{
147 if (proc->validation_bits & CPER_PROC_VALID_TYPE)
148 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
149 proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
150 proc_type_strs[proc->proc_type] : "unknown");
151 if (proc->validation_bits & CPER_PROC_VALID_ISA)
152 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
153 proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
154 proc_isa_strs[proc->proc_isa] : "unknown");
155 if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
156 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
157 cper_print_bits(pfx, proc->proc_error_type,
158 cper_proc_error_type_strs,
159 ARRAY_SIZE(cper_proc_error_type_strs));
160 }
161 if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
162 printk("%s""operation: %d, %s\n", pfx, proc->operation,
163 proc->operation < ARRAY_SIZE(proc_op_strs) ?
164 proc_op_strs[proc->operation] : "unknown");
165 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
166 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
167 cper_print_bits(pfx, proc->flags, proc_flag_strs,
168 ARRAY_SIZE(proc_flag_strs));
169 }
170 if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
171 printk("%s""level: %d\n", pfx, proc->level);
172 if (proc->validation_bits & CPER_PROC_VALID_VERSION)
173 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
174 if (proc->validation_bits & CPER_PROC_VALID_ID)
175 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
176 if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
177 printk("%s""target_address: 0x%016llx\n",
178 pfx, proc->target_addr);
179 if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
180 printk("%s""requestor_id: 0x%016llx\n",
181 pfx, proc->requestor_id);
182 if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
183 printk("%s""responder_id: 0x%016llx\n",
184 pfx, proc->responder_id);
185 if (proc->validation_bits & CPER_PROC_VALID_IP)
186 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
187}
188
189static const char * const mem_err_type_strs[] = {
190 "unknown",
191 "no error",
192 "single-bit ECC",
193 "multi-bit ECC",
194 "single-symbol chipkill ECC",
195 "multi-symbol chipkill ECC",
196 "master abort",
197 "target abort",
198 "parity error",
199 "watchdog timeout",
200 "invalid address",
201 "mirror Broken",
202 "memory sparing",
203 "scrub corrected error",
204 "scrub uncorrected error",
205 "physical memory map-out event",
206};
207
208const char *cper_mem_err_type_str(unsigned int etype)
209{
210 return etype < ARRAY_SIZE(mem_err_type_strs) ?
211 mem_err_type_strs[etype] : "unknown";
212}
213EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
214
215const char *cper_mem_err_status_str(u64 status)
216{
217 switch ((status >> 8) & 0xff) {
218 case 1: return "Error detected internal to the component";
219 case 4: return "Storage error in DRAM memory";
220 case 5: return "Storage error in TLB";
221 case 6: return "Storage error in cache";
222 case 7: return "Error in one or more functional units";
223 case 8: return "Component failed self test";
224 case 9: return "Overflow or undervalue of internal queue";
225 case 16: return "Error detected in the bus";
226 case 17: return "Virtual address not found on IO-TLB or IO-PDIR";
227 case 18: return "Improper access error";
228 case 19: return "Access to a memory address which is not mapped to any component";
229 case 20: return "Loss of Lockstep";
230 case 21: return "Response not associated with a request";
231 case 22: return "Bus parity error - must also set the A, C, or D Bits";
232 case 23: return "Detection of a protocol error";
233 case 24: return "Detection of a PATH_ERROR";
234 case 25: return "Bus operation timeout";
235 case 26: return "A read was issued to data that has been poisoned";
236 default: return "Reserved";
237 }
238}
239EXPORT_SYMBOL_GPL(cper_mem_err_status_str);
240
241int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
242{
243 u32 len, n;
244
245 if (!msg)
246 return 0;
247
248 n = 0;
249 len = CPER_REC_LEN;
250 if (mem->validation_bits & CPER_MEM_VALID_NODE)
251 n += scnprintf(msg + n, len - n, "node:%d ", mem->node);
252 if (mem->validation_bits & CPER_MEM_VALID_CARD)
253 n += scnprintf(msg + n, len - n, "card:%d ", mem->card);
254 if (mem->validation_bits & CPER_MEM_VALID_MODULE)
255 n += scnprintf(msg + n, len - n, "module:%d ", mem->module);
256 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
257 n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank);
258 if (mem->validation_bits & CPER_MEM_VALID_BANK)
259 n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank);
260 if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
261 n += scnprintf(msg + n, len - n, "bank_group:%d ",
262 mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
263 if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
264 n += scnprintf(msg + n, len - n, "bank_address:%d ",
265 mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
266 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
267 n += scnprintf(msg + n, len - n, "device:%d ", mem->device);
268 if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
269 u32 row = mem->row;
270
271 row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
272 n += scnprintf(msg + n, len - n, "row:%d ", row);
273 }
274 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
275 n += scnprintf(msg + n, len - n, "column:%d ", mem->column);
276 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
277 n += scnprintf(msg + n, len - n, "bit_position:%d ",
278 mem->bit_pos);
279 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
280 n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ",
281 mem->requestor_id);
282 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
283 n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ",
284 mem->responder_id);
285 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
286 n += scnprintf(msg + n, len - n, "target_id:0x%016llx ",
287 mem->target_id);
288 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
289 n += scnprintf(msg + n, len - n, "chip_id:%d ",
290 mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
291
292 return n;
293}
294EXPORT_SYMBOL_GPL(cper_mem_err_location);
295
296int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
297{
298 u32 len, n;
299 const char *bank = NULL, *device = NULL;
300
301 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
302 return 0;
303
304 len = CPER_REC_LEN;
305 dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
306 if (bank && device)
307 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
308 else
309 n = snprintf(msg, len,
310 "DIMM location: not present. DMI handle: 0x%.4x ",
311 mem->mem_dev_handle);
312
313 return n;
314}
315EXPORT_SYMBOL_GPL(cper_dimm_err_location);
316
317void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
318 struct cper_mem_err_compact *cmem)
319{
320 cmem->validation_bits = mem->validation_bits;
321 cmem->node = mem->node;
322 cmem->card = mem->card;
323 cmem->module = mem->module;
324 cmem->bank = mem->bank;
325 cmem->device = mem->device;
326 cmem->row = mem->row;
327 cmem->column = mem->column;
328 cmem->bit_pos = mem->bit_pos;
329 cmem->requestor_id = mem->requestor_id;
330 cmem->responder_id = mem->responder_id;
331 cmem->target_id = mem->target_id;
332 cmem->extended = mem->extended;
333 cmem->rank = mem->rank;
334 cmem->mem_array_handle = mem->mem_array_handle;
335 cmem->mem_dev_handle = mem->mem_dev_handle;
336}
337EXPORT_SYMBOL_GPL(cper_mem_err_pack);
338
339const char *cper_mem_err_unpack(struct trace_seq *p,
340 struct cper_mem_err_compact *cmem)
341{
342 const char *ret = trace_seq_buffer_ptr(p);
343 char rcd_decode_str[CPER_REC_LEN];
344
345 if (cper_mem_err_location(cmem, rcd_decode_str))
346 trace_seq_printf(p, "%s", rcd_decode_str);
347 if (cper_dimm_err_location(cmem, rcd_decode_str))
348 trace_seq_printf(p, "%s", rcd_decode_str);
349 trace_seq_putc(p, '\0');
350
351 return ret;
352}
353
354static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
355 int len)
356{
357 struct cper_mem_err_compact cmem;
358 char rcd_decode_str[CPER_REC_LEN];
359
360 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
361 if (len == sizeof(struct cper_sec_mem_err_old) &&
362 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
363 pr_err(FW_WARN "valid bits set for fields beyond structure\n");
364 return;
365 }
366 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
367 printk("%s error_status: %s (0x%016llx)\n",
368 pfx, cper_mem_err_status_str(mem->error_status),
369 mem->error_status);
370 if (mem->validation_bits & CPER_MEM_VALID_PA)
371 printk("%s""physical_address: 0x%016llx\n",
372 pfx, mem->physical_addr);
373 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
374 printk("%s""physical_address_mask: 0x%016llx\n",
375 pfx, mem->physical_addr_mask);
376 cper_mem_err_pack(mem, &cmem);
377 if (cper_mem_err_location(&cmem, rcd_decode_str))
378 printk("%s%s\n", pfx, rcd_decode_str);
379 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
380 u8 etype = mem->error_type;
381 printk("%s""error_type: %d, %s\n", pfx, etype,
382 cper_mem_err_type_str(etype));
383 }
384 if (cper_dimm_err_location(&cmem, rcd_decode_str))
385 printk("%s%s\n", pfx, rcd_decode_str);
386}
387
388static const char * const pcie_port_type_strs[] = {
389 "PCIe end point",
390 "legacy PCI end point",
391 "unknown",
392 "unknown",
393 "root port",
394 "upstream switch port",
395 "downstream switch port",
396 "PCIe to PCI/PCI-X bridge",
397 "PCI/PCI-X to PCIe bridge",
398 "root complex integrated endpoint device",
399 "root complex event collector",
400};
401
402static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
403 const struct acpi_hest_generic_data *gdata)
404{
405 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
406 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
407 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
408 pcie_port_type_strs[pcie->port_type] : "unknown");
409 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
410 printk("%s""version: %d.%d\n", pfx,
411 pcie->version.major, pcie->version.minor);
412 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
413 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
414 pcie->command, pcie->status);
415 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
416 const __u8 *p;
417 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
418 pcie->device_id.segment, pcie->device_id.bus,
419 pcie->device_id.device, pcie->device_id.function);
420 printk("%s""slot: %d\n", pfx,
421 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
422 printk("%s""secondary_bus: 0x%02x\n", pfx,
423 pcie->device_id.secondary_bus);
424 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
425 pcie->device_id.vendor_id, pcie->device_id.device_id);
426 p = pcie->device_id.class_code;
427 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
428 }
429 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
430 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
431 pcie->serial_number.lower, pcie->serial_number.upper);
432 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
433 printk(
434 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
435 pfx, pcie->bridge.secondary_status, pcie->bridge.control);
436
437 /* Fatal errors call __ghes_panic() before AER handler prints this */
438 if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
439 (gdata->error_severity & CPER_SEV_FATAL)) {
440 struct aer_capability_regs *aer;
441
442 aer = (struct aer_capability_regs *)pcie->aer_info;
443 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
444 pfx, aer->uncor_status, aer->uncor_mask);
445 printk("%saer_uncor_severity: 0x%08x\n",
446 pfx, aer->uncor_severity);
447 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
448 aer->header_log.dw0, aer->header_log.dw1,
449 aer->header_log.dw2, aer->header_log.dw3);
450 }
451}
452
453static const char * const fw_err_rec_type_strs[] = {
454 "IPF SAL Error Record",
455 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
456 "SOC Firmware Error Record Type2",
457};
458
459static void cper_print_fw_err(const char *pfx,
460 struct acpi_hest_generic_data *gdata,
461 const struct cper_sec_fw_err_rec_ref *fw_err)
462{
463 void *buf = acpi_hest_get_payload(gdata);
464 u32 offset, length = gdata->error_data_length;
465
466 printk("%s""Firmware Error Record Type: %s\n", pfx,
467 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
468 fw_err_rec_type_strs[fw_err->record_type] : "unknown");
469 printk("%s""Revision: %d\n", pfx, fw_err->revision);
470
471 /* Record Type based on UEFI 2.7 */
472 if (fw_err->revision == 0) {
473 printk("%s""Record Identifier: %08llx\n", pfx,
474 fw_err->record_identifier);
475 } else if (fw_err->revision == 2) {
476 printk("%s""Record Identifier: %pUl\n", pfx,
477 &fw_err->record_identifier_guid);
478 }
479
480 /*
481 * The FW error record may contain trailing data beyond the
482 * structure defined by the specification. As the fields
483 * defined (and hence the offset of any trailing data) vary
484 * with the revision, set the offset to account for this
485 * variation.
486 */
487 if (fw_err->revision == 0) {
488 /* record_identifier_guid not defined */
489 offset = offsetof(struct cper_sec_fw_err_rec_ref,
490 record_identifier_guid);
491 } else if (fw_err->revision == 1) {
492 /* record_identifier not defined */
493 offset = offsetof(struct cper_sec_fw_err_rec_ref,
494 record_identifier);
495 } else {
496 offset = sizeof(*fw_err);
497 }
498
499 buf += offset;
500 length -= offset;
501
502 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
503}
504
505static void cper_print_tstamp(const char *pfx,
506 struct acpi_hest_generic_data_v300 *gdata)
507{
508 __u8 hour, min, sec, day, mon, year, century, *timestamp;
509
510 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
511 timestamp = (__u8 *)&(gdata->time_stamp);
512 sec = bcd2bin(timestamp[0]);
513 min = bcd2bin(timestamp[1]);
514 hour = bcd2bin(timestamp[2]);
515 day = bcd2bin(timestamp[4]);
516 mon = bcd2bin(timestamp[5]);
517 year = bcd2bin(timestamp[6]);
518 century = bcd2bin(timestamp[7]);
519
520 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
521 (timestamp[3] & 0x1 ? "precise " : "imprecise "),
522 century, year, mon, day, hour, min, sec);
523 }
524}
525
526static void
527cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
528 int sec_no)
529{
530 guid_t *sec_type = (guid_t *)gdata->section_type;
531 __u16 severity;
532 char newpfx[64];
533
534 if (acpi_hest_get_version(gdata) >= 3)
535 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
536
537 severity = gdata->error_severity;
538 printk("%s""Error %d, type: %s\n", pfx, sec_no,
539 cper_severity_str(severity));
540 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
541 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
542 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
543 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
544
545 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
546 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
547 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
548
549 printk("%s""section_type: general processor error\n", newpfx);
550 if (gdata->error_data_length >= sizeof(*proc_err))
551 cper_print_proc_generic(newpfx, proc_err);
552 else
553 goto err_section_too_small;
554 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
555 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
556
557 printk("%s""section_type: memory error\n", newpfx);
558 if (gdata->error_data_length >=
559 sizeof(struct cper_sec_mem_err_old))
560 cper_print_mem(newpfx, mem_err,
561 gdata->error_data_length);
562 else
563 goto err_section_too_small;
564 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
565 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
566
567 printk("%s""section_type: PCIe error\n", newpfx);
568 if (gdata->error_data_length >= sizeof(*pcie))
569 cper_print_pcie(newpfx, pcie, gdata);
570 else
571 goto err_section_too_small;
572#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
573 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
574 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
575
576 printk("%ssection_type: ARM processor error\n", newpfx);
577 if (gdata->error_data_length >= sizeof(*arm_err))
578 cper_print_proc_arm(newpfx, arm_err);
579 else
580 goto err_section_too_small;
581#endif
582#if defined(CONFIG_UEFI_CPER_X86)
583 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
584 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
585
586 printk("%ssection_type: IA32/X64 processor error\n", newpfx);
587 if (gdata->error_data_length >= sizeof(*ia_err))
588 cper_print_proc_ia(newpfx, ia_err);
589 else
590 goto err_section_too_small;
591#endif
592 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
593 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
594
595 printk("%ssection_type: Firmware Error Record Reference\n",
596 newpfx);
597 /* The minimal FW Error Record contains 16 bytes */
598 if (gdata->error_data_length >= SZ_16)
599 cper_print_fw_err(newpfx, gdata, fw_err);
600 else
601 goto err_section_too_small;
602 } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
603 struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
604
605 printk("%ssection_type: CXL Protocol Error\n", newpfx);
606 if (gdata->error_data_length >= sizeof(*prot_err))
607 cper_print_prot_err(newpfx, prot_err);
608 else
609 goto err_section_too_small;
610 } else {
611 const void *err = acpi_hest_get_payload(gdata);
612
613 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
614 printk("%ssection length: %#x\n", newpfx,
615 gdata->error_data_length);
616 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
617 gdata->error_data_length, true);
618 }
619
620 return;
621
622err_section_too_small:
623 pr_err(FW_WARN "error section length is too small\n");
624}
625
626void cper_estatus_print(const char *pfx,
627 const struct acpi_hest_generic_status *estatus)
628{
629 struct acpi_hest_generic_data *gdata;
630 int sec_no = 0;
631 char newpfx[64];
632 __u16 severity;
633
634 severity = estatus->error_severity;
635 if (severity == CPER_SEV_CORRECTED)
636 printk("%s%s\n", pfx,
637 "It has been corrected by h/w "
638 "and requires no further action");
639 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
640 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
641
642 apei_estatus_for_each_section(estatus, gdata) {
643 cper_estatus_print_section(newpfx, gdata, sec_no);
644 sec_no++;
645 }
646}
647EXPORT_SYMBOL_GPL(cper_estatus_print);
648
649int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
650{
651 if (estatus->data_length &&
652 estatus->data_length < sizeof(struct acpi_hest_generic_data))
653 return -EINVAL;
654 if (estatus->raw_data_length &&
655 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
656 return -EINVAL;
657
658 return 0;
659}
660EXPORT_SYMBOL_GPL(cper_estatus_check_header);
661
662int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
663{
664 struct acpi_hest_generic_data *gdata;
665 unsigned int data_len, record_size;
666 int rc;
667
668 rc = cper_estatus_check_header(estatus);
669 if (rc)
670 return rc;
671
672 data_len = estatus->data_length;
673
674 apei_estatus_for_each_section(estatus, gdata) {
675 if (acpi_hest_get_size(gdata) > data_len)
676 return -EINVAL;
677
678 record_size = acpi_hest_get_record_size(gdata);
679 if (record_size > data_len)
680 return -EINVAL;
681
682 data_len -= record_size;
683 }
684 if (data_len)
685 return -EINVAL;
686
687 return 0;
688}
689EXPORT_SYMBOL_GPL(cper_estatus_check);
1// SPDX-License-Identifier: GPL-2.0
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
9 * various tables, such as ERST, BERT and HEST etc.
10 *
11 * For more information about CPER, please refer to Appendix N of UEFI
12 * Specification version 2.4.
13 */
14
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/time.h>
18#include <linux/cper.h>
19#include <linux/dmi.h>
20#include <linux/acpi.h>
21#include <linux/pci.h>
22#include <linux/aer.h>
23#include <linux/printk.h>
24#include <linux/bcd.h>
25#include <acpi/ghes.h>
26#include <ras/ras_event.h>
27
28/*
29 * CPER record ID need to be unique even after reboot, because record
30 * ID is used as index for ERST storage, while CPER records from
31 * multiple boot may co-exist in ERST.
32 */
33u64 cper_next_record_id(void)
34{
35 static atomic64_t seq;
36
37 if (!atomic64_read(&seq)) {
38 time64_t time = ktime_get_real_seconds();
39
40 /*
41 * This code is unlikely to still be needed in year 2106,
42 * but just in case, let's use a few more bits for timestamps
43 * after y2038 to be sure they keep increasing monotonically
44 * for the next few hundred years...
45 */
46 if (time < 0x80000000)
47 atomic64_set(&seq, (ktime_get_real_seconds()) << 32);
48 else
49 atomic64_set(&seq, 0x8000000000000000ull |
50 ktime_get_real_seconds() << 24);
51 }
52
53 return atomic64_inc_return(&seq);
54}
55EXPORT_SYMBOL_GPL(cper_next_record_id);
56
57static const char * const severity_strs[] = {
58 "recoverable",
59 "fatal",
60 "corrected",
61 "info",
62};
63
64const char *cper_severity_str(unsigned int severity)
65{
66 return severity < ARRAY_SIZE(severity_strs) ?
67 severity_strs[severity] : "unknown";
68}
69EXPORT_SYMBOL_GPL(cper_severity_str);
70
71/*
72 * cper_print_bits - print strings for set bits
73 * @pfx: prefix for each line, including log level and prefix string
74 * @bits: bit mask
75 * @strs: string array, indexed by bit position
76 * @strs_size: size of the string array: @strs
77 *
78 * For each set bit in @bits, print the corresponding string in @strs.
79 * If the output length is longer than 80, multiple line will be
80 * printed, with @pfx is printed at the beginning of each line.
81 */
82void cper_print_bits(const char *pfx, unsigned int bits,
83 const char * const strs[], unsigned int strs_size)
84{
85 int i, len = 0;
86 const char *str;
87 char buf[84];
88
89 for (i = 0; i < strs_size; i++) {
90 if (!(bits & (1U << i)))
91 continue;
92 str = strs[i];
93 if (!str)
94 continue;
95 if (len && len + strlen(str) + 2 > 80) {
96 printk("%s\n", buf);
97 len = 0;
98 }
99 if (!len)
100 len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
101 else
102 len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
103 }
104 if (len)
105 printk("%s\n", buf);
106}
107
108static const char * const proc_type_strs[] = {
109 "IA32/X64",
110 "IA64",
111 "ARM",
112};
113
114static const char * const proc_isa_strs[] = {
115 "IA32",
116 "IA64",
117 "X64",
118 "ARM A32/T32",
119 "ARM A64",
120};
121
122const char * const cper_proc_error_type_strs[] = {
123 "cache error",
124 "TLB error",
125 "bus error",
126 "micro-architectural error",
127};
128
129static const char * const proc_op_strs[] = {
130 "unknown or generic",
131 "data read",
132 "data write",
133 "instruction execution",
134};
135
136static const char * const proc_flag_strs[] = {
137 "restartable",
138 "precise IP",
139 "overflow",
140 "corrected",
141};
142
143static void cper_print_proc_generic(const char *pfx,
144 const struct cper_sec_proc_generic *proc)
145{
146 if (proc->validation_bits & CPER_PROC_VALID_TYPE)
147 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,
148 proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
149 proc_type_strs[proc->proc_type] : "unknown");
150 if (proc->validation_bits & CPER_PROC_VALID_ISA)
151 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,
152 proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
153 proc_isa_strs[proc->proc_isa] : "unknown");
154 if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
155 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);
156 cper_print_bits(pfx, proc->proc_error_type,
157 cper_proc_error_type_strs,
158 ARRAY_SIZE(cper_proc_error_type_strs));
159 }
160 if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
161 printk("%s""operation: %d, %s\n", pfx, proc->operation,
162 proc->operation < ARRAY_SIZE(proc_op_strs) ?
163 proc_op_strs[proc->operation] : "unknown");
164 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
165 printk("%s""flags: 0x%02x\n", pfx, proc->flags);
166 cper_print_bits(pfx, proc->flags, proc_flag_strs,
167 ARRAY_SIZE(proc_flag_strs));
168 }
169 if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
170 printk("%s""level: %d\n", pfx, proc->level);
171 if (proc->validation_bits & CPER_PROC_VALID_VERSION)
172 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);
173 if (proc->validation_bits & CPER_PROC_VALID_ID)
174 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);
175 if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
176 printk("%s""target_address: 0x%016llx\n",
177 pfx, proc->target_addr);
178 if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
179 printk("%s""requestor_id: 0x%016llx\n",
180 pfx, proc->requestor_id);
181 if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
182 printk("%s""responder_id: 0x%016llx\n",
183 pfx, proc->responder_id);
184 if (proc->validation_bits & CPER_PROC_VALID_IP)
185 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);
186}
187
188static const char * const mem_err_type_strs[] = {
189 "unknown",
190 "no error",
191 "single-bit ECC",
192 "multi-bit ECC",
193 "single-symbol chipkill ECC",
194 "multi-symbol chipkill ECC",
195 "master abort",
196 "target abort",
197 "parity error",
198 "watchdog timeout",
199 "invalid address",
200 "mirror Broken",
201 "memory sparing",
202 "scrub corrected error",
203 "scrub uncorrected error",
204 "physical memory map-out event",
205};
206
207const char *cper_mem_err_type_str(unsigned int etype)
208{
209 return etype < ARRAY_SIZE(mem_err_type_strs) ?
210 mem_err_type_strs[etype] : "unknown";
211}
212EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
213
214static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
215{
216 u32 len, n;
217
218 if (!msg)
219 return 0;
220
221 n = 0;
222 len = CPER_REC_LEN - 1;
223 if (mem->validation_bits & CPER_MEM_VALID_NODE)
224 n += scnprintf(msg + n, len - n, "node: %d ", mem->node);
225 if (mem->validation_bits & CPER_MEM_VALID_CARD)
226 n += scnprintf(msg + n, len - n, "card: %d ", mem->card);
227 if (mem->validation_bits & CPER_MEM_VALID_MODULE)
228 n += scnprintf(msg + n, len - n, "module: %d ", mem->module);
229 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
230 n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank);
231 if (mem->validation_bits & CPER_MEM_VALID_BANK)
232 n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank);
233 if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
234 n += scnprintf(msg + n, len - n, "bank_group: %d ",
235 mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
236 if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
237 n += scnprintf(msg + n, len - n, "bank_address: %d ",
238 mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
239 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
240 n += scnprintf(msg + n, len - n, "device: %d ", mem->device);
241 if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
242 u32 row = mem->row;
243
244 row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
245 n += scnprintf(msg + n, len - n, "row: %d ", row);
246 }
247 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
248 n += scnprintf(msg + n, len - n, "column: %d ", mem->column);
249 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
250 n += scnprintf(msg + n, len - n, "bit_position: %d ",
251 mem->bit_pos);
252 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
253 n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ",
254 mem->requestor_id);
255 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
256 n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ",
257 mem->responder_id);
258 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
259 scnprintf(msg + n, len - n, "target_id: 0x%016llx ",
260 mem->target_id);
261 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
262 scnprintf(msg + n, len - n, "chip_id: %d ",
263 mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
264
265 msg[n] = '\0';
266 return n;
267}
268
269static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
270{
271 u32 len, n;
272 const char *bank = NULL, *device = NULL;
273
274 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
275 return 0;
276
277 len = CPER_REC_LEN;
278 dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
279 if (bank && device)
280 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);
281 else
282 n = snprintf(msg, len,
283 "DIMM location: not present. DMI handle: 0x%.4x ",
284 mem->mem_dev_handle);
285
286 return n;
287}
288
289void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
290 struct cper_mem_err_compact *cmem)
291{
292 cmem->validation_bits = mem->validation_bits;
293 cmem->node = mem->node;
294 cmem->card = mem->card;
295 cmem->module = mem->module;
296 cmem->bank = mem->bank;
297 cmem->device = mem->device;
298 cmem->row = mem->row;
299 cmem->column = mem->column;
300 cmem->bit_pos = mem->bit_pos;
301 cmem->requestor_id = mem->requestor_id;
302 cmem->responder_id = mem->responder_id;
303 cmem->target_id = mem->target_id;
304 cmem->extended = mem->extended;
305 cmem->rank = mem->rank;
306 cmem->mem_array_handle = mem->mem_array_handle;
307 cmem->mem_dev_handle = mem->mem_dev_handle;
308}
309
310const char *cper_mem_err_unpack(struct trace_seq *p,
311 struct cper_mem_err_compact *cmem)
312{
313 const char *ret = trace_seq_buffer_ptr(p);
314 char rcd_decode_str[CPER_REC_LEN];
315
316 if (cper_mem_err_location(cmem, rcd_decode_str))
317 trace_seq_printf(p, "%s", rcd_decode_str);
318 if (cper_dimm_err_location(cmem, rcd_decode_str))
319 trace_seq_printf(p, "%s", rcd_decode_str);
320 trace_seq_putc(p, '\0');
321
322 return ret;
323}
324
325static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
326 int len)
327{
328 struct cper_mem_err_compact cmem;
329 char rcd_decode_str[CPER_REC_LEN];
330
331 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
332 if (len == sizeof(struct cper_sec_mem_err_old) &&
333 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {
334 pr_err(FW_WARN "valid bits set for fields beyond structure\n");
335 return;
336 }
337 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
338 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status);
339 if (mem->validation_bits & CPER_MEM_VALID_PA)
340 printk("%s""physical_address: 0x%016llx\n",
341 pfx, mem->physical_addr);
342 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
343 printk("%s""physical_address_mask: 0x%016llx\n",
344 pfx, mem->physical_addr_mask);
345 cper_mem_err_pack(mem, &cmem);
346 if (cper_mem_err_location(&cmem, rcd_decode_str))
347 printk("%s%s\n", pfx, rcd_decode_str);
348 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
349 u8 etype = mem->error_type;
350 printk("%s""error_type: %d, %s\n", pfx, etype,
351 cper_mem_err_type_str(etype));
352 }
353 if (cper_dimm_err_location(&cmem, rcd_decode_str))
354 printk("%s%s\n", pfx, rcd_decode_str);
355}
356
357static const char * const pcie_port_type_strs[] = {
358 "PCIe end point",
359 "legacy PCI end point",
360 "unknown",
361 "unknown",
362 "root port",
363 "upstream switch port",
364 "downstream switch port",
365 "PCIe to PCI/PCI-X bridge",
366 "PCI/PCI-X to PCIe bridge",
367 "root complex integrated endpoint device",
368 "root complex event collector",
369};
370
371static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
372 const struct acpi_hest_generic_data *gdata)
373{
374 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
375 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,
376 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
377 pcie_port_type_strs[pcie->port_type] : "unknown");
378 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
379 printk("%s""version: %d.%d\n", pfx,
380 pcie->version.major, pcie->version.minor);
381 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
382 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,
383 pcie->command, pcie->status);
384 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
385 const __u8 *p;
386 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,
387 pcie->device_id.segment, pcie->device_id.bus,
388 pcie->device_id.device, pcie->device_id.function);
389 printk("%s""slot: %d\n", pfx,
390 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
391 printk("%s""secondary_bus: 0x%02x\n", pfx,
392 pcie->device_id.secondary_bus);
393 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,
394 pcie->device_id.vendor_id, pcie->device_id.device_id);
395 p = pcie->device_id.class_code;
396 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);
397 }
398 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
399 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,
400 pcie->serial_number.lower, pcie->serial_number.upper);
401 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
402 printk(
403 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",
404 pfx, pcie->bridge.secondary_status, pcie->bridge.control);
405
406 /* Fatal errors call __ghes_panic() before AER handler prints this */
407 if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) &&
408 (gdata->error_severity & CPER_SEV_FATAL)) {
409 struct aer_capability_regs *aer;
410
411 aer = (struct aer_capability_regs *)pcie->aer_info;
412 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",
413 pfx, aer->uncor_status, aer->uncor_mask);
414 printk("%saer_uncor_severity: 0x%08x\n",
415 pfx, aer->uncor_severity);
416 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,
417 aer->header_log.dw0, aer->header_log.dw1,
418 aer->header_log.dw2, aer->header_log.dw3);
419 }
420}
421
422static const char * const fw_err_rec_type_strs[] = {
423 "IPF SAL Error Record",
424 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",
425 "SOC Firmware Error Record Type2",
426};
427
428static void cper_print_fw_err(const char *pfx,
429 struct acpi_hest_generic_data *gdata,
430 const struct cper_sec_fw_err_rec_ref *fw_err)
431{
432 void *buf = acpi_hest_get_payload(gdata);
433 u32 offset, length = gdata->error_data_length;
434
435 printk("%s""Firmware Error Record Type: %s\n", pfx,
436 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
437 fw_err_rec_type_strs[fw_err->record_type] : "unknown");
438 printk("%s""Revision: %d\n", pfx, fw_err->revision);
439
440 /* Record Type based on UEFI 2.7 */
441 if (fw_err->revision == 0) {
442 printk("%s""Record Identifier: %08llx\n", pfx,
443 fw_err->record_identifier);
444 } else if (fw_err->revision == 2) {
445 printk("%s""Record Identifier: %pUl\n", pfx,
446 &fw_err->record_identifier_guid);
447 }
448
449 /*
450 * The FW error record may contain trailing data beyond the
451 * structure defined by the specification. As the fields
452 * defined (and hence the offset of any trailing data) vary
453 * with the revision, set the offset to account for this
454 * variation.
455 */
456 if (fw_err->revision == 0) {
457 /* record_identifier_guid not defined */
458 offset = offsetof(struct cper_sec_fw_err_rec_ref,
459 record_identifier_guid);
460 } else if (fw_err->revision == 1) {
461 /* record_identifier not defined */
462 offset = offsetof(struct cper_sec_fw_err_rec_ref,
463 record_identifier);
464 } else {
465 offset = sizeof(*fw_err);
466 }
467
468 buf += offset;
469 length -= offset;
470
471 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);
472}
473
474static void cper_print_tstamp(const char *pfx,
475 struct acpi_hest_generic_data_v300 *gdata)
476{
477 __u8 hour, min, sec, day, mon, year, century, *timestamp;
478
479 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
480 timestamp = (__u8 *)&(gdata->time_stamp);
481 sec = bcd2bin(timestamp[0]);
482 min = bcd2bin(timestamp[1]);
483 hour = bcd2bin(timestamp[2]);
484 day = bcd2bin(timestamp[4]);
485 mon = bcd2bin(timestamp[5]);
486 year = bcd2bin(timestamp[6]);
487 century = bcd2bin(timestamp[7]);
488
489 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,
490 (timestamp[3] & 0x1 ? "precise " : "imprecise "),
491 century, year, mon, day, hour, min, sec);
492 }
493}
494
495static void
496cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
497 int sec_no)
498{
499 guid_t *sec_type = (guid_t *)gdata->section_type;
500 __u16 severity;
501 char newpfx[64];
502
503 if (acpi_hest_get_version(gdata) >= 3)
504 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
505
506 severity = gdata->error_severity;
507 printk("%s""Error %d, type: %s\n", pfx, sec_no,
508 cper_severity_str(severity));
509 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
510 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);
511 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
512 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);
513
514 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
515 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
516 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
517
518 printk("%s""section_type: general processor error\n", newpfx);
519 if (gdata->error_data_length >= sizeof(*proc_err))
520 cper_print_proc_generic(newpfx, proc_err);
521 else
522 goto err_section_too_small;
523 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
524 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
525
526 printk("%s""section_type: memory error\n", newpfx);
527 if (gdata->error_data_length >=
528 sizeof(struct cper_sec_mem_err_old))
529 cper_print_mem(newpfx, mem_err,
530 gdata->error_data_length);
531 else
532 goto err_section_too_small;
533 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
534 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
535
536 printk("%s""section_type: PCIe error\n", newpfx);
537 if (gdata->error_data_length >= sizeof(*pcie))
538 cper_print_pcie(newpfx, pcie, gdata);
539 else
540 goto err_section_too_small;
541#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
542 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
543 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
544
545 printk("%ssection_type: ARM processor error\n", newpfx);
546 if (gdata->error_data_length >= sizeof(*arm_err))
547 cper_print_proc_arm(newpfx, arm_err);
548 else
549 goto err_section_too_small;
550#endif
551#if defined(CONFIG_UEFI_CPER_X86)
552 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
553 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
554
555 printk("%ssection_type: IA32/X64 processor error\n", newpfx);
556 if (gdata->error_data_length >= sizeof(*ia_err))
557 cper_print_proc_ia(newpfx, ia_err);
558 else
559 goto err_section_too_small;
560#endif
561 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
562 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
563
564 printk("%ssection_type: Firmware Error Record Reference\n",
565 newpfx);
566 /* The minimal FW Error Record contains 16 bytes */
567 if (gdata->error_data_length >= SZ_16)
568 cper_print_fw_err(newpfx, gdata, fw_err);
569 else
570 goto err_section_too_small;
571 } else {
572 const void *err = acpi_hest_get_payload(gdata);
573
574 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);
575 printk("%ssection length: %#x\n", newpfx,
576 gdata->error_data_length);
577 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,
578 gdata->error_data_length, true);
579 }
580
581 return;
582
583err_section_too_small:
584 pr_err(FW_WARN "error section length is too small\n");
585}
586
587void cper_estatus_print(const char *pfx,
588 const struct acpi_hest_generic_status *estatus)
589{
590 struct acpi_hest_generic_data *gdata;
591 int sec_no = 0;
592 char newpfx[64];
593 __u16 severity;
594
595 severity = estatus->error_severity;
596 if (severity == CPER_SEV_CORRECTED)
597 printk("%s%s\n", pfx,
598 "It has been corrected by h/w "
599 "and requires no further action");
600 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));
601 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);
602
603 apei_estatus_for_each_section(estatus, gdata) {
604 cper_estatus_print_section(newpfx, gdata, sec_no);
605 sec_no++;
606 }
607}
608EXPORT_SYMBOL_GPL(cper_estatus_print);
609
610int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
611{
612 if (estatus->data_length &&
613 estatus->data_length < sizeof(struct acpi_hest_generic_data))
614 return -EINVAL;
615 if (estatus->raw_data_length &&
616 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)
617 return -EINVAL;
618
619 return 0;
620}
621EXPORT_SYMBOL_GPL(cper_estatus_check_header);
622
623int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
624{
625 struct acpi_hest_generic_data *gdata;
626 unsigned int data_len, record_size;
627 int rc;
628
629 rc = cper_estatus_check_header(estatus);
630 if (rc)
631 return rc;
632
633 data_len = estatus->data_length;
634
635 apei_estatus_for_each_section(estatus, gdata) {
636 if (sizeof(struct acpi_hest_generic_data) > data_len)
637 return -EINVAL;
638
639 record_size = acpi_hest_get_record_size(gdata);
640 if (record_size > data_len)
641 return -EINVAL;
642
643 data_len -= record_size;
644 }
645 if (data_len)
646 return -EINVAL;
647
648 return 0;
649}
650EXPORT_SYMBOL_GPL(cper_estatus_check);