714 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * UEFI Common Platform Error Record (CPER) support4 *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 by9 * various tables, such as ERST, BERT and HEST etc.10 *11 * For more information about CPER, please refer to Appendix N of UEFI12 * 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#include "cper_cxl.h"28 29/*30 * CPER record ID need to be unique even after reboot, because record31 * ID is used as index for ERST storage, while CPER records from32 * multiple boot may co-exist in ERST.33 */34u64 cper_next_record_id(void)35{36 static atomic64_t seq;37 38 if (!atomic64_read(&seq)) {39 time64_t time = ktime_get_real_seconds();40 41 /*42 * This code is unlikely to still be needed in year 2106,43 * but just in case, let's use a few more bits for timestamps44 * after y2038 to be sure they keep increasing monotonically45 * for the next few hundred years...46 */47 if (time < 0x80000000)48 atomic64_set(&seq, (ktime_get_real_seconds()) << 32);49 else50 atomic64_set(&seq, 0x8000000000000000ull |51 ktime_get_real_seconds() << 24);52 }53 54 return atomic64_inc_return(&seq);55}56EXPORT_SYMBOL_GPL(cper_next_record_id);57 58static const char * const severity_strs[] = {59 "recoverable",60 "fatal",61 "corrected",62 "info",63};64 65const char *cper_severity_str(unsigned int severity)66{67 return severity < ARRAY_SIZE(severity_strs) ?68 severity_strs[severity] : "unknown";69}70EXPORT_SYMBOL_GPL(cper_severity_str);71 72/*73 * cper_print_bits - print strings for set bits74 * @pfx: prefix for each line, including log level and prefix string75 * @bits: bit mask76 * @strs: string array, indexed by bit position77 * @strs_size: size of the string array: @strs78 *79 * For each set bit in @bits, print the corresponding string in @strs.80 * If the output length is longer than 80, multiple line will be81 * printed, with @pfx is printed at the beginning of each line.82 */83void cper_print_bits(const char *pfx, unsigned int bits,84 const char * const strs[], unsigned int strs_size)85{86 int i, len = 0;87 const char *str;88 char buf[84];89 90 for (i = 0; i < strs_size; i++) {91 if (!(bits & (1U << i)))92 continue;93 str = strs[i];94 if (!str)95 continue;96 if (len && len + strlen(str) + 2 > 80) {97 printk("%s\n", buf);98 len = 0;99 }100 if (!len)101 len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);102 else103 len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);104 }105 if (len)106 printk("%s\n", buf);107}108 109static const char * const proc_type_strs[] = {110 "IA32/X64",111 "IA64",112 "ARM",113};114 115static const char * const proc_isa_strs[] = {116 "IA32",117 "IA64",118 "X64",119 "ARM A32/T32",120 "ARM A64",121};122 123const char * const cper_proc_error_type_strs[] = {124 "cache error",125 "TLB error",126 "bus error",127 "micro-architectural error",128};129 130static const char * const proc_op_strs[] = {131 "unknown or generic",132 "data read",133 "data write",134 "instruction execution",135};136 137static const char * const proc_flag_strs[] = {138 "restartable",139 "precise IP",140 "overflow",141 "corrected",142};143 144static void cper_print_proc_generic(const char *pfx,145 const struct cper_sec_proc_generic *proc)146{147 if (proc->validation_bits & CPER_PROC_VALID_TYPE)148 printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type,149 proc->proc_type < ARRAY_SIZE(proc_type_strs) ?150 proc_type_strs[proc->proc_type] : "unknown");151 if (proc->validation_bits & CPER_PROC_VALID_ISA)152 printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa,153 proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?154 proc_isa_strs[proc->proc_isa] : "unknown");155 if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {156 printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type);157 cper_print_bits(pfx, proc->proc_error_type,158 cper_proc_error_type_strs,159 ARRAY_SIZE(cper_proc_error_type_strs));160 }161 if (proc->validation_bits & CPER_PROC_VALID_OPERATION)162 printk("%s""operation: %d, %s\n", pfx, proc->operation,163 proc->operation < ARRAY_SIZE(proc_op_strs) ?164 proc_op_strs[proc->operation] : "unknown");165 if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {166 printk("%s""flags: 0x%02x\n", pfx, proc->flags);167 cper_print_bits(pfx, proc->flags, proc_flag_strs,168 ARRAY_SIZE(proc_flag_strs));169 }170 if (proc->validation_bits & CPER_PROC_VALID_LEVEL)171 printk("%s""level: %d\n", pfx, proc->level);172 if (proc->validation_bits & CPER_PROC_VALID_VERSION)173 printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version);174 if (proc->validation_bits & CPER_PROC_VALID_ID)175 printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id);176 if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)177 printk("%s""target_address: 0x%016llx\n",178 pfx, proc->target_addr);179 if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)180 printk("%s""requestor_id: 0x%016llx\n",181 pfx, proc->requestor_id);182 if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)183 printk("%s""responder_id: 0x%016llx\n",184 pfx, proc->responder_id);185 if (proc->validation_bits & CPER_PROC_VALID_IP)186 printk("%s""IP: 0x%016llx\n", pfx, proc->ip);187}188 189static const char * const mem_err_type_strs[] = {190 "unknown",191 "no error",192 "single-bit ECC",193 "multi-bit ECC",194 "single-symbol chipkill ECC",195 "multi-symbol chipkill ECC",196 "master abort",197 "target abort",198 "parity error",199 "watchdog timeout",200 "invalid address",201 "mirror Broken",202 "memory sparing",203 "scrub corrected error",204 "scrub uncorrected error",205 "physical memory map-out event",206};207 208const char *cper_mem_err_type_str(unsigned int etype)209{210 return etype < ARRAY_SIZE(mem_err_type_strs) ?211 mem_err_type_strs[etype] : "unknown";212}213EXPORT_SYMBOL_GPL(cper_mem_err_type_str);214 215const char *cper_mem_err_status_str(u64 status)216{217 switch ((status >> 8) & 0xff) {218 case 1: return "Error detected internal to the component";219 case 4: return "Storage error in DRAM memory";220 case 5: return "Storage error in TLB";221 case 6: return "Storage error in cache";222 case 7: return "Error in one or more functional units";223 case 8: return "Component failed self test";224 case 9: return "Overflow or undervalue of internal queue";225 case 16: return "Error detected in the bus";226 case 17: return "Virtual address not found on IO-TLB or IO-PDIR";227 case 18: return "Improper access error";228 case 19: return "Access to a memory address which is not mapped to any component";229 case 20: return "Loss of Lockstep";230 case 21: return "Response not associated with a request";231 case 22: return "Bus parity error - must also set the A, C, or D Bits";232 case 23: return "Detection of a protocol error";233 case 24: return "Detection of a PATH_ERROR";234 case 25: return "Bus operation timeout";235 case 26: return "A read was issued to data that has been poisoned";236 default: return "Reserved";237 }238}239EXPORT_SYMBOL_GPL(cper_mem_err_status_str);240 241int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)242{243 u32 len, n;244 245 if (!msg)246 return 0;247 248 n = 0;249 len = CPER_REC_LEN;250 if (mem->validation_bits & CPER_MEM_VALID_NODE)251 n += scnprintf(msg + n, len - n, "node:%d ", mem->node);252 if (mem->validation_bits & CPER_MEM_VALID_CARD)253 n += scnprintf(msg + n, len - n, "card:%d ", mem->card);254 if (mem->validation_bits & CPER_MEM_VALID_MODULE)255 n += scnprintf(msg + n, len - n, "module:%d ", mem->module);256 if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)257 n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank);258 if (mem->validation_bits & CPER_MEM_VALID_BANK)259 n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank);260 if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)261 n += scnprintf(msg + n, len - n, "bank_group:%d ",262 mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);263 if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)264 n += scnprintf(msg + n, len - n, "bank_address:%d ",265 mem->bank & CPER_MEM_BANK_ADDRESS_MASK);266 if (mem->validation_bits & CPER_MEM_VALID_DEVICE)267 n += scnprintf(msg + n, len - n, "device:%d ", mem->device);268 if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {269 u32 row = mem->row;270 271 row |= cper_get_mem_extension(mem->validation_bits, mem->extended);272 n += scnprintf(msg + n, len - n, "row:%d ", row);273 }274 if (mem->validation_bits & CPER_MEM_VALID_COLUMN)275 n += scnprintf(msg + n, len - n, "column:%d ", mem->column);276 if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)277 n += scnprintf(msg + n, len - n, "bit_position:%d ",278 mem->bit_pos);279 if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)280 n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ",281 mem->requestor_id);282 if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)283 n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ",284 mem->responder_id);285 if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)286 n += scnprintf(msg + n, len - n, "target_id:0x%016llx ",287 mem->target_id);288 if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)289 n += scnprintf(msg + n, len - n, "chip_id:%d ",290 mem->extended >> CPER_MEM_CHIP_ID_SHIFT);291 292 return n;293}294EXPORT_SYMBOL_GPL(cper_mem_err_location);295 296int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)297{298 u32 len, n;299 const char *bank = NULL, *device = NULL;300 301 if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))302 return 0;303 304 len = CPER_REC_LEN;305 dmi_memdev_name(mem->mem_dev_handle, &bank, &device);306 if (bank && device)307 n = snprintf(msg, len, "DIMM location: %s %s ", bank, device);308 else309 n = snprintf(msg, len,310 "DIMM location: not present. DMI handle: 0x%.4x ",311 mem->mem_dev_handle);312 313 return n;314}315EXPORT_SYMBOL_GPL(cper_dimm_err_location);316 317void cper_mem_err_pack(const struct cper_sec_mem_err *mem,318 struct cper_mem_err_compact *cmem)319{320 cmem->validation_bits = mem->validation_bits;321 cmem->node = mem->node;322 cmem->card = mem->card;323 cmem->module = mem->module;324 cmem->bank = mem->bank;325 cmem->device = mem->device;326 cmem->row = mem->row;327 cmem->column = mem->column;328 cmem->bit_pos = mem->bit_pos;329 cmem->requestor_id = mem->requestor_id;330 cmem->responder_id = mem->responder_id;331 cmem->target_id = mem->target_id;332 cmem->extended = mem->extended;333 cmem->rank = mem->rank;334 cmem->mem_array_handle = mem->mem_array_handle;335 cmem->mem_dev_handle = mem->mem_dev_handle;336}337EXPORT_SYMBOL_GPL(cper_mem_err_pack);338 339const char *cper_mem_err_unpack(struct trace_seq *p,340 struct cper_mem_err_compact *cmem)341{342 const char *ret = trace_seq_buffer_ptr(p);343 char rcd_decode_str[CPER_REC_LEN];344 345 if (cper_mem_err_location(cmem, rcd_decode_str))346 trace_seq_printf(p, "%s", rcd_decode_str);347 if (cper_dimm_err_location(cmem, rcd_decode_str))348 trace_seq_printf(p, "%s", rcd_decode_str);349 trace_seq_putc(p, '\0');350 351 return ret;352}353 354static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,355 int len)356{357 struct cper_mem_err_compact cmem;358 char rcd_decode_str[CPER_REC_LEN];359 360 /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */361 if (len == sizeof(struct cper_sec_mem_err_old) &&362 (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) {363 pr_err(FW_WARN "valid bits set for fields beyond structure\n");364 return;365 }366 if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)367 printk("%s error_status: %s (0x%016llx)\n",368 pfx, cper_mem_err_status_str(mem->error_status),369 mem->error_status);370 if (mem->validation_bits & CPER_MEM_VALID_PA)371 printk("%s""physical_address: 0x%016llx\n",372 pfx, mem->physical_addr);373 if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)374 printk("%s""physical_address_mask: 0x%016llx\n",375 pfx, mem->physical_addr_mask);376 cper_mem_err_pack(mem, &cmem);377 if (cper_mem_err_location(&cmem, rcd_decode_str))378 printk("%s%s\n", pfx, rcd_decode_str);379 if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {380 u8 etype = mem->error_type;381 printk("%s""error_type: %d, %s\n", pfx, etype,382 cper_mem_err_type_str(etype));383 }384 if (cper_dimm_err_location(&cmem, rcd_decode_str))385 printk("%s%s\n", pfx, rcd_decode_str);386}387 388static const char * const pcie_port_type_strs[] = {389 "PCIe end point",390 "legacy PCI end point",391 "unknown",392 "unknown",393 "root port",394 "upstream switch port",395 "downstream switch port",396 "PCIe to PCI/PCI-X bridge",397 "PCI/PCI-X to PCIe bridge",398 "root complex integrated endpoint device",399 "root complex event collector",400};401 402static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,403 const struct acpi_hest_generic_data *gdata)404{405 if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)406 printk("%s""port_type: %d, %s\n", pfx, pcie->port_type,407 pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?408 pcie_port_type_strs[pcie->port_type] : "unknown");409 if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)410 printk("%s""version: %d.%d\n", pfx,411 pcie->version.major, pcie->version.minor);412 if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)413 printk("%s""command: 0x%04x, status: 0x%04x\n", pfx,414 pcie->command, pcie->status);415 if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {416 const __u8 *p;417 printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx,418 pcie->device_id.segment, pcie->device_id.bus,419 pcie->device_id.device, pcie->device_id.function);420 printk("%s""slot: %d\n", pfx,421 pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);422 printk("%s""secondary_bus: 0x%02x\n", pfx,423 pcie->device_id.secondary_bus);424 printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx,425 pcie->device_id.vendor_id, pcie->device_id.device_id);426 p = pcie->device_id.class_code;427 printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]);428 }429 if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)430 printk("%s""serial number: 0x%04x, 0x%04x\n", pfx,431 pcie->serial_number.lower, pcie->serial_number.upper);432 if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)433 printk(434 "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n",435 pfx, pcie->bridge.secondary_status, pcie->bridge.control);436 437 /*438 * Print all valid AER info. Record may be from BERT (boot-time) or GHES (run-time).439 *440 * Fatal errors call __ghes_panic() before AER handler prints this.441 */442 if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) {443 struct aer_capability_regs *aer;444 445 aer = (struct aer_capability_regs *)pcie->aer_info;446 printk("%saer_cor_status: 0x%08x, aer_cor_mask: 0x%08x\n",447 pfx, aer->cor_status, aer->cor_mask);448 printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n",449 pfx, aer->uncor_status, aer->uncor_mask);450 printk("%saer_uncor_severity: 0x%08x\n",451 pfx, aer->uncor_severity);452 printk("%sTLP Header: %08x %08x %08x %08x\n", pfx,453 aer->header_log.dw[0], aer->header_log.dw[1],454 aer->header_log.dw[2], aer->header_log.dw[3]);455 }456}457 458static const char * const fw_err_rec_type_strs[] = {459 "IPF SAL Error Record",460 "SOC Firmware Error Record Type1 (Legacy CrashLog Support)",461 "SOC Firmware Error Record Type2",462};463 464static void cper_print_fw_err(const char *pfx,465 struct acpi_hest_generic_data *gdata,466 const struct cper_sec_fw_err_rec_ref *fw_err)467{468 void *buf = acpi_hest_get_payload(gdata);469 u32 offset, length = gdata->error_data_length;470 471 printk("%s""Firmware Error Record Type: %s\n", pfx,472 fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?473 fw_err_rec_type_strs[fw_err->record_type] : "unknown");474 printk("%s""Revision: %d\n", pfx, fw_err->revision);475 476 /* Record Type based on UEFI 2.7 */477 if (fw_err->revision == 0) {478 printk("%s""Record Identifier: %08llx\n", pfx,479 fw_err->record_identifier);480 } else if (fw_err->revision == 2) {481 printk("%s""Record Identifier: %pUl\n", pfx,482 &fw_err->record_identifier_guid);483 }484 485 /*486 * The FW error record may contain trailing data beyond the487 * structure defined by the specification. As the fields488 * defined (and hence the offset of any trailing data) vary489 * with the revision, set the offset to account for this490 * variation.491 */492 if (fw_err->revision == 0) {493 /* record_identifier_guid not defined */494 offset = offsetof(struct cper_sec_fw_err_rec_ref,495 record_identifier_guid);496 } else if (fw_err->revision == 1) {497 /* record_identifier not defined */498 offset = offsetof(struct cper_sec_fw_err_rec_ref,499 record_identifier);500 } else {501 offset = sizeof(*fw_err);502 }503 504 buf += offset;505 length -= offset;506 507 print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true);508}509 510static void cper_print_tstamp(const char *pfx,511 struct acpi_hest_generic_data_v300 *gdata)512{513 __u8 hour, min, sec, day, mon, year, century, *timestamp;514 515 if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {516 timestamp = (__u8 *)&(gdata->time_stamp);517 sec = bcd2bin(timestamp[0]);518 min = bcd2bin(timestamp[1]);519 hour = bcd2bin(timestamp[2]);520 day = bcd2bin(timestamp[4]);521 mon = bcd2bin(timestamp[5]);522 year = bcd2bin(timestamp[6]);523 century = bcd2bin(timestamp[7]);524 525 printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx,526 (timestamp[3] & 0x1 ? "precise " : "imprecise "),527 century, year, mon, day, hour, min, sec);528 }529}530 531struct ignore_section {532 guid_t guid;533 const char *name;534};535 536static const struct ignore_section ignore_sections[] = {537 { .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" },538 { .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" },539 { .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" },540};541 542static void543cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,544 int sec_no)545{546 guid_t *sec_type = (guid_t *)gdata->section_type;547 __u16 severity;548 char newpfx[64];549 550 if (acpi_hest_get_version(gdata) >= 3)551 cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);552 553 severity = gdata->error_severity;554 printk("%s""Error %d, type: %s\n", pfx, sec_no,555 cper_severity_str(severity));556 if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)557 printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id);558 if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)559 printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text);560 561 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);562 563 for (int i = 0; i < ARRAY_SIZE(ignore_sections); i++) {564 if (guid_equal(sec_type, &ignore_sections[i].guid)) {565 printk("%ssection_type: %s\n", newpfx, ignore_sections[i].name);566 return;567 }568 }569 570 if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {571 struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);572 573 printk("%s""section_type: general processor error\n", newpfx);574 if (gdata->error_data_length >= sizeof(*proc_err))575 cper_print_proc_generic(newpfx, proc_err);576 else577 goto err_section_too_small;578 } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {579 struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);580 581 printk("%s""section_type: memory error\n", newpfx);582 if (gdata->error_data_length >=583 sizeof(struct cper_sec_mem_err_old))584 cper_print_mem(newpfx, mem_err,585 gdata->error_data_length);586 else587 goto err_section_too_small;588 } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {589 struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);590 591 printk("%s""section_type: PCIe error\n", newpfx);592 if (gdata->error_data_length >= sizeof(*pcie))593 cper_print_pcie(newpfx, pcie, gdata);594 else595 goto err_section_too_small;596#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)597 } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {598 struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);599 600 printk("%ssection_type: ARM processor error\n", newpfx);601 if (gdata->error_data_length >= sizeof(*arm_err))602 cper_print_proc_arm(newpfx, arm_err);603 else604 goto err_section_too_small;605#endif606#if defined(CONFIG_UEFI_CPER_X86)607 } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {608 struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);609 610 printk("%ssection_type: IA32/X64 processor error\n", newpfx);611 if (gdata->error_data_length >= sizeof(*ia_err))612 cper_print_proc_ia(newpfx, ia_err);613 else614 goto err_section_too_small;615#endif616 } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {617 struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);618 619 printk("%ssection_type: Firmware Error Record Reference\n",620 newpfx);621 /* The minimal FW Error Record contains 16 bytes */622 if (gdata->error_data_length >= SZ_16)623 cper_print_fw_err(newpfx, gdata, fw_err);624 else625 goto err_section_too_small;626 } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {627 struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);628 629 printk("%ssection_type: CXL Protocol Error\n", newpfx);630 if (gdata->error_data_length >= sizeof(*prot_err))631 cper_print_prot_err(newpfx, prot_err);632 else633 goto err_section_too_small;634 } else {635 const void *err = acpi_hest_get_payload(gdata);636 637 printk("%ssection type: unknown, %pUl\n", newpfx, sec_type);638 printk("%ssection length: %#x\n", newpfx,639 gdata->error_data_length);640 print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err,641 gdata->error_data_length, true);642 }643 644 return;645 646err_section_too_small:647 pr_err(FW_WARN "error section length is too small\n");648}649 650void cper_estatus_print(const char *pfx,651 const struct acpi_hest_generic_status *estatus)652{653 struct acpi_hest_generic_data *gdata;654 int sec_no = 0;655 char newpfx[64];656 __u16 severity;657 658 severity = estatus->error_severity;659 if (severity == CPER_SEV_CORRECTED)660 printk("%s%s\n", pfx,661 "It has been corrected by h/w "662 "and requires no further action");663 printk("%s""event severity: %s\n", pfx, cper_severity_str(severity));664 snprintf(newpfx, sizeof(newpfx), "%s ", pfx);665 666 apei_estatus_for_each_section(estatus, gdata) {667 cper_estatus_print_section(newpfx, gdata, sec_no);668 sec_no++;669 }670}671EXPORT_SYMBOL_GPL(cper_estatus_print);672 673int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)674{675 if (estatus->data_length &&676 estatus->data_length < sizeof(struct acpi_hest_generic_data))677 return -EINVAL;678 if (estatus->raw_data_length &&679 estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length)680 return -EINVAL;681 682 return 0;683}684EXPORT_SYMBOL_GPL(cper_estatus_check_header);685 686int cper_estatus_check(const struct acpi_hest_generic_status *estatus)687{688 struct acpi_hest_generic_data *gdata;689 unsigned int data_len, record_size;690 int rc;691 692 rc = cper_estatus_check_header(estatus);693 if (rc)694 return rc;695 696 data_len = estatus->data_length;697 698 apei_estatus_for_each_section(estatus, gdata) {699 if (acpi_hest_get_size(gdata) > data_len)700 return -EINVAL;701 702 record_size = acpi_hest_get_record_size(gdata);703 if (record_size > data_len)704 return -EINVAL;705 706 data_len -= record_size;707 }708 if (data_len)709 return -EINVAL;710 711 return 0;712}713EXPORT_SYMBOL_GPL(cper_estatus_check);714