2014-01-24 09:18:52 +01:00
/*
* Hypervisor filesystem for Linux on s390 .
* Set Partition - Resource Parameter interface .
*
* Copyright IBM Corp . 2013
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com >
*/
# include <linux/compat.h>
# include <linux/errno.h>
# include <linux/gfp.h>
# include <linux/string.h>
# include <linux/types.h>
# include <linux/uaccess.h>
# include <asm/compat.h>
2015-08-20 17:28:44 +02:00
# include <asm/diag.h>
2014-01-24 09:18:52 +01:00
# include <asm/sclp.h>
# include "hypfs.h"
# define DIAG304_SET_WEIGHTS 0
# define DIAG304_QUERY_PRP 1
# define DIAG304_SET_CAPPING 2
# define DIAG304_CMD_MAX 2
2015-08-20 17:28:44 +02:00
static inline unsigned long __hypfs_sprp_diag304 ( void * data , unsigned long cmd )
2014-01-24 09:18:52 +01:00
{
register unsigned long _data asm ( " 2 " ) = ( unsigned long ) data ;
register unsigned long _rc asm ( " 3 " ) ;
register unsigned long _cmd asm ( " 4 " ) = cmd ;
asm volatile ( " diag %1,%2,0x304 \n "
: " =d " ( _rc ) : " d " ( _data ) , " d " ( _cmd ) : " memory " ) ;
return _rc ;
}
2015-08-20 17:28:44 +02:00
static unsigned long hypfs_sprp_diag304 ( void * data , unsigned long cmd )
{
diag_stat_inc ( DIAG_STAT_X304 ) ;
return __hypfs_sprp_diag304 ( data , cmd ) ;
}
2014-01-24 09:18:52 +01:00
static void hypfs_sprp_free ( const void * data )
{
free_page ( ( unsigned long ) data ) ;
}
static int hypfs_sprp_create ( void * * data_ptr , void * * free_ptr , size_t * size )
{
unsigned long rc ;
void * data ;
data = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
rc = hypfs_sprp_diag304 ( data , DIAG304_QUERY_PRP ) ;
if ( rc ! = 1 ) {
* data_ptr = * free_ptr = NULL ;
* size = 0 ;
free_page ( ( unsigned long ) data ) ;
return - EIO ;
}
* data_ptr = * free_ptr = data ;
* size = PAGE_SIZE ;
return 0 ;
}
static int __hypfs_sprp_ioctl ( void __user * user_area )
{
struct hypfs_diag304 diag304 ;
unsigned long cmd ;
void __user * udata ;
void * data ;
int rc ;
if ( copy_from_user ( & diag304 , user_area , sizeof ( diag304 ) ) )
return - EFAULT ;
if ( ( diag304 . args [ 0 ] > > 8 ) ! = 0 | | diag304 . args [ 1 ] > DIAG304_CMD_MAX )
return - EINVAL ;
data = ( void * ) get_zeroed_page ( GFP_KERNEL | GFP_DMA ) ;
if ( ! data )
return - ENOMEM ;
udata = ( void __user * ) ( unsigned long ) diag304 . data ;
if ( diag304 . args [ 1 ] = = DIAG304_SET_WEIGHTS | |
diag304 . args [ 1 ] = = DIAG304_SET_CAPPING )
if ( copy_from_user ( data , udata , PAGE_SIZE ) ) {
rc = - EFAULT ;
goto out ;
}
cmd = * ( unsigned long * ) & diag304 . args [ 0 ] ;
diag304 . rc = hypfs_sprp_diag304 ( data , cmd ) ;
if ( diag304 . args [ 1 ] = = DIAG304_QUERY_PRP )
if ( copy_to_user ( udata , data , PAGE_SIZE ) ) {
rc = - EFAULT ;
goto out ;
}
rc = copy_to_user ( user_area , & diag304 , sizeof ( diag304 ) ) ? - EFAULT : 0 ;
out :
free_page ( ( unsigned long ) data ) ;
return rc ;
}
static long hypfs_sprp_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
void __user * argp ;
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
if ( is_compat_task ( ) )
argp = compat_ptr ( arg ) ;
else
argp = ( void __user * ) arg ;
switch ( cmd ) {
case HYPFS_DIAG304 :
return __hypfs_sprp_ioctl ( argp ) ;
default : /* unknown ioctl number */
return - ENOTTY ;
}
return 0 ;
}
static struct hypfs_dbfs_file hypfs_sprp_file = {
. name = " diag_304 " ,
. data_create = hypfs_sprp_create ,
. data_free = hypfs_sprp_free ,
. unlocked_ioctl = hypfs_sprp_ioctl ,
} ;
int hypfs_sprp_init ( void )
{
2015-05-06 13:18:59 +02:00
if ( ! sclp . has_sprp )
2014-01-24 09:18:52 +01:00
return 0 ;
return hypfs_dbfs_create_file ( & hypfs_sprp_file ) ;
}
void hypfs_sprp_exit ( void )
{
2015-05-06 13:18:59 +02:00
if ( ! sclp . has_sprp )
2014-01-24 09:18:52 +01:00
return ;
hypfs_dbfs_remove_file ( & hypfs_sprp_file ) ;
}