2009-06-25 21:32:38 +04:00
# include <linux/module.h>
2010-08-18 17:11:35 +04:00
# include <linux/slab.h>
2010-09-27 17:30:39 +04:00
# include "mce_amd.h"
2009-05-06 19:57:20 +04:00
2010-08-18 17:11:35 +04:00
static struct amd_decoder_ops * fam_ops ;
2010-09-17 21:11:47 +04:00
static u8 xec_mask = 0xf ;
2010-08-31 20:28:08 +04:00
static u8 nb_err_cpumask = 0xf ;
2009-07-24 15:51:42 +04:00
static bool report_gart_errors ;
2011-08-24 20:44:22 +04:00
static void ( * nb_bus_decoder ) ( int node_id , struct mce * m ) ;
2009-07-24 15:51:42 +04:00
void amd_report_gart_errors ( bool v )
{
report_gart_errors = v ;
}
EXPORT_SYMBOL_GPL ( amd_report_gart_errors ) ;
2011-08-24 20:44:22 +04:00
void amd_register_ecc_decoder ( void ( * f ) ( int , struct mce * ) )
2009-07-24 15:51:42 +04:00
{
nb_bus_decoder = f ;
}
EXPORT_SYMBOL_GPL ( amd_register_ecc_decoder ) ;
2011-08-24 20:44:22 +04:00
void amd_unregister_ecc_decoder ( void ( * f ) ( int , struct mce * ) )
2009-07-24 15:51:42 +04:00
{
if ( nb_bus_decoder ) {
WARN_ON ( nb_bus_decoder ! = f ) ;
nb_bus_decoder = NULL ;
}
}
EXPORT_SYMBOL_GPL ( amd_unregister_ecc_decoder ) ;
2009-05-06 19:57:20 +04:00
/*
* string representation for the different MCA reported error types , see F3x48
* or MSR0000_0411 .
*/
2010-09-06 20:13:39 +04:00
/* transaction type */
2011-11-29 22:03:25 +04:00
const char * const tt_msgs [ ] = { " INSN " , " DATA " , " GEN " , " RESV " } ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( tt_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-09-06 20:13:39 +04:00
/* cache level */
2011-11-29 22:03:25 +04:00
const char * const ll_msgs [ ] = { " RESV " , " L1 " , " L2 " , " L3/GEN " } ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( ll_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-09-06 20:13:39 +04:00
/* memory transaction type */
2011-11-29 22:03:25 +04:00
const char * const rrrr_msgs [ ] = {
2010-09-06 20:13:39 +04:00
" GEN " , " RD " , " WR " , " DRD " , " DWR " , " IRD " , " PRF " , " EV " , " SNP "
2009-05-06 19:57:20 +04:00
} ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( rrrr_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-09-06 20:13:39 +04:00
/* participating processor */
2011-11-29 22:03:25 +04:00
const char * const pp_msgs [ ] = { " SRC " , " RES " , " OBS " , " GEN " } ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( pp_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-09-06 20:13:39 +04:00
/* request timeout */
2011-11-29 22:03:25 +04:00
const char * const to_msgs [ ] = { " no timeout " , " timed out " } ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( to_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-09-06 20:13:39 +04:00
/* memory or i/o */
2011-11-29 22:03:25 +04:00
const char * const ii_msgs [ ] = { " MEM " , " RESV " , " IO " , " GEN " } ;
2009-06-25 21:32:38 +04:00
EXPORT_SYMBOL_GPL ( ii_msgs ) ;
2009-05-06 19:57:20 +04:00
2010-11-08 17:03:35 +03:00
static const char * const f15h_ic_mce_desc [ ] = {
" UC during a demand linefill from L2 " ,
" Parity error during data load from IC " ,
" Parity error for IC valid bit " ,
" Main tag parity error " ,
" Parity error in prediction queue " ,
" PFB data/address parity error " ,
" Parity error in the branch status reg " ,
" PFB promotion address error " ,
" Tag error during probe/victimization " ,
" Parity error for IC probe tag valid bit " ,
" PFB non-cacheable bit parity error " ,
" PFB valid bit parity error " , /* xec = 0xd */
2011-11-21 22:45:34 +04:00
" Microcode Patch Buffer " , /* xec = 010 */
2010-11-08 17:03:35 +03:00
" uop queue " ,
" insn buffer " ,
" predecode buffer " ,
" fetch address FIFO "
} ;
2010-09-21 22:45:10 +04:00
static const char * const f15h_cu_mce_desc [ ] = {
" Fill ECC error on data fills " , /* xec = 0x4 */
" Fill parity error on insn fills " ,
" Prefetcher request FIFO parity error " ,
" PRQ address parity error " ,
" PRQ data parity error " ,
" WCC Tag ECC error " ,
" WCC Data ECC error " ,
" WCB Data parity error " ,
2011-11-23 17:50:44 +04:00
" VB Data ECC or parity error " ,
2010-09-21 22:45:10 +04:00
" L2 Tag ECC error " , /* xec = 0x10 */
" Hard L2 Tag ECC error " ,
" Multiple hits on L2 tag " ,
" XAB parity error " ,
" PRB address parity error "
} ;
2011-11-29 22:03:25 +04:00
static const char * const nb_mce_desc [ ] = {
2011-11-25 00:29:57 +04:00
" DRAM ECC error detected on the NB " ,
" CRC error detected on HT link " ,
" Link-defined sync error packets detected on HT link " ,
" HT Master abort " ,
" HT Target abort " ,
" Invalid GART PTE entry during GART table walk " ,
" Unsupported atomic RMW received from an IO link " ,
" Watchdog timeout due to lack of progress " ,
" DRAM ECC error detected on the NB " ,
" SVM DMA Exclusion Vector error " ,
" HT data error detected on link " ,
" Protocol error (link, L3, probe filter) " ,
" NB internal arrays parity error " ,
" DRAM addr/ctl signals parity error " ,
" IO link transmission error " ,
" L3 data cache ECC error " , /* xec = 0x1c */
" L3 cache tag error " ,
" L3 LRU parity bits error " ,
" ECC Error in the Probe Filter directory "
} ;
2010-09-22 17:28:59 +04:00
static const char * const fr_ex_mce_desc [ ] = {
" CPU Watchdog timer expire " ,
" Wakeup array dest tag " ,
" AG payload array " ,
" EX payload array " ,
" IDRF array " ,
" Retire dispatch queue " ,
" Mapper checkpoint array " ,
" Physical register file EX0 port " ,
" Physical register file EX1 port " ,
" Physical register file AG0 port " ,
" Physical register file AG1 port " ,
" Flag register file " ,
2011-11-25 18:42:59 +04:00
" DE error occurred "
2010-09-22 17:28:59 +04:00
} ;
2010-09-17 21:22:34 +04:00
static bool f12h_dc_mce ( u16 ec , u8 xec )
2009-07-28 15:50:43 +04:00
{
2010-08-18 17:11:35 +04:00
bool ret = false ;
2009-07-28 15:50:43 +04:00
2010-08-18 17:11:35 +04:00
if ( MEM_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
u8 ll = LL ( ec ) ;
2010-08-18 17:11:35 +04:00
ret = true ;
2009-07-28 15:50:43 +04:00
2010-08-18 17:11:35 +04:00
if ( ll = = LL_L2 )
pr_cont ( " during L1 linefill from L2. \n " ) ;
else if ( ll = = LL_L1 )
2010-09-22 18:08:37 +04:00
pr_cont ( " Data/Tag %s error. \n " , R4_MSG ( ec ) ) ;
2010-08-18 17:11:35 +04:00
else
ret = false ;
}
return ret ;
}
2009-07-28 15:50:43 +04:00
2010-09-17 21:22:34 +04:00
static bool f10h_dc_mce ( u16 ec , u8 xec )
2010-09-16 17:08:14 +04:00
{
2010-09-22 18:08:37 +04:00
if ( R4 ( ec ) = = R4_GEN & & LL ( ec ) = = LL_L1 ) {
2010-09-16 17:08:14 +04:00
pr_cont ( " during data scrub. \n " ) ;
return true ;
}
2010-09-17 21:22:34 +04:00
return f12h_dc_mce ( ec , xec ) ;
2010-09-16 17:08:14 +04:00
}
2010-09-17 21:22:34 +04:00
static bool k8_dc_mce ( u16 ec , u8 xec )
2010-08-18 17:11:35 +04:00
{
if ( BUS_ERROR ( ec ) ) {
pr_cont ( " during system linefill. \n " ) ;
return true ;
}
2009-07-28 15:50:43 +04:00
2010-09-17 21:22:34 +04:00
return f10h_dc_mce ( ec , xec ) ;
2010-08-18 17:11:35 +04:00
}
2010-09-17 21:22:34 +04:00
static bool f14h_dc_mce ( u16 ec , u8 xec )
2010-08-18 17:11:35 +04:00
{
2010-09-22 18:08:37 +04:00
u8 r4 = R4 ( ec ) ;
2010-08-18 17:11:35 +04:00
bool ret = true ;
if ( MEM_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
if ( TT ( ec ) ! = TT_DATA | | LL ( ec ) ! = LL_L1 )
2010-08-18 17:11:35 +04:00
return false ;
switch ( r4 ) {
case R4_DRD :
case R4_DWR :
pr_cont ( " Data/Tag parity error due to %s. \n " ,
( r4 = = R4_DRD ? " load/hw prf " : " store " ) ) ;
break ;
case R4_EVICT :
pr_cont ( " Copyback parity error on a tag miss. \n " ) ;
break ;
case R4_SNOOP :
pr_cont ( " Tag parity error during snoop. \n " ) ;
break ;
default :
ret = false ;
}
} else if ( BUS_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
if ( ( II ( ec ) ! = II_MEM & & II ( ec ) ! = II_IO ) | | LL ( ec ) ! = LL_LG )
2010-08-18 17:11:35 +04:00
return false ;
pr_cont ( " System read data error on a " ) ;
switch ( r4 ) {
case R4_RD :
pr_cont ( " TLB reload. \n " ) ;
break ;
case R4_DWR :
pr_cont ( " store. \n " ) ;
break ;
case R4_DRD :
pr_cont ( " load. \n " ) ;
break ;
default :
ret = false ;
}
} else {
ret = false ;
}
return ret ;
}
2010-09-17 21:22:34 +04:00
static bool f15h_dc_mce ( u16 ec , u8 xec )
{
bool ret = true ;
if ( MEM_ERROR ( ec ) ) {
switch ( xec ) {
case 0x0 :
pr_cont ( " Data Array access error. \n " ) ;
break ;
case 0x1 :
pr_cont ( " UC error during a linefill from L2/NB. \n " ) ;
break ;
case 0x2 :
case 0x11 :
pr_cont ( " STQ access error. \n " ) ;
break ;
case 0x3 :
pr_cont ( " SCB access error. \n " ) ;
break ;
case 0x10 :
pr_cont ( " Tag error. \n " ) ;
break ;
case 0x12 :
pr_cont ( " LDQ access error. \n " ) ;
break ;
default :
ret = false ;
}
} else if ( BUS_ERROR ( ec ) ) {
if ( ! xec )
2011-11-15 20:10:58 +04:00
pr_cont ( " System Read Data Error. \n " ) ;
2010-09-17 21:22:34 +04:00
else
2011-11-15 20:10:58 +04:00
pr_cont ( " Internal error condition type %d. \n " , xec ) ;
2010-09-17 21:22:34 +04:00
} else
ret = false ;
return ret ;
}
2010-08-18 17:11:35 +04:00
static void amd_decode_dc_mce ( struct mce * m )
{
2010-09-22 18:08:37 +04:00
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-08-18 17:11:35 +04:00
pr_emerg ( HW_ERR " Data Cache Error: " ) ;
/* TLB error signatures are the same across families */
if ( TLB_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
if ( TT ( ec ) = = TT_DATA ) {
2010-08-18 17:11:35 +04:00
pr_cont ( " %s TLB %s. \n " , LL_MSG ( ec ) ,
2010-09-17 21:22:34 +04:00
( ( xec = = 2 ) ? " locked miss "
: ( xec ? " multimatch " : " parity " ) ) ) ;
2010-08-18 17:11:35 +04:00
return ;
}
2010-09-17 21:22:34 +04:00
} else if ( fam_ops - > dc_mce ( ec , xec ) )
;
else
pr_emerg ( HW_ERR " Corrupted DC MCE info? \n " ) ;
2009-07-28 15:50:43 +04:00
}
2010-11-08 17:03:35 +03:00
static bool k8_ic_mce ( u16 ec , u8 xec )
2009-07-28 16:06:26 +04:00
{
2010-09-22 18:08:37 +04:00
u8 ll = LL ( ec ) ;
2010-08-26 21:05:49 +04:00
bool ret = true ;
2009-07-28 16:06:26 +04:00
2010-08-26 21:05:49 +04:00
if ( ! MEM_ERROR ( ec ) )
return false ;
2009-07-28 16:06:26 +04:00
2010-08-26 21:05:49 +04:00
if ( ll = = 0x2 )
pr_cont ( " during a linefill from L2. \n " ) ;
else if ( ll = = 0x1 ) {
2010-09-22 18:08:37 +04:00
switch ( R4 ( ec ) ) {
2010-08-26 21:05:49 +04:00
case R4_IRD :
pr_cont ( " Parity error during data load. \n " ) ;
break ;
2009-07-28 16:06:26 +04:00
2010-08-26 21:05:49 +04:00
case R4_EVICT :
pr_cont ( " Copyback Parity/Victim error. \n " ) ;
break ;
case R4_SNOOP :
pr_cont ( " Tag Snoop error. \n " ) ;
break ;
default :
ret = false ;
break ;
}
2009-07-28 16:06:26 +04:00
} else
2010-08-26 21:05:49 +04:00
ret = false ;
2009-07-28 16:06:26 +04:00
2010-08-26 21:05:49 +04:00
return ret ;
}
2010-11-08 17:03:35 +03:00
static bool f14h_ic_mce ( u16 ec , u8 xec )
2010-08-26 21:05:49 +04:00
{
2010-09-22 18:08:37 +04:00
u8 r4 = R4 ( ec ) ;
2010-08-26 21:05:49 +04:00
bool ret = true ;
2009-07-28 16:06:26 +04:00
2010-08-26 21:05:49 +04:00
if ( MEM_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
if ( TT ( ec ) ! = 0 | | LL ( ec ) ! = 1 )
2010-08-26 21:05:49 +04:00
ret = false ;
if ( r4 = = R4_IRD )
pr_cont ( " Data/tag array parity error for a tag hit. \n " ) ;
else if ( r4 = = R4_SNOOP )
pr_cont ( " Tag error during snoop/victimization. \n " ) ;
else
ret = false ;
}
return ret ;
}
2010-11-08 17:03:35 +03:00
static bool f15h_ic_mce ( u16 ec , u8 xec )
{
bool ret = true ;
if ( ! MEM_ERROR ( ec ) )
return false ;
switch ( xec ) {
case 0x0 . . . 0xa :
pr_cont ( " %s. \n " , f15h_ic_mce_desc [ xec ] ) ;
break ;
case 0xd :
pr_cont ( " %s. \n " , f15h_ic_mce_desc [ xec - 2 ] ) ;
break ;
2011-11-21 22:45:34 +04:00
case 0x10 :
pr_cont ( " %s. \n " , f15h_ic_mce_desc [ xec - 4 ] ) ;
break ;
case 0x11 . . . 0x14 :
2010-11-08 17:03:35 +03:00
pr_cont ( " Decoder %s parity error. \n " , f15h_ic_mce_desc [ xec - 4 ] ) ;
break ;
default :
ret = false ;
}
return ret ;
}
2010-08-26 21:05:49 +04:00
static void amd_decode_ic_mce ( struct mce * m )
{
2010-09-22 18:08:37 +04:00
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-08-26 21:05:49 +04:00
pr_emerg ( HW_ERR " Instruction Cache Error: " ) ;
if ( TLB_ERROR ( ec ) )
pr_cont ( " %s TLB %s. \n " , LL_MSG ( ec ) ,
( xec ? " multimatch " : " parity error " ) ) ;
else if ( BUS_ERROR ( ec ) ) {
2010-10-15 17:27:02 +04:00
bool k8 = ( boot_cpu_data . x86 = = 0xf & & ( m - > status & BIT_64 ( 58 ) ) ) ;
2010-08-26 21:05:49 +04:00
pr_cont ( " during %s. \n " , ( k8 ? " system linefill " : " NB data read " ) ) ;
2010-11-08 17:03:35 +03:00
} else if ( fam_ops - > ic_mce ( ec , xec ) )
2010-08-26 21:05:49 +04:00
;
else
pr_emerg ( HW_ERR " Corrupted IC MCE info? \n " ) ;
2009-07-28 16:06:26 +04:00
}
2010-09-01 16:45:20 +04:00
static void amd_decode_bu_mce ( struct mce * m )
2009-07-28 16:14:24 +04:00
{
2010-09-22 18:08:37 +04:00
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , xec_mask ) ;
2009-07-28 16:14:24 +04:00
2010-08-18 20:21:42 +04:00
pr_emerg ( HW_ERR " Bus Unit Error " ) ;
2009-07-28 16:14:24 +04:00
if ( xec = = 0x1 )
pr_cont ( " in the write data buffers. \n " ) ;
else if ( xec = = 0x3 )
pr_cont ( " in the victim data buffers. \n " ) ;
else if ( xec = = 0x2 & & MEM_ERROR ( ec ) )
2010-09-22 18:08:37 +04:00
pr_cont ( " : %s error in the L2 cache tags. \n " , R4_MSG ( ec ) ) ;
2009-07-28 16:14:24 +04:00
else if ( xec = = 0x0 ) {
if ( TLB_ERROR ( ec ) )
pr_cont ( " : %s error in a Page Descriptor Cache or "
" Guest TLB. \n " , TT_MSG ( ec ) ) ;
else if ( BUS_ERROR ( ec ) )
pr_cont ( " : %s/ECC error in data read from NB: %s. \n " ,
2010-09-22 18:08:37 +04:00
R4_MSG ( ec ) , PP_MSG ( ec ) ) ;
2009-07-28 16:14:24 +04:00
else if ( MEM_ERROR ( ec ) ) {
2010-09-22 18:08:37 +04:00
u8 r4 = R4 ( ec ) ;
2009-07-28 16:14:24 +04:00
2010-09-22 18:08:37 +04:00
if ( r4 > = 0x7 )
2009-07-28 16:14:24 +04:00
pr_cont ( " : %s error during data copyback. \n " ,
2010-09-22 18:08:37 +04:00
R4_MSG ( ec ) ) ;
else if ( r4 < = 0x1 )
2009-07-28 16:14:24 +04:00
pr_cont ( " : %s parity/ECC error during data "
2010-09-22 18:08:37 +04:00
" access from L2. \n " , R4_MSG ( ec ) ) ;
2009-07-28 16:14:24 +04:00
else
goto wrong_bu_mce ;
} else
goto wrong_bu_mce ;
} else
goto wrong_bu_mce ;
return ;
wrong_bu_mce :
2010-08-18 20:21:42 +04:00
pr_emerg ( HW_ERR " Corrupted BU MCE info? \n " ) ;
2009-07-28 16:14:24 +04:00
}
2010-09-21 22:45:10 +04:00
static void amd_decode_cu_mce ( struct mce * m )
{
2010-09-22 18:08:37 +04:00
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-09-21 22:45:10 +04:00
pr_emerg ( HW_ERR " Combined Unit Error: " ) ;
if ( TLB_ERROR ( ec ) ) {
if ( xec = = 0x0 )
pr_cont ( " Data parity TLB read error. \n " ) ;
else if ( xec = = 0x1 )
pr_cont ( " Poison data provided for TLB fill. \n " ) ;
else
goto wrong_cu_mce ;
} else if ( BUS_ERROR ( ec ) ) {
if ( xec > 2 )
goto wrong_cu_mce ;
pr_cont ( " Error during attempted NB data read. \n " ) ;
} else if ( MEM_ERROR ( ec ) ) {
switch ( xec ) {
case 0x4 . . . 0xc :
pr_cont ( " %s. \n " , f15h_cu_mce_desc [ xec - 0x4 ] ) ;
break ;
case 0x10 . . . 0x14 :
pr_cont ( " %s. \n " , f15h_cu_mce_desc [ xec - 0x7 ] ) ;
break ;
default :
goto wrong_cu_mce ;
}
}
return ;
wrong_cu_mce :
pr_emerg ( HW_ERR " Corrupted CU MCE info? \n " ) ;
}
2010-09-01 16:45:20 +04:00
static void amd_decode_ls_mce ( struct mce * m )
2009-07-28 16:17:30 +04:00
{
2010-09-22 18:08:37 +04:00
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-08-27 19:03:34 +04:00
2010-09-22 13:53:32 +04:00
if ( boot_cpu_data . x86 > = 0x14 ) {
2010-08-27 19:03:34 +04:00
pr_emerg ( " You shouldn't be seeing an LS MCE on this cpu family, "
" please report on LKML. \n " ) ;
return ;
}
2009-07-28 16:17:30 +04:00
2010-08-18 20:21:42 +04:00
pr_emerg ( HW_ERR " Load Store Error " ) ;
2009-07-28 16:17:30 +04:00
if ( xec = = 0x0 ) {
2010-09-22 18:08:37 +04:00
u8 r4 = R4 ( ec ) ;
2009-07-28 16:17:30 +04:00
2010-08-27 19:03:34 +04:00
if ( ! BUS_ERROR ( ec ) | | ( r4 ! = R4_DRD & & r4 ! = R4_DWR ) )
2009-07-28 16:17:30 +04:00
goto wrong_ls_mce ;
2010-09-22 18:08:37 +04:00
pr_cont ( " during %s. \n " , R4_MSG ( ec ) ) ;
2010-08-27 19:03:34 +04:00
} else
goto wrong_ls_mce ;
2009-07-28 16:17:30 +04:00
return ;
wrong_ls_mce :
2010-08-18 20:21:42 +04:00
pr_emerg ( HW_ERR " Corrupted LS MCE info? \n " ) ;
2009-07-28 16:17:30 +04:00
}
2011-11-25 00:29:57 +04:00
void amd_decode_nb_mce ( struct mce * m )
2010-08-31 20:28:08 +04:00
{
2011-11-25 00:29:57 +04:00
struct cpuinfo_x86 * c = & boot_cpu_data ;
int node_id = amd_get_nb_id ( m - > extcpu ) ;
u16 ec = EC ( m - > status ) ;
u8 xec = XEC ( m - > status , 0x1f ) ;
u8 offset = 0 ;
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
pr_emerg ( HW_ERR " Northbridge Error (node %d): " , node_id ) ;
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
switch ( xec ) {
case 0x0 . . . 0xe :
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
/* special handling for DRAM ECCs */
if ( xec = = 0x0 | | xec = = 0x8 ) {
/* no ECCs on F11h */
if ( c - > x86 = = 0x11 )
goto wrong_nb_mce ;
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
pr_cont ( " %s. \n " , nb_mce_desc [ xec ] ) ;
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
if ( nb_bus_decoder )
nb_bus_decoder ( node_id , m ) ;
return ;
}
2010-08-31 20:28:08 +04:00
break ;
case 0xf :
if ( TLB_ERROR ( ec ) )
pr_cont ( " GART Table Walk data error. \n " ) ;
else if ( BUS_ERROR ( ec ) )
pr_cont ( " DMA Exclusion Vector Table Walk error. \n " ) ;
else
2011-11-25 00:29:57 +04:00
goto wrong_nb_mce ;
return ;
2010-08-31 20:28:08 +04:00
2010-09-22 17:06:24 +04:00
case 0x19 :
if ( boot_cpu_data . x86 = = 0x15 )
pr_cont ( " Compute Unit Data Error. \n " ) ;
else
2011-11-25 00:29:57 +04:00
goto wrong_nb_mce ;
return ;
2010-09-22 17:06:24 +04:00
2010-08-31 20:28:08 +04:00
case 0x1c . . . 0x1f :
2011-11-25 00:29:57 +04:00
offset = 13 ;
2010-08-31 20:28:08 +04:00
break ;
default :
goto wrong_nb_mce ;
2011-11-25 00:29:57 +04:00
}
2010-08-31 20:28:08 +04:00
2011-11-25 00:29:57 +04:00
pr_cont ( " %s. \n " , nb_mce_desc [ xec - offset ] ) ;
2010-08-31 20:28:08 +04:00
return ;
wrong_nb_mce :
pr_emerg ( HW_ERR " Corrupted NB MCE info? \n " ) ;
2009-07-28 12:56:15 +04:00
}
EXPORT_SYMBOL_GPL ( amd_decode_nb_mce ) ;
2010-09-01 16:45:20 +04:00
static void amd_decode_fr_mce ( struct mce * m )
2009-07-28 16:20:46 +04:00
{
2010-09-22 17:28:59 +04:00
struct cpuinfo_x86 * c = & boot_cpu_data ;
2010-09-22 18:08:37 +04:00
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-09-22 17:28:59 +04:00
if ( c - > x86 = = 0xf | | c - > x86 = = 0x11 )
2010-08-31 20:38:24 +04:00
goto wrong_fr_mce ;
2010-09-22 17:28:59 +04:00
pr_emerg ( HW_ERR " %s Error: " ,
( c - > x86 = = 0x15 ? " Execution Unit " : " FIROB " ) ) ;
if ( xec = = 0x0 | | xec = = 0xc )
pr_cont ( " %s. \n " , fr_ex_mce_desc [ xec ] ) ;
else if ( xec < 0xd )
pr_cont ( " %s parity error. \n " , fr_ex_mce_desc [ xec ] ) ;
else
goto wrong_fr_mce ;
return ;
2010-08-31 20:38:24 +04:00
wrong_fr_mce :
pr_emerg ( HW_ERR " Corrupted FR MCE info? \n " ) ;
2009-07-28 16:20:46 +04:00
}
2010-09-22 17:37:58 +04:00
static void amd_decode_fp_mce ( struct mce * m )
{
2010-09-22 18:08:37 +04:00
u8 xec = XEC ( m - > status , xec_mask ) ;
2010-09-22 17:37:58 +04:00
pr_emerg ( HW_ERR " Floating Point Unit Error: " ) ;
switch ( xec ) {
case 0x1 :
pr_cont ( " Free List " ) ;
break ;
case 0x2 :
pr_cont ( " Physical Register File " ) ;
break ;
case 0x3 :
pr_cont ( " Retire Queue " ) ;
break ;
case 0x4 :
pr_cont ( " Scheduler table " ) ;
break ;
case 0x5 :
pr_cont ( " Status Register File " ) ;
break ;
default :
goto wrong_fp_mce ;
break ;
}
pr_cont ( " parity error. \n " ) ;
return ;
wrong_fp_mce :
pr_emerg ( HW_ERR " Corrupted FP MCE info? \n " ) ;
}
2010-09-06 20:13:39 +04:00
static inline void amd_decode_err_code ( u16 ec )
2009-07-28 12:56:15 +04:00
{
2010-09-22 19:42:27 +04:00
pr_emerg ( HW_ERR " cache level: %s " , LL_MSG ( ec ) ) ;
if ( BUS_ERROR ( ec ) )
pr_cont ( " , mem/io: %s " , II_MSG ( ec ) ) ;
else
pr_cont ( " , tx: %s " , TT_MSG ( ec ) ) ;
if ( MEM_ERROR ( ec ) | | BUS_ERROR ( ec ) ) {
pr_cont ( " , mem-tx: %s " , R4_MSG ( ec ) ) ;
if ( BUS_ERROR ( ec ) )
pr_cont ( " , part-proc: %s (%s) " , PP_MSG ( ec ) , TO_MSG ( ec ) ) ;
}
pr_cont ( " \n " ) ;
2009-07-24 15:51:42 +04:00
}
2010-08-31 20:28:08 +04:00
/*
* Filter out unwanted MCE signatures here .
*/
static bool amd_filter_mce ( struct mce * m )
{
u8 xec = ( m - > status > > 16 ) & 0x1f ;
/*
* NB GART TLB error reporting is disabled by default .
*/
if ( m - > bank = = 4 & & xec = = 0x5 & & ! report_gart_errors )
return true ;
return false ;
}
2010-09-02 20:33:24 +04:00
int amd_decode_mce ( struct notifier_block * nb , unsigned long val , void * data )
2009-07-24 15:51:42 +04:00
{
2009-10-07 15:20:38 +04:00
struct mce * m = ( struct mce * ) data ;
2010-09-22 19:42:27 +04:00
struct cpuinfo_x86 * c = & boot_cpu_data ;
2011-08-24 20:44:22 +04:00
int ecc ;
2009-07-24 15:51:42 +04:00
2010-08-31 20:28:08 +04:00
if ( amd_filter_mce ( m ) )
return NOTIFY_STOP ;
2011-09-30 18:34:44 +04:00
pr_emerg ( HW_ERR " CPU:%d \t MC%d_STATUS[%s|%s|%s|%s|%s " ,
2011-08-04 21:25:24 +04:00
m - > extcpu , m - > bank ,
2010-09-22 19:42:27 +04:00
( ( m - > status & MCI_STATUS_OVER ) ? " Over " : " - " ) ,
( ( m - > status & MCI_STATUS_UC ) ? " UE " : " CE " ) ,
( ( m - > status & MCI_STATUS_MISCV ) ? " MiscV " : " - " ) ,
( ( m - > status & MCI_STATUS_PCC ) ? " PCC " : " - " ) ,
( ( m - > status & MCI_STATUS_ADDRV ) ? " AddrV " : " - " ) ) ;
2009-07-24 15:51:42 +04:00
2010-09-22 19:42:27 +04:00
if ( c - > x86 = = 0x15 )
pr_cont ( " |%s|%s " ,
2010-11-13 19:44:26 +03:00
( ( m - > status & BIT_64 ( 44 ) ) ? " Deferred " : " - " ) ,
( ( m - > status & BIT_64 ( 43 ) ) ? " Poison " : " - " ) ) ;
2009-07-24 15:51:42 +04:00
2009-07-27 18:21:14 +04:00
/* do the two bits[14:13] together */
2010-04-30 17:19:02 +04:00
ecc = ( m - > status > > 45 ) & 0x3 ;
2009-07-27 18:21:14 +04:00
if ( ecc )
2010-09-22 19:42:27 +04:00
pr_cont ( " |%sECC " , ( ( ecc = = 2 ) ? " C " : " U " ) ) ;
pr_cont ( " ]: 0x%016llx \n " , m - > status ) ;
2009-07-27 18:21:14 +04:00
2011-09-30 18:34:44 +04:00
if ( m - > status & MCI_STATUS_ADDRV )
pr_emerg ( HW_ERR " \t MC%d_ADDR: 0x%016llx \n " , m - > bank , m - > addr ) ;
2009-07-27 18:21:14 +04:00
2009-07-28 15:50:43 +04:00
switch ( m - > bank ) {
case 0 :
2010-09-01 16:45:20 +04:00
amd_decode_dc_mce ( m ) ;
2009-07-28 15:50:43 +04:00
break ;
2009-07-28 12:56:15 +04:00
2009-07-28 16:06:26 +04:00
case 1 :
2010-09-01 16:45:20 +04:00
amd_decode_ic_mce ( m ) ;
2009-07-28 16:06:26 +04:00
break ;
2009-07-28 16:14:24 +04:00
case 2 :
2010-09-22 19:42:27 +04:00
if ( c - > x86 = = 0x15 )
2010-09-21 22:45:10 +04:00
amd_decode_cu_mce ( m ) ;
else
amd_decode_bu_mce ( m ) ;
2009-07-28 16:14:24 +04:00
break ;
2009-07-28 16:17:30 +04:00
case 3 :
2010-09-01 16:45:20 +04:00
amd_decode_ls_mce ( m ) ;
2009-07-28 16:17:30 +04:00
break ;
2009-07-28 15:50:43 +04:00
case 4 :
2011-08-24 20:44:22 +04:00
amd_decode_nb_mce ( m ) ;
2009-07-28 15:50:43 +04:00
break ;
2009-07-28 16:20:46 +04:00
case 5 :
2010-09-01 16:45:20 +04:00
amd_decode_fr_mce ( m ) ;
2009-07-28 16:20:46 +04:00
break ;
2010-09-22 17:37:58 +04:00
case 6 :
amd_decode_fp_mce ( m ) ;
break ;
2009-07-28 15:50:43 +04:00
default :
break ;
2009-07-27 18:21:14 +04:00
}
2009-07-28 15:50:43 +04:00
amd_decode_err_code ( m - > status & 0xffff ) ;
2009-10-07 15:20:38 +04:00
return NOTIFY_STOP ;
2009-07-24 15:51:42 +04:00
}
2010-09-02 20:33:24 +04:00
EXPORT_SYMBOL_GPL ( amd_decode_mce ) ;
2009-10-01 18:14:32 +04:00
2009-10-07 15:20:38 +04:00
static struct notifier_block amd_mce_dec_nb = {
. notifier_call = amd_decode_mce ,
} ;
2009-10-01 18:14:32 +04:00
static int __init mce_amd_init ( void )
{
2010-09-22 19:44:51 +04:00
struct cpuinfo_x86 * c = & boot_cpu_data ;
if ( c - > x86_vendor ! = X86_VENDOR_AMD )
2010-08-06 20:55:45 +04:00
return 0 ;
2012-04-04 16:21:02 +04:00
if ( c - > x86 < 0xf | | c - > x86 > 0x15 )
2010-08-06 20:55:45 +04:00
return 0 ;
2010-08-18 17:11:35 +04:00
fam_ops = kzalloc ( sizeof ( struct amd_decoder_ops ) , GFP_KERNEL ) ;
if ( ! fam_ops )
return - ENOMEM ;
2010-09-22 19:44:51 +04:00
switch ( c - > x86 ) {
2010-08-18 17:11:35 +04:00
case 0xf :
fam_ops - > dc_mce = k8_dc_mce ;
2010-08-26 21:05:49 +04:00
fam_ops - > ic_mce = k8_ic_mce ;
2010-08-18 17:11:35 +04:00
break ;
case 0x10 :
fam_ops - > dc_mce = f10h_dc_mce ;
2010-08-26 21:05:49 +04:00
fam_ops - > ic_mce = k8_ic_mce ;
2010-08-18 17:11:35 +04:00
break ;
2010-10-05 21:07:16 +04:00
case 0x11 :
fam_ops - > dc_mce = k8_dc_mce ;
fam_ops - > ic_mce = k8_ic_mce ;
break ;
2010-09-16 17:08:14 +04:00
case 0x12 :
fam_ops - > dc_mce = f12h_dc_mce ;
2010-09-16 18:45:22 +04:00
fam_ops - > ic_mce = k8_ic_mce ;
2010-09-16 17:08:14 +04:00
break ;
2010-08-18 17:11:35 +04:00
case 0x14 :
2010-08-31 20:28:08 +04:00
nb_err_cpumask = 0x3 ;
2010-08-18 17:11:35 +04:00
fam_ops - > dc_mce = f14h_dc_mce ;
2010-08-26 21:05:49 +04:00
fam_ops - > ic_mce = f14h_ic_mce ;
2010-08-18 17:11:35 +04:00
break ;
2010-09-17 21:11:47 +04:00
case 0x15 :
xec_mask = 0x1f ;
2010-09-17 21:22:34 +04:00
fam_ops - > dc_mce = f15h_dc_mce ;
2010-11-08 17:03:35 +03:00
fam_ops - > ic_mce = f15h_ic_mce ;
2010-09-17 21:11:47 +04:00
break ;
2010-08-18 17:11:35 +04:00
default :
2012-04-04 16:21:02 +04:00
printk ( KERN_WARNING " Huh? What family is it: 0x%x?! \n " , c - > x86 ) ;
2010-08-18 17:11:35 +04:00
kfree ( fam_ops ) ;
return - EINVAL ;
}
2010-09-06 17:05:45 +04:00
pr_info ( " MCE: In-kernel MCE decoding enabled. \n " ) ;
2011-12-04 18:12:09 +04:00
mce_register_decode_chain ( & amd_mce_dec_nb ) ;
2009-10-01 18:14:32 +04:00
return 0 ;
}
early_initcall ( mce_amd_init ) ;
2009-10-02 17:31:48 +04:00
# ifdef MODULE
static void __exit mce_amd_exit ( void )
{
2011-12-04 18:12:09 +04:00
mce_unregister_decode_chain ( & amd_mce_dec_nb ) ;
2010-08-18 17:11:35 +04:00
kfree ( fam_ops ) ;
2009-10-02 17:31:48 +04:00
}
MODULE_DESCRIPTION ( " AMD MCE decoder " ) ;
MODULE_ALIAS ( " edac-mce-amd " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_exit ( mce_amd_exit ) ;
# endif