2006-12-08 16:15:16 -08:00
/*
* err_inject . c -
* 1. ) Inject errors to a processor .
* 2. ) Query error injection capabilities .
* This driver along with user space code can be acting as an error
* injection tool .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for more
* details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Written by : Fenghua Yu < fenghua . yu @ intel . com > , Intel Corporation
* Copyright ( C ) 2006 , Intel Corp . All rights reserved .
*
*/
2011-12-21 14:29:42 -08:00
# include <linux/device.h>
2006-12-08 16:15:16 -08:00
# include <linux/init.h>
# include <linux/mm.h>
# include <linux/cpu.h>
# include <linux/module.h>
# define ERR_INJ_DEBUG
# define ERR_DATA_BUFFER_SIZE 3 // Three 8-byte;
# define define_one_ro(name) \
2011-12-21 14:29:42 -08:00
static DEVICE_ATTR ( name , 0444 , show_ # # name , NULL )
2006-12-08 16:15:16 -08:00
# define define_one_rw(name) \
2011-12-21 14:29:42 -08:00
static DEVICE_ATTR ( name , 0644 , show_ # # name , store_ # # name )
2006-12-08 16:15:16 -08:00
static u64 call_start [ NR_CPUS ] ;
static u64 phys_addr [ NR_CPUS ] ;
static u64 err_type_info [ NR_CPUS ] ;
static u64 err_struct_info [ NR_CPUS ] ;
static struct {
u64 data1 ;
u64 data2 ;
u64 data3 ;
} __attribute__ ( ( __aligned__ ( 16 ) ) ) err_data_buffer [ NR_CPUS ] ;
static s64 status [ NR_CPUS ] ;
static u64 capabilities [ NR_CPUS ] ;
static u64 resources [ NR_CPUS ] ;
# define show(name) \
static ssize_t \
2011-12-21 14:29:42 -08:00
show_ # # name ( struct device * dev , struct device_attribute * attr , \
2008-07-01 18:48:41 +02:00
char * buf ) \
2006-12-08 16:15:16 -08:00
{ \
u32 cpu = dev - > id ; \
return sprintf ( buf , " %lx \n " , name [ cpu ] ) ; \
}
# define store(name) \
static ssize_t \
2011-12-21 14:29:42 -08:00
store_ # # name ( struct device * dev , struct device_attribute * attr , \
2008-07-01 18:48:41 +02:00
const char * buf , size_t size ) \
2006-12-08 16:15:16 -08:00
{ \
unsigned int cpu = dev - > id ; \
name [ cpu ] = simple_strtoull ( buf , NULL , 16 ) ; \
return size ; \
}
show ( call_start )
/* It's user's responsibility to call the PAL procedure on a specific
* processor . The cpu number in driver is only used for storing data .
*/
static ssize_t
2011-12-21 14:29:42 -08:00
store_call_start ( struct device * dev , struct device_attribute * attr ,
2008-07-01 18:48:41 +02:00
const char * buf , size_t size )
2006-12-08 16:15:16 -08:00
{
unsigned int cpu = dev - > id ;
unsigned long call_start = simple_strtoull ( buf , NULL , 16 ) ;
# ifdef ERR_INJ_DEBUG
printk ( KERN_DEBUG " pal_mc_err_inject for cpu%d: \n " , cpu ) ;
printk ( KERN_DEBUG " err_type_info=%lx, \n " , err_type_info [ cpu ] ) ;
printk ( KERN_DEBUG " err_struct_info=%lx, \n " , err_struct_info [ cpu ] ) ;
printk ( KERN_DEBUG " err_data_buffer=%lx, %lx, %lx. \n " ,
err_data_buffer [ cpu ] . data1 ,
err_data_buffer [ cpu ] . data2 ,
err_data_buffer [ cpu ] . data3 ) ;
# endif
switch ( call_start ) {
case 0 : /* Do nothing. */
break ;
case 1 : /* Call pal_mc_error_inject in physical mode. */
status [ cpu ] = ia64_pal_mc_error_inject_phys ( err_type_info [ cpu ] ,
err_struct_info [ cpu ] ,
ia64_tpa ( & err_data_buffer [ cpu ] ) ,
& capabilities [ cpu ] ,
& resources [ cpu ] ) ;
break ;
case 2 : /* Call pal_mc_error_inject in virtual mode. */
status [ cpu ] = ia64_pal_mc_error_inject_virt ( err_type_info [ cpu ] ,
err_struct_info [ cpu ] ,
ia64_tpa ( & err_data_buffer [ cpu ] ) ,
& capabilities [ cpu ] ,
& resources [ cpu ] ) ;
break ;
default :
status [ cpu ] = - EINVAL ;
break ;
}
# ifdef ERR_INJ_DEBUG
printk ( KERN_DEBUG " Returns: status=%d, \n " , ( int ) status [ cpu ] ) ;
2018-03-02 09:10:30 +00:00
printk ( KERN_DEBUG " capabilities=%lx, \n " , capabilities [ cpu ] ) ;
2006-12-08 16:15:16 -08:00
printk ( KERN_DEBUG " resources=%lx \n " , resources [ cpu ] ) ;
# endif
return size ;
}
show ( err_type_info )
store ( err_type_info )
static ssize_t
2011-12-21 14:29:42 -08:00
show_virtual_to_phys ( struct device * dev , struct device_attribute * attr ,
2008-07-01 18:48:41 +02:00
char * buf )
2006-12-08 16:15:16 -08:00
{
unsigned int cpu = dev - > id ;
return sprintf ( buf , " %lx \n " , phys_addr [ cpu ] ) ;
}
static ssize_t
2011-12-21 14:29:42 -08:00
store_virtual_to_phys ( struct device * dev , struct device_attribute * attr ,
2008-07-01 18:48:41 +02:00
const char * buf , size_t size )
2006-12-08 16:15:16 -08:00
{
unsigned int cpu = dev - > id ;
u64 virt_addr = simple_strtoull ( buf , NULL , 16 ) ;
int ret ;
2018-01-22 09:21:37 -08:00
ret = get_user_pages_fast ( virt_addr , 1 , FOLL_WRITE , NULL ) ;
2006-12-08 16:15:16 -08:00
if ( ret < = 0 ) {
# ifdef ERR_INJ_DEBUG
printk ( " Virtual address %lx is not existing. \n " , virt_addr ) ;
# endif
return - EINVAL ;
}
phys_addr [ cpu ] = ia64_tpa ( virt_addr ) ;
return size ;
}
show ( err_struct_info )
store ( err_struct_info )
static ssize_t
2011-12-21 14:29:42 -08:00
show_err_data_buffer ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-12-08 16:15:16 -08:00
{
unsigned int cpu = dev - > id ;
return sprintf ( buf , " %lx, %lx, %lx \n " ,
err_data_buffer [ cpu ] . data1 ,
err_data_buffer [ cpu ] . data2 ,
err_data_buffer [ cpu ] . data3 ) ;
}
static ssize_t
2011-12-21 14:29:42 -08:00
store_err_data_buffer ( struct device * dev ,
struct device_attribute * attr ,
2008-07-01 18:48:41 +02:00
const char * buf , size_t size )
2006-12-08 16:15:16 -08:00
{
unsigned int cpu = dev - > id ;
int ret ;
# ifdef ERR_INJ_DEBUG
printk ( " write err_data_buffer=[%lx,%lx,%lx] on cpu%d \n " ,
err_data_buffer [ cpu ] . data1 ,
err_data_buffer [ cpu ] . data2 ,
err_data_buffer [ cpu ] . data3 ,
cpu ) ;
# endif
ret = sscanf ( buf , " %lx, %lx, %lx " ,
& err_data_buffer [ cpu ] . data1 ,
& err_data_buffer [ cpu ] . data2 ,
& err_data_buffer [ cpu ] . data3 ) ;
if ( ret ! = ERR_DATA_BUFFER_SIZE )
return - EINVAL ;
return size ;
}
show ( status )
show ( capabilities )
show ( resources )
define_one_rw ( call_start ) ;
define_one_rw ( err_type_info ) ;
define_one_rw ( err_struct_info ) ;
define_one_rw ( err_data_buffer ) ;
define_one_rw ( virtual_to_phys ) ;
define_one_ro ( status ) ;
define_one_ro ( capabilities ) ;
define_one_ro ( resources ) ;
static struct attribute * default_attrs [ ] = {
2011-12-21 14:29:42 -08:00
& dev_attr_call_start . attr ,
& dev_attr_virtual_to_phys . attr ,
& dev_attr_err_type_info . attr ,
& dev_attr_err_struct_info . attr ,
& dev_attr_err_data_buffer . attr ,
& dev_attr_status . attr ,
& dev_attr_capabilities . attr ,
& dev_attr_resources . attr ,
2006-12-08 16:15:16 -08:00
NULL
} ;
static struct attribute_group err_inject_attr_group = {
. attrs = default_attrs ,
. name = " err_inject "
} ;
/* Add/Remove err_inject interface for CPU device */
2016-11-03 15:50:10 +01:00
static int err_inject_add_dev ( unsigned int cpu )
2006-12-08 16:15:16 -08:00
{
2016-11-03 15:50:10 +01:00
struct device * sys_dev = get_cpu_device ( cpu ) ;
2006-12-08 16:15:16 -08:00
return sysfs_create_group ( & sys_dev - > kobj , & err_inject_attr_group ) ;
}
2016-11-03 15:50:10 +01:00
static int err_inject_remove_dev ( unsigned int cpu )
2006-12-08 16:15:16 -08:00
{
2016-11-03 15:50:10 +01:00
struct device * sys_dev = get_cpu_device ( cpu ) ;
2006-12-08 16:15:16 -08:00
sysfs_remove_group ( & sys_dev - > kobj , & err_inject_attr_group ) ;
return 0 ;
}
2016-11-03 15:50:10 +01:00
static enum cpuhp_state hp_online ;
2006-12-08 16:15:16 -08:00
2016-11-03 15:50:10 +01:00
static int __init err_inject_init ( void )
2006-12-08 16:15:16 -08:00
{
2016-11-03 15:50:10 +01:00
int ret ;
2006-12-08 16:15:16 -08:00
# ifdef ERR_INJ_DEBUG
printk ( KERN_INFO " Enter error injection driver. \n " ) ;
# endif
2014-03-11 02:05:10 +05:30
2016-11-03 15:50:10 +01:00
ret = cpuhp_setup_state ( CPUHP_AP_ONLINE_DYN , " ia64/err_inj:online " ,
err_inject_add_dev , err_inject_remove_dev ) ;
if ( ret > = 0 ) {
hp_online = ret ;
ret = 0 ;
2006-12-08 16:15:16 -08:00
}
2016-11-03 15:50:10 +01:00
return ret ;
2006-12-08 16:15:16 -08:00
}
2016-11-03 15:50:10 +01:00
static void __exit err_inject_exit ( void )
2006-12-08 16:15:16 -08:00
{
# ifdef ERR_INJ_DEBUG
printk ( KERN_INFO " Exit error injection driver. \n " ) ;
# endif
2016-11-03 15:50:10 +01:00
cpuhp_remove_state ( hp_online ) ;
2006-12-08 16:15:16 -08:00
}
module_init ( err_inject_init ) ;
module_exit ( err_inject_exit ) ;
MODULE_AUTHOR ( " Fenghua Yu <fenghua.yu@intel.com> " ) ;
2007-05-10 09:35:30 -07:00
MODULE_DESCRIPTION ( " MC error injection kernel sysfs interface " ) ;
2006-12-08 16:15:16 -08:00
MODULE_LICENSE ( " GPL " ) ;