2019-02-02 12:41:15 +03:00
// SPDX-License-Identifier: GPL-2.0
2018-01-02 21:10:41 +03:00
/*
* UEFI Common Platform Error Record ( CPER ) support
*
* Copyright ( C ) 2017 , The Linux Foundation . All rights reserved .
*/
# 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>
static const char * const arm_reg_ctx_strs [ ] = {
" AArch32 general purpose registers " ,
" AArch32 EL1 context registers " ,
" AArch32 EL2 context registers " ,
" AArch32 secure context registers " ,
" AArch64 general purpose registers " ,
" AArch64 EL1 context registers " ,
" AArch64 EL2 context registers " ,
" AArch64 EL3 context registers " ,
" Misc. system register structure " ,
} ;
2018-01-02 21:10:42 +03:00
static const char * const arm_err_trans_type_strs [ ] = {
" Instruction " ,
" Data Access " ,
" Generic " ,
} ;
static const char * const arm_bus_err_op_strs [ ] = {
" Generic error (type cannot be determined) " ,
" Generic read (type of instruction or data request cannot be determined) " ,
" Generic write (type of instruction of data request cannot be determined) " ,
" Data read " ,
" Data write " ,
" Instruction fetch " ,
" Prefetch " ,
} ;
static const char * const arm_cache_err_op_strs [ ] = {
" Generic error (type cannot be determined) " ,
" Generic read (type of instruction or data request cannot be determined) " ,
" Generic write (type of instruction of data request cannot be determined) " ,
" Data read " ,
" Data write " ,
" Instruction fetch " ,
" Prefetch " ,
" Eviction " ,
" Snooping (processor initiated a cache snoop that resulted in an error) " ,
" Snooped (processor raised a cache error caused by another processor or device snooping its cache) " ,
" Management " ,
} ;
static const char * const arm_tlb_err_op_strs [ ] = {
" Generic error (type cannot be determined) " ,
" Generic read (type of instruction or data request cannot be determined) " ,
" Generic write (type of instruction of data request cannot be determined) " ,
" Data read " ,
" Data write " ,
" Instruction fetch " ,
" Prefetch " ,
" Local management operation (processor initiated a TLB management operation that resulted in an error) " ,
" External management operation (processor raised a TLB error caused by another processor or device broadcasting TLB operations) " ,
} ;
static const char * const arm_bus_err_part_type_strs [ ] = {
" Local processor originated request " ,
" Local processor responded to request " ,
" Local processor observed " ,
" Generic " ,
} ;
static const char * const arm_bus_err_addr_space_strs [ ] = {
" External Memory Access " ,
" Internal Memory Access " ,
" Unknown " ,
" Device Memory Access " ,
} ;
static void cper_print_arm_err_info ( const char * pfx , u32 type ,
u64 error_info )
{
u8 trans_type , op_type , level , participation_type , address_space ;
u16 mem_attributes ;
bool proc_context_corrupt , corrected , precise_pc , restartable_pc ;
bool time_out , access_mode ;
/* If the type is unknown, bail. */
if ( type > CPER_ARM_MAX_TYPE )
return ;
/*
* Vendor type errors have error information values that are vendor
* specific .
*/
if ( type = = CPER_ARM_VENDOR_ERROR )
return ;
if ( error_info & CPER_ARM_ERR_VALID_TRANSACTION_TYPE ) {
trans_type = ( ( error_info > > CPER_ARM_ERR_TRANSACTION_SHIFT )
& CPER_ARM_ERR_TRANSACTION_MASK ) ;
if ( trans_type < ARRAY_SIZE ( arm_err_trans_type_strs ) ) {
printk ( " %stransaction type: %s \n " , pfx ,
arm_err_trans_type_strs [ trans_type ] ) ;
}
}
if ( error_info & CPER_ARM_ERR_VALID_OPERATION_TYPE ) {
op_type = ( ( error_info > > CPER_ARM_ERR_OPERATION_SHIFT )
& CPER_ARM_ERR_OPERATION_MASK ) ;
switch ( type ) {
case CPER_ARM_CACHE_ERROR :
if ( op_type < ARRAY_SIZE ( arm_cache_err_op_strs ) ) {
printk ( " %soperation type: %s \n " , pfx ,
arm_cache_err_op_strs [ op_type ] ) ;
}
break ;
case CPER_ARM_TLB_ERROR :
if ( op_type < ARRAY_SIZE ( arm_tlb_err_op_strs ) ) {
printk ( " %soperation type: %s \n " , pfx ,
arm_tlb_err_op_strs [ op_type ] ) ;
}
break ;
case CPER_ARM_BUS_ERROR :
if ( op_type < ARRAY_SIZE ( arm_bus_err_op_strs ) ) {
printk ( " %soperation type: %s \n " , pfx ,
arm_bus_err_op_strs [ op_type ] ) ;
}
break ;
}
}
if ( error_info & CPER_ARM_ERR_VALID_LEVEL ) {
level = ( ( error_info > > CPER_ARM_ERR_LEVEL_SHIFT )
& CPER_ARM_ERR_LEVEL_MASK ) ;
switch ( type ) {
case CPER_ARM_CACHE_ERROR :
printk ( " %scache level: %d \n " , pfx , level ) ;
break ;
case CPER_ARM_TLB_ERROR :
printk ( " %sTLB level: %d \n " , pfx , level ) ;
break ;
case CPER_ARM_BUS_ERROR :
printk ( " %saffinity level at which the bus error occurred: %d \n " ,
pfx , level ) ;
break ;
}
}
if ( error_info & CPER_ARM_ERR_VALID_PROC_CONTEXT_CORRUPT ) {
proc_context_corrupt = ( ( error_info > > CPER_ARM_ERR_PC_CORRUPT_SHIFT )
& CPER_ARM_ERR_PC_CORRUPT_MASK ) ;
if ( proc_context_corrupt )
printk ( " %sprocessor context corrupted \n " , pfx ) ;
else
printk ( " %sprocessor context not corrupted \n " , pfx ) ;
}
if ( error_info & CPER_ARM_ERR_VALID_CORRECTED ) {
corrected = ( ( error_info > > CPER_ARM_ERR_CORRECTED_SHIFT )
& CPER_ARM_ERR_CORRECTED_MASK ) ;
if ( corrected )
printk ( " %sthe error has been corrected \n " , pfx ) ;
else
printk ( " %sthe error has not been corrected \n " , pfx ) ;
}
if ( error_info & CPER_ARM_ERR_VALID_PRECISE_PC ) {
precise_pc = ( ( error_info > > CPER_ARM_ERR_PRECISE_PC_SHIFT )
& CPER_ARM_ERR_PRECISE_PC_MASK ) ;
if ( precise_pc )
printk ( " %sPC is precise \n " , pfx ) ;
else
printk ( " %sPC is imprecise \n " , pfx ) ;
}
if ( error_info & CPER_ARM_ERR_VALID_RESTARTABLE_PC ) {
restartable_pc = ( ( error_info > > CPER_ARM_ERR_RESTARTABLE_PC_SHIFT )
& CPER_ARM_ERR_RESTARTABLE_PC_MASK ) ;
if ( restartable_pc )
printk ( " %sProgram execution can be restarted reliably at the PC associated with the error. \n " , pfx ) ;
}
/* The rest of the fields are specific to bus errors */
if ( type ! = CPER_ARM_BUS_ERROR )
return ;
if ( error_info & CPER_ARM_ERR_VALID_PARTICIPATION_TYPE ) {
participation_type = ( ( error_info > > CPER_ARM_ERR_PARTICIPATION_TYPE_SHIFT )
& CPER_ARM_ERR_PARTICIPATION_TYPE_MASK ) ;
if ( participation_type < ARRAY_SIZE ( arm_bus_err_part_type_strs ) ) {
printk ( " %sparticipation type: %s \n " , pfx ,
arm_bus_err_part_type_strs [ participation_type ] ) ;
}
}
if ( error_info & CPER_ARM_ERR_VALID_TIME_OUT ) {
time_out = ( ( error_info > > CPER_ARM_ERR_TIME_OUT_SHIFT )
& CPER_ARM_ERR_TIME_OUT_MASK ) ;
if ( time_out )
printk ( " %srequest timed out \n " , pfx ) ;
}
if ( error_info & CPER_ARM_ERR_VALID_ADDRESS_SPACE ) {
address_space = ( ( error_info > > CPER_ARM_ERR_ADDRESS_SPACE_SHIFT )
& CPER_ARM_ERR_ADDRESS_SPACE_MASK ) ;
if ( address_space < ARRAY_SIZE ( arm_bus_err_addr_space_strs ) ) {
printk ( " %saddress space: %s \n " , pfx ,
arm_bus_err_addr_space_strs [ address_space ] ) ;
}
}
if ( error_info & CPER_ARM_ERR_VALID_MEM_ATTRIBUTES ) {
mem_attributes = ( ( error_info > > CPER_ARM_ERR_MEM_ATTRIBUTES_SHIFT )
& CPER_ARM_ERR_MEM_ATTRIBUTES_MASK ) ;
printk ( " %smemory access attributes:0x%x \n " , pfx , mem_attributes ) ;
}
if ( error_info & CPER_ARM_ERR_VALID_ACCESS_MODE ) {
access_mode = ( ( error_info > > CPER_ARM_ERR_ACCESS_MODE_SHIFT )
& CPER_ARM_ERR_ACCESS_MODE_MASK ) ;
if ( access_mode )
printk ( " %saccess mode: normal \n " , pfx ) ;
else
printk ( " %saccess mode: secure \n " , pfx ) ;
}
}
2018-01-02 21:10:41 +03:00
void cper_print_proc_arm ( const char * pfx ,
const struct cper_sec_proc_arm * proc )
{
int i , len , max_ctx_type ;
struct cper_arm_err_info * err_info ;
struct cper_arm_ctx_info * ctx_info ;
2018-01-02 21:10:42 +03:00
char newpfx [ 64 ] , infopfx [ 64 ] ;
2018-01-02 21:10:41 +03:00
printk ( " %sMIDR: 0x%016llx \n " , pfx , proc - > midr ) ;
len = proc - > section_length - ( sizeof ( * proc ) +
proc - > err_info_num * ( sizeof ( * err_info ) ) ) ;
if ( len < 0 ) {
printk ( " %ssection length: %d \n " , pfx , proc - > section_length ) ;
printk ( " %ssection length is too small \n " , pfx ) ;
printk ( " %sfirmware-generated error record is incorrect \n " , pfx ) ;
printk ( " %sERR_INFO_NUM is %d \n " , pfx , proc - > err_info_num ) ;
return ;
}
if ( proc - > validation_bits & CPER_ARM_VALID_MPIDR )
printk ( " %sMultiprocessor Affinity Register (MPIDR): 0x%016llx \n " ,
pfx , proc - > mpidr ) ;
if ( proc - > validation_bits & CPER_ARM_VALID_AFFINITY_LEVEL )
printk ( " %serror affinity level: %d \n " , pfx ,
proc - > affinity_level ) ;
if ( proc - > validation_bits & CPER_ARM_VALID_RUNNING_STATE ) {
printk ( " %srunning state: 0x%x \n " , pfx , proc - > running_state ) ;
printk ( " %sPower State Coordination Interface state: %d \n " ,
pfx , proc - > psci_state ) ;
}
2018-05-04 08:59:48 +03:00
snprintf ( newpfx , sizeof ( newpfx ) , " %s " , pfx ) ;
2018-01-02 21:10:41 +03:00
err_info = ( struct cper_arm_err_info * ) ( proc + 1 ) ;
for ( i = 0 ; i < proc - > err_info_num ; i + + ) {
printk ( " %sError info structure %d: \n " , pfx , i ) ;
printk ( " %snum errors: %d \n " , pfx , err_info - > multiple_error + 1 ) ;
if ( err_info - > validation_bits & CPER_ARM_INFO_VALID_FLAGS ) {
if ( err_info - > flags & CPER_ARM_INFO_FLAGS_FIRST )
printk ( " %sfirst error captured \n " , newpfx ) ;
if ( err_info - > flags & CPER_ARM_INFO_FLAGS_LAST )
printk ( " %slast error captured \n " , newpfx ) ;
if ( err_info - > flags & CPER_ARM_INFO_FLAGS_PROPAGATED )
printk ( " %spropagated error captured \n " ,
newpfx ) ;
if ( err_info - > flags & CPER_ARM_INFO_FLAGS_OVERFLOW )
printk ( " %soverflow occurred, error info is incomplete \n " ,
newpfx ) ;
}
printk ( " %serror_type: %d, %s \n " , newpfx , err_info - > type ,
err_info - > type < ARRAY_SIZE ( cper_proc_error_type_strs ) ?
cper_proc_error_type_strs [ err_info - > type ] : " unknown " ) ;
2018-01-02 21:10:42 +03:00
if ( err_info - > validation_bits & CPER_ARM_INFO_VALID_ERR_INFO ) {
2018-01-02 21:10:41 +03:00
printk ( " %serror_info: 0x%016llx \n " , newpfx ,
err_info - > error_info ) ;
2018-05-04 08:59:48 +03:00
snprintf ( infopfx , sizeof ( infopfx ) , " %s " , newpfx ) ;
2018-01-02 21:10:42 +03:00
cper_print_arm_err_info ( infopfx , err_info - > type ,
err_info - > error_info ) ;
}
2018-01-02 21:10:41 +03:00
if ( err_info - > validation_bits & CPER_ARM_INFO_VALID_VIRT_ADDR )
printk ( " %svirtual fault address: 0x%016llx \n " ,
newpfx , err_info - > virt_fault_addr ) ;
if ( err_info - > validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR )
printk ( " %sphysical fault address: 0x%016llx \n " ,
newpfx , err_info - > physical_fault_addr ) ;
err_info + = 1 ;
}
ctx_info = ( struct cper_arm_ctx_info * ) err_info ;
max_ctx_type = ARRAY_SIZE ( arm_reg_ctx_strs ) - 1 ;
for ( i = 0 ; i < proc - > context_info_num ; i + + ) {
int size = sizeof ( * ctx_info ) + ctx_info - > size ;
printk ( " %sContext info structure %d: \n " , pfx , i ) ;
if ( len < size ) {
printk ( " %ssection length is too small \n " , newpfx ) ;
printk ( " %sfirmware-generated error record is incorrect \n " , pfx ) ;
return ;
}
if ( ctx_info - > type > max_ctx_type ) {
printk ( " %sInvalid context type: %d (max: %d) \n " ,
newpfx , ctx_info - > type , max_ctx_type ) ;
return ;
}
printk ( " %sregister context type: %s \n " , newpfx ,
arm_reg_ctx_strs [ ctx_info - > type ] ) ;
print_hex_dump ( newpfx , " " , DUMP_PREFIX_OFFSET , 16 , 4 ,
( ctx_info + 1 ) , ctx_info - > size , 0 ) ;
len - = size ;
ctx_info = ( struct cper_arm_ctx_info * ) ( ( long ) ctx_info + size ) ;
}
if ( len > 0 ) {
printk ( " %sVendor specific error info has %u bytes: \n " , pfx ,
len ) ;
print_hex_dump ( newpfx , " " , DUMP_PREFIX_OFFSET , 16 , 4 , ctx_info ,
len , true ) ;
}
}