17ea6c6c1SLuck, Tony /* 27ea6c6c1SLuck, Tony * UEFI Common Platform Error Record (CPER) support 37ea6c6c1SLuck, Tony * 47ea6c6c1SLuck, Tony * Copyright (C) 2010, Intel Corp. 57ea6c6c1SLuck, Tony * Author: Huang Ying <ying.huang@intel.com> 67ea6c6c1SLuck, Tony * 77ea6c6c1SLuck, Tony * CPER is the format used to describe platform hardware error by 87ea6c6c1SLuck, Tony * various tables, such as ERST, BERT and HEST etc. 97ea6c6c1SLuck, Tony * 107ea6c6c1SLuck, Tony * For more information about CPER, please refer to Appendix N of UEFI 117ea6c6c1SLuck, Tony * Specification version 2.4. 127ea6c6c1SLuck, Tony * 137ea6c6c1SLuck, Tony * This program is free software; you can redistribute it and/or 147ea6c6c1SLuck, Tony * modify it under the terms of the GNU General Public License version 157ea6c6c1SLuck, Tony * 2 as published by the Free Software Foundation. 167ea6c6c1SLuck, Tony * 177ea6c6c1SLuck, Tony * This program is distributed in the hope that it will be useful, 187ea6c6c1SLuck, Tony * but WITHOUT ANY WARRANTY; without even the implied warranty of 197ea6c6c1SLuck, Tony * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 207ea6c6c1SLuck, Tony * GNU General Public License for more details. 217ea6c6c1SLuck, Tony * 227ea6c6c1SLuck, Tony * You should have received a copy of the GNU General Public License 237ea6c6c1SLuck, Tony * along with this program; if not, write to the Free Software 247ea6c6c1SLuck, Tony * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 257ea6c6c1SLuck, Tony */ 267ea6c6c1SLuck, Tony 277ea6c6c1SLuck, Tony #include <linux/kernel.h> 287ea6c6c1SLuck, Tony #include <linux/module.h> 297ea6c6c1SLuck, Tony #include <linux/time.h> 307ea6c6c1SLuck, Tony #include <linux/cper.h> 317ea6c6c1SLuck, Tony #include <linux/dmi.h> 327ea6c6c1SLuck, Tony #include <linux/acpi.h> 337ea6c6c1SLuck, Tony #include <linux/pci.h> 347ea6c6c1SLuck, Tony #include <linux/aer.h> 357ea6c6c1SLuck, Tony 367ea6c6c1SLuck, Tony #define INDENT_SP " " 377ea6c6c1SLuck, Tony /* 387ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 397ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 407ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 417ea6c6c1SLuck, Tony */ 427ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 437ea6c6c1SLuck, Tony { 447ea6c6c1SLuck, Tony static atomic64_t seq; 457ea6c6c1SLuck, Tony 467ea6c6c1SLuck, Tony if (!atomic64_read(&seq)) 477ea6c6c1SLuck, Tony atomic64_set(&seq, ((u64)get_seconds()) << 32); 487ea6c6c1SLuck, Tony 497ea6c6c1SLuck, Tony return atomic64_inc_return(&seq); 507ea6c6c1SLuck, Tony } 517ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id); 527ea6c6c1SLuck, Tony 537ea6c6c1SLuck, Tony static const char *cper_severity_strs[] = { 547ea6c6c1SLuck, Tony "recoverable", 557ea6c6c1SLuck, Tony "fatal", 567ea6c6c1SLuck, Tony "corrected", 577ea6c6c1SLuck, Tony "info", 587ea6c6c1SLuck, Tony }; 597ea6c6c1SLuck, Tony 607ea6c6c1SLuck, Tony static const char *cper_severity_str(unsigned int severity) 617ea6c6c1SLuck, Tony { 627ea6c6c1SLuck, Tony return severity < ARRAY_SIZE(cper_severity_strs) ? 637ea6c6c1SLuck, Tony cper_severity_strs[severity] : "unknown"; 647ea6c6c1SLuck, Tony } 657ea6c6c1SLuck, Tony 667ea6c6c1SLuck, Tony /* 677ea6c6c1SLuck, Tony * cper_print_bits - print strings for set bits 687ea6c6c1SLuck, Tony * @pfx: prefix for each line, including log level and prefix string 697ea6c6c1SLuck, Tony * @bits: bit mask 707ea6c6c1SLuck, Tony * @strs: string array, indexed by bit position 717ea6c6c1SLuck, Tony * @strs_size: size of the string array: @strs 727ea6c6c1SLuck, Tony * 737ea6c6c1SLuck, Tony * For each set bit in @bits, print the corresponding string in @strs. 747ea6c6c1SLuck, Tony * If the output length is longer than 80, multiple line will be 757ea6c6c1SLuck, Tony * printed, with @pfx is printed at the beginning of each line. 767ea6c6c1SLuck, Tony */ 777ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits, 787ea6c6c1SLuck, Tony const char * const strs[], unsigned int strs_size) 797ea6c6c1SLuck, Tony { 807ea6c6c1SLuck, Tony int i, len = 0; 817ea6c6c1SLuck, Tony const char *str; 827ea6c6c1SLuck, Tony char buf[84]; 837ea6c6c1SLuck, Tony 847ea6c6c1SLuck, Tony for (i = 0; i < strs_size; i++) { 857ea6c6c1SLuck, Tony if (!(bits & (1U << i))) 867ea6c6c1SLuck, Tony continue; 877ea6c6c1SLuck, Tony str = strs[i]; 887ea6c6c1SLuck, Tony if (!str) 897ea6c6c1SLuck, Tony continue; 907ea6c6c1SLuck, Tony if (len && len + strlen(str) + 2 > 80) { 917ea6c6c1SLuck, Tony printk("%s\n", buf); 927ea6c6c1SLuck, Tony len = 0; 937ea6c6c1SLuck, Tony } 947ea6c6c1SLuck, Tony if (!len) 957ea6c6c1SLuck, Tony len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 967ea6c6c1SLuck, Tony else 977ea6c6c1SLuck, Tony len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); 987ea6c6c1SLuck, Tony } 997ea6c6c1SLuck, Tony if (len) 1007ea6c6c1SLuck, Tony printk("%s\n", buf); 1017ea6c6c1SLuck, Tony } 1027ea6c6c1SLuck, Tony 1037ea6c6c1SLuck, Tony static const char * const cper_proc_type_strs[] = { 1047ea6c6c1SLuck, Tony "IA32/X64", 1057ea6c6c1SLuck, Tony "IA64", 1067ea6c6c1SLuck, Tony }; 1077ea6c6c1SLuck, Tony 1087ea6c6c1SLuck, Tony static const char * const cper_proc_isa_strs[] = { 1097ea6c6c1SLuck, Tony "IA32", 1107ea6c6c1SLuck, Tony "IA64", 1117ea6c6c1SLuck, Tony "X64", 1127ea6c6c1SLuck, Tony }; 1137ea6c6c1SLuck, Tony 1147ea6c6c1SLuck, Tony static const char * const cper_proc_error_type_strs[] = { 1157ea6c6c1SLuck, Tony "cache error", 1167ea6c6c1SLuck, Tony "TLB error", 1177ea6c6c1SLuck, Tony "bus error", 1187ea6c6c1SLuck, Tony "micro-architectural error", 1197ea6c6c1SLuck, Tony }; 1207ea6c6c1SLuck, Tony 1217ea6c6c1SLuck, Tony static const char * const cper_proc_op_strs[] = { 1227ea6c6c1SLuck, Tony "unknown or generic", 1237ea6c6c1SLuck, Tony "data read", 1247ea6c6c1SLuck, Tony "data write", 1257ea6c6c1SLuck, Tony "instruction execution", 1267ea6c6c1SLuck, Tony }; 1277ea6c6c1SLuck, Tony 1287ea6c6c1SLuck, Tony static const char * const cper_proc_flag_strs[] = { 1297ea6c6c1SLuck, Tony "restartable", 1307ea6c6c1SLuck, Tony "precise IP", 1317ea6c6c1SLuck, Tony "overflow", 1327ea6c6c1SLuck, Tony "corrected", 1337ea6c6c1SLuck, Tony }; 1347ea6c6c1SLuck, Tony 1357ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx, 1367ea6c6c1SLuck, Tony const struct cper_sec_proc_generic *proc) 1377ea6c6c1SLuck, Tony { 1387ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TYPE) 1397ea6c6c1SLuck, Tony printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 1407ea6c6c1SLuck, Tony proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? 1417ea6c6c1SLuck, Tony cper_proc_type_strs[proc->proc_type] : "unknown"); 1427ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ISA) 1437ea6c6c1SLuck, Tony printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 1447ea6c6c1SLuck, Tony proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? 1457ea6c6c1SLuck, Tony cper_proc_isa_strs[proc->proc_isa] : "unknown"); 1467ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 1477ea6c6c1SLuck, Tony printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 1487ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->proc_error_type, 1497ea6c6c1SLuck, Tony cper_proc_error_type_strs, 1507ea6c6c1SLuck, Tony ARRAY_SIZE(cper_proc_error_type_strs)); 1517ea6c6c1SLuck, Tony } 1527ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 1537ea6c6c1SLuck, Tony printk("%s""operation: %d, %s\n", pfx, proc->operation, 1547ea6c6c1SLuck, Tony proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? 1557ea6c6c1SLuck, Tony cper_proc_op_strs[proc->operation] : "unknown"); 1567ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 1577ea6c6c1SLuck, Tony printk("%s""flags: 0x%02x\n", pfx, proc->flags); 1587ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, 1597ea6c6c1SLuck, Tony ARRAY_SIZE(cper_proc_flag_strs)); 1607ea6c6c1SLuck, Tony } 1617ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 1627ea6c6c1SLuck, Tony printk("%s""level: %d\n", pfx, proc->level); 1637ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_VERSION) 1647ea6c6c1SLuck, Tony printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 1657ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ID) 1667ea6c6c1SLuck, Tony printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 1677ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 1687ea6c6c1SLuck, Tony printk("%s""target_address: 0x%016llx\n", 1697ea6c6c1SLuck, Tony pfx, proc->target_addr); 1707ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 1717ea6c6c1SLuck, Tony printk("%s""requestor_id: 0x%016llx\n", 1727ea6c6c1SLuck, Tony pfx, proc->requestor_id); 1737ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 1747ea6c6c1SLuck, Tony printk("%s""responder_id: 0x%016llx\n", 1757ea6c6c1SLuck, Tony pfx, proc->responder_id); 1767ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_IP) 1777ea6c6c1SLuck, Tony printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 1787ea6c6c1SLuck, Tony } 1797ea6c6c1SLuck, Tony 1807ea6c6c1SLuck, Tony static const char *cper_mem_err_type_strs[] = { 1817ea6c6c1SLuck, Tony "unknown", 1827ea6c6c1SLuck, Tony "no error", 1837ea6c6c1SLuck, Tony "single-bit ECC", 1847ea6c6c1SLuck, Tony "multi-bit ECC", 1857ea6c6c1SLuck, Tony "single-symbol chipkill ECC", 1867ea6c6c1SLuck, Tony "multi-symbol chipkill ECC", 1877ea6c6c1SLuck, Tony "master abort", 1887ea6c6c1SLuck, Tony "target abort", 1897ea6c6c1SLuck, Tony "parity error", 1907ea6c6c1SLuck, Tony "watchdog timeout", 1917ea6c6c1SLuck, Tony "invalid address", 1927ea6c6c1SLuck, Tony "mirror Broken", 1937ea6c6c1SLuck, Tony "memory sparing", 1947ea6c6c1SLuck, Tony "scrub corrected error", 1957ea6c6c1SLuck, Tony "scrub uncorrected error", 1967ea6c6c1SLuck, Tony "physical memory map-out event", 1977ea6c6c1SLuck, Tony }; 1987ea6c6c1SLuck, Tony 1997ea6c6c1SLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) 2007ea6c6c1SLuck, Tony { 2017ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 2027ea6c6c1SLuck, Tony printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 2037ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 2047ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 2057ea6c6c1SLuck, Tony pfx, mem->physical_addr); 2067ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 2077ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 2087ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 2097ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_NODE) 2107ea6c6c1SLuck, Tony pr_debug("node: %d\n", mem->node); 2117ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_CARD) 2127ea6c6c1SLuck, Tony pr_debug("card: %d\n", mem->card); 2137ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_MODULE) 2147ea6c6c1SLuck, Tony pr_debug("module: %d\n", mem->module); 2157ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 2167ea6c6c1SLuck, Tony pr_debug("rank: %d\n", mem->rank); 2177ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_BANK) 2187ea6c6c1SLuck, Tony pr_debug("bank: %d\n", mem->bank); 2197ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 2207ea6c6c1SLuck, Tony pr_debug("device: %d\n", mem->device); 2217ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ROW) 2227ea6c6c1SLuck, Tony pr_debug("row: %d\n", mem->row); 2237ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 2247ea6c6c1SLuck, Tony pr_debug("column: %d\n", mem->column); 2257ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 2267ea6c6c1SLuck, Tony pr_debug("bit_position: %d\n", mem->bit_pos); 2277ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 2287ea6c6c1SLuck, Tony pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); 2297ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 2307ea6c6c1SLuck, Tony pr_debug("responder_id: 0x%016llx\n", mem->responder_id); 2317ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 2327ea6c6c1SLuck, Tony pr_debug("target_id: 0x%016llx\n", mem->target_id); 2337ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 2347ea6c6c1SLuck, Tony u8 etype = mem->error_type; 2357ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 2367ea6c6c1SLuck, Tony etype < ARRAY_SIZE(cper_mem_err_type_strs) ? 2377ea6c6c1SLuck, Tony cper_mem_err_type_strs[etype] : "unknown"); 2387ea6c6c1SLuck, Tony } 2397ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { 2407ea6c6c1SLuck, Tony const char *bank = NULL, *device = NULL; 2417ea6c6c1SLuck, Tony dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 2427ea6c6c1SLuck, Tony if (bank != NULL && device != NULL) 2437ea6c6c1SLuck, Tony printk("%s""DIMM location: %s %s", pfx, bank, device); 2447ea6c6c1SLuck, Tony else 2457ea6c6c1SLuck, Tony printk("%s""DIMM DMI handle: 0x%.4x", 2467ea6c6c1SLuck, Tony pfx, mem->mem_dev_handle); 2477ea6c6c1SLuck, Tony } 2487ea6c6c1SLuck, Tony } 2497ea6c6c1SLuck, Tony 2507ea6c6c1SLuck, Tony static const char *cper_pcie_port_type_strs[] = { 2517ea6c6c1SLuck, Tony "PCIe end point", 2527ea6c6c1SLuck, Tony "legacy PCI end point", 2537ea6c6c1SLuck, Tony "unknown", 2547ea6c6c1SLuck, Tony "unknown", 2557ea6c6c1SLuck, Tony "root port", 2567ea6c6c1SLuck, Tony "upstream switch port", 2577ea6c6c1SLuck, Tony "downstream switch port", 2587ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 2597ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 2607ea6c6c1SLuck, Tony "root complex integrated endpoint device", 2617ea6c6c1SLuck, Tony "root complex event collector", 2627ea6c6c1SLuck, Tony }; 2637ea6c6c1SLuck, Tony 2647ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 265*0a00fd5eSLv Zheng const struct acpi_hest_generic_data *gdata) 2667ea6c6c1SLuck, Tony { 2677ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 2687ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 2697ea6c6c1SLuck, Tony pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? 2707ea6c6c1SLuck, Tony cper_pcie_port_type_strs[pcie->port_type] : "unknown"); 2717ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 2727ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 2737ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 2747ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 2757ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 2767ea6c6c1SLuck, Tony pcie->command, pcie->status); 2777ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 2787ea6c6c1SLuck, Tony const __u8 *p; 2797ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 2807ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 2817ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 2827ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 2837ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 2847ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 2857ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 2867ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 2877ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 2887ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 2897ea6c6c1SLuck, Tony printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); 2907ea6c6c1SLuck, Tony } 2917ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 2927ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 2937ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 2947ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 2957ea6c6c1SLuck, Tony printk( 2967ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 2977ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 2987ea6c6c1SLuck, Tony } 2997ea6c6c1SLuck, Tony 3007ea6c6c1SLuck, Tony static void cper_estatus_print_section( 301*0a00fd5eSLv Zheng const char *pfx, const struct acpi_hest_generic_data *gdata, int sec_no) 3027ea6c6c1SLuck, Tony { 3037ea6c6c1SLuck, Tony uuid_le *sec_type = (uuid_le *)gdata->section_type; 3047ea6c6c1SLuck, Tony __u16 severity; 3057ea6c6c1SLuck, Tony char newpfx[64]; 3067ea6c6c1SLuck, Tony 3077ea6c6c1SLuck, Tony severity = gdata->error_severity; 3087ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 3097ea6c6c1SLuck, Tony cper_severity_str(severity)); 3107ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 3117ea6c6c1SLuck, Tony printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); 3127ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 3137ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 3147ea6c6c1SLuck, Tony 3157ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 3167ea6c6c1SLuck, Tony if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { 3177ea6c6c1SLuck, Tony struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); 3187ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 3197ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 3207ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 3217ea6c6c1SLuck, Tony else 3227ea6c6c1SLuck, Tony goto err_section_too_small; 3237ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { 3247ea6c6c1SLuck, Tony struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); 3257ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 3267ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*mem_err)) 3277ea6c6c1SLuck, Tony cper_print_mem(newpfx, mem_err); 3287ea6c6c1SLuck, Tony else 3297ea6c6c1SLuck, Tony goto err_section_too_small; 3307ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { 3317ea6c6c1SLuck, Tony struct cper_sec_pcie *pcie = (void *)(gdata + 1); 3327ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 3337ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 3347ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 3357ea6c6c1SLuck, Tony else 3367ea6c6c1SLuck, Tony goto err_section_too_small; 3377ea6c6c1SLuck, Tony } else 3387ea6c6c1SLuck, Tony printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); 3397ea6c6c1SLuck, Tony 3407ea6c6c1SLuck, Tony return; 3417ea6c6c1SLuck, Tony 3427ea6c6c1SLuck, Tony err_section_too_small: 3437ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 3447ea6c6c1SLuck, Tony } 3457ea6c6c1SLuck, Tony 3467ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 347*0a00fd5eSLv Zheng const struct acpi_hest_generic_status *estatus) 3487ea6c6c1SLuck, Tony { 349*0a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 3507ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 3517ea6c6c1SLuck, Tony int sec_no = 0; 3527ea6c6c1SLuck, Tony char newpfx[64]; 3537ea6c6c1SLuck, Tony __u16 severity; 3547ea6c6c1SLuck, Tony 3557ea6c6c1SLuck, Tony severity = estatus->error_severity; 3567ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 3577ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 3587ea6c6c1SLuck, Tony "It has been corrected by h/w " 3597ea6c6c1SLuck, Tony "and requires no further action"); 3607ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 3617ea6c6c1SLuck, Tony data_len = estatus->data_length; 362*0a00fd5eSLv Zheng gdata = (struct acpi_hest_generic_data *)(estatus + 1); 3637ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 3647ea6c6c1SLuck, Tony while (data_len >= sizeof(*gdata)) { 3657ea6c6c1SLuck, Tony gedata_len = gdata->error_data_length; 3667ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 3677ea6c6c1SLuck, Tony data_len -= gedata_len + sizeof(*gdata); 3687ea6c6c1SLuck, Tony gdata = (void *)(gdata + 1) + gedata_len; 3697ea6c6c1SLuck, Tony sec_no++; 3707ea6c6c1SLuck, Tony } 3717ea6c6c1SLuck, Tony } 3727ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 3737ea6c6c1SLuck, Tony 374*0a00fd5eSLv Zheng int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 3757ea6c6c1SLuck, Tony { 3767ea6c6c1SLuck, Tony if (estatus->data_length && 377*0a00fd5eSLv Zheng estatus->data_length < sizeof(struct acpi_hest_generic_data)) 3787ea6c6c1SLuck, Tony return -EINVAL; 3797ea6c6c1SLuck, Tony if (estatus->raw_data_length && 3807ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 3817ea6c6c1SLuck, Tony return -EINVAL; 3827ea6c6c1SLuck, Tony 3837ea6c6c1SLuck, Tony return 0; 3847ea6c6c1SLuck, Tony } 3857ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 3867ea6c6c1SLuck, Tony 387*0a00fd5eSLv Zheng int cper_estatus_check(const struct acpi_hest_generic_status *estatus) 3887ea6c6c1SLuck, Tony { 389*0a00fd5eSLv Zheng struct acpi_hest_generic_data *gdata; 3907ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 3917ea6c6c1SLuck, Tony int rc; 3927ea6c6c1SLuck, Tony 3937ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 3947ea6c6c1SLuck, Tony if (rc) 3957ea6c6c1SLuck, Tony return rc; 3967ea6c6c1SLuck, Tony data_len = estatus->data_length; 397*0a00fd5eSLv Zheng gdata = (struct acpi_hest_generic_data *)(estatus + 1); 3987ea6c6c1SLuck, Tony while (data_len >= sizeof(*gdata)) { 3997ea6c6c1SLuck, Tony gedata_len = gdata->error_data_length; 4007ea6c6c1SLuck, Tony if (gedata_len > data_len - sizeof(*gdata)) 4017ea6c6c1SLuck, Tony return -EINVAL; 4027ea6c6c1SLuck, Tony data_len -= gedata_len + sizeof(*gdata); 4037ea6c6c1SLuck, Tony gdata = (void *)(gdata + 1) + gedata_len; 4047ea6c6c1SLuck, Tony } 4057ea6c6c1SLuck, Tony if (data_len) 4067ea6c6c1SLuck, Tony return -EINVAL; 4077ea6c6c1SLuck, Tony 4087ea6c6c1SLuck, Tony return 0; 4097ea6c6c1SLuck, Tony } 4107ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 411