2010-09-02 18:33:24 +02:00
/*
2014-11-22 11:09:12 +01:00
* A simple MCE injection facility for testing different aspects of the RAS
* code . This driver should be built as module so that it can be loaded
* on production kernels for testing purposes .
2010-09-02 18:33:24 +02:00
*
* This file may be distributed under the terms of the GNU General Public
* License version 2.
*
2014-11-22 11:09:12 +01:00
* Copyright ( c ) 2010 - 14 : Borislav Petkov < bp @ alien8 . de >
2010-09-02 18:33:24 +02:00
* Advanced Micro Devices Inc .
*/
# include <linux/kobject.h>
2014-11-22 11:09:12 +01:00
# include <linux/debugfs.h>
2012-01-22 11:23:42 -05:00
# include <linux/device.h>
2011-07-03 13:37:56 -04:00
# include <linux/module.h>
2014-11-22 11:47:07 +01:00
# include <linux/cpu.h>
2010-09-02 18:33:24 +02:00
# include <asm/mce.h>
2010-09-27 15:30:39 +02:00
# include "mce_amd.h"
2010-09-02 18:33:24 +02:00
/*
* Collect all the MCi_XXX settings
*/
static struct mce i_mce ;
2014-11-22 11:09:12 +01:00
static struct dentry * dfs_inj ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
# define MCE_INJECT_SET(reg) \
static int inj_ # # reg # # _set ( void * data , u64 val ) \
2010-09-02 18:33:24 +02:00
{ \
2014-11-22 11:09:12 +01:00
struct mce * m = ( struct mce * ) data ; \
2010-09-02 18:33:24 +02:00
\
2014-11-22 11:09:12 +01:00
m - > reg = val ; \
return 0 ; \
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
MCE_INJECT_SET ( status ) ;
MCE_INJECT_SET ( misc ) ;
MCE_INJECT_SET ( addr ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
# define MCE_INJECT_GET(reg) \
static int inj_ # # reg # # _get ( void * data , u64 * val ) \
2010-09-02 18:33:24 +02:00
{ \
2014-11-22 11:09:12 +01:00
struct mce * m = ( struct mce * ) data ; \
\
* val = m - > reg ; \
return 0 ; \
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
MCE_INJECT_GET ( status ) ;
MCE_INJECT_GET ( misc ) ;
MCE_INJECT_GET ( addr ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
DEFINE_SIMPLE_ATTRIBUTE ( status_fops , inj_status_get , inj_status_set , " %llx \n " ) ;
DEFINE_SIMPLE_ATTRIBUTE ( misc_fops , inj_misc_get , inj_misc_set , " %llx \n " ) ;
DEFINE_SIMPLE_ATTRIBUTE ( addr_fops , inj_addr_get , inj_addr_set , " %llx \n " ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:22:35 +01:00
/*
* Caller needs to be make sure this cpu doesn ' t disappear
* from under us , i . e . : get_cpu / put_cpu .
*/
static int toggle_hw_mce_inject ( unsigned int cpu , bool enable )
{
u32 l , h ;
int err ;
err = rdmsr_on_cpu ( cpu , MSR_K7_HWCR , & l , & h ) ;
if ( err ) {
pr_err ( " %s: error reading HWCR \n " , __func__ ) ;
return err ;
}
enable ? ( l | = BIT ( 18 ) ) : ( l & = ~ BIT ( 18 ) ) ;
err = wrmsr_on_cpu ( cpu , MSR_K7_HWCR , l , h ) ;
if ( err )
pr_err ( " %s: error writing HWCR \n " , __func__ ) ;
return err ;
}
2014-11-22 11:35:26 +01:00
static int flags_get ( void * data , u64 * val )
{
struct mce * m = ( struct mce * ) data ;
* val = m - > inject_flags ;
return 0 ;
}
static int flags_set ( void * data , u64 val )
{
struct mce * m = ( struct mce * ) data ;
m - > inject_flags = ( u8 ) val ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( flags_fops , flags_get , flags_set , " %llu \n " ) ;
/*
* On which CPU to inject ?
*/
MCE_INJECT_GET ( extcpu ) ;
static int inj_extcpu_set ( void * data , u64 val )
{
struct mce * m = ( struct mce * ) data ;
if ( val > = nr_cpu_ids | | ! cpu_online ( val ) ) {
pr_err ( " %s: Invalid CPU: %llu \n " , __func__ , val ) ;
return - EINVAL ;
}
m - > extcpu = val ;
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( extcpu_fops , inj_extcpu_get , inj_extcpu_set , " %llu \n " ) ;
2014-11-22 11:47:07 +01:00
static void trigger_mce ( void * info )
{
asm volatile ( " int $18 " ) ;
}
static void do_inject ( void )
{
u64 mcg_status = 0 ;
unsigned int cpu = i_mce . extcpu ;
u8 b = i_mce . bank ;
if ( ! ( i_mce . inject_flags & MCJ_EXCEPTION ) ) {
amd_decode_mce ( NULL , 0 , & i_mce ) ;
return ;
}
get_online_cpus ( ) ;
if ( ! cpu_online ( cpu ) )
goto err ;
/* prep MCE global settings for the injection */
mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV ;
if ( ! ( i_mce . status & MCI_STATUS_PCC ) )
mcg_status | = MCG_STATUS_RIPV ;
toggle_hw_mce_inject ( cpu , true ) ;
wrmsr_on_cpu ( cpu , MSR_IA32_MCG_STATUS ,
( u32 ) mcg_status , ( u32 ) ( mcg_status > > 32 ) ) ;
wrmsr_on_cpu ( cpu , MSR_IA32_MCx_STATUS ( b ) ,
( u32 ) i_mce . status , ( u32 ) ( i_mce . status > > 32 ) ) ;
wrmsr_on_cpu ( cpu , MSR_IA32_MCx_ADDR ( b ) ,
( u32 ) i_mce . addr , ( u32 ) ( i_mce . addr > > 32 ) ) ;
wrmsr_on_cpu ( cpu , MSR_IA32_MCx_MISC ( b ) ,
( u32 ) i_mce . misc , ( u32 ) ( i_mce . misc > > 32 ) ) ;
toggle_hw_mce_inject ( cpu , false ) ;
smp_call_function_single ( cpu , trigger_mce , NULL , 0 ) ;
err :
put_online_cpus ( ) ;
}
2010-09-02 18:33:24 +02:00
/*
* This denotes into which bank we ' re injecting and triggers
* the injection , at the same time .
*/
2014-11-22 11:09:12 +01:00
static int inj_bank_set ( void * data , u64 val )
2010-09-02 18:33:24 +02:00
{
2014-11-22 11:09:12 +01:00
struct mce * m = ( struct mce * ) data ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
if ( val > 5 ) {
if ( boot_cpu_data . x86 ! = 0x15 | | val > 6 ) {
pr_err ( " Non-existent MCE bank: %llu \n " , val ) ;
2010-11-09 19:41:49 +01:00
return - EINVAL ;
}
2014-11-22 11:09:12 +01:00
}
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
m - > bank = val ;
2014-11-22 11:47:07 +01:00
do_inject ( ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
return 0 ;
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
static int inj_bank_get ( void * data , u64 * val )
2010-09-02 18:33:24 +02:00
{
2014-11-22 11:09:12 +01:00
struct mce * m = ( struct mce * ) data ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
* val = m - > bank ;
return 0 ;
}
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
DEFINE_SIMPLE_ATTRIBUTE ( bank_fops , inj_bank_get , inj_bank_set , " %llu \n " ) ;
struct dfs_node {
char * name ;
struct dentry * d ;
const struct file_operations * fops ;
} dfs_fls [ ] = {
{ . name = " status " , . fops = & status_fops } ,
{ . name = " misc " , . fops = & misc_fops } ,
{ . name = " addr " , . fops = & addr_fops } ,
{ . name = " bank " , . fops = & bank_fops } ,
2014-11-22 11:35:26 +01:00
{ . name = " flags " , . fops = & flags_fops } ,
{ . name = " cpu " , . fops = & extcpu_fops } ,
2010-09-02 18:33:24 +02:00
} ;
2014-11-22 11:09:12 +01:00
static int __init init_mce_inject ( void )
2010-09-02 18:33:24 +02:00
{
2014-11-22 11:09:12 +01:00
int i ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
dfs_inj = debugfs_create_dir ( " mce-inject " , NULL ) ;
if ( ! dfs_inj )
2010-09-02 18:33:24 +02:00
return - EINVAL ;
2014-11-22 11:09:12 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( dfs_fls ) ; i + + ) {
dfs_fls [ i ] . d = debugfs_create_file ( dfs_fls [ i ] . name ,
S_IRUSR | S_IWUSR ,
dfs_inj ,
& i_mce ,
dfs_fls [ i ] . fops ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
if ( ! dfs_fls [ i ] . d )
goto err_dfs_add ;
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
2010-09-02 18:33:24 +02:00
return 0 ;
2014-11-22 11:09:12 +01:00
err_dfs_add :
2010-11-18 22:05:43 -05:00
while ( - - i > = 0 )
2014-11-22 11:09:12 +01:00
debugfs_remove ( dfs_fls [ i ] . d ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
debugfs_remove ( dfs_inj ) ;
dfs_inj = NULL ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
return - ENOMEM ;
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
static void __exit exit_mce_inject ( void )
2010-09-02 18:33:24 +02:00
{
int i ;
2014-11-22 11:09:12 +01:00
for ( i = 0 ; i < ARRAY_SIZE ( dfs_fls ) ; i + + )
debugfs_remove ( dfs_fls [ i ] . d ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
memset ( & dfs_fls , 0 , sizeof ( dfs_fls ) ) ;
2010-09-02 18:33:24 +02:00
2014-11-22 11:09:12 +01:00
debugfs_remove ( dfs_inj ) ;
dfs_inj = NULL ;
2010-09-02 18:33:24 +02:00
}
2014-11-22 11:09:12 +01:00
module_init ( init_mce_inject ) ;
module_exit ( exit_mce_inject ) ;
2010-09-02 18:33:24 +02:00
MODULE_LICENSE ( " GPL " ) ;
2012-10-29 18:40:09 +01:00
MODULE_AUTHOR ( " Borislav Petkov <bp@alien8.de> " ) ;
2010-09-02 18:33:24 +02:00
MODULE_AUTHOR ( " AMD Inc. " ) ;
2014-11-22 11:09:12 +01:00
MODULE_DESCRIPTION ( " MCE injection facility for RAS testing " ) ;