// SPDX-License-Identifier: GPL-2.0
/*
* UEFI Common Platform Error Record ( CPER ) support
*
* Copyright ( C ) 2010 , Intel Corp .
* Author : Huang Ying < ying . huang @ intel . com >
*
* CPER is the format used to describe platform hardware error by
* various tables , such as ERST , BERT and HEST etc .
*
* For more information about CPER , please refer to Appendix N of UEFI
* Specification version 2 . 4 .
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/cper.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/aer.h>
#include <linux/printk.h>
#include <linux/bcd.h>
#include <acpi/ghes.h>
#include <ras/ras_event.h>
#include <cxl/event.h>
/*
* CPER record ID need to be unique even after reboot , because record
* ID is used as index for ERST storage , while CPER records from
* multiple boot may co - exist in ERST .
*/
u64 cper_next_record_id(void )
{
static atomic64_t seq;
if (!atomic64_read(&seq)) {
time64_t time = ktime_get_real_seconds();
/*
* This code is unlikely to still be needed in year 2106 ,
* but just in case , let ' s use a few more bits for timestamps
* after y2038 to be sure they keep increasing monotonically
* for the next few hundred years . . .
*/
if (time < 0 x80000000)
atomic64_set(&seq, (ktime_get_real_seconds()) << 32 );
else
atomic64_set(&seq, 0 x8000000000000000ull |
ktime_get_real_seconds() << 24 );
}
return atomic64_inc_return(&seq);
}
EXPORT_SYMBOL_GPL(cper_next_record_id);
static const char * const severity_strs[] = {
"recoverable" ,
"fatal" ,
"corrected" ,
"info" ,
};
const char *cper_severity_str(unsigned int severity)
{
return severity < ARRAY_SIZE(severity_strs) ?
severity_strs[severity] : "unknown" ;
}
EXPORT_SYMBOL_GPL(cper_severity_str);
/*
* cper_print_bits - print strings for set bits
* @ pfx : prefix for each line , including log level and prefix string
* @ bits : bit mask
* @ strs : string array , indexed by bit position
* @ strs_size : size of the string array : @ strs
*
* For each set bit in @ bits , print the corresponding string in @ strs .
* If the output length is longer than 80 , multiple line will be
* printed , with @ pfx is printed at the beginning of each line .
*/
void cper_print_bits(const char *pfx, unsigned int bits,
const char * const strs[], unsigned int strs_size)
{
int i, len = 0 ;
const char *str;
char buf[84 ];
for (i = 0 ; i < strs_size; i++) {
if (!(bits & (1 U << i)))
continue ;
str = strs[i];
if (!str)
continue ;
if (len && len + strlen(str) + 2 > 80 ) {
printk("%s\n" , buf);
len = 0 ;
}
if (!len)
len = snprintf(buf, sizeof (buf), "%s%s" , pfx, str);
else
len += scnprintf(buf+len, sizeof (buf)-len, ", %s" , str);
}
if (len)
printk("%s\n" , buf);
}
static const char * const proc_type_strs[] = {
"IA32/X64" ,
"IA64" ,
"ARM" ,
};
static const char * const proc_isa_strs[] = {
"IA32" ,
"IA64" ,
"X64" ,
"ARM A32/T32" ,
"ARM A64" ,
};
const char * const cper_proc_error_type_strs[] = {
"cache error" ,
"TLB error" ,
"bus error" ,
"micro-architectural error" ,
};
static const char * const proc_op_strs[] = {
"unknown or generic" ,
"data read" ,
"data write" ,
"instruction execution" ,
};
static const char * const proc_flag_strs[] = {
"restartable" ,
"precise IP" ,
"overflow" ,
"corrected" ,
};
static void cper_print_proc_generic(const char *pfx,
const struct cper_sec_proc_generic *proc)
{
if (proc->validation_bits & CPER_PROC_VALID_TYPE)
printk("%s" "processor_type: %d, %s\n" , pfx, proc->proc_type,
proc->proc_type < ARRAY_SIZE(proc_type_strs) ?
proc_type_strs[proc->proc_type] : "unknown" );
if (proc->validation_bits & CPER_PROC_VALID_ISA)
printk("%s" "processor_isa: %d, %s\n" , pfx, proc->proc_isa,
proc->proc_isa < ARRAY_SIZE(proc_isa_strs) ?
proc_isa_strs[proc->proc_isa] : "unknown" );
if (proc->validation_bits & CPER_PROC_VALID_ERROR_TYPE) {
printk("%s" "error_type: 0x%02x\n" , pfx, proc->proc_error_type);
cper_print_bits(pfx, proc->proc_error_type,
cper_proc_error_type_strs,
ARRAY_SIZE(cper_proc_error_type_strs));
}
if (proc->validation_bits & CPER_PROC_VALID_OPERATION)
printk("%s" "operation: %d, %s\n" , pfx, proc->operation,
proc->operation < ARRAY_SIZE(proc_op_strs) ?
proc_op_strs[proc->operation] : "unknown" );
if (proc->validation_bits & CPER_PROC_VALID_FLAGS) {
printk("%s" "flags: 0x%02x\n" , pfx, proc->flags);
cper_print_bits(pfx, proc->flags, proc_flag_strs,
ARRAY_SIZE(proc_flag_strs));
}
if (proc->validation_bits & CPER_PROC_VALID_LEVEL)
printk("%s" "level: %d\n" , pfx, proc->level);
if (proc->validation_bits & CPER_PROC_VALID_VERSION)
printk("%s" "version_info: 0x%016llx\n" , pfx, proc->cpu_version);
if (proc->validation_bits & CPER_PROC_VALID_ID)
printk("%s" "processor_id: 0x%016llx\n" , pfx, proc->proc_id);
if (proc->validation_bits & CPER_PROC_VALID_TARGET_ADDRESS)
printk("%s" "target_address: 0x%016llx\n" ,
pfx, proc->target_addr);
if (proc->validation_bits & CPER_PROC_VALID_REQUESTOR_ID)
printk("%s" "requestor_id: 0x%016llx\n" ,
pfx, proc->requestor_id);
if (proc->validation_bits & CPER_PROC_VALID_RESPONDER_ID)
printk("%s" "responder_id: 0x%016llx\n" ,
pfx, proc->responder_id);
if (proc->validation_bits & CPER_PROC_VALID_IP)
printk("%s" "IP: 0x%016llx\n" , pfx, proc->ip);
}
static const char * const mem_err_type_strs[] = {
"unknown" ,
"no error" ,
"single-bit ECC" ,
"multi-bit ECC" ,
"single-symbol chipkill ECC" ,
"multi-symbol chipkill ECC" ,
"master abort" ,
"target abort" ,
"parity error" ,
"watchdog timeout" ,
"invalid address" ,
"mirror Broken" ,
"memory sparing" ,
"scrub corrected error" ,
"scrub uncorrected error" ,
"physical memory map-out event" ,
};
const char *cper_mem_err_type_str(unsigned int etype)
{
return etype < ARRAY_SIZE(mem_err_type_strs) ?
mem_err_type_strs[etype] : "unknown" ;
}
EXPORT_SYMBOL_GPL(cper_mem_err_type_str);
const char *cper_mem_err_status_str(u64 status)
{
switch ((status >> 8 ) & 0 xff) {
case 1 : return "Error detected internal to the component" ;
case 4 : return "Storage error in DRAM memory" ;
case 5 : return "Storage error in TLB" ;
case 6 : return "Storage error in cache" ;
case 7 : return "Error in one or more functional units" ;
case 8 : return "Component failed self test" ;
case 9 : return "Overflow or undervalue of internal queue" ;
case 16 : return "Error detected in the bus" ;
case 17 : return "Virtual address not found on IO-TLB or IO-PDIR" ;
case 18 : return "Improper access error" ;
case 19 : return "Access to a memory address which is not mapped to any component" ;
case 20 : return "Loss of Lockstep" ;
case 21 : return "Response not associated with a request" ;
case 22 : return "Bus parity error - must also set the A, C, or D Bits" ;
case 23 : return "Detection of a protocol error" ;
case 24 : return "Detection of a PATH_ERROR" ;
case 25 : return "Bus operation timeout" ;
case 26 : return "A read was issued to data that has been poisoned" ;
default : return "Reserved" ;
}
}
EXPORT_SYMBOL_GPL(cper_mem_err_status_str);
int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg)
{
u32 len, n;
if (!msg)
return 0 ;
n = 0 ;
len = CPER_REC_LEN;
if (mem->validation_bits & CPER_MEM_VALID_NODE)
n += scnprintf(msg + n, len - n, "node:%d " , mem->node);
if (mem->validation_bits & CPER_MEM_VALID_CARD)
n += scnprintf(msg + n, len - n, "card:%d " , mem->card);
if (mem->validation_bits & CPER_MEM_VALID_MODULE)
n += scnprintf(msg + n, len - n, "module:%d " , mem->module);
if (mem->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
n += scnprintf(msg + n, len - n, "rank:%d " , mem->rank);
if (mem->validation_bits & CPER_MEM_VALID_BANK)
n += scnprintf(msg + n, len - n, "bank:%d " , mem->bank);
if (mem->validation_bits & CPER_MEM_VALID_BANK_GROUP)
n += scnprintf(msg + n, len - n, "bank_group:%d " ,
mem->bank >> CPER_MEM_BANK_GROUP_SHIFT);
if (mem->validation_bits & CPER_MEM_VALID_BANK_ADDRESS)
n += scnprintf(msg + n, len - n, "bank_address:%d " ,
mem->bank & CPER_MEM_BANK_ADDRESS_MASK);
if (mem->validation_bits & CPER_MEM_VALID_DEVICE)
n += scnprintf(msg + n, len - n, "device:%d " , mem->device);
if (mem->validation_bits & (CPER_MEM_VALID_ROW | CPER_MEM_VALID_ROW_EXT)) {
u32 row = mem->row;
row |= cper_get_mem_extension(mem->validation_bits, mem->extended);
n += scnprintf(msg + n, len - n, "row:%d " , row);
}
if (mem->validation_bits & CPER_MEM_VALID_COLUMN)
n += scnprintf(msg + n, len - n, "column:%d " , mem->column);
if (mem->validation_bits & CPER_MEM_VALID_BIT_POSITION)
n += scnprintf(msg + n, len - n, "bit_position:%d " ,
mem->bit_pos);
if (mem->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
n += scnprintf(msg + n, len - n, "requestor_id:0x%016llx " ,
mem->requestor_id);
if (mem->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
n += scnprintf(msg + n, len - n, "responder_id:0x%016llx " ,
mem->responder_id);
if (mem->validation_bits & CPER_MEM_VALID_TARGET_ID)
n += scnprintf(msg + n, len - n, "target_id:0x%016llx " ,
mem->target_id);
if (mem->validation_bits & CPER_MEM_VALID_CHIP_ID)
n += scnprintf(msg + n, len - n, "chip_id:%d " ,
mem->extended >> CPER_MEM_CHIP_ID_SHIFT);
return n;
}
EXPORT_SYMBOL_GPL(cper_mem_err_location);
int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg)
{
u32 len, n;
const char *bank = NULL, *device = NULL;
if (!msg || !(mem->validation_bits & CPER_MEM_VALID_MODULE_HANDLE))
return 0 ;
len = CPER_REC_LEN;
dmi_memdev_name(mem->mem_dev_handle, &bank, &device);
if (bank && device)
n = snprintf(msg, len, "DIMM location: %s %s " , bank, device);
else
n = snprintf(msg, len,
"DIMM location: not present. DMI handle: 0x%.4x " ,
mem->mem_dev_handle);
return n;
}
EXPORT_SYMBOL_GPL(cper_dimm_err_location);
void cper_mem_err_pack(const struct cper_sec_mem_err *mem,
struct cper_mem_err_compact *cmem)
{
cmem->validation_bits = mem->validation_bits;
cmem->node = mem->node;
cmem->card = mem->card;
cmem->module = mem->module;
cmem->bank = mem->bank;
cmem->device = mem->device;
cmem->row = mem->row;
cmem->column = mem->column;
cmem->bit_pos = mem->bit_pos;
cmem->requestor_id = mem->requestor_id;
cmem->responder_id = mem->responder_id;
cmem->target_id = mem->target_id;
cmem->extended = mem->extended;
cmem->rank = mem->rank;
cmem->mem_array_handle = mem->mem_array_handle;
cmem->mem_dev_handle = mem->mem_dev_handle;
}
EXPORT_SYMBOL_GPL(cper_mem_err_pack);
const char *cper_mem_err_unpack(struct trace_seq *p,
struct cper_mem_err_compact *cmem)
{
const char *ret = trace_seq_buffer_ptr(p);
char rcd_decode_str[CPER_REC_LEN];
if (cper_mem_err_location(cmem, rcd_decode_str))
trace_seq_printf(p, "%s" , rcd_decode_str);
if (cper_dimm_err_location(cmem, rcd_decode_str))
trace_seq_printf(p, "%s" , rcd_decode_str);
trace_seq_putc(p, '\0' );
return ret;
}
static void cper_print_mem(const char *pfx, const struct cper_sec_mem_err *mem,
int len)
{
struct cper_mem_err_compact cmem;
char rcd_decode_str[CPER_REC_LEN];
/* Don't trust UEFI 2.1/2.2 structure with bad validation bits */
if (len == sizeof (struct cper_sec_mem_err_old) &&
(mem->validation_bits & ~(CPER_MEM_VALID_RANK_NUMBER - 1 ))) {
pr_err(FW_WARN "valid bits set for fields beyond structure\n" );
return ;
}
if (mem->validation_bits & CPER_MEM_VALID_ERROR_STATUS)
printk("%s error_status: %s (0x%016llx)\n" ,
pfx, cper_mem_err_status_str(mem->error_status),
mem->error_status);
if (mem->validation_bits & CPER_MEM_VALID_PA)
printk("%s" "physical_address: 0x%016llx\n" ,
pfx, mem->physical_addr);
if (mem->validation_bits & CPER_MEM_VALID_PA_MASK)
printk("%s" "physical_address_mask: 0x%016llx\n" ,
pfx, mem->physical_addr_mask);
cper_mem_err_pack(mem, &cmem);
if (cper_mem_err_location(&cmem, rcd_decode_str))
printk("%s%s\n" , pfx, rcd_decode_str);
if (mem->validation_bits & CPER_MEM_VALID_ERROR_TYPE) {
u8 etype = mem->error_type;
printk("%s" "error_type: %d, %s\n" , pfx, etype,
cper_mem_err_type_str(etype));
}
if (cper_dimm_err_location(&cmem, rcd_decode_str))
printk("%s%s\n" , pfx, rcd_decode_str);
}
static const char * const pcie_port_type_strs[] = {
"PCIe end point" ,
"legacy PCI end point" ,
"unknown" ,
"unknown" ,
"root port" ,
"upstream switch port" ,
"downstream switch port" ,
"PCIe to PCI/PCI-X bridge" ,
"PCI/PCI-X to PCIe bridge" ,
"root complex integrated endpoint device" ,
"root complex event collector" ,
};
static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie,
const struct acpi_hest_generic_data *gdata)
{
if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE)
printk("%s" "port_type: %d, %s\n" , pfx, pcie->port_type,
pcie->port_type < ARRAY_SIZE(pcie_port_type_strs) ?
pcie_port_type_strs[pcie->port_type] : "unknown" );
if (pcie->validation_bits & CPER_PCIE_VALID_VERSION)
printk("%s" "version: %d.%d\n" , pfx,
pcie->version.major, pcie->version.minor);
if (pcie->validation_bits & CPER_PCIE_VALID_COMMAND_STATUS)
printk("%s" "command: 0x%04x, status: 0x%04x\n" , pfx,
pcie->command, pcie->status);
if (pcie->validation_bits & CPER_PCIE_VALID_DEVICE_ID) {
const __u8 *p;
printk("%s" "device_id: %04x:%02x:%02x.%x\n" , pfx,
pcie->device_id.segment, pcie->device_id.bus,
pcie->device_id.device, pcie->device_id.function);
printk("%s" "slot: %d\n" , pfx,
pcie->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
printk("%s" "secondary_bus: 0x%02x\n" , pfx,
pcie->device_id.secondary_bus);
printk("%s" "vendor_id: 0x%04x, device_id: 0x%04x\n" , pfx,
pcie->device_id.vendor_id, pcie->device_id.device_id);
p = pcie->device_id.class_code;
printk("%s" "class_code: %02x%02x%02x\n" , pfx, p[2 ], p[1 ], p[0 ]);
}
if (pcie->validation_bits & CPER_PCIE_VALID_SERIAL_NUMBER)
printk("%s" "serial number: 0x%04x, 0x%04x\n" , pfx,
pcie->serial_number.lower, pcie->serial_number.upper);
if (pcie->validation_bits & CPER_PCIE_VALID_BRIDGE_CONTROL_STATUS)
printk(
"%s" "bridge: secondary_status: 0x%04x, control: 0x%04x\n" ,
pfx, pcie->bridge.secondary_status, pcie->bridge.control);
/*
* Print all valid AER info . Record may be from BERT ( boot - time ) or GHES ( run - time ) .
*
* Fatal errors call _ _ ghes_panic ( ) before AER handler prints this .
*/
if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) {
struct aer_capability_regs *aer;
aer = (struct aer_capability_regs *)pcie->aer_info;
printk("%saer_cor_status: 0x%08x, aer_cor_mask: 0x%08x\n" ,
pfx, aer->cor_status, aer->cor_mask);
printk("%saer_uncor_status: 0x%08x, aer_uncor_mask: 0x%08x\n" ,
pfx, aer->uncor_status, aer->uncor_mask);
printk("%saer_uncor_severity: 0x%08x\n" ,
pfx, aer->uncor_severity);
printk("%sTLP Header: %08x %08x %08x %08x\n" , pfx,
aer->header_log.dw[0 ], aer->header_log.dw[1 ],
aer->header_log.dw[2 ], aer->header_log.dw[3 ]);
}
}
static const char * const fw_err_rec_type_strs[] = {
"IPF SAL Error Record" ,
"SOC Firmware Error Record Type1 (Legacy CrashLog Support)" ,
"SOC Firmware Error Record Type2" ,
};
static void cper_print_fw_err(const char *pfx,
struct acpi_hest_generic_data *gdata,
const struct cper_sec_fw_err_rec_ref *fw_err)
{
void *buf = acpi_hest_get_payload(gdata);
u32 offset, length = gdata->error_data_length;
printk("%s" "Firmware Error Record Type: %s\n" , pfx,
fw_err->record_type < ARRAY_SIZE(fw_err_rec_type_strs) ?
fw_err_rec_type_strs[fw_err->record_type] : "unknown" );
printk("%s" "Revision: %d\n" , pfx, fw_err->revision);
/* Record Type based on UEFI 2.7 */
if (fw_err->revision == 0 ) {
printk("%s" "Record Identifier: %08llx\n" , pfx,
fw_err->record_identifier);
} else if (fw_err->revision == 2 ) {
printk("%s" "Record Identifier: %pUl\n" , pfx,
&fw_err->record_identifier_guid);
}
/*
* The FW error record may contain trailing data beyond the
* structure defined by the specification . As the fields
* defined ( and hence the offset of any trailing data ) vary
* with the revision , set the offset to account for this
* variation .
*/
if (fw_err->revision == 0 ) {
/* record_identifier_guid not defined */
offset = offsetof(struct cper_sec_fw_err_rec_ref,
record_identifier_guid);
} else if (fw_err->revision == 1 ) {
/* record_identifier not defined */
offset = offsetof(struct cper_sec_fw_err_rec_ref,
record_identifier);
} else {
offset = sizeof (*fw_err);
}
buf += offset;
length -= offset;
print_hex_dump(pfx, "" , DUMP_PREFIX_OFFSET, 16 , 4 , buf, length, true );
}
static void cper_print_tstamp(const char *pfx,
struct acpi_hest_generic_data_v300 *gdata)
{
__u8 hour, min, sec, day, mon, year, century, *timestamp;
if (gdata->validation_bits & ACPI_HEST_GEN_VALID_TIMESTAMP) {
timestamp = (__u8 *)&(gdata->time_stamp);
sec = bcd2bin(timestamp[0 ]);
min = bcd2bin(timestamp[1 ]);
hour = bcd2bin(timestamp[2 ]);
day = bcd2bin(timestamp[4 ]);
mon = bcd2bin(timestamp[5 ]);
year = bcd2bin(timestamp[6 ]);
century = bcd2bin(timestamp[7 ]);
printk("%s%ststamp: %02d%02d-%02d-%02d %02d:%02d:%02d\n" , pfx,
(timestamp[3 ] & 0 x1 ? "precise " : "imprecise " ),
century, year, mon, day, hour, min, sec);
}
}
struct ignore_section {
guid_t guid;
const char *name;
};
static const struct ignore_section ignore_sections[] = {
{ .guid = CPER_SEC_CXL_GEN_MEDIA_GUID, .name = "CXL General Media Event" },
{ .guid = CPER_SEC_CXL_DRAM_GUID, .name = "CXL DRAM Event" },
{ .guid = CPER_SEC_CXL_MEM_MODULE_GUID, .name = "CXL Memory Module Event" },
};
static void
cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata,
int sec_no)
{
guid_t *sec_type = (guid_t *)gdata->section_type;
__u16 severity;
char newpfx[64 ];
if (acpi_hest_get_version(gdata) >= 3 )
cper_print_tstamp(pfx, (struct acpi_hest_generic_data_v300 *)gdata);
severity = gdata->error_severity;
printk("%s" "Error %d, type: %s\n" , pfx, sec_no,
cper_severity_str(severity));
if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
printk("%s" "fru_id: %pUl\n" , pfx, gdata->fru_id);
if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
printk("%s" "fru_text: %.20s\n" , pfx, gdata->fru_text);
snprintf(newpfx, sizeof (newpfx), "%s " , pfx);
for (int i = 0 ; i < ARRAY_SIZE(ignore_sections); i++) {
if (guid_equal(sec_type, &ignore_sections[i].guid)) {
printk("%ssection_type: %s\n" , newpfx, ignore_sections[i].name);
return ;
}
}
if (guid_equal(sec_type, &CPER_SEC_PROC_GENERIC)) {
struct cper_sec_proc_generic *proc_err = acpi_hest_get_payload(gdata);
printk("%s" "section_type: general processor error\n" , newpfx);
if (gdata->error_data_length >= sizeof (*proc_err))
cper_print_proc_generic(newpfx, proc_err);
else
goto err_section_too_small;
} else if (guid_equal(sec_type, &CPER_SEC_PLATFORM_MEM)) {
struct cper_sec_mem_err *mem_err = acpi_hest_get_payload(gdata);
printk("%s" "section_type: memory error\n" , newpfx);
if (gdata->error_data_length >=
sizeof (struct cper_sec_mem_err_old))
cper_print_mem(newpfx, mem_err,
gdata->error_data_length);
else
goto err_section_too_small;
} else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
struct cper_sec_pcie *pcie = acpi_hest_get_payload(gdata);
printk("%s" "section_type: PCIe error\n" , newpfx);
if (gdata->error_data_length >= sizeof (*pcie))
cper_print_pcie(newpfx, pcie, gdata);
else
goto err_section_too_small;
#if defined (CONFIG_ARM64) || defined (CONFIG_ARM)
} else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
struct cper_sec_proc_arm *arm_err = acpi_hest_get_payload(gdata);
printk("%ssection_type: ARM processor error\n" , newpfx);
if (gdata->error_data_length >= sizeof (*arm_err))
cper_print_proc_arm(newpfx, arm_err);
else
goto err_section_too_small;
#endif
#if defined (CONFIG_UEFI_CPER_X86)
} else if (guid_equal(sec_type, &CPER_SEC_PROC_IA)) {
struct cper_sec_proc_ia *ia_err = acpi_hest_get_payload(gdata);
printk("%ssection_type: IA32/X64 processor error\n" , newpfx);
if (gdata->error_data_length >= sizeof (*ia_err))
cper_print_proc_ia(newpfx, ia_err);
else
goto err_section_too_small;
#endif
} else if (guid_equal(sec_type, &CPER_SEC_FW_ERR_REC_REF)) {
struct cper_sec_fw_err_rec_ref *fw_err = acpi_hest_get_payload(gdata);
printk("%ssection_type: Firmware Error Record Reference\n" ,
newpfx);
/* The minimal FW Error Record contains 16 bytes */
if (gdata->error_data_length >= SZ_16)
cper_print_fw_err(newpfx, gdata, fw_err);
else
goto err_section_too_small;
} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
printk("%ssection_type: CXL Protocol Error\n" , newpfx);
if (gdata->error_data_length >= sizeof (*prot_err))
cxl_cper_print_prot_err(newpfx, prot_err);
else
goto err_section_too_small;
} else {
const void *err = acpi_hest_get_payload(gdata);
printk("%ssection type: unknown, %pUl\n" , newpfx, sec_type);
printk("%ssection length: %#x\n" , newpfx,
gdata->error_data_length);
print_hex_dump(newpfx, "" , DUMP_PREFIX_OFFSET, 16 , 4 , err,
gdata->error_data_length, true );
}
return ;
err_section_too_small:
pr_err(FW_WARN "error section length is too small\n" );
}
void cper_estatus_print(const char *pfx,
const struct acpi_hest_generic_status *estatus)
{
struct acpi_hest_generic_data *gdata;
int sec_no = 0 ;
char newpfx[64 ];
__u16 severity;
severity = estatus->error_severity;
if (severity == CPER_SEV_CORRECTED)
printk("%s%s\n" , pfx,
"It has been corrected by h/w "
"and requires no further action" );
printk("%s" "event severity: %s\n" , pfx, cper_severity_str(severity));
snprintf(newpfx, sizeof (newpfx), "%s " , pfx);
apei_estatus_for_each_section(estatus, gdata) {
cper_estatus_print_section(newpfx, gdata, sec_no);
sec_no++;
}
}
EXPORT_SYMBOL_GPL(cper_estatus_print);
int cper_estatus_check_header(const struct acpi_hest_generic_status *estatus)
{
if (estatus->data_length &&
estatus->data_length < sizeof (struct acpi_hest_generic_data))
return -EINVAL;
if (estatus->raw_data_length &&
estatus->raw_data_offset < sizeof (*estatus) + estatus->data_length)
return -EINVAL;
return 0 ;
}
EXPORT_SYMBOL_GPL(cper_estatus_check_header);
int cper_estatus_check(const struct acpi_hest_generic_status *estatus)
{
struct acpi_hest_generic_data *gdata;
unsigned int data_len, record_size;
int rc;
rc = cper_estatus_check_header(estatus);
if (rc)
return rc;
data_len = estatus->data_length;
apei_estatus_for_each_section(estatus, gdata) {
if (acpi_hest_get_size(gdata) > data_len)
return -EINVAL;
record_size = acpi_hest_get_record_size(gdata);
if (record_size > data_len)
return -EINVAL;
data_len -= record_size;
}
if (data_len)
return -EINVAL;
return 0 ;
}
EXPORT_SYMBOL_GPL(cper_estatus_check);
Messung V0.5 in Prozent C=88 H=95 G=91
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland