2005-04-17 02:20:36 +04:00
/*
* S390 version
2012-07-20 13:15:04 +04:00
* Copyright IBM Corp . 1999 , 2007
2005-04-17 02:20:36 +04:00
* Author ( s ) : Martin Schwidefsky ( schwidefsky @ de . ibm . com ) ,
* Christian Borntraeger ( cborntra @ de . ibm . com ) ,
*/
2008-12-25 15:39:34 +03:00
# define KMSG_COMPONENT "cpcmd"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
2017-02-09 23:20:23 +03:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/stddef.h>
# include <linux/string.h>
2017-08-07 16:16:15 +03:00
# include <linux/mm.h>
2015-08-20 18:28:44 +03:00
# include <asm/diag.h>
2005-04-17 02:20:36 +04:00
# include <asm/ebcdic.h>
# include <asm/cpcmd.h>
2007-02-05 23:16:54 +03:00
# include <asm/io.h>
2005-04-17 02:20:36 +04:00
static DEFINE_SPINLOCK ( cpcmd_lock ) ;
2005-06-26 01:55:32 +04:00
static char cpcmd_buf [ 241 ] ;
2005-04-17 02:20:36 +04:00
2007-10-12 18:11:46 +04:00
static int diag8_noresponse ( int cmdlen )
{
register unsigned long reg2 asm ( " 2 " ) = ( addr_t ) cpcmd_buf ;
register unsigned long reg3 asm ( " 3 " ) = cmdlen ;
asm volatile (
" diag %1,%0,0x8 \n "
: " +d " ( reg3 ) : " d " ( reg2 ) : " cc " ) ;
return reg3 ;
}
static int diag8_response ( int cmdlen , char * response , int * rlen )
{
register unsigned long reg2 asm ( " 2 " ) = ( addr_t ) cpcmd_buf ;
register unsigned long reg3 asm ( " 3 " ) = ( addr_t ) response ;
register unsigned long reg4 asm ( " 4 " ) = cmdlen | 0x40000000L ;
register unsigned long reg5 asm ( " 5 " ) = * rlen ;
asm volatile (
" diag %2,%0,0x8 \n "
" brc 8,1f \n "
" agr %1,%4 \n "
" 1: \n "
: " +d " ( reg4 ) , " +d " ( reg5 )
: " d " ( reg2 ) , " d " ( reg3 ) , " d " ( * rlen ) : " cc " ) ;
* rlen = reg5 ;
return reg4 ;
}
2005-04-17 02:20:36 +04:00
/*
2006-12-04 17:40:30 +03:00
* __cpcmd has some restrictions over cpcmd
* - __cpcmd is unlocked and therefore not SMP - safe
2005-04-17 02:20:36 +04:00
*/
2005-06-26 01:55:32 +04:00
int __cpcmd ( const char * cmd , char * response , int rlen , int * response_code )
2005-04-17 02:20:36 +04:00
{
2007-10-12 18:11:46 +04:00
int cmdlen ;
int rc ;
int response_len ;
2005-04-17 02:20:36 +04:00
cmdlen = strlen ( cmd ) ;
BUG_ON ( cmdlen > 240 ) ;
2005-06-26 01:55:32 +04:00
memcpy ( cpcmd_buf , cmd , cmdlen ) ;
2005-04-17 02:20:36 +04:00
ASCEBC ( cpcmd_buf , cmdlen ) ;
2015-08-20 18:28:44 +03:00
diag_stat_inc ( DIAG_STAT_X008 ) ;
2007-10-12 18:11:46 +04:00
if ( response ) {
2005-04-17 02:20:36 +04:00
memset ( response , 0 , rlen ) ;
2007-10-12 18:11:46 +04:00
response_len = rlen ;
rc = diag8_response ( cmdlen , response , & rlen ) ;
EBCASC ( response , response_len ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-10-12 18:11:46 +04:00
rc = diag8_noresponse ( cmdlen ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-12 18:11:46 +04:00
if ( response_code )
* response_code = rc ;
return rlen ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( __cpcmd ) ;
2005-06-26 01:55:32 +04:00
int cpcmd ( const char * cmd , char * response , int rlen , int * response_code )
2005-04-17 02:20:36 +04:00
{
2017-08-07 16:16:15 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
char * lowbuf ;
2005-06-26 01:55:32 +04:00
int len ;
2017-08-07 16:16:15 +03:00
if ( is_vmalloc_or_module_addr ( response ) ) {
lowbuf = kmalloc ( rlen , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! lowbuf ) {
2016-03-04 07:49:57 +03:00
pr_warn ( " The cpcmd kernel function failed to allocate a response buffer \n " ) ;
2005-06-26 01:55:32 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2006-12-04 17:40:30 +03:00
spin_lock_irqsave ( & cpcmd_lock , flags ) ;
2005-06-26 01:55:32 +04:00
len = __cpcmd ( cmd , lowbuf , rlen , response_code ) ;
2006-12-04 17:40:30 +03:00
spin_unlock_irqrestore ( & cpcmd_lock , flags ) ;
2005-04-17 02:20:36 +04:00
memcpy ( response , lowbuf , rlen ) ;
kfree ( lowbuf ) ;
2007-02-05 23:16:54 +03:00
} else {
spin_lock_irqsave ( & cpcmd_lock , flags ) ;
len = __cpcmd ( cmd , response , rlen , response_code ) ;
spin_unlock_irqrestore ( & cpcmd_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:55:32 +04:00
return len ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( cpcmd ) ;