cper.c (21992B)
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 214const char *cper_mem_err_status_str(u64 status) 215{ 216 switch ((status >> 8) & 0xff) { 217 case 1: return "Error detected internal to the component"; 218 case 4: return "Storage error in DRAM memory"; 219 case 5: return "Storage error in TLB"; 220 case 6: return "Storage error in cache"; 221 case 7: return "Error in one or more functional units"; 222 case 8: return "Component failed self test"; 223 case 9: return "Overflow or undervalue of internal queue"; 224 case 16: return "Error detected in the bus"; 225 case 17: return "Virtual address not found on IO-TLB or IO-PDIR"; 226 case 18: return "Improper access error"; 227 case 19: return "Access to a memory address which is not mapped to any component"; 228 case 20: return "Loss of Lockstep"; 229 case 21: return "Response not associated with a request"; 230 case 22: return "Bus parity error - must also set the A, C, or D Bits"; 231 case 23: return "Detection of a protocol error"; 232 case 24: return "Detection of a PATH_ERROR"; 233 case 25: return "Bus operation timeout"; 234 case 26: return "A read was issued to data that has been poisoned"; 235 default: return "Reserved"; 236 } 237} 238EXPORT_SYMBOL_GPL(cper_mem_err_status_str); 239 240int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 241{ 242 u32 len, n; 243 244 if (!msg) 245 return 0; 246 247 n = 0; 248 len = CPER_REC_LEN; 249 if (mem->validation_bits & CPER_MEM_VALID_NODE) 250 n += scnprintf(msg + n, len - n, "node:%d ", mem->node); 251 if (mem->validation_bits & CPER_MEM_VALID_CARD) 252 n += scnprintf(msg + n, len - n, "card:%d ", mem->card); 253 if (mem->validation_bits & CPER_MEM_VALID_MODULE) 254 n += scnprintf(msg + n, len - n, "module:%d ", mem->module); 255 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 256 n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank); 257 if (mem->validation_bits & CPER_MEM_VALID_BANK) 258 n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank); 259 if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) 260 n += scnprintf(msg + n, len - n, "bank_group:%d ", 261 mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); 262 if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) 263 n += scnprintf(msg + n, len - n, "bank_address:%d ", 264 mem->bank & CPER_MEM_BANK_ADDRESS_MASK); 265 if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 266 n += scnprintf(msg + n, len - n, "device:%d ", mem->device); 267 if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { 268 u32 row = mem->row; 269 270 row |= cper_get_mem_extension(mem->validation_bits, mem->extended); 271 n += scnprintf(msg + n, len - n, "row:%d ", row); 272 } 273 if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 274 n += scnprintf(msg + n, len - n, "column:%d ", mem->column); 275 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 276 n += scnprintf(msg + n, len - n, "bit_position:%d ", 277 mem->bit_pos); 278 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 279 n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ", 280 mem->requestor_id); 281 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 282 n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ", 283 mem->responder_id); 284 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 285 n += scnprintf(msg + n, len - n, "target_id:0x%016llx ", 286 mem->target_id); 287 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 288 n += scnprintf(msg + n, len - n, "chip_id:%d ", 289 mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 290 291 return n; 292} 293 294int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 295{ 296 u32 len, n; 297 const char *bank = NULL, *device = NULL; 298 299 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 300 return 0; 301 302 len = CPER_REC_LEN; 303 dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 304 if (bank && device) 305 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 306 else 307 n = snprintf(msg, len, 308 "DIMM location: not present. DMI handle: 0x%.4x ", 309 mem->mem_dev_handle); 310 311 return n; 312} 313 314void cper_mem_err_pack(const struct cper_sec_mem_err *mem, 315 struct cper_mem_err_compact *cmem) 316{ 317 cmem->validation_bits = mem->validation_bits; 318 cmem->node = mem->node; 319 cmem->card = mem->card; 320 cmem->module = mem->module; 321 cmem->bank = mem->bank; 322 cmem->device = mem->device; 323 cmem->row = mem->row; 324 cmem->column = mem->column; 325 cmem->bit_pos = mem->bit_pos; 326 cmem->requestor_id = mem->requestor_id; 327 cmem->responder_id = mem->responder_id; 328 cmem->target_id = mem->target_id; 329 cmem->extended = mem->extended; 330 cmem->rank = mem->rank; 331 cmem->mem_array_handle = mem->mem_array_handle; 332 cmem->mem_dev_handle = mem->mem_dev_handle; 333} 334 335const char *cper_mem_err_unpack(struct trace_seq *p, 336 struct cper_mem_err_compact *cmem) 337{ 338 const char *ret = trace_seq_buffer_ptr(p); 339 char rcd_decode_str[CPER_REC_LEN]; 340 341 if (cper_mem_err_location(cmem, rcd_decode_str)) 342 trace_seq_printf(p, "%s", rcd_decode_str); 343 if (cper_dimm_err_location(cmem, rcd_decode_str)) 344 trace_seq_printf(p, "%s", rcd_decode_str); 345 trace_seq_putc(p, '\0'); 346 347 return ret; 348} 349 350static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 351 int len) 352{ 353 struct cper_mem_err_compact cmem; 354 char rcd_decode_str[CPER_REC_LEN]; 355 356 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 357 if (len == sizeof(struct cper_sec_mem_err_old) && 358 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 359 pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 360 return; 361 } 362 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 363 printk("%s error_status: %s (0x%016llx)\n", 364 pfx, cper_mem_err_status_str(mem->error_status), 365 mem->error_status); 366 if (mem->validation_bits & CPER_MEM_VALID_PA) 367 printk("%s""physical_address: 0x%016llx\n", 368 pfx, mem->physical_addr); 369 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 370 printk("%s""physical_address_mask: 0x%016llx\n", 371 pfx, mem->physical_addr_mask); 372 cper_mem_err_pack(mem, &cmem); 373 if (cper_mem_err_location(&cmem, rcd_decode_str)) 374 printk("%s%s\n", pfx, rcd_decode_str); 375 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 376 u8 etype = mem->error_type; 377 printk("%s""error_type: %d, %s\n", pfx, etype, 378 cper_mem_err_type_str(etype)); 379 } 380 if (cper_dimm_err_location(&cmem, rcd_decode_str)) 381 printk("%s%s\n", pfx, rcd_decode_str); 382} 383 384static const char * const pcie_port_type_strs[] = { 385 "PCIe end point", 386 "legacy PCI end point", 387 "unknown", 388 "unknown", 389 "root port", 390 "upstream switch port", 391 "downstream switch port", 392 "PCIe to PCI/PCI-X bridge", 393 "PCI/PCI-X to PCIe bridge", 394 "root complex integrated endpoint device", 395 "root complex event collector", 396}; 397 398static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 399 const struct acpi_hest_generic_data *gdata) 400{ 401 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 402 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 403 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 404 pcie_port_type_strs[pcie->port_type] : "unknown"); 405 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 406 printk("%s""version: %d.%d\n", pfx, 407 pcie->version.major, pcie->version.minor); 408 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 409 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 410 pcie->command, pcie->status); 411 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 412 const __u8 *p; 413 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 414 pcie->device_id.segment, pcie->device_id.bus, 415 pcie->device_id.device, pcie->device_id.function); 416 printk("%s""slot: %d\n", pfx, 417 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 418 printk("%s""secondary_bus: 0x%02x\n", pfx, 419 pcie->device_id.secondary_bus); 420 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 421 pcie->device_id.vendor_id, pcie->device_id.device_id); 422 p = pcie->device_id.class_code; 423 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 424 } 425 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 426 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 427 pcie->serial_number.lower, pcie->serial_number.upper); 428 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 429 printk( 430 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 431 pfx, pcie->bridge.secondary_status, pcie->bridge.control); 432 433 /* Fatal errors call __ghes_panic() before AER handler prints this */ 434 if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 435 (gdata->error_severity & CPER_SEV_FATAL)) { 436 struct aer_capability_regs *aer; 437 438 aer = (struct aer_capability_regs *)pcie->aer_info; 439 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 440 pfx, aer->uncor_status, aer->uncor_mask); 441 printk("%saer_uncor_severity: 0x%08x\n", 442 pfx, aer->uncor_severity); 443 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 444 aer->header_log.dw0, aer->header_log.dw1, 445 aer->header_log.dw2, aer->header_log.dw3); 446 } 447} 448 449static const char * const fw_err_rec_type_strs[] = { 450 "IPF SAL Error Record", 451 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 452 "SOC Firmware Error Record Type2", 453}; 454 455static void cper_print_fw_err(const char *pfx, 456 struct acpi_hest_generic_data *gdata, 457 const struct cper_sec_fw_err_rec_ref *fw_err) 458{ 459 void *buf = acpi_hest_get_payload(gdata); 460 u32 offset, length = gdata->error_data_length; 461 462 printk("%s""Firmware Error Record Type: %s\n", pfx, 463 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 464 fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 465 printk("%s""Revision: %d\n", pfx, fw_err->revision); 466 467 /* Record Type based on UEFI 2.7 */ 468 if (fw_err->revision == 0) { 469 printk("%s""Record Identifier: %08llx\n", pfx, 470 fw_err->record_identifier); 471 } else if (fw_err->revision == 2) { 472 printk("%s""Record Identifier: %pUl\n", pfx, 473 &fw_err->record_identifier_guid); 474 } 475 476 /* 477 * The FW error record may contain trailing data beyond the 478 * structure defined by the specification. As the fields 479 * defined (and hence the offset of any trailing data) vary 480 * with the revision, set the offset to account for this 481 * variation. 482 */ 483 if (fw_err->revision == 0) { 484 /* record_identifier_guid not defined */ 485 offset = offsetof(struct cper_sec_fw_err_rec_ref, 486 record_identifier_guid); 487 } else if (fw_err->revision == 1) { 488 /* record_identifier not defined */ 489 offset = offsetof(struct cper_sec_fw_err_rec_ref, 490 record_identifier); 491 } else { 492 offset = sizeof(*fw_err); 493 } 494 495 buf += offset; 496 length -= offset; 497 498 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 499} 500 501static void cper_print_tstamp(const char *pfx, 502 struct acpi_hest_generic_data_v300 *gdata) 503{ 504 __u8 hour, min, sec, day, mon, year, century, *timestamp; 505 506 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 507 timestamp = (__u8 *)&(gdata->time_stamp); 508 sec = bcd2bin(timestamp[0]); 509 min = bcd2bin(timestamp[1]); 510 hour = bcd2bin(timestamp[2]); 511 day = bcd2bin(timestamp[4]); 512 mon = bcd2bin(timestamp[5]); 513 year = bcd2bin(timestamp[6]); 514 century = bcd2bin(timestamp[7]); 515 516 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 517 (timestamp[3] & 0x1 ? "precise " : "imprecise "), 518 century, year, mon, day, hour, min, sec); 519 } 520} 521 522static void 523cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 524 int sec_no) 525{ 526 guid_t *sec_type = (guid_t *)gdata->section_type; 527 __u16 severity; 528 char newpfx[64]; 529 530 if (acpi_hest_get_version(gdata) >= 3) 531 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 532 533 severity = gdata->error_severity; 534 printk("%s""Error %d, type: %s\n", pfx, sec_no, 535 cper_severity_str(severity)); 536 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 537 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 538 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 539 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 540 541 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 542 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 543 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 544 545 printk("%s""section_type: general processor error\n", newpfx); 546 if (gdata->error_data_length >= sizeof(*proc_err)) 547 cper_print_proc_generic(newpfx, proc_err); 548 else 549 goto err_section_too_small; 550 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 551 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 552 553 printk("%s""section_type: memory error\n", newpfx); 554 if (gdata->error_data_length >= 555 sizeof(struct cper_sec_mem_err_old)) 556 cper_print_mem(newpfx, mem_err, 557 gdata->error_data_length); 558 else 559 goto err_section_too_small; 560 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 561 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 562 563 printk("%s""section_type: PCIe error\n", newpfx); 564 if (gdata->error_data_length >= sizeof(*pcie)) 565 cper_print_pcie(newpfx, pcie, gdata); 566 else 567 goto err_section_too_small; 568#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 569 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 570 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 571 572 printk("%ssection_type: ARM processor error\n", newpfx); 573 if (gdata->error_data_length >= sizeof(*arm_err)) 574 cper_print_proc_arm(newpfx, arm_err); 575 else 576 goto err_section_too_small; 577#endif 578#if defined(CONFIG_UEFI_CPER_X86) 579 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 580 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 581 582 printk("%ssection_type: IA32/X64 processor error\n", newpfx); 583 if (gdata->error_data_length >= sizeof(*ia_err)) 584 cper_print_proc_ia(newpfx, ia_err); 585 else 586 goto err_section_too_small; 587#endif 588 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 589 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 590 591 printk("%ssection_type: Firmware Error Record Reference\n", 592 newpfx); 593 /* The minimal FW Error Record contains 16 bytes */ 594 if (gdata->error_data_length >= SZ_16) 595 cper_print_fw_err(newpfx, gdata, fw_err); 596 else 597 goto err_section_too_small; 598 } else { 599 const void *err = acpi_hest_get_payload(gdata); 600 601 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 602 printk("%ssection length: %#x\n", newpfx, 603 gdata->error_data_length); 604 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 605 gdata->error_data_length, true); 606 } 607 608 return; 609 610err_section_too_small: 611 pr_err(FW_WARN "error section length is too small\n"); 612} 613 614void cper_estatus_print(const char *pfx, 615 const struct acpi_hest_generic_status *estatus) 616{ 617 struct acpi_hest_generic_data *gdata; 618 int sec_no = 0; 619 char newpfx[64]; 620 __u16 severity; 621 622 severity = estatus->error_severity; 623 if (severity == CPER_SEV_CORRECTED) 624 printk("%s%s\n", pfx, 625 "It has been corrected by h/w " 626 "and requires no further action"); 627 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 628 snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 629 630 apei_estatus_for_each_section(estatus, gdata) { 631 cper_estatus_print_section(newpfx, gdata, sec_no); 632 sec_no++; 633 } 634} 635EXPORT_SYMBOL_GPL(cper_estatus_print); 636 637int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 638{ 639 if (estatus->data_length && 640 estatus->data_length < sizeof(struct acpi_hest_generic_data)) 641 return -EINVAL; 642 if (estatus->raw_data_length && 643 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 644 return -EINVAL; 645 646 return 0; 647} 648EXPORT_SYMBOL_GPL(cper_estatus_check_header); 649 650int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 651{ 652 struct acpi_hest_generic_data *gdata; 653 unsigned int data_len, record_size; 654 int rc; 655 656 rc = cper_estatus_check_header(estatus); 657 if (rc) 658 return rc; 659 660 data_len = estatus->data_length; 661 662 apei_estatus_for_each_section(estatus, gdata) { 663 if (acpi_hest_get_size(gdata) > data_len) 664 return -EINVAL; 665 666 record_size = acpi_hest_get_record_size(gdata); 667 if (record_size > data_len) 668 return -EINVAL; 669 670 data_len -= record_size; 671 } 672 if (data_len) 673 return -EINVAL; 674 675 return 0; 676} 677EXPORT_SYMBOL_GPL(cper_estatus_check);