xref: /openbmc/linux/drivers/firmware/efi/cper.c (revision 0a00fd5e20fd5dc89e976e163588d7c54edaf745)
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