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 */ 33 u64 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 } 55 EXPORT_SYMBOL_GPL(cper_next_record_id); 56 57 static const char * const severity_strs[] = { 58 "recoverable", 59 "fatal", 60 "corrected", 61 "info", 62 }; 63 64 const char *cper_severity_str(unsigned int severity) 65 { 66 return severity < ARRAY_SIZE(severity_strs) ? 67 severity_strs[severity] : "unknown"; 68 } 69 EXPORT_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 */ 82 void 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 108 static const char * const proc_type_strs[] = { 109 "IA32/X64", 110 "IA64", 111 "ARM", 112 }; 113 114 static const char * const proc_isa_strs[] = { 115 "IA32", 116 "IA64", 117 "X64", 118 "ARM A32/T32", 119 "ARM A64", 120 }; 121 122 const char * const cper_proc_error_type_strs[] = { 123 "cache error", 124 "TLB error", 125 "bus error", 126 "micro-architectural error", 127 }; 128 129 static const char * const proc_op_strs[] = { 130 "unknown or generic", 131 "data read", 132 "data write", 133 "instruction execution", 134 }; 135 136 static const char * const proc_flag_strs[] = { 137 "restartable", 138 "precise IP", 139 "overflow", 140 "corrected", 141 }; 142 143 static 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 188 static 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 207 const 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 } 212 EXPORT_SYMBOL_GPL(cper_mem_err_type_str); 213 214 static 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; 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 n += scnprintf(msg + n, len - n, "target_id: 0x%016llx ", 260 mem->target_id); 261 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 262 n += scnprintf(msg + n, len - n, "chip_id: %d ", 263 mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 264 265 return n; 266 } 267 268 static int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 269 { 270 u32 len, n; 271 const char *bank = NULL, *device = NULL; 272 273 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 274 return 0; 275 276 len = CPER_REC_LEN; 277 dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 278 if (bank && device) 279 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 280 else 281 n = snprintf(msg, len, 282 "DIMM location: not present. DMI handle: 0x%.4x ", 283 mem->mem_dev_handle); 284 285 return n; 286 } 287 288 void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 289 struct cper_mem_err_compact *cmem) 290 { 291 cmem->validation_bits = mem->validation_bits; 292 cmem->node = mem->node; 293 cmem->card = mem->card; 294 cmem->module = mem->module; 295 cmem->bank = mem->bank; 296 cmem->device = mem->device; 297 cmem->row = mem->row; 298 cmem->column = mem->column; 299 cmem->bit_pos = mem->bit_pos; 300 cmem->requestor_id = mem->requestor_id; 301 cmem->responder_id = mem->responder_id; 302 cmem->target_id = mem->target_id; 303 cmem->extended = mem->extended; 304 cmem->rank = mem->rank; 305 cmem->mem_array_handle = mem->mem_array_handle; 306 cmem->mem_dev_handle = mem->mem_dev_handle; 307 } 308 309 const char *cper_mem_err_unpack(struct trace_seq *p, 310 struct cper_mem_err_compact *cmem) 311 { 312 const char *ret = trace_seq_buffer_ptr(p); 313 char rcd_decode_str[CPER_REC_LEN]; 314 315 if (cper_mem_err_location(cmem, rcd_decode_str)) 316 trace_seq_printf(p, "%s", rcd_decode_str); 317 if (cper_dimm_err_location(cmem, rcd_decode_str)) 318 trace_seq_printf(p, "%s", rcd_decode_str); 319 trace_seq_putc(p, '\0'); 320 321 return ret; 322 } 323 324 static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 325 int len) 326 { 327 struct cper_mem_err_compact cmem; 328 char rcd_decode_str[CPER_REC_LEN]; 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