1*7ea6c6c1SLuck, Tony /* 2*7ea6c6c1SLuck, Tony * UEFI Common Platform Error Record (CPER) support 3*7ea6c6c1SLuck, Tony * 4*7ea6c6c1SLuck, Tony * Copyright (C) 2010, Intel Corp. 5*7ea6c6c1SLuck, Tony * Author: Huang Ying <ying.huang@intel.com> 6*7ea6c6c1SLuck, Tony * 7*7ea6c6c1SLuck, Tony * CPER is the format used to describe platform hardware error by 8*7ea6c6c1SLuck, Tony * various tables, such as ERST, BERT and HEST etc. 9*7ea6c6c1SLuck, Tony * 10*7ea6c6c1SLuck, Tony * For more information about CPER, please refer to Appendix N of UEFI 11*7ea6c6c1SLuck, Tony * Specification version 2.4. 12*7ea6c6c1SLuck, Tony * 13*7ea6c6c1SLuck, Tony * This program is free software; you can redistribute it and/or 14*7ea6c6c1SLuck, Tony * modify it under the terms of the GNU General Public License version 15*7ea6c6c1SLuck, Tony * 2 as published by the Free Software Foundation. 16*7ea6c6c1SLuck, Tony * 17*7ea6c6c1SLuck, Tony * This program is distributed in the hope that it will be useful, 18*7ea6c6c1SLuck, Tony * but WITHOUT ANY WARRANTY; without even the implied warranty of 19*7ea6c6c1SLuck, Tony * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20*7ea6c6c1SLuck, Tony * GNU General Public License for more details. 21*7ea6c6c1SLuck, Tony * 22*7ea6c6c1SLuck, Tony * You should have received a copy of the GNU General Public License 23*7ea6c6c1SLuck, Tony * along with this program; if not, write to the Free Software 24*7ea6c6c1SLuck, Tony * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25*7ea6c6c1SLuck, Tony */ 26*7ea6c6c1SLuck, Tony 27*7ea6c6c1SLuck, Tony #include <linux/kernel.h> 28*7ea6c6c1SLuck, Tony #include <linux/module.h> 29*7ea6c6c1SLuck, Tony #include <linux/time.h> 30*7ea6c6c1SLuck, Tony #include <linux/cper.h> 31*7ea6c6c1SLuck, Tony #include <linux/dmi.h> 32*7ea6c6c1SLuck, Tony #include <linux/acpi.h> 33*7ea6c6c1SLuck, Tony #include <linux/pci.h> 34*7ea6c6c1SLuck, Tony #include <linux/aer.h> 35*7ea6c6c1SLuck, Tony 36*7ea6c6c1SLuck, Tony #define INDENT_SP " " 37*7ea6c6c1SLuck, Tony /* 38*7ea6c6c1SLuck, Tony * CPER record ID need to be unique even after reboot, because record 39*7ea6c6c1SLuck, Tony * ID is used as index for ERST storage, while CPER records from 40*7ea6c6c1SLuck, Tony * multiple boot may co-exist in ERST. 41*7ea6c6c1SLuck, Tony */ 42*7ea6c6c1SLuck, Tony u64 cper_next_record_id(void) 43*7ea6c6c1SLuck, Tony { 44*7ea6c6c1SLuck, Tony static atomic64_t seq; 45*7ea6c6c1SLuck, Tony 46*7ea6c6c1SLuck, Tony if (!atomic64_read(&seq)) 47*7ea6c6c1SLuck, Tony atomic64_set(&seq, ((u64)get_seconds()) << 32); 48*7ea6c6c1SLuck, Tony 49*7ea6c6c1SLuck, Tony return atomic64_inc_return(&seq); 50*7ea6c6c1SLuck, Tony } 51*7ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_next_record_id); 52*7ea6c6c1SLuck, Tony 53*7ea6c6c1SLuck, Tony static const char *cper_severity_strs[] = { 54*7ea6c6c1SLuck, Tony "recoverable", 55*7ea6c6c1SLuck, Tony "fatal", 56*7ea6c6c1SLuck, Tony "corrected", 57*7ea6c6c1SLuck, Tony "info", 58*7ea6c6c1SLuck, Tony }; 59*7ea6c6c1SLuck, Tony 60*7ea6c6c1SLuck, Tony static const char *cper_severity_str(unsigned int severity) 61*7ea6c6c1SLuck, Tony { 62*7ea6c6c1SLuck, Tony return severity < ARRAY_SIZE(cper_severity_strs) ? 63*7ea6c6c1SLuck, Tony cper_severity_strs[severity] : "unknown"; 64*7ea6c6c1SLuck, Tony } 65*7ea6c6c1SLuck, Tony 66*7ea6c6c1SLuck, Tony /* 67*7ea6c6c1SLuck, Tony * cper_print_bits - print strings for set bits 68*7ea6c6c1SLuck, Tony * @pfx: prefix for each line, including log level and prefix string 69*7ea6c6c1SLuck, Tony * @bits: bit mask 70*7ea6c6c1SLuck, Tony * @strs: string array, indexed by bit position 71*7ea6c6c1SLuck, Tony * @strs_size: size of the string array: @strs 72*7ea6c6c1SLuck, Tony * 73*7ea6c6c1SLuck, Tony * For each set bit in @bits, print the corresponding string in @strs. 74*7ea6c6c1SLuck, Tony * If the output length is longer than 80, multiple line will be 75*7ea6c6c1SLuck, Tony * printed, with @pfx is printed at the beginning of each line. 76*7ea6c6c1SLuck, Tony */ 77*7ea6c6c1SLuck, Tony void cper_print_bits(const char *pfx, unsigned int bits, 78*7ea6c6c1SLuck, Tony const char * const strs[], unsigned int strs_size) 79*7ea6c6c1SLuck, Tony { 80*7ea6c6c1SLuck, Tony int i, len = 0; 81*7ea6c6c1SLuck, Tony const char *str; 82*7ea6c6c1SLuck, Tony char buf[84]; 83*7ea6c6c1SLuck, Tony 84*7ea6c6c1SLuck, Tony for (i = 0; i < strs_size; i++) { 85*7ea6c6c1SLuck, Tony if (!(bits & (1U << i))) 86*7ea6c6c1SLuck, Tony continue; 87*7ea6c6c1SLuck, Tony str = strs[i]; 88*7ea6c6c1SLuck, Tony if (!str) 89*7ea6c6c1SLuck, Tony continue; 90*7ea6c6c1SLuck, Tony if (len && len + strlen(str) + 2 > 80) { 91*7ea6c6c1SLuck, Tony printk("%s\n", buf); 92*7ea6c6c1SLuck, Tony len = 0; 93*7ea6c6c1SLuck, Tony } 94*7ea6c6c1SLuck, Tony if (!len) 95*7ea6c6c1SLuck, Tony len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 96*7ea6c6c1SLuck, Tony else 97*7ea6c6c1SLuck, Tony len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); 98*7ea6c6c1SLuck, Tony } 99*7ea6c6c1SLuck, Tony if (len) 100*7ea6c6c1SLuck, Tony printk("%s\n", buf); 101*7ea6c6c1SLuck, Tony } 102*7ea6c6c1SLuck, Tony 103*7ea6c6c1SLuck, Tony static const char * const cper_proc_type_strs[] = { 104*7ea6c6c1SLuck, Tony "IA32/X64", 105*7ea6c6c1SLuck, Tony "IA64", 106*7ea6c6c1SLuck, Tony }; 107*7ea6c6c1SLuck, Tony 108*7ea6c6c1SLuck, Tony static const char * const cper_proc_isa_strs[] = { 109*7ea6c6c1SLuck, Tony "IA32", 110*7ea6c6c1SLuck, Tony "IA64", 111*7ea6c6c1SLuck, Tony "X64", 112*7ea6c6c1SLuck, Tony }; 113*7ea6c6c1SLuck, Tony 114*7ea6c6c1SLuck, Tony static const char * const cper_proc_error_type_strs[] = { 115*7ea6c6c1SLuck, Tony "cache error", 116*7ea6c6c1SLuck, Tony "TLB error", 117*7ea6c6c1SLuck, Tony "bus error", 118*7ea6c6c1SLuck, Tony "micro-architectural error", 119*7ea6c6c1SLuck, Tony }; 120*7ea6c6c1SLuck, Tony 121*7ea6c6c1SLuck, Tony static const char * const cper_proc_op_strs[] = { 122*7ea6c6c1SLuck, Tony "unknown or generic", 123*7ea6c6c1SLuck, Tony "data read", 124*7ea6c6c1SLuck, Tony "data write", 125*7ea6c6c1SLuck, Tony "instruction execution", 126*7ea6c6c1SLuck, Tony }; 127*7ea6c6c1SLuck, Tony 128*7ea6c6c1SLuck, Tony static const char * const cper_proc_flag_strs[] = { 129*7ea6c6c1SLuck, Tony "restartable", 130*7ea6c6c1SLuck, Tony "precise IP", 131*7ea6c6c1SLuck, Tony "overflow", 132*7ea6c6c1SLuck, Tony "corrected", 133*7ea6c6c1SLuck, Tony }; 134*7ea6c6c1SLuck, Tony 135*7ea6c6c1SLuck, Tony static void cper_print_proc_generic(const char *pfx, 136*7ea6c6c1SLuck, Tony const struct cper_sec_proc_generic *proc) 137*7ea6c6c1SLuck, Tony { 138*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TYPE) 139*7ea6c6c1SLuck, Tony printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 140*7ea6c6c1SLuck, Tony proc->proc_type < ARRAY_SIZE(cper_proc_type_strs) ? 141*7ea6c6c1SLuck, Tony cper_proc_type_strs[proc->proc_type] : "unknown"); 142*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ISA) 143*7ea6c6c1SLuck, Tony printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 144*7ea6c6c1SLuck, Tony proc->proc_isa < ARRAY_SIZE(cper_proc_isa_strs) ? 145*7ea6c6c1SLuck, Tony cper_proc_isa_strs[proc->proc_isa] : "unknown"); 146*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 147*7ea6c6c1SLuck, Tony printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 148*7ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->proc_error_type, 149*7ea6c6c1SLuck, Tony cper_proc_error_type_strs, 150*7ea6c6c1SLuck, Tony ARRAY_SIZE(cper_proc_error_type_strs)); 151*7ea6c6c1SLuck, Tony } 152*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 153*7ea6c6c1SLuck, Tony printk("%s""operation: %d, %s\n", pfx, proc->operation, 154*7ea6c6c1SLuck, Tony proc->operation < ARRAY_SIZE(cper_proc_op_strs) ? 155*7ea6c6c1SLuck, Tony cper_proc_op_strs[proc->operation] : "unknown"); 156*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 157*7ea6c6c1SLuck, Tony printk("%s""flags: 0x%02x\n", pfx, proc->flags); 158*7ea6c6c1SLuck, Tony cper_print_bits(pfx, proc->flags, cper_proc_flag_strs, 159*7ea6c6c1SLuck, Tony ARRAY_SIZE(cper_proc_flag_strs)); 160*7ea6c6c1SLuck, Tony } 161*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 162*7ea6c6c1SLuck, Tony printk("%s""level: %d\n", pfx, proc->level); 163*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_VERSION) 164*7ea6c6c1SLuck, Tony printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 165*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_ID) 166*7ea6c6c1SLuck, Tony printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 167*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 168*7ea6c6c1SLuck, Tony printk("%s""target_address: 0x%016llx\n", 169*7ea6c6c1SLuck, Tony pfx, proc->target_addr); 170*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 171*7ea6c6c1SLuck, Tony printk("%s""requestor_id: 0x%016llx\n", 172*7ea6c6c1SLuck, Tony pfx, proc->requestor_id); 173*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 174*7ea6c6c1SLuck, Tony printk("%s""responder_id: 0x%016llx\n", 175*7ea6c6c1SLuck, Tony pfx, proc->responder_id); 176*7ea6c6c1SLuck, Tony if (proc->validation_bits & CPER_PROC_VALID_IP) 177*7ea6c6c1SLuck, Tony printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 178*7ea6c6c1SLuck, Tony } 179*7ea6c6c1SLuck, Tony 180*7ea6c6c1SLuck, Tony static const char *cper_mem_err_type_strs[] = { 181*7ea6c6c1SLuck, Tony "unknown", 182*7ea6c6c1SLuck, Tony "no error", 183*7ea6c6c1SLuck, Tony "single-bit ECC", 184*7ea6c6c1SLuck, Tony "multi-bit ECC", 185*7ea6c6c1SLuck, Tony "single-symbol chipkill ECC", 186*7ea6c6c1SLuck, Tony "multi-symbol chipkill ECC", 187*7ea6c6c1SLuck, Tony "master abort", 188*7ea6c6c1SLuck, Tony "target abort", 189*7ea6c6c1SLuck, Tony "parity error", 190*7ea6c6c1SLuck, Tony "watchdog timeout", 191*7ea6c6c1SLuck, Tony "invalid address", 192*7ea6c6c1SLuck, Tony "mirror Broken", 193*7ea6c6c1SLuck, Tony "memory sparing", 194*7ea6c6c1SLuck, Tony "scrub corrected error", 195*7ea6c6c1SLuck, Tony "scrub uncorrected error", 196*7ea6c6c1SLuck, Tony "physical memory map-out event", 197*7ea6c6c1SLuck, Tony }; 198*7ea6c6c1SLuck, Tony 199*7ea6c6c1SLuck, Tony static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem) 200*7ea6c6c1SLuck, Tony { 201*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 202*7ea6c6c1SLuck, Tony printk("%s""error_status: 0x%016llx\n", pfx, mem->error_status); 203*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA) 204*7ea6c6c1SLuck, Tony printk("%s""physical_address: 0x%016llx\n", 205*7ea6c6c1SLuck, Tony pfx, mem->physical_addr); 206*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 207*7ea6c6c1SLuck, Tony printk("%s""physical_address_mask: 0x%016llx\n", 208*7ea6c6c1SLuck, Tony pfx, mem->physical_addr_mask); 209*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_NODE) 210*7ea6c6c1SLuck, Tony pr_debug("node: %d\n", mem->node); 211*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_CARD) 212*7ea6c6c1SLuck, Tony pr_debug("card: %d\n", mem->card); 213*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_MODULE) 214*7ea6c6c1SLuck, Tony pr_debug("module: %d\n", mem->module); 215*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 216*7ea6c6c1SLuck, Tony pr_debug("rank: %d\n", mem->rank); 217*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_BANK) 218*7ea6c6c1SLuck, Tony pr_debug("bank: %d\n", mem->bank); 219*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 220*7ea6c6c1SLuck, Tony pr_debug("device: %d\n", mem->device); 221*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ROW) 222*7ea6c6c1SLuck, Tony pr_debug("row: %d\n", mem->row); 223*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 224*7ea6c6c1SLuck, Tony pr_debug("column: %d\n", mem->column); 225*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 226*7ea6c6c1SLuck, Tony pr_debug("bit_position: %d\n", mem->bit_pos); 227*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 228*7ea6c6c1SLuck, Tony pr_debug("requestor_id: 0x%016llx\n", mem->requestor_id); 229*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 230*7ea6c6c1SLuck, Tony pr_debug("responder_id: 0x%016llx\n", mem->responder_id); 231*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 232*7ea6c6c1SLuck, Tony pr_debug("target_id: 0x%016llx\n", mem->target_id); 233*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 234*7ea6c6c1SLuck, Tony u8 etype = mem->error_type; 235*7ea6c6c1SLuck, Tony printk("%s""error_type: %d, %s\n", pfx, etype, 236*7ea6c6c1SLuck, Tony etype < ARRAY_SIZE(cper_mem_err_type_strs) ? 237*7ea6c6c1SLuck, Tony cper_mem_err_type_strs[etype] : "unknown"); 238*7ea6c6c1SLuck, Tony } 239*7ea6c6c1SLuck, Tony if (mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE) { 240*7ea6c6c1SLuck, Tony const char *bank = NULL, *device = NULL; 241*7ea6c6c1SLuck, Tony dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 242*7ea6c6c1SLuck, Tony if (bank != NULL && device != NULL) 243*7ea6c6c1SLuck, Tony printk("%s""DIMM location: %s %s", pfx, bank, device); 244*7ea6c6c1SLuck, Tony else 245*7ea6c6c1SLuck, Tony printk("%s""DIMM DMI handle: 0x%.4x", 246*7ea6c6c1SLuck, Tony pfx, mem->mem_dev_handle); 247*7ea6c6c1SLuck, Tony } 248*7ea6c6c1SLuck, Tony } 249*7ea6c6c1SLuck, Tony 250*7ea6c6c1SLuck, Tony static const char *cper_pcie_port_type_strs[] = { 251*7ea6c6c1SLuck, Tony "PCIe end point", 252*7ea6c6c1SLuck, Tony "legacy PCI end point", 253*7ea6c6c1SLuck, Tony "unknown", 254*7ea6c6c1SLuck, Tony "unknown", 255*7ea6c6c1SLuck, Tony "root port", 256*7ea6c6c1SLuck, Tony "upstream switch port", 257*7ea6c6c1SLuck, Tony "downstream switch port", 258*7ea6c6c1SLuck, Tony "PCIe to PCI/PCI-X bridge", 259*7ea6c6c1SLuck, Tony "PCI/PCI-X to PCIe bridge", 260*7ea6c6c1SLuck, Tony "root complex integrated endpoint device", 261*7ea6c6c1SLuck, Tony "root complex event collector", 262*7ea6c6c1SLuck, Tony }; 263*7ea6c6c1SLuck, Tony 264*7ea6c6c1SLuck, Tony static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 265*7ea6c6c1SLuck, Tony const struct acpi_generic_data *gdata) 266*7ea6c6c1SLuck, Tony { 267*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 268*7ea6c6c1SLuck, Tony printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 269*7ea6c6c1SLuck, Tony pcie->port_type < ARRAY_SIZE(cper_pcie_port_type_strs) ? 270*7ea6c6c1SLuck, Tony cper_pcie_port_type_strs[pcie->port_type] : "unknown"); 271*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 272*7ea6c6c1SLuck, Tony printk("%s""version: %d.%d\n", pfx, 273*7ea6c6c1SLuck, Tony pcie->version.major, pcie->version.minor); 274*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 275*7ea6c6c1SLuck, Tony printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 276*7ea6c6c1SLuck, Tony pcie->command, pcie->status); 277*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 278*7ea6c6c1SLuck, Tony const __u8 *p; 279*7ea6c6c1SLuck, Tony printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 280*7ea6c6c1SLuck, Tony pcie->device_id.segment, pcie->device_id.bus, 281*7ea6c6c1SLuck, Tony pcie->device_id.device, pcie->device_id.function); 282*7ea6c6c1SLuck, Tony printk("%s""slot: %d\n", pfx, 283*7ea6c6c1SLuck, Tony pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 284*7ea6c6c1SLuck, Tony printk("%s""secondary_bus: 0x%02x\n", pfx, 285*7ea6c6c1SLuck, Tony pcie->device_id.secondary_bus); 286*7ea6c6c1SLuck, Tony printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 287*7ea6c6c1SLuck, Tony pcie->device_id.vendor_id, pcie->device_id.device_id); 288*7ea6c6c1SLuck, Tony p = pcie->device_id.class_code; 289*7ea6c6c1SLuck, Tony printk("%s""class_code: %02x%02x%02x\n", pfx, p[0], p[1], p[2]); 290*7ea6c6c1SLuck, Tony } 291*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 292*7ea6c6c1SLuck, Tony printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 293*7ea6c6c1SLuck, Tony pcie->serial_number.lower, pcie->serial_number.upper); 294*7ea6c6c1SLuck, Tony if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 295*7ea6c6c1SLuck, Tony printk( 296*7ea6c6c1SLuck, Tony "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 297*7ea6c6c1SLuck, Tony pfx, pcie->bridge.secondary_status, pcie->bridge.control); 298*7ea6c6c1SLuck, Tony } 299*7ea6c6c1SLuck, Tony 300*7ea6c6c1SLuck, Tony static void cper_estatus_print_section( 301*7ea6c6c1SLuck, Tony const char *pfx, const struct acpi_generic_data *gdata, int sec_no) 302*7ea6c6c1SLuck, Tony { 303*7ea6c6c1SLuck, Tony uuid_le *sec_type = (uuid_le *)gdata->section_type; 304*7ea6c6c1SLuck, Tony __u16 severity; 305*7ea6c6c1SLuck, Tony char newpfx[64]; 306*7ea6c6c1SLuck, Tony 307*7ea6c6c1SLuck, Tony severity = gdata->error_severity; 308*7ea6c6c1SLuck, Tony printk("%s""Error %d, type: %s\n", pfx, sec_no, 309*7ea6c6c1SLuck, Tony cper_severity_str(severity)); 310*7ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 311*7ea6c6c1SLuck, Tony printk("%s""fru_id: %pUl\n", pfx, (uuid_le *)gdata->fru_id); 312*7ea6c6c1SLuck, Tony if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 313*7ea6c6c1SLuck, Tony printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 314*7ea6c6c1SLuck, Tony 315*7ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 316*7ea6c6c1SLuck, Tony if (!uuid_le_cmp(*sec_type, CPER_SEC_PROC_GENERIC)) { 317*7ea6c6c1SLuck, Tony struct cper_sec_proc_generic *proc_err = (void *)(gdata + 1); 318*7ea6c6c1SLuck, Tony printk("%s""section_type: general processor error\n", newpfx); 319*7ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*proc_err)) 320*7ea6c6c1SLuck, Tony cper_print_proc_generic(newpfx, proc_err); 321*7ea6c6c1SLuck, Tony else 322*7ea6c6c1SLuck, Tony goto err_section_too_small; 323*7ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) { 324*7ea6c6c1SLuck, Tony struct cper_sec_mem_err *mem_err = (void *)(gdata + 1); 325*7ea6c6c1SLuck, Tony printk("%s""section_type: memory error\n", newpfx); 326*7ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*mem_err)) 327*7ea6c6c1SLuck, Tony cper_print_mem(newpfx, mem_err); 328*7ea6c6c1SLuck, Tony else 329*7ea6c6c1SLuck, Tony goto err_section_too_small; 330*7ea6c6c1SLuck, Tony } else if (!uuid_le_cmp(*sec_type, CPER_SEC_PCIE)) { 331*7ea6c6c1SLuck, Tony struct cper_sec_pcie *pcie = (void *)(gdata + 1); 332*7ea6c6c1SLuck, Tony printk("%s""section_type: PCIe error\n", newpfx); 333*7ea6c6c1SLuck, Tony if (gdata->error_data_length >= sizeof(*pcie)) 334*7ea6c6c1SLuck, Tony cper_print_pcie(newpfx, pcie, gdata); 335*7ea6c6c1SLuck, Tony else 336*7ea6c6c1SLuck, Tony goto err_section_too_small; 337*7ea6c6c1SLuck, Tony } else 338*7ea6c6c1SLuck, Tony printk("%s""section type: unknown, %pUl\n", newpfx, sec_type); 339*7ea6c6c1SLuck, Tony 340*7ea6c6c1SLuck, Tony return; 341*7ea6c6c1SLuck, Tony 342*7ea6c6c1SLuck, Tony err_section_too_small: 343*7ea6c6c1SLuck, Tony pr_err(FW_WARN "error section length is too small\n"); 344*7ea6c6c1SLuck, Tony } 345*7ea6c6c1SLuck, Tony 346*7ea6c6c1SLuck, Tony void cper_estatus_print(const char *pfx, 347*7ea6c6c1SLuck, Tony const struct acpi_generic_status *estatus) 348*7ea6c6c1SLuck, Tony { 349*7ea6c6c1SLuck, Tony struct acpi_generic_data *gdata; 350*7ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 351*7ea6c6c1SLuck, Tony int sec_no = 0; 352*7ea6c6c1SLuck, Tony char newpfx[64]; 353*7ea6c6c1SLuck, Tony __u16 severity; 354*7ea6c6c1SLuck, Tony 355*7ea6c6c1SLuck, Tony severity = estatus->error_severity; 356*7ea6c6c1SLuck, Tony if (severity == CPER_SEV_CORRECTED) 357*7ea6c6c1SLuck, Tony printk("%s%s\n", pfx, 358*7ea6c6c1SLuck, Tony "It has been corrected by h/w " 359*7ea6c6c1SLuck, Tony "and requires no further action"); 360*7ea6c6c1SLuck, Tony printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 361*7ea6c6c1SLuck, Tony data_len = estatus->data_length; 362*7ea6c6c1SLuck, Tony gdata = (struct acpi_generic_data *)(estatus + 1); 363*7ea6c6c1SLuck, Tony snprintf(newpfx, sizeof(newpfx), "%s%s", pfx, INDENT_SP); 364*7ea6c6c1SLuck, Tony while (data_len >= sizeof(*gdata)) { 365*7ea6c6c1SLuck, Tony gedata_len = gdata->error_data_length; 366*7ea6c6c1SLuck, Tony cper_estatus_print_section(newpfx, gdata, sec_no); 367*7ea6c6c1SLuck, Tony data_len -= gedata_len + sizeof(*gdata); 368*7ea6c6c1SLuck, Tony gdata = (void *)(gdata + 1) + gedata_len; 369*7ea6c6c1SLuck, Tony sec_no++; 370*7ea6c6c1SLuck, Tony } 371*7ea6c6c1SLuck, Tony } 372*7ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_print); 373*7ea6c6c1SLuck, Tony 374*7ea6c6c1SLuck, Tony int cper_estatus_check_header(const struct acpi_generic_status *estatus) 375*7ea6c6c1SLuck, Tony { 376*7ea6c6c1SLuck, Tony if (estatus->data_length && 377*7ea6c6c1SLuck, Tony estatus->data_length < sizeof(struct acpi_generic_data)) 378*7ea6c6c1SLuck, Tony return -EINVAL; 379*7ea6c6c1SLuck, Tony if (estatus->raw_data_length && 380*7ea6c6c1SLuck, Tony estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 381*7ea6c6c1SLuck, Tony return -EINVAL; 382*7ea6c6c1SLuck, Tony 383*7ea6c6c1SLuck, Tony return 0; 384*7ea6c6c1SLuck, Tony } 385*7ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check_header); 386*7ea6c6c1SLuck, Tony 387*7ea6c6c1SLuck, Tony int cper_estatus_check(const struct acpi_generic_status *estatus) 388*7ea6c6c1SLuck, Tony { 389*7ea6c6c1SLuck, Tony struct acpi_generic_data *gdata; 390*7ea6c6c1SLuck, Tony unsigned int data_len, gedata_len; 391*7ea6c6c1SLuck, Tony int rc; 392*7ea6c6c1SLuck, Tony 393*7ea6c6c1SLuck, Tony rc = cper_estatus_check_header(estatus); 394*7ea6c6c1SLuck, Tony if (rc) 395*7ea6c6c1SLuck, Tony return rc; 396*7ea6c6c1SLuck, Tony data_len = estatus->data_length; 397*7ea6c6c1SLuck, Tony gdata = (struct acpi_generic_data *)(estatus + 1); 398*7ea6c6c1SLuck, Tony while (data_len >= sizeof(*gdata)) { 399*7ea6c6c1SLuck, Tony gedata_len = gdata->error_data_length; 400*7ea6c6c1SLuck, Tony if (gedata_len > data_len - sizeof(*gdata)) 401*7ea6c6c1SLuck, Tony return -EINVAL; 402*7ea6c6c1SLuck, Tony data_len -= gedata_len + sizeof(*gdata); 403*7ea6c6c1SLuck, Tony gdata = (void *)(gdata + 1) + gedata_len; 404*7ea6c6c1SLuck, Tony } 405*7ea6c6c1SLuck, Tony if (data_len) 406*7ea6c6c1SLuck, Tony return -EINVAL; 407*7ea6c6c1SLuck, Tony 408*7ea6c6c1SLuck, Tony return 0; 409*7ea6c6c1SLuck, Tony } 410*7ea6c6c1SLuck, Tony EXPORT_SYMBOL_GPL(cper_estatus_check); 411