162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * UEFI Common Platform Error Record (CPER) support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010, Intel Corp. 662306a36Sopenharmony_ci * Author: Huang Ying <ying.huang@intel.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * CPER is the format used to describe platform hardware error by 962306a36Sopenharmony_ci * various tables, such as ERST, BERT and HEST etc. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * For more information about CPER, please refer to Appendix N of UEFI 1262306a36Sopenharmony_ci * Specification version 2.4. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/time.h> 1862306a36Sopenharmony_ci#include <linux/cper.h> 1962306a36Sopenharmony_ci#include <linux/dmi.h> 2062306a36Sopenharmony_ci#include <linux/acpi.h> 2162306a36Sopenharmony_ci#include <linux/pci.h> 2262306a36Sopenharmony_ci#include <linux/aer.h> 2362306a36Sopenharmony_ci#include <linux/printk.h> 2462306a36Sopenharmony_ci#include <linux/bcd.h> 2562306a36Sopenharmony_ci#include <acpi/ghes.h> 2662306a36Sopenharmony_ci#include <ras/ras_event.h> 2762306a36Sopenharmony_ci#include "cper_cxl.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * CPER record ID need to be unique even after reboot, because record 3162306a36Sopenharmony_ci * ID is used as index for ERST storage, while CPER records from 3262306a36Sopenharmony_ci * multiple boot may co-exist in ERST. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ciu64 cper_next_record_id(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci static atomic64_t seq; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (!atomic64_read(&seq)) { 3962306a36Sopenharmony_ci time64_t time = ktime_get_real_seconds(); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci /* 4262306a36Sopenharmony_ci * This code is unlikely to still be needed in year 2106, 4362306a36Sopenharmony_ci * but just in case, let's use a few more bits for timestamps 4462306a36Sopenharmony_ci * after y2038 to be sure they keep increasing monotonically 4562306a36Sopenharmony_ci * for the next few hundred years... 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci if (time < 0x80000000) 4862306a36Sopenharmony_ci atomic64_set(&seq, (ktime_get_real_seconds()) << 32); 4962306a36Sopenharmony_ci else 5062306a36Sopenharmony_ci atomic64_set(&seq, 0x8000000000000000ull | 5162306a36Sopenharmony_ci ktime_get_real_seconds() << 24); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return atomic64_inc_return(&seq); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_next_record_id); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const char * const severity_strs[] = { 5962306a36Sopenharmony_ci "recoverable", 6062306a36Sopenharmony_ci "fatal", 6162306a36Sopenharmony_ci "corrected", 6262306a36Sopenharmony_ci "info", 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ciconst char *cper_severity_str(unsigned int severity) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return severity < ARRAY_SIZE(severity_strs) ? 6862306a36Sopenharmony_ci severity_strs[severity] : "unknown"; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_severity_str); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * cper_print_bits - print strings for set bits 7462306a36Sopenharmony_ci * @pfx: prefix for each line, including log level and prefix string 7562306a36Sopenharmony_ci * @bits: bit mask 7662306a36Sopenharmony_ci * @strs: string array, indexed by bit position 7762306a36Sopenharmony_ci * @strs_size: size of the string array: @strs 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * For each set bit in @bits, print the corresponding string in @strs. 8062306a36Sopenharmony_ci * If the output length is longer than 80, multiple line will be 8162306a36Sopenharmony_ci * printed, with @pfx is printed at the beginning of each line. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_civoid cper_print_bits(const char *pfx, unsigned int bits, 8462306a36Sopenharmony_ci const char * const strs[], unsigned int strs_size) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int i, len = 0; 8762306a36Sopenharmony_ci const char *str; 8862306a36Sopenharmony_ci char buf[84]; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < strs_size; i++) { 9162306a36Sopenharmony_ci if (!(bits & (1U << i))) 9262306a36Sopenharmony_ci continue; 9362306a36Sopenharmony_ci str = strs[i]; 9462306a36Sopenharmony_ci if (!str) 9562306a36Sopenharmony_ci continue; 9662306a36Sopenharmony_ci if (len && len + strlen(str) + 2 > 80) { 9762306a36Sopenharmony_ci printk("%s\n", buf); 9862306a36Sopenharmony_ci len = 0; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci if (!len) 10162306a36Sopenharmony_ci len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); 10262306a36Sopenharmony_ci else 10362306a36Sopenharmony_ci len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if (len) 10662306a36Sopenharmony_ci printk("%s\n", buf); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const char * const proc_type_strs[] = { 11062306a36Sopenharmony_ci "IA32/X64", 11162306a36Sopenharmony_ci "IA64", 11262306a36Sopenharmony_ci "ARM", 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const char * const proc_isa_strs[] = { 11662306a36Sopenharmony_ci "IA32", 11762306a36Sopenharmony_ci "IA64", 11862306a36Sopenharmony_ci "X64", 11962306a36Sopenharmony_ci "ARM A32/T32", 12062306a36Sopenharmony_ci "ARM A64", 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciconst char * const cper_proc_error_type_strs[] = { 12462306a36Sopenharmony_ci "cache error", 12562306a36Sopenharmony_ci "TLB error", 12662306a36Sopenharmony_ci "bus error", 12762306a36Sopenharmony_ci "micro-architectural error", 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const char * const proc_op_strs[] = { 13162306a36Sopenharmony_ci "unknown or generic", 13262306a36Sopenharmony_ci "data read", 13362306a36Sopenharmony_ci "data write", 13462306a36Sopenharmony_ci "instruction execution", 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const char * const proc_flag_strs[] = { 13862306a36Sopenharmony_ci "restartable", 13962306a36Sopenharmony_ci "precise IP", 14062306a36Sopenharmony_ci "overflow", 14162306a36Sopenharmony_ci "corrected", 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void cper_print_proc_generic(const char *pfx, 14562306a36Sopenharmony_ci const struct cper_sec_proc_generic *proc) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_TYPE) 14862306a36Sopenharmony_ci printk("%s""processor_type: %d, %s\n", pfx, proc->proc_type, 14962306a36Sopenharmony_ci proc->proc_type < ARRAY_SIZE(proc_type_strs) ? 15062306a36Sopenharmony_ci proc_type_strs[proc->proc_type] : "unknown"); 15162306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_ISA) 15262306a36Sopenharmony_ci printk("%s""processor_isa: %d, %s\n", pfx, proc->proc_isa, 15362306a36Sopenharmony_ci proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ? 15462306a36Sopenharmony_ci proc_isa_strs[proc->proc_isa] : "unknown"); 15562306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) { 15662306a36Sopenharmony_ci printk("%s""error_type: 0x%02x\n", pfx, proc->proc_error_type); 15762306a36Sopenharmony_ci cper_print_bits(pfx, proc->proc_error_type, 15862306a36Sopenharmony_ci cper_proc_error_type_strs, 15962306a36Sopenharmony_ci ARRAY_SIZE(cper_proc_error_type_strs)); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_OPERATION) 16262306a36Sopenharmony_ci printk("%s""operation: %d, %s\n", pfx, proc->operation, 16362306a36Sopenharmony_ci proc->operation < ARRAY_SIZE(proc_op_strs) ? 16462306a36Sopenharmony_ci proc_op_strs[proc->operation] : "unknown"); 16562306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_FLAGS) { 16662306a36Sopenharmony_ci printk("%s""flags: 0x%02x\n", pfx, proc->flags); 16762306a36Sopenharmony_ci cper_print_bits(pfx, proc->flags, proc_flag_strs, 16862306a36Sopenharmony_ci ARRAY_SIZE(proc_flag_strs)); 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_LEVEL) 17162306a36Sopenharmony_ci printk("%s""level: %d\n", pfx, proc->level); 17262306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_VERSION) 17362306a36Sopenharmony_ci printk("%s""version_info: 0x%016llx\n", pfx, proc->cpu_version); 17462306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_ID) 17562306a36Sopenharmony_ci printk("%s""processor_id: 0x%016llx\n", pfx, proc->proc_id); 17662306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS) 17762306a36Sopenharmony_ci printk("%s""target_address: 0x%016llx\n", 17862306a36Sopenharmony_ci pfx, proc->target_addr); 17962306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID) 18062306a36Sopenharmony_ci printk("%s""requestor_id: 0x%016llx\n", 18162306a36Sopenharmony_ci pfx, proc->requestor_id); 18262306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID) 18362306a36Sopenharmony_ci printk("%s""responder_id: 0x%016llx\n", 18462306a36Sopenharmony_ci pfx, proc->responder_id); 18562306a36Sopenharmony_ci if (proc->validation_bits & CPER_PROC_VALID_IP) 18662306a36Sopenharmony_ci printk("%s""IP: 0x%016llx\n", pfx, proc->ip); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic const char * const mem_err_type_strs[] = { 19062306a36Sopenharmony_ci "unknown", 19162306a36Sopenharmony_ci "no error", 19262306a36Sopenharmony_ci "single-bit ECC", 19362306a36Sopenharmony_ci "multi-bit ECC", 19462306a36Sopenharmony_ci "single-symbol chipkill ECC", 19562306a36Sopenharmony_ci "multi-symbol chipkill ECC", 19662306a36Sopenharmony_ci "master abort", 19762306a36Sopenharmony_ci "target abort", 19862306a36Sopenharmony_ci "parity error", 19962306a36Sopenharmony_ci "watchdog timeout", 20062306a36Sopenharmony_ci "invalid address", 20162306a36Sopenharmony_ci "mirror Broken", 20262306a36Sopenharmony_ci "memory sparing", 20362306a36Sopenharmony_ci "scrub corrected error", 20462306a36Sopenharmony_ci "scrub uncorrected error", 20562306a36Sopenharmony_ci "physical memory map-out event", 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciconst char *cper_mem_err_type_str(unsigned int etype) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci return etype < ARRAY_SIZE(mem_err_type_strs) ? 21162306a36Sopenharmony_ci mem_err_type_strs[etype] : "unknown"; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_mem_err_type_str); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciconst char *cper_mem_err_status_str(u64 status) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci switch ((status >> 8) & 0xff) { 21862306a36Sopenharmony_ci case 1: return "Error detected internal to the component"; 21962306a36Sopenharmony_ci case 4: return "Storage error in DRAM memory"; 22062306a36Sopenharmony_ci case 5: return "Storage error in TLB"; 22162306a36Sopenharmony_ci case 6: return "Storage error in cache"; 22262306a36Sopenharmony_ci case 7: return "Error in one or more functional units"; 22362306a36Sopenharmony_ci case 8: return "Component failed self test"; 22462306a36Sopenharmony_ci case 9: return "Overflow or undervalue of internal queue"; 22562306a36Sopenharmony_ci case 16: return "Error detected in the bus"; 22662306a36Sopenharmony_ci case 17: return "Virtual address not found on IO-TLB or IO-PDIR"; 22762306a36Sopenharmony_ci case 18: return "Improper access error"; 22862306a36Sopenharmony_ci case 19: return "Access to a memory address which is not mapped to any component"; 22962306a36Sopenharmony_ci case 20: return "Loss of Lockstep"; 23062306a36Sopenharmony_ci case 21: return "Response not associated with a request"; 23162306a36Sopenharmony_ci case 22: return "Bus parity error - must also set the A, C, or D Bits"; 23262306a36Sopenharmony_ci case 23: return "Detection of a protocol error"; 23362306a36Sopenharmony_ci case 24: return "Detection of a PATH_ERROR"; 23462306a36Sopenharmony_ci case 25: return "Bus operation timeout"; 23562306a36Sopenharmony_ci case 26: return "A read was issued to data that has been poisoned"; 23662306a36Sopenharmony_ci default: return "Reserved"; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_mem_err_status_str); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci u32 len, n; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!msg) 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci n = 0; 24962306a36Sopenharmony_ci len = CPER_REC_LEN; 25062306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_NODE) 25162306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "node:%d ", mem->node); 25262306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_CARD) 25362306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "card:%d ", mem->card); 25462306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_MODULE) 25562306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "module:%d ", mem->module); 25662306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER) 25762306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "rank:%d ", mem->rank); 25862306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_BANK) 25962306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "bank:%d ", mem->bank); 26062306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP) 26162306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "bank_group:%d ", 26262306a36Sopenharmony_ci mem->bank >> CPER_MEM_BANK_GROUP_SHIFT); 26362306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS) 26462306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "bank_address:%d ", 26562306a36Sopenharmony_ci mem->bank & CPER_MEM_BANK_ADDRESS_MASK); 26662306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_DEVICE) 26762306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "device:%d ", mem->device); 26862306a36Sopenharmony_ci if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) { 26962306a36Sopenharmony_ci u32 row = mem->row; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci row |= cper_get_mem_extension(mem->validation_bits, mem->extended); 27262306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "row:%d ", row); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_COLUMN) 27562306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "column:%d ", mem->column); 27662306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION) 27762306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "bit_position:%d ", 27862306a36Sopenharmony_ci mem->bit_pos); 27962306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID) 28062306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx ", 28162306a36Sopenharmony_ci mem->requestor_id); 28262306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID) 28362306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "responder_id:0x%016llx ", 28462306a36Sopenharmony_ci mem->responder_id); 28562306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID) 28662306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "target_id:0x%016llx ", 28762306a36Sopenharmony_ci mem->target_id); 28862306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID) 28962306a36Sopenharmony_ci n += scnprintf(msg + n, len - n, "chip_id:%d ", 29062306a36Sopenharmony_ci mem->extended >> CPER_MEM_CHIP_ID_SHIFT); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return n; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_mem_err_location); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciint cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci u32 len, n; 29962306a36Sopenharmony_ci const char *bank = NULL, *device = NULL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)) 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci len = CPER_REC_LEN; 30562306a36Sopenharmony_ci dmi_memdev_name(mem->mem_dev_handle, &bank, &device); 30662306a36Sopenharmony_ci if (bank && device) 30762306a36Sopenharmony_ci n = snprintf(msg, len, "DIMM location: %s %s ", bank, device); 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci n = snprintf(msg, len, 31062306a36Sopenharmony_ci "DIMM location: not present. DMI handle: 0x%.4x ", 31162306a36Sopenharmony_ci mem->mem_dev_handle); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return n; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_dimm_err_location); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_civoid cper_mem_err_pack(const struct cper_sec_mem_err *mem, 31862306a36Sopenharmony_ci struct cper_mem_err_compact *cmem) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci cmem->validation_bits = mem->validation_bits; 32162306a36Sopenharmony_ci cmem->node = mem->node; 32262306a36Sopenharmony_ci cmem->card = mem->card; 32362306a36Sopenharmony_ci cmem->module = mem->module; 32462306a36Sopenharmony_ci cmem->bank = mem->bank; 32562306a36Sopenharmony_ci cmem->device = mem->device; 32662306a36Sopenharmony_ci cmem->row = mem->row; 32762306a36Sopenharmony_ci cmem->column = mem->column; 32862306a36Sopenharmony_ci cmem->bit_pos = mem->bit_pos; 32962306a36Sopenharmony_ci cmem->requestor_id = mem->requestor_id; 33062306a36Sopenharmony_ci cmem->responder_id = mem->responder_id; 33162306a36Sopenharmony_ci cmem->target_id = mem->target_id; 33262306a36Sopenharmony_ci cmem->extended = mem->extended; 33362306a36Sopenharmony_ci cmem->rank = mem->rank; 33462306a36Sopenharmony_ci cmem->mem_array_handle = mem->mem_array_handle; 33562306a36Sopenharmony_ci cmem->mem_dev_handle = mem->mem_dev_handle; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_mem_err_pack); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ciconst char *cper_mem_err_unpack(struct trace_seq *p, 34062306a36Sopenharmony_ci struct cper_mem_err_compact *cmem) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci const char *ret = trace_seq_buffer_ptr(p); 34362306a36Sopenharmony_ci char rcd_decode_str[CPER_REC_LEN]; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (cper_mem_err_location(cmem, rcd_decode_str)) 34662306a36Sopenharmony_ci trace_seq_printf(p, "%s", rcd_decode_str); 34762306a36Sopenharmony_ci if (cper_dimm_err_location(cmem, rcd_decode_str)) 34862306a36Sopenharmony_ci trace_seq_printf(p, "%s", rcd_decode_str); 34962306a36Sopenharmony_ci trace_seq_putc(p, '\0'); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem, 35562306a36Sopenharmony_ci int len) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct cper_mem_err_compact cmem; 35862306a36Sopenharmony_ci char rcd_decode_str[CPER_REC_LEN]; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Don't trust UEFI 2.1/2.2 structure with bad validation bits */ 36162306a36Sopenharmony_ci if (len == sizeof(struct cper_sec_mem_err_old) && 36262306a36Sopenharmony_ci (mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1))) { 36362306a36Sopenharmony_ci pr_err(FW_WARN "valid bits set for fields beyond structure\n"); 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS) 36762306a36Sopenharmony_ci printk("%s error_status: %s (0x%016llx)\n", 36862306a36Sopenharmony_ci pfx, cper_mem_err_status_str(mem->error_status), 36962306a36Sopenharmony_ci mem->error_status); 37062306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_PA) 37162306a36Sopenharmony_ci printk("%s""physical_address: 0x%016llx\n", 37262306a36Sopenharmony_ci pfx, mem->physical_addr); 37362306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_PA_MASK) 37462306a36Sopenharmony_ci printk("%s""physical_address_mask: 0x%016llx\n", 37562306a36Sopenharmony_ci pfx, mem->physical_addr_mask); 37662306a36Sopenharmony_ci cper_mem_err_pack(mem, &cmem); 37762306a36Sopenharmony_ci if (cper_mem_err_location(&cmem, rcd_decode_str)) 37862306a36Sopenharmony_ci printk("%s%s\n", pfx, rcd_decode_str); 37962306a36Sopenharmony_ci if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) { 38062306a36Sopenharmony_ci u8 etype = mem->error_type; 38162306a36Sopenharmony_ci printk("%s""error_type: %d, %s\n", pfx, etype, 38262306a36Sopenharmony_ci cper_mem_err_type_str(etype)); 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci if (cper_dimm_err_location(&cmem, rcd_decode_str)) 38562306a36Sopenharmony_ci printk("%s%s\n", pfx, rcd_decode_str); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic const char * const pcie_port_type_strs[] = { 38962306a36Sopenharmony_ci "PCIe end point", 39062306a36Sopenharmony_ci "legacy PCI end point", 39162306a36Sopenharmony_ci "unknown", 39262306a36Sopenharmony_ci "unknown", 39362306a36Sopenharmony_ci "root port", 39462306a36Sopenharmony_ci "upstream switch port", 39562306a36Sopenharmony_ci "downstream switch port", 39662306a36Sopenharmony_ci "PCIe to PCI/PCI-X bridge", 39762306a36Sopenharmony_ci "PCI/PCI-X to PCIe bridge", 39862306a36Sopenharmony_ci "root complex integrated endpoint device", 39962306a36Sopenharmony_ci "root complex event collector", 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, 40362306a36Sopenharmony_ci const struct acpi_hest_generic_data *gdata) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) 40662306a36Sopenharmony_ci printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, 40762306a36Sopenharmony_ci pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ? 40862306a36Sopenharmony_ci pcie_port_type_strs[pcie->port_type] : "unknown"); 40962306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_VERSION) 41062306a36Sopenharmony_ci printk("%s""version: %d.%d\n", pfx, 41162306a36Sopenharmony_ci pcie->version.major, pcie->version.minor); 41262306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS) 41362306a36Sopenharmony_ci printk("%s""command: 0x%04x, status: 0x%04x\n", pfx, 41462306a36Sopenharmony_ci pcie->command, pcie->status); 41562306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) { 41662306a36Sopenharmony_ci const __u8 *p; 41762306a36Sopenharmony_ci printk("%s""device_id: %04x:%02x:%02x.%x\n", pfx, 41862306a36Sopenharmony_ci pcie->device_id.segment, pcie->device_id.bus, 41962306a36Sopenharmony_ci pcie->device_id.device, pcie->device_id.function); 42062306a36Sopenharmony_ci printk("%s""slot: %d\n", pfx, 42162306a36Sopenharmony_ci pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT); 42262306a36Sopenharmony_ci printk("%s""secondary_bus: 0x%02x\n", pfx, 42362306a36Sopenharmony_ci pcie->device_id.secondary_bus); 42462306a36Sopenharmony_ci printk("%s""vendor_id: 0x%04x, device_id: 0x%04x\n", pfx, 42562306a36Sopenharmony_ci pcie->device_id.vendor_id, pcie->device_id.device_id); 42662306a36Sopenharmony_ci p = pcie->device_id.class_code; 42762306a36Sopenharmony_ci printk("%s""class_code: %02x%02x%02x\n", pfx, p[2], p[1], p[0]); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER) 43062306a36Sopenharmony_ci printk("%s""serial number: 0x%04x, 0x%04x\n", pfx, 43162306a36Sopenharmony_ci pcie->serial_number.lower, pcie->serial_number.upper); 43262306a36Sopenharmony_ci if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS) 43362306a36Sopenharmony_ci printk( 43462306a36Sopenharmony_ci "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", 43562306a36Sopenharmony_ci pfx, pcie->bridge.secondary_status, pcie->bridge.control); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Fatal errors call __ghes_panic() before AER handler prints this */ 43862306a36Sopenharmony_ci if ((pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) && 43962306a36Sopenharmony_ci (gdata->error_severity & CPER_SEV_FATAL)) { 44062306a36Sopenharmony_ci struct aer_capability_regs *aer; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci aer = (struct aer_capability_regs *)pcie->aer_info; 44362306a36Sopenharmony_ci printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n", 44462306a36Sopenharmony_ci pfx, aer->uncor_status, aer->uncor_mask); 44562306a36Sopenharmony_ci printk("%saer_uncor_severity: 0x%08x\n", 44662306a36Sopenharmony_ci pfx, aer->uncor_severity); 44762306a36Sopenharmony_ci printk("%sTLP Header: %08x %08x %08x %08x\n", pfx, 44862306a36Sopenharmony_ci aer->header_log.dw0, aer->header_log.dw1, 44962306a36Sopenharmony_ci aer->header_log.dw2, aer->header_log.dw3); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const char * const fw_err_rec_type_strs[] = { 45462306a36Sopenharmony_ci "IPF SAL Error Record", 45562306a36Sopenharmony_ci "SOC Firmware Error Record Type1 (Legacy CrashLog Support)", 45662306a36Sopenharmony_ci "SOC Firmware Error Record Type2", 45762306a36Sopenharmony_ci}; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic void cper_print_fw_err(const char *pfx, 46062306a36Sopenharmony_ci struct acpi_hest_generic_data *gdata, 46162306a36Sopenharmony_ci const struct cper_sec_fw_err_rec_ref *fw_err) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci void *buf = acpi_hest_get_payload(gdata); 46462306a36Sopenharmony_ci u32 offset, length = gdata->error_data_length; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci printk("%s""Firmware Error Record Type: %s\n", pfx, 46762306a36Sopenharmony_ci fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ? 46862306a36Sopenharmony_ci fw_err_rec_type_strs[fw_err->record_type] : "unknown"); 46962306a36Sopenharmony_ci printk("%s""Revision: %d\n", pfx, fw_err->revision); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Record Type based on UEFI 2.7 */ 47262306a36Sopenharmony_ci if (fw_err->revision == 0) { 47362306a36Sopenharmony_ci printk("%s""Record Identifier: %08llx\n", pfx, 47462306a36Sopenharmony_ci fw_err->record_identifier); 47562306a36Sopenharmony_ci } else if (fw_err->revision == 2) { 47662306a36Sopenharmony_ci printk("%s""Record Identifier: %pUl\n", pfx, 47762306a36Sopenharmony_ci &fw_err->record_identifier_guid); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * The FW error record may contain trailing data beyond the 48262306a36Sopenharmony_ci * structure defined by the specification. As the fields 48362306a36Sopenharmony_ci * defined (and hence the offset of any trailing data) vary 48462306a36Sopenharmony_ci * with the revision, set the offset to account for this 48562306a36Sopenharmony_ci * variation. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci if (fw_err->revision == 0) { 48862306a36Sopenharmony_ci /* record_identifier_guid not defined */ 48962306a36Sopenharmony_ci offset = offsetof(struct cper_sec_fw_err_rec_ref, 49062306a36Sopenharmony_ci record_identifier_guid); 49162306a36Sopenharmony_ci } else if (fw_err->revision == 1) { 49262306a36Sopenharmony_ci /* record_identifier not defined */ 49362306a36Sopenharmony_ci offset = offsetof(struct cper_sec_fw_err_rec_ref, 49462306a36Sopenharmony_ci record_identifier); 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci offset = sizeof(*fw_err); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci buf += offset; 50062306a36Sopenharmony_ci length -= offset; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void cper_print_tstamp(const char *pfx, 50662306a36Sopenharmony_ci struct acpi_hest_generic_data_v300 *gdata) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci __u8 hour, min, sec, day, mon, year, century, *timestamp; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) { 51162306a36Sopenharmony_ci timestamp = (__u8 *)&(gdata->time_stamp); 51262306a36Sopenharmony_ci sec = bcd2bin(timestamp[0]); 51362306a36Sopenharmony_ci min = bcd2bin(timestamp[1]); 51462306a36Sopenharmony_ci hour = bcd2bin(timestamp[2]); 51562306a36Sopenharmony_ci day = bcd2bin(timestamp[4]); 51662306a36Sopenharmony_ci mon = bcd2bin(timestamp[5]); 51762306a36Sopenharmony_ci year = bcd2bin(timestamp[6]); 51862306a36Sopenharmony_ci century = bcd2bin(timestamp[7]); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n", pfx, 52162306a36Sopenharmony_ci (timestamp[3] & 0x1 ? "precise " : "imprecise "), 52262306a36Sopenharmony_ci century, year, mon, day, hour, min, sec); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic void 52762306a36Sopenharmony_cicper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata, 52862306a36Sopenharmony_ci int sec_no) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci guid_t *sec_type = (guid_t *)gdata->section_type; 53162306a36Sopenharmony_ci __u16 severity; 53262306a36Sopenharmony_ci char newpfx[64]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (acpi_hest_get_version(gdata) >= 3) 53562306a36Sopenharmony_ci cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci severity = gdata->error_severity; 53862306a36Sopenharmony_ci printk("%s""Error %d, type: %s\n", pfx, sec_no, 53962306a36Sopenharmony_ci cper_severity_str(severity)); 54062306a36Sopenharmony_ci if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID) 54162306a36Sopenharmony_ci printk("%s""fru_id: %pUl\n", pfx, gdata->fru_id); 54262306a36Sopenharmony_ci if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT) 54362306a36Sopenharmony_ci printk("%s""fru_text: %.20s\n", pfx, gdata->fru_text); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 54662306a36Sopenharmony_ci if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) { 54762306a36Sopenharmony_ci struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci printk("%s""section_type: general processor error\n", newpfx); 55062306a36Sopenharmony_ci if (gdata->error_data_length >= sizeof(*proc_err)) 55162306a36Sopenharmony_ci cper_print_proc_generic(newpfx, proc_err); 55262306a36Sopenharmony_ci else 55362306a36Sopenharmony_ci goto err_section_too_small; 55462306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) { 55562306a36Sopenharmony_ci struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci printk("%s""section_type: memory error\n", newpfx); 55862306a36Sopenharmony_ci if (gdata->error_data_length >= 55962306a36Sopenharmony_ci sizeof(struct cper_sec_mem_err_old)) 56062306a36Sopenharmony_ci cper_print_mem(newpfx, mem_err, 56162306a36Sopenharmony_ci gdata->error_data_length); 56262306a36Sopenharmony_ci else 56362306a36Sopenharmony_ci goto err_section_too_small; 56462306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { 56562306a36Sopenharmony_ci struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci printk("%s""section_type: PCIe error\n", newpfx); 56862306a36Sopenharmony_ci if (gdata->error_data_length >= sizeof(*pcie)) 56962306a36Sopenharmony_ci cper_print_pcie(newpfx, pcie, gdata); 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci goto err_section_too_small; 57262306a36Sopenharmony_ci#if defined(CONFIG_ARM64) || defined(CONFIG_ARM) 57362306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { 57462306a36Sopenharmony_ci struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci printk("%ssection_type: ARM processor error\n", newpfx); 57762306a36Sopenharmony_ci if (gdata->error_data_length >= sizeof(*arm_err)) 57862306a36Sopenharmony_ci cper_print_proc_arm(newpfx, arm_err); 57962306a36Sopenharmony_ci else 58062306a36Sopenharmony_ci goto err_section_too_small; 58162306a36Sopenharmony_ci#endif 58262306a36Sopenharmony_ci#if defined(CONFIG_UEFI_CPER_X86) 58362306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) { 58462306a36Sopenharmony_ci struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci printk("%ssection_type: IA32/X64 processor error\n", newpfx); 58762306a36Sopenharmony_ci if (gdata->error_data_length >= sizeof(*ia_err)) 58862306a36Sopenharmony_ci cper_print_proc_ia(newpfx, ia_err); 58962306a36Sopenharmony_ci else 59062306a36Sopenharmony_ci goto err_section_too_small; 59162306a36Sopenharmony_ci#endif 59262306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) { 59362306a36Sopenharmony_ci struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci printk("%ssection_type: Firmware Error Record Reference\n", 59662306a36Sopenharmony_ci newpfx); 59762306a36Sopenharmony_ci /* The minimal FW Error Record contains 16 bytes */ 59862306a36Sopenharmony_ci if (gdata->error_data_length >= SZ_16) 59962306a36Sopenharmony_ci cper_print_fw_err(newpfx, gdata, fw_err); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci goto err_section_too_small; 60262306a36Sopenharmony_ci } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { 60362306a36Sopenharmony_ci struct cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci printk("%ssection_type: CXL Protocol Error\n", newpfx); 60662306a36Sopenharmony_ci if (gdata->error_data_length >= sizeof(*prot_err)) 60762306a36Sopenharmony_ci cper_print_prot_err(newpfx, prot_err); 60862306a36Sopenharmony_ci else 60962306a36Sopenharmony_ci goto err_section_too_small; 61062306a36Sopenharmony_ci } else { 61162306a36Sopenharmony_ci const void *err = acpi_hest_get_payload(gdata); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci printk("%ssection type: unknown, %pUl\n", newpfx, sec_type); 61462306a36Sopenharmony_ci printk("%ssection length: %#x\n", newpfx, 61562306a36Sopenharmony_ci gdata->error_data_length); 61662306a36Sopenharmony_ci print_hex_dump(newpfx, "", DUMP_PREFIX_OFFSET, 16, 4, err, 61762306a36Sopenharmony_ci gdata->error_data_length, true); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cierr_section_too_small: 62362306a36Sopenharmony_ci pr_err(FW_WARN "error section length is too small\n"); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_civoid cper_estatus_print(const char *pfx, 62762306a36Sopenharmony_ci const struct acpi_hest_generic_status *estatus) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct acpi_hest_generic_data *gdata; 63062306a36Sopenharmony_ci int sec_no = 0; 63162306a36Sopenharmony_ci char newpfx[64]; 63262306a36Sopenharmony_ci __u16 severity; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci severity = estatus->error_severity; 63562306a36Sopenharmony_ci if (severity == CPER_SEV_CORRECTED) 63662306a36Sopenharmony_ci printk("%s%s\n", pfx, 63762306a36Sopenharmony_ci "It has been corrected by h/w " 63862306a36Sopenharmony_ci "and requires no further action"); 63962306a36Sopenharmony_ci printk("%s""event severity: %s\n", pfx, cper_severity_str(severity)); 64062306a36Sopenharmony_ci snprintf(newpfx, sizeof(newpfx), "%s ", pfx); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci apei_estatus_for_each_section(estatus, gdata) { 64362306a36Sopenharmony_ci cper_estatus_print_section(newpfx, gdata, sec_no); 64462306a36Sopenharmony_ci sec_no++; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_print); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ciint cper_estatus_check_header(const struct acpi_hest_generic_status *estatus) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci if (estatus->data_length && 65262306a36Sopenharmony_ci estatus->data_length < sizeof(struct acpi_hest_generic_data)) 65362306a36Sopenharmony_ci return -EINVAL; 65462306a36Sopenharmony_ci if (estatus->raw_data_length && 65562306a36Sopenharmony_ci estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_check_header); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciint cper_estatus_check(const struct acpi_hest_generic_status *estatus) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct acpi_hest_generic_data *gdata; 66562306a36Sopenharmony_ci unsigned int data_len, record_size; 66662306a36Sopenharmony_ci int rc; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci rc = cper_estatus_check_header(estatus); 66962306a36Sopenharmony_ci if (rc) 67062306a36Sopenharmony_ci return rc; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci data_len = estatus->data_length; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci apei_estatus_for_each_section(estatus, gdata) { 67562306a36Sopenharmony_ci if (acpi_hest_get_size(gdata) > data_len) 67662306a36Sopenharmony_ci return -EINVAL; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci record_size = acpi_hest_get_record_size(gdata); 67962306a36Sopenharmony_ci if (record_size > data_len) 68062306a36Sopenharmony_ci return -EINVAL; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci data_len -= record_size; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci if (data_len) 68562306a36Sopenharmony_ci return -EINVAL; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cper_estatus_check); 690