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 n = 0; 280 len = CPER_REC_LEN - 1; 281 dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 282 if (bank && device) 283 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 284 else 285 n = snprintf(msg, len, 286 "DIMM location: not present. DMI handle: 0x%.4x ", 287 mem->mem_dev_handle); 288 289 msg[n] = '\0'; 290 return n; 291 } 292 293 void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 294 struct cper_mem_err_compact *cmem) 295 { 296 cmem->validation_bits = mem->validation_bits; 297 cmem->node = mem->node; 298 cmem->card = mem->card; 299 cmem->module = mem->module; 300 cmem->bank = mem->bank; 301 cmem->device = mem->device; 302 cmem->row = mem->row; 303 cmem->column = mem->column; 304 cmem->bit_pos = mem->bit_pos; 305 cmem->requestor_id = mem->requestor_id; 306 cmem->responder_id = mem->responder_id; 307 cmem->target_id = mem->target_id; 308 cmem->extended = mem->extended; 309 cmem->rank = mem->rank; 310 cmem->mem_array_handle = mem->mem_array_handle; 311 cmem->mem_dev_handle = mem->mem_dev_handle; 312 } 313 314 const char *cper_mem_err_unpack(struct trace_seq *p, 315 struct cper_mem_err_compact *cmem) 316 { 317 const char *ret = trace_seq_buffer_ptr(p); 318 319 if (cper_mem_err_location(cmem, rcd_decode_str)) 320 trace_seq_printf(p, "%s", rcd_decode_str); 321 if (cper_dimm_err_location(cmem, rcd_decode_str)) 322 trace_seq_printf(p, "%s", rcd_decode_str); 323 trace_seq_putc(p, '\0'); 324 325 return ret; 326 } 327 328 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 329 int len) 330 { 331 struct cper_mem_err_compact cmem; 332 333 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 334 if (len == sizeof(struct cper_sec_mem_err_old) && 335 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 336 pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 337 return; 338 } 339 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 340 printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 341 if (mem->validation_bits & CPER_MEM_VALID_PA) 342 printk("%s""physical_address: 0x%016llx\n", 343 pfx, mem->physical_addr); 344 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 345 printk("%s""physical_address_mask: 0x%016llx\n", 346 pfx, mem->physical_addr_mask); 347 cper_mem_err_pack(mem, &cmem); 348 if (cper_mem_err_location(&cmem, rcd_decode_str)) 349 printk("%s%s\n", pfx, rcd_decode_str); 350 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 351 u8 etype = mem->error_type; 352 printk("%s""error_type: %d, %s\n", pfx, etype, 353 cper_mem_err_type_str(etype)); 354 } 355 if (cper_dimm_err_location(&cmem, rcd_decode_str)) 356 printk("%s%s\n", pfx, rcd_decode_str); 357 } 358 359 static const char * const pcie_port_type_strs[] = { 360 "PCIe end point", 361 "legacy PCI end point", 362 "unknown", 363 "unknown", 364 "root port", 365 "upstream switch port", 366 "downstream switch port", 367 "PCIe to PCI/PCI-X bridge", 368 "PCI/PCI-X to PCIe bridge", 369 "root complex integrated endpoint device", 370 "root complex event collector", 371 }; 372 373 static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 374 const struct acpi_hest_generic_data *gdata) 375 { 376 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 377 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 378 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 379 pcie_port_type_strs[pcie->port_type] : "unknown"); 380 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 381 printk("%s""version: %d.%d\n", pfx, 382 pcie->version.major, pcie->version.minor); 383 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 384 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 385 pcie->command, pcie->status); 386 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 387 const __u8 *p; 388 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 389 pcie->device_id.segment, pcie->device_id.bus, 390 pcie->device_id.device, pcie->device_id.function); 391 printk("%s""slot: %d\n", pfx, 392 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 393 printk("%s""secondary_bus: 0x%02x\n", pfx, 394 pcie->device_id.secondary_bus); 395 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 396 pcie->device_id.vendor_id, pcie->device_id.device_id); 397 p = pcie->device_id.class_code; 398 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 399 } 400 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 401 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 402 pcie->serial_number.lower, pcie->serial_number.upper); 403 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 404 printk( 405 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 406 pfx, pcie->bridge.secondary_status, pcie->bridge.control); 407 408 /* Fatal errors call __ghes_panic() before AER handler prints this */ 409 if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 410 (gdata->error_severity & CPER_SEV_FATAL)) { 411 struct aer_capability_regs *aer; 412 413 aer = (struct aer_capability_regs *)pcie->aer_info; 414 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 415 pfx, aer->uncor_status, aer->uncor_mask); 416 printk("%saer_uncor_severity: 0x%08x\n", 417 pfx, aer->uncor_severity); 418 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 419 aer->header_log.dw0, aer->header_log.dw1, 420 aer->header_log.dw2, aer->header_log.dw3); 421 } 422 } 423 424 static const char * const fw_err_rec_type_strs[] = { 425 "IPF SAL Error Record", 426 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 427 "SOC Firmware Error Record Type2", 428 }; 429 430 static void cper_print_fw_err(const char *pfx, 431 struct acpi_hest_generic_data *gdata, 432 const struct cper_sec_fw_err_rec_ref *fw_err) 433 { 434 void *buf = acpi_hest_get_payload(gdata); 435 u32 offset, length = gdata->error_data_length; 436 437 printk("%s""Firmware Error Record Type: %s\n", pfx, 438 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 439 fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 440 printk("%s""Revision: %d\n", pfx, fw_err->revision); 441 442 /* Record Type based on UEFI 2.7 */ 443 if (fw_err->revision == 0) { 444 printk("%s""Record Identifier: %08llx\n", pfx, 445 fw_err->record_identifier); 446 } else if (fw_err->revision == 2) { 447 printk("%s""Record Identifier: %pUl\n", pfx, 448 &fw_err->record_identifier_guid); 449 } 450 451 /* 452 * The FW error record may contain trailing data beyond the 453 * structure defined by the specification. As the fields 454 * defined (and hence the offset of any trailing data) vary 455 * with the revision, set the offset to account for this 456 * variation. 457 */ 458 if (fw_err->revision == 0) { 459 /* record_identifier_guid not defined */ 460 offset = offsetof(struct cper_sec_fw_err_rec_ref, 461 record_identifier_guid); 462 } else if (fw_err->revision == 1) { 463 /* record_identifier not defined */ 464 offset = offsetof(struct cper_sec_fw_err_rec_ref, 465 record_identifier); 466 } else { 467 offset = sizeof(*fw_err); 468 } 469 470 buf += offset; 471 length -= offset; 472 473 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 474 } 475 476 static void cper_print_tstamp(const char *pfx, 477 struct acpi_hest_generic_data_v300 *gdata) 478 { 479 __u8 hour, min, sec, day, mon, year, century, *timestamp; 480 481 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 482 timestamp = (__u8 *)&(gdata->time_stamp); 483 sec = bcd2bin(timestamp[0]); 484 min = bcd2bin(timestamp[1]); 485 hour = bcd2bin(timestamp[2]); 486 day = bcd2bin(timestamp[4]); 487 mon = bcd2bin(timestamp[5]); 488 year = bcd2bin(timestamp[6]); 489 century = bcd2bin(timestamp[7]); 490 491 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 492 (timestamp[3] & 0x1 ? "precise " : "imprecise "), 493 century, year, mon, day, hour, min, sec); 494 } 495 } 496 497 static void 498 cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 499 int sec_no) 500 { 501 guid_t *sec_type = (guid_t *)gdata->section_type; 502 __u16 severity; 503 char newpfx[64]; 504 505 if (acpi_hest_get_version(gdata) >= 3) 506 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 507 508 severity = gdata->error_severity; 509 printk("%s""Error %d, type: %s\n", pfx, sec_no, 510 cper_severity_str(severity)); 511 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 512 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 513 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 514 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 515 516 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 517 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 518 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 519 520 printk("%s""section_type: general processor error\n", newpfx); 521 if (gdata->error_data_length >= sizeof(*proc_err)) 522 cper_print_proc_generic(newpfx, proc_err); 523 else 524 goto err_section_too_small; 525 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 526 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 527 528 printk("%s""section_type: memory error\n", newpfx); 529 if (gdata->error_data_length >= 530 sizeof(struct cper_sec_mem_err_old)) 531 cper_print_mem(newpfx, mem_err, 532 gdata->error_data_length); 533 else 534 goto err_section_too_small; 535 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 536 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 537 538 printk("%s""section_type: PCIe error\n", newpfx); 539 if (gdata->error_data_length >= sizeof(*pcie)) 540 cper_print_pcie(newpfx, pcie, gdata); 541 else 542 goto err_section_too_small; 543 #if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 544 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 545 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 546 547 printk("%ssection_type: ARM processor error\n", newpfx); 548 if (gdata->error_data_length >= sizeof(*arm_err)) 549 cper_print_proc_arm(newpfx, arm_err); 550 else 551 goto err_section_too_small; 552 #endif 553 #if defined(CONFIG_UEFI_CPER_X86) 554 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 555 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 556 557 printk("%ssection_type: IA32/X64 processor error\n", newpfx); 558 if (gdata->error_data_length >= sizeof(*ia_err)) 559 cper_print_proc_ia(newpfx, ia_err); 560 else 561 goto err_section_too_small; 562 #endif 563 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 564 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 565 566 printk("%ssection_type: Firmware Error Record Reference\n", 567 newpfx); 568 /* The minimal FW Error Record contains 16 bytes */ 569 if (gdata->error_data_length >= SZ_16) 570 cper_print_fw_err(newpfx, gdata, fw_err); 571 else 572 goto err_section_too_small; 573 } else { 574 const void *err = acpi_hest_get_payload(gdata); 575 576 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 577 printk("%ssection length: %#x\n", newpfx, 578 gdata->error_data_length); 579 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 580 gdata->error_data_length, true); 581 } 582 583 return; 584 585 err_section_too_small: 586 pr_err(FW_WARN "error section length is too small\n"); 587 } 588 589 void cper_estatus_print(const char *pfx, 590 const struct acpi_hest_generic_status *estatus) 591 { 592 struct acpi_hest_generic_data *gdata; 593 int sec_no = 0; 594 char newpfx[64]; 595 __u16 severity; 596 597 severity = estatus->error_severity; 598 if (severity == CPER_SEV_CORRECTED) 599 printk("%s%s\n", pfx, 600 "It has been corrected by h/w " 601 "and requires no further action"); 602 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 603 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 604 605 apei_estatus_for_each_section(estatus, gdata) { 606 cper_estatus_print_section(newpfx, gdata, sec_no); 607 sec_no++; 608 } 609 } 610 EXPORT_SYMBOL_GPL(cper_estatus_print); 611 612 int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 613 { 614 if (estatus->data_length && 615 estatus->data_length < sizeof(struct acpi_hest_generic_data)) 616 return -EINVAL; 617 if (estatus->raw_data_length && 618 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 619 return -EINVAL; 620 621 return 0; 622 } 623 EXPORT_SYMBOL_GPL(cper_estatus_check_header); 624 625 int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 626 { 627 struct acpi_hest_generic_data *gdata; 628 unsigned int data_len, record_size; 629 int rc; 630 631 rc = cper_estatus_check_header(estatus); 632 if (rc) 633 return rc; 634 635 data_len = estatus->data_length; 636 637 apei_estatus_for_each_section(estatus, gdata) { 638 if (sizeof(struct acpi_hest_generic_data) > data_len) 639 return -EINVAL; 640 641 record_size = acpi_hest_get_record_size(gdata); 642 if (record_size > data_len) 643 return -EINVAL; 644 645 data_len -= record_size; 646 } 647 if (data_len) 648 return -EINVAL; 649 650 return 0; 651 } 652 EXPORT_SYMBOL_GPL(cper_estatus_check); 653