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 static char rcd_decode_str[CPER_REC_LEN]; 29 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 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 } 54 55 return atomic64_inc_return(&seq); 56 } 57 EXPORT_SYMBOL_GPL(cper_next_record_id); 58 59 static const char * const severity_strs[] = { 60 "recoverable", 61 "fatal", 62 "corrected", 63 "info", 64 }; 65 66 const char *cper_severity_str(unsigned int severity) 67 { 68 return severity < ARRAY_SIZE(severity_strs) ? 69 severity_strs[severity] : "unknown"; 70 } 71 EXPORT_SYMBOL_GPL(cper_severity_str); 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 */ 84 void cper_print_bits(const char *pfx, unsigned int bits, 85 const char * const strs[], unsigned int strs_size) 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]; 95 if (!str) 96 continue; 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 104 len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str); 105 } 106 if (len) 107 printk("%s\n", buf); 108 } 109 110 static const char * const proc_type_strs[] = { 111 "IA32/X64", 112 "IA64", 113 "ARM", 114 }; 115 116 static const char * const proc_isa_strs[] = { 117 "IA32", 118 "IA64", 119 "X64", 120 "ARM A32/T32", 121 "ARM A64", 122 }; 123 124 const char * const cper_proc_error_type_strs[] = { 125 "cache error", 126 "TLB error", 127 "bus error", 128 "micro-architectural error", 129 }; 130 131 static const char * const proc_op_strs[] = { 132 "unknown or generic", 133 "data read", 134 "data write", 135 "instruction execution", 136 }; 137 138 static const char * const proc_flag_strs[] = { 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, 150 proc->proc_type < ARRAY_SIZE(proc_type_strs) ? 151 proc_type_strs[proc->proc_type] : "unknown"); 152 if (proc->validation_bits & CPER_PROC_VALID_ISA) 153 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 154 proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? 155 proc_isa_strs[proc->proc_isa] : "unknown"); 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, 159 cper_proc_error_type_strs, 160 ARRAY_SIZE(cper_proc_error_type_strs)); 161 } 162 if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 163 printk("%s""operation: %d, %s\n", pfx, proc->operation, 164 proc->operation < ARRAY_SIZE(proc_op_strs) ? 165 proc_op_strs[proc->operation] : "unknown"); 166 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 167 printk("%s""flags: 0x%02x\n", pfx, proc->flags); 168 cper_print_bits(pfx, proc->flags, proc_flag_strs, 169 ARRAY_SIZE(proc_flag_strs)); 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 190 static const char * const mem_err_type_strs[] = { 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", 206 "physical memory map-out event", 207 }; 208 209 const char *cper_mem_err_type_str(unsigned int etype) 210 { 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 216 static int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 217 { 218 u32 len, n; 219 220 if (!msg) 221 return 0; 222 223 n = 0; 224 len = CPER_REC_LEN; 225 if (mem->validation_bits & CPER_MEM_VALID_NODE) 226 n += scnprintf(msg + n, len - n, "node: %d ", mem->node); 227 if (mem->validation_bits & CPER_MEM_VALID_CARD) 228 n += scnprintf(msg + n, len - n, "card: %d ", mem->card); 229 if (mem->validation_bits & CPER_MEM_VALID_MODULE) 230 n += scnprintf(msg + n, len - n, "module: %d ", mem->module); 231 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 232 n += scnprintf(msg + n, len - n, "rank: %d ", mem->rank); 233 if (mem->validation_bits & CPER_MEM_VALID_BANK) 234 n += scnprintf(msg + n, len - n, "bank: %d ", mem->bank); 235 if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) 236 n += scnprintf(msg + n, len - n, "bank_group: %d ", 237 mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); 238 if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) 239 n += scnprintf(msg + n, len - n, "bank_address: %d ", 240 mem->bank & CPER_MEM_BANK_ADDRESS_MASK); 241 if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 242 n += scnprintf(msg + n, len - n, "device: %d ", mem->device); 243 if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { 244 u32 row = mem->row; 245 246 row |= cper_get_mem_extension(mem->validation_bits, mem->extended); 247 n += scnprintf(msg + n, len - n, "row: %d ", row); 248 } 249 if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 250 n += scnprintf(msg + n, len - n, "column: %d ", mem->column); 251 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 252 n += scnprintf(msg + n, len - n, "bit_position: %d ", 253 mem->bit_pos); 254 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 255 n += scnprintf(msg + n, len - n, "requestor_id: 0x%016llx ", 256 mem->requestor_id); 257 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 258 n += scnprintf(msg + n, len - n, "responder_id: 0x%016llx ", 259 mem->responder_id); 260 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 261 n += scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 262 mem->target_id); 263 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 264 n += scnprintf(msg + n, len - n, "chip_id: %d ", 265 mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 266 267 return n; 268 } 269 270 static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 271 { 272 u32 len, n; 273 const char *bank = NULL, *device = NULL; 274 275 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 276 return 0; 277 278 len = CPER_REC_LEN; 279 dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 280 if (bank && device) 281 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 282 else 283 n = snprintf(msg, len, 284 "DIMM location: not present. DMI handle: 0x%.4x ", 285 mem->mem_dev_handle); 286 287 return n; 288 } 289 290 void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 291 struct cper_mem_err_compact *cmem) 292 { 293 cmem->validation_bits = mem->validation_bits; 294 cmem->node = mem->node; 295 cmem->card = mem->card; 296 cmem->module = mem->module; 297 cmem->bank = mem->bank; 298 cmem->device = mem->device; 299 cmem->row = mem->row; 300 cmem->column = mem->column; 301 cmem->bit_pos = mem->bit_pos; 302 cmem->requestor_id = mem->requestor_id; 303 cmem->responder_id = mem->responder_id; 304 cmem->target_id = mem->target_id; 305 cmem->extended = mem->extended; 306 cmem->rank = mem->rank; 307 cmem->mem_array_handle = mem->mem_array_handle; 308 cmem->mem_dev_handle = mem->mem_dev_handle; 309 } 310 311 const char *cper_mem_err_unpack(struct trace_seq *p, 312 struct cper_mem_err_compact *cmem) 313 { 314 const char *ret = trace_seq_buffer_ptr(p); 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 325 static 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 330 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 331 if (len == sizeof(struct cper_sec_mem_err_old) && 332 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 333 pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 334 return; 335 } 336 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 337 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 338 if (mem->validation_bits & CPER_MEM_VALID_PA) 339 printk("%s""physical_address: 0x%016llx\n", 340 pfx, mem->physical_addr); 341 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 342 printk("%s""physical_address_mask: 0x%016llx\n", 343 pfx, mem->physical_addr_mask); 344 cper_mem_err_pack(mem, &cmem); 345 if (cper_mem_err_location(&cmem, rcd_decode_str)) 346 printk("%s%s\n", pfx, rcd_decode_str); 347 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 348 u8 etype = mem->error_type; 349 printk("%s""error_type: %d, %s\n", pfx, etype, 350 cper_mem_err_type_str(etype)); 351 } 352 if (cper_dimm_err_location(&cmem, rcd_decode_str)) 353 printk("%s%s\n", pfx, rcd_decode_str); 354 } 355 356 static const char * const pcie_port_type_strs[] = { 357 "PCIe end point", 358 "legacy PCI end point", 359 "unknown", 360 "unknown", 361 "root port", 362 "upstream switch port", 363 "downstream switch port", 364 "PCIe to PCI/PCI-X bridge", 365 "PCI/PCI-X to PCIe bridge", 366 "root complex integrated endpoint device", 367 "root complex event collector", 368 }; 369 370 static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 371 const struct acpi_hest_generic_data *gdata) 372 { 373 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 374 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 375 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 376 pcie_port_type_strs[pcie->port_type] : "unknown"); 377 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 378 printk("%s""version: %d.%d\n", pfx, 379 pcie->version.major, pcie->version.minor); 380 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 381 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 382 pcie->command, pcie->status); 383 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 384 const __u8 *p; 385 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 386 pcie->device_id.segment, pcie->device_id.bus, 387 pcie->device_id.device, pcie->device_id.function); 388 printk("%s""slot: %d\n", pfx, 389 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 390 printk("%s""secondary_bus: 0x%02x\n", pfx, 391 pcie->device_id.secondary_bus); 392 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 393 pcie->device_id.vendor_id, pcie->device_id.device_id); 394 p = pcie->device_id.class_code; 395 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 396 } 397 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 398 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 399 pcie->serial_number.lower, pcie->serial_number.upper); 400 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 401 printk( 402 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 403 pfx, pcie->bridge.secondary_status, pcie->bridge.control); 404 405 /* Fatal errors call __ghes_panic() before AER handler prints this */ 406 if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 407 (gdata->error_severity & CPER_SEV_FATAL)) { 408 struct aer_capability_regs *aer; 409 410 aer = (struct aer_capability_regs *)pcie->aer_info; 411 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 412 pfx, aer->uncor_status, aer->uncor_mask); 413 printk("%saer_uncor_severity: 0x%08x\n", 414 pfx, aer->uncor_severity); 415 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 416 aer->header_log.dw0, aer->header_log.dw1, 417 aer->header_log.dw2, aer->header_log.dw3); 418 } 419 } 420 421 static const char * const fw_err_rec_type_strs[] = { 422 "IPF SAL Error Record", 423 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 424 "SOC Firmware Error Record Type2", 425 }; 426 427 static void cper_print_fw_err(const char *pfx, 428 struct acpi_hest_generic_data *gdata, 429 const struct cper_sec_fw_err_rec_ref *fw_err) 430 { 431 void *buf = acpi_hest_get_payload(gdata); 432 u32 offset, length = gdata->error_data_length; 433 434 printk("%s""Firmware Error Record Type: %s\n", pfx, 435 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 436 fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 437 printk("%s""Revision: %d\n", pfx, fw_err->revision); 438 439 /* Record Type based on UEFI 2.7 */ 440 if (fw_err->revision == 0) { 441 printk("%s""Record Identifier: %08llx\n", pfx, 442 fw_err->record_identifier); 443 } else if (fw_err->revision == 2) { 444 printk("%s""Record Identifier: %pUl\n", pfx, 445 &fw_err->record_identifier_guid); 446 } 447 448 /* 449 * The FW error record may contain trailing data beyond the 450 * structure defined by the specification. As the fields 451 * defined (and hence the offset of any trailing data) vary 452 * with the revision, set the offset to account for this 453 * variation. 454 */ 455 if (fw_err->revision == 0) { 456 /* record_identifier_guid not defined */ 457 offset = offsetof(struct cper_sec_fw_err_rec_ref, 458 record_identifier_guid); 459 } else if (fw_err->revision == 1) { 460 /* record_identifier not defined */ 461 offset = offsetof(struct cper_sec_fw_err_rec_ref, 462 record_identifier); 463 } else { 464 offset = sizeof(*fw_err); 465 } 466 467 buf += offset; 468 length -= offset; 469 470 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 471 } 472 473 static void cper_print_tstamp(const char *pfx, 474 struct acpi_hest_generic_data_v300 *gdata) 475 { 476 __u8 hour, min, sec, day, mon, year, century, *timestamp; 477 478 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 479 timestamp = (__u8 *)&(gdata->time_stamp); 480 sec = bcd2bin(timestamp[0]); 481 min = bcd2bin(timestamp[1]); 482 hour = bcd2bin(timestamp[2]); 483 day = bcd2bin(timestamp[4]); 484 mon = bcd2bin(timestamp[5]); 485 year = bcd2bin(timestamp[6]); 486 century = bcd2bin(timestamp[7]); 487 488 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 489 (timestamp[3] & 0x1 ? "precise " : "imprecise "), 490 century, year, mon, day, hour, min, sec); 491 } 492 } 493 494 static void 495 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 496 int sec_no) 497 { 498 guid_t *sec_type = (guid_t *)gdata->section_type; 499 __u16 severity; 500 char newpfx[64]; 501 502 if (acpi_hest_get_version(gdata) >= 3) 503 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 504 505 severity = gdata->error_severity; 506 printk("%s""Error %d, type: %s\n", pfx, sec_no, 507 cper_severity_str(severity)); 508 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 509 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 510 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 511 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 512 513 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 514 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 515 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 516 517 printk("%s""section_type: general processor error\n", newpfx); 518 if (gdata->error_data_length >= sizeof(*proc_err)) 519 cper_print_proc_generic(newpfx, proc_err); 520 else 521 goto err_section_too_small; 522 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 523 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 524 525 printk("%s""section_type: memory error\n", newpfx); 526 if (gdata->error_data_length >= 527 sizeof(struct cper_sec_mem_err_old)) 528 cper_print_mem(newpfx, mem_err, 529 gdata->error_data_length); 530 else 531 goto err_section_too_small; 532 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 533 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 534 535 printk("%s""section_type: PCIe error\n", newpfx); 536 if (gdata->error_data_length >= sizeof(*pcie)) 537 cper_print_pcie(newpfx, pcie, gdata); 538 else 539 goto err_section_too_small; 540 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 541 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 542 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 543 544 printk("%ssection_type: ARM processor error\n", newpfx); 545 if (gdata->error_data_length >= sizeof(*arm_err)) 546 cper_print_proc_arm(newpfx, arm_err); 547 else 548 goto err_section_too_small; 549 #endif 550 #if defined(CONFIG_UEFI_CPER_X86) 551 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 552 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 553 554 printk("%ssection_type: IA32/X64 processor error\n", newpfx); 555 if (gdata->error_data_length >= sizeof(*ia_err)) 556 cper_print_proc_ia(newpfx, ia_err); 557 else 558 goto err_section_too_small; 559 #endif 560 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 561 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 562 563 printk("%ssection_type: Firmware Error Record Reference\n", 564 newpfx); 565 /* The minimal FW Error Record contains 16 bytes */ 566 if (gdata->error_data_length >= SZ_16) 567 cper_print_fw_err(newpfx, gdata, fw_err); 568 else 569 goto err_section_too_small; 570 } else { 571 const void *err = acpi_hest_get_payload(gdata); 572 573 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 574 printk("%ssection length: %#x\n", newpfx, 575 gdata->error_data_length); 576 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 577 gdata->error_data_length, true); 578 } 579 580 return; 581 582 err_section_too_small: 583 pr_err(FW_WARN "error section length is too small\n"); 584 } 585 586 void cper_estatus_print(const char *pfx, 587 const struct acpi_hest_generic_status *estatus) 588 { 589 struct acpi_hest_generic_data *gdata; 590 int sec_no = 0; 591 char newpfx[64]; 592 __u16 severity; 593 594 severity = estatus->error_severity; 595 if (severity == CPER_SEV_CORRECTED) 596 printk("%s%s\n", pfx, 597 "It has been corrected by h/w " 598 "and requires no further action"); 599 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 600 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 601 602 apei_estatus_for_each_section(estatus, gdata) { 603 cper_estatus_print_section(newpfx, gdata, sec_no); 604 sec_no++; 605 } 606 } 607 EXPORT_SYMBOL_GPL(cper_estatus_print); 608 609 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 610 { 611 if (estatus->data_length && 612 estatus->data_length < sizeof(struct acpi_hest_generic_data)) 613 return -EINVAL; 614 if (estatus->raw_data_length && 615 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 616 return -EINVAL; 617 618 return 0; 619 } 620 EXPORT_SYMBOL_GPL(cper_estatus_check_header); 621 622 int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 623 { 624 struct acpi_hest_generic_data *gdata; 625 unsigned int data_len, record_size; 626 int rc; 627 628 rc = cper_estatus_check_header(estatus); 629 if (rc) 630 return rc; 631 632 data_len = estatus->data_length; 633 634 apei_estatus_for_each_section(estatus, gdata) { 635 if (acpi_hest_get_size(gdata) > data_len) 636 return -EINVAL; 637 638 record_size = acpi_hest_get_record_size(gdata); 639 if (record_size > data_len) 640 return -EINVAL; 641 642 data_len -= record_size; 643 } 644 if (data_len) 645 return -EINVAL; 646 647 return 0; 648 } 649 EXPORT_SYMBOL_GPL(cper_estatus_check); 650