2013-06-06 09:52:08 +02:00
/*
* IOCTL interface for SCLP
*
* Copyright IBM Corp . 2012
*
* Author : Michael Holzheu < holzheu @ linux . vnet . ibm . com >
*/
# include <linux/compat.h>
# include <linux/uaccess.h>
# include <linux/miscdevice.h>
# include <linux/gfp.h>
2016-10-29 16:38:43 -04:00
# include <linux/init.h>
2013-06-06 09:52:08 +02:00
# include <linux/ioctl.h>
# include <linux/fs.h>
# include <asm/compat.h>
# include <asm/sclp_ctl.h>
# include <asm/sclp.h>
# include "sclp.h"
/*
* Supported command words
*/
static unsigned int sclp_ctl_sccb_wlist [ ] = {
0x00400002 ,
0x00410002 ,
} ;
/*
* Check if command word is supported
*/
static int sclp_ctl_cmdw_supported ( unsigned int cmdw )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( sclp_ctl_sccb_wlist ) ; i + + ) {
if ( cmdw = = sclp_ctl_sccb_wlist [ i ] )
return 1 ;
}
return 0 ;
}
static void __user * u64_to_uptr ( u64 value )
{
if ( is_compat_task ( ) )
return compat_ptr ( value ) ;
else
return ( void __user * ) ( unsigned long ) value ;
}
/*
* Start SCLP request
*/
static int sclp_ctl_ioctl_sccb ( void __user * user_area )
{
struct sclp_ctl_sccb ctl_sccb ;
struct sccb_header * sccb ;
2016-04-25 17:54:28 +02:00
unsigned long copied ;
2013-06-06 09:52:08 +02:00
int rc ;
if ( copy_from_user ( & ctl_sccb , user_area , sizeof ( ctl_sccb ) ) )
return - EFAULT ;
if ( ! sclp_ctl_cmdw_supported ( ctl_sccb . cmdw ) )
return - EOPNOTSUPP ;
sccb = ( void * ) get_zeroed_page ( GFP_KERNEL | GFP_DMA ) ;
if ( ! sccb )
return - ENOMEM ;
2016-04-25 17:54:28 +02:00
copied = PAGE_SIZE -
copy_from_user ( sccb , u64_to_uptr ( ctl_sccb . sccb ) , PAGE_SIZE ) ;
if ( offsetof ( struct sccb_header , length ) +
sizeof ( sccb - > length ) > copied | | sccb - > length > copied ) {
2013-06-06 09:52:08 +02:00
rc = - EFAULT ;
goto out_free ;
}
2016-04-25 17:54:28 +02:00
if ( sccb - > length < 8 ) {
rc = - EINVAL ;
2013-06-06 09:52:08 +02:00
goto out_free ;
}
rc = sclp_sync_request ( ctl_sccb . cmdw , sccb ) ;
if ( rc )
goto out_free ;
if ( copy_to_user ( u64_to_uptr ( ctl_sccb . sccb ) , sccb , sccb - > length ) )
rc = - EFAULT ;
out_free :
free_page ( ( unsigned long ) sccb ) ;
return rc ;
}
/*
* SCLP SCCB ioctl function
*/
static long sclp_ctl_ioctl ( struct file * filp , unsigned int cmd ,
unsigned long arg )
{
void __user * argp ;
if ( is_compat_task ( ) )
argp = compat_ptr ( arg ) ;
else
argp = ( void __user * ) arg ;
switch ( cmd ) {
case SCLP_CTL_SCCB :
return sclp_ctl_ioctl_sccb ( argp ) ;
default : /* unknown ioctl number */
return - ENOTTY ;
}
}
/*
* File operations
*/
static const struct file_operations sclp_ctl_fops = {
. owner = THIS_MODULE ,
. open = nonseekable_open ,
. unlocked_ioctl = sclp_ctl_ioctl ,
. compat_ioctl = sclp_ctl_ioctl ,
. llseek = no_llseek ,
} ;
/*
* Misc device definition
*/
static struct miscdevice sclp_ctl_device = {
. minor = MISC_DYNAMIC_MINOR ,
. name = " sclp " ,
. fops = & sclp_ctl_fops ,
} ;
2016-10-29 16:38:43 -04:00
builtin_misc_device ( sclp_ctl_device ) ;