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 - 1; 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 scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 262 mem->target_id); 263 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 264 scnprintf(msg + n, len - n, "chip_id: %d ", 265 mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 266 267 msg[n] = '\0'; 268 return n; 269 } 270 271 static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 272 { 273 u32 len, n; 274 const char *bank = NULL, *device = NULL; 275 276 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 277 return 0; 278 279 len = CPER_REC_LEN; 280 dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 281 if (bank && device) 282 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 283 else 284 n = snprintf(msg, len, 285 "DIMM location: not present. DMI handle: 0x%.4x ", 286 mem->mem_dev_handle); 287 288 return n; 289 } 290 291 void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 292 struct cper_mem_err_compact *cmem) 293 { 294 cmem->validation_bits = mem->validation_bits; 295 cmem->node = mem->node; 296 cmem->card = mem->card; 297 cmem->module = mem->module; 298 cmem->bank = mem->bank; 299 cmem->device = mem->device; 300 cmem->row = mem->row; 301 cmem->column = mem->column; 302 cmem->bit_pos = mem->bit_pos; 303 cmem->requestor_id = mem->requestor_id; 304 cmem->responder_id = mem->responder_id; 305 cmem->target_id = mem->target_id; 306 cmem->extended = mem->extended; 307 cmem->rank = mem->rank; 308 cmem->mem_array_handle = mem->mem_array_handle; 309 cmem->mem_dev_handle = mem->mem_dev_handle; 310 } 311 312 const char *cper_mem_err_unpack(struct trace_seq *p, 313 struct cper_mem_err_compact *cmem) 314 { 315 const char *ret = trace_seq_buffer_ptr(p); 316 317 if (cper_mem_err_location(cmem, rcd_decode_str)) 318 trace_seq_printf(p, "%s", rcd_decode_str); 319 if (cper_dimm_err_location(cmem, rcd_decode_str)) 320 trace_seq_printf(p, "%s", rcd_decode_str); 321 trace_seq_putc(p, '\0'); 322 323 return ret; 324 } 325 326 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 327 int len) 328 { 329 struct cper_mem_err_compact cmem; 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 357 static 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 371 static 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 422 static 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 428 static 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 474 static 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 495 static void 496 cper_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 583 err_section_too_small: 584 pr_err(FW_WARN "error section length is too small\n"); 585 } 586 587 void 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 } 608 EXPORT_SYMBOL_GPL(cper_estatus_print); 609 610 int 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 } 621 EXPORT_SYMBOL_GPL(cper_estatus_check_header); 622 623 int 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 } 650 EXPORT_SYMBOL_GPL(cper_estatus_check); 651