2006-09-20 15:58:49 +02:00
/*
* arch / s390 / kernel / ipl . c
* ipl / reipl / dump support for Linux on s390 .
*
2008-01-26 14:11:11 +01:00
* Copyright IBM Corp . 2005 , 2007
2006-09-20 15:58:49 +02:00
* Author ( s ) : Michael Holzheu < holzheu @ de . ibm . com >
* Heiko Carstens < heiko . carstens @ de . ibm . com >
* Volker Sameske < sameske @ de . ibm . com >
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/delay.h>
# include <linux/reboot.h>
2006-12-04 15:39:58 +01:00
# include <linux/ctype.h>
2008-07-14 09:59:17 +02:00
# include <linux/fs.h>
2007-02-21 10:55:21 +01:00
# include <asm/ipl.h>
2006-09-20 15:58:49 +02:00
# include <asm/smp.h>
# include <asm/setup.h>
# include <asm/cpcmd.h>
# include <asm/cio.h>
2006-12-04 15:39:58 +01:00
# include <asm/ebcdic.h>
2006-12-04 15:40:26 +01:00
# include <asm/reset.h>
2007-02-05 21:18:37 +01:00
# include <asm/sclp.h>
2009-04-23 13:58:06 +02:00
# include <asm/sigp.h>
2009-03-26 15:24:45 +01:00
# include <asm/checksum.h>
2006-09-20 15:58:49 +02:00
# define IPL_PARM_BLOCK_VERSION 0
2006-12-04 15:39:58 +01:00
2007-04-27 16:01:49 +02:00
# define IPL_UNKNOWN_STR "unknown"
# define IPL_CCW_STR "ccw"
# define IPL_FCP_STR "fcp"
# define IPL_FCP_DUMP_STR "fcp_dump"
# define IPL_NSS_STR "nss"
2007-02-21 10:55:37 +01:00
2008-01-26 14:11:11 +01:00
# define DUMP_CCW_STR "ccw"
# define DUMP_FCP_STR "fcp"
# define DUMP_NONE_STR "none"
/*
* Four shutdown trigger types are supported :
* - panic
* - halt
* - power off
* - reipl
*/
# define ON_PANIC_STR "on_panic"
# define ON_HALT_STR "on_halt"
# define ON_POFF_STR "on_poff"
# define ON_REIPL_STR "on_reboot"
struct shutdown_action ;
struct shutdown_trigger {
char * name ;
struct shutdown_action * action ;
} ;
/*
2009-03-26 15:23:43 +01:00
* The following shutdown action types are supported :
2008-01-26 14:11:11 +01:00
*/
# define SHUTDOWN_ACTION_IPL_STR "ipl"
# define SHUTDOWN_ACTION_REIPL_STR "reipl"
# define SHUTDOWN_ACTION_DUMP_STR "dump"
# define SHUTDOWN_ACTION_VMCMD_STR "vmcmd"
# define SHUTDOWN_ACTION_STOP_STR "stop"
2009-03-26 15:23:43 +01:00
# define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl"
2008-01-26 14:11:11 +01:00
struct shutdown_action {
char * name ;
void ( * fn ) ( struct shutdown_trigger * trigger ) ;
int ( * init ) ( void ) ;
2009-07-07 16:37:07 +02:00
int init_rc ;
2008-01-26 14:11:11 +01:00
} ;
2006-09-20 15:58:49 +02:00
static char * ipl_type_str ( enum ipl_type type )
{
switch ( type ) {
case IPL_TYPE_CCW :
return IPL_CCW_STR ;
case IPL_TYPE_FCP :
return IPL_FCP_STR ;
2007-04-27 16:01:49 +02:00
case IPL_TYPE_FCP_DUMP :
return IPL_FCP_DUMP_STR ;
2007-02-05 21:18:24 +01:00
case IPL_TYPE_NSS :
return IPL_NSS_STR ;
2006-09-20 15:58:49 +02:00
case IPL_TYPE_UNKNOWN :
default :
return IPL_UNKNOWN_STR ;
}
}
2007-04-27 16:01:49 +02:00
enum dump_type {
DUMP_TYPE_NONE = 1 ,
DUMP_TYPE_CCW = 2 ,
DUMP_TYPE_FCP = 4 ,
} ;
static char * dump_type_str ( enum dump_type type )
{
switch ( type ) {
case DUMP_TYPE_NONE :
return DUMP_NONE_STR ;
case DUMP_TYPE_CCW :
return DUMP_CCW_STR ;
case DUMP_TYPE_FCP :
return DUMP_FCP_STR ;
default :
return NULL ;
}
}
/*
* Must be in data section since the bss section
* is not cleared when these are accessed .
*/
static u16 ipl_devno __attribute__ ( ( __section__ ( " .data " ) ) ) = 0 ;
u32 ipl_flags __attribute__ ( ( __section__ ( " .data " ) ) ) = 0 ;
2006-09-20 15:58:49 +02:00
enum ipl_method {
2007-04-27 16:01:49 +02:00
REIPL_METHOD_CCW_CIO ,
REIPL_METHOD_CCW_DIAG ,
REIPL_METHOD_CCW_VM ,
REIPL_METHOD_FCP_RO_DIAG ,
REIPL_METHOD_FCP_RW_DIAG ,
REIPL_METHOD_FCP_RO_VM ,
REIPL_METHOD_FCP_DUMP ,
REIPL_METHOD_NSS ,
2008-07-14 09:59:09 +02:00
REIPL_METHOD_NSS_DIAG ,
2007-04-27 16:01:49 +02:00
REIPL_METHOD_DEFAULT ,
} ;
enum dump_method {
DUMP_METHOD_NONE ,
DUMP_METHOD_CCW_CIO ,
DUMP_METHOD_CCW_DIAG ,
DUMP_METHOD_CCW_VM ,
DUMP_METHOD_FCP_DIAG ,
2006-09-20 15:58:49 +02:00
} ;
static int diag308_set_works = 0 ;
2008-07-14 09:59:09 +02:00
static struct ipl_parameter_block ipl_block ;
2006-09-20 15:58:49 +02:00
static int reipl_capabilities = IPL_TYPE_UNKNOWN ;
2007-02-05 21:18:24 +01:00
2006-09-20 15:58:49 +02:00
static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN ;
2007-04-27 16:01:49 +02:00
static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT ;
2006-09-20 15:58:49 +02:00
static struct ipl_parameter_block * reipl_block_fcp ;
static struct ipl_parameter_block * reipl_block_ccw ;
2008-07-14 09:59:09 +02:00
static struct ipl_parameter_block * reipl_block_nss ;
2009-03-26 15:23:43 +01:00
static struct ipl_parameter_block * reipl_block_actual ;
2007-02-05 21:18:24 +01:00
2007-04-27 16:01:49 +02:00
static int dump_capabilities = DUMP_TYPE_NONE ;
static enum dump_type dump_type = DUMP_TYPE_NONE ;
static enum dump_method dump_method = DUMP_METHOD_NONE ;
2006-09-20 15:58:49 +02:00
static struct ipl_parameter_block * dump_block_fcp ;
static struct ipl_parameter_block * dump_block_ccw ;
2007-07-10 11:24:09 +02:00
static struct sclp_ipl_info sclp_ipl_info ;
2007-02-21 10:55:21 +01:00
int diag308 ( unsigned long subcode , void * addr )
2006-09-20 15:58:49 +02:00
{
2006-09-28 16:56:43 +02:00
register unsigned long _addr asm ( " 0 " ) = ( unsigned long ) addr ;
2006-09-20 15:58:49 +02:00
register unsigned long _rc asm ( " 1 " ) = 0 ;
2006-09-28 16:56:43 +02:00
asm volatile (
" diag %0,%2,0x308 \n "
" 0: \n "
EX_TABLE ( 0 b , 0 b )
2006-09-20 15:58:49 +02:00
: " +d " ( _addr ) , " +d " ( _rc )
2006-09-28 16:56:43 +02:00
: " d " ( subcode ) : " cc " , " memory " ) ;
2006-09-20 15:58:49 +02:00
return _rc ;
}
2007-04-27 16:01:49 +02:00
EXPORT_SYMBOL_GPL ( diag308 ) ;
2006-09-20 15:58:49 +02:00
/* SYSFS */
# define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \
2007-11-02 17:17:00 -04:00
static ssize_t sys_ # # _prefix # # _ # # _name # # _show ( struct kobject * kobj , \
struct kobj_attribute * attr , \
2006-09-20 15:58:49 +02:00
char * page ) \
{ \
return sprintf ( page , _format , _value ) ; \
} \
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ # # _prefix # # _ # # _name # # _attr = \
2006-09-20 15:58:49 +02:00
__ATTR ( _name , S_IRUGO , sys_ # # _prefix # # _ # # _name # # _show , NULL ) ;
# define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \
2007-11-02 17:17:00 -04:00
static ssize_t sys_ # # _prefix # # _ # # _name # # _show ( struct kobject * kobj , \
struct kobj_attribute * attr , \
2006-09-20 15:58:49 +02:00
char * page ) \
{ \
return sprintf ( page , _fmt_out , \
( unsigned long long ) _value ) ; \
} \
2007-11-02 17:17:00 -04:00
static ssize_t sys_ # # _prefix # # _ # # _name # # _store ( struct kobject * kobj , \
struct kobj_attribute * attr , \
2006-09-20 15:58:49 +02:00
const char * buf , size_t len ) \
{ \
unsigned long long value ; \
if ( sscanf ( buf , _fmt_in , & value ) ! = 1 ) \
return - EINVAL ; \
_value = value ; \
return len ; \
} \
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ # # _prefix # # _ # # _name # # _attr = \
2006-09-20 15:58:49 +02:00
__ATTR ( _name , ( S_IRUGO | S_IWUSR ) , \
sys_ # # _prefix # # _ # # _name # # _show , \
sys_ # # _prefix # # _ # # _name # # _store ) ;
2007-02-05 21:18:24 +01:00
# define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\
2007-11-02 17:17:00 -04:00
static ssize_t sys_ # # _prefix # # _ # # _name # # _show ( struct kobject * kobj , \
struct kobj_attribute * attr , \
2007-02-05 21:18:24 +01:00
char * page ) \
{ \
return sprintf ( page , _fmt_out , _value ) ; \
} \
2007-11-02 17:17:00 -04:00
static ssize_t sys_ # # _prefix # # _ # # _name # # _store ( struct kobject * kobj , \
struct kobj_attribute * attr , \
2007-02-05 21:18:24 +01:00
const char * buf , size_t len ) \
{ \
2008-01-26 14:11:11 +01:00
strncpy ( _value , buf , sizeof ( _value ) - 1 ) ; \
strstrip ( _value ) ; \
2007-02-05 21:18:24 +01:00
return len ; \
} \
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ # # _prefix # # _ # # _name # # _attr = \
2007-02-05 21:18:24 +01:00
__ATTR ( _name , ( S_IRUGO | S_IWUSR ) , \
sys_ # # _prefix # # _ # # _name # # _show , \
sys_ # # _prefix # # _ # # _name # # _store ) ;
2006-09-20 15:58:49 +02:00
static void make_attrs_ro ( struct attribute * * attrs )
{
while ( * attrs ) {
( * attrs ) - > mode = S_IRUGO ;
attrs + + ;
}
}
/*
* ipl section
*/
2007-04-27 16:01:49 +02:00
static __init enum ipl_type get_ipl_type ( void )
2006-09-20 15:58:49 +02:00
{
struct ipl_parameter_block * ipl = IPL_PARMBLOCK_START ;
2007-02-05 21:18:24 +01:00
if ( ipl_flags & IPL_NSS_VALID )
return IPL_TYPE_NSS ;
2006-09-20 15:59:15 +02:00
if ( ! ( ipl_flags & IPL_DEVNO_VALID ) )
2006-09-20 15:58:49 +02:00
return IPL_TYPE_UNKNOWN ;
2006-09-20 15:59:15 +02:00
if ( ! ( ipl_flags & IPL_PARMBLOCK_VALID ) )
2006-09-20 15:58:49 +02:00
return IPL_TYPE_CCW ;
if ( ipl - > hdr . version > IPL_MAX_SUPPORTED_VERSION )
return IPL_TYPE_UNKNOWN ;
if ( ipl - > hdr . pbt ! = DIAG308_IPL_TYPE_FCP )
return IPL_TYPE_UNKNOWN ;
2007-04-27 16:01:49 +02:00
if ( ipl - > ipl_info . fcp . opt = = DIAG308_IPL_OPT_DUMP )
return IPL_TYPE_FCP_DUMP ;
2006-09-20 15:58:49 +02:00
return IPL_TYPE_FCP ;
}
2007-04-27 16:01:49 +02:00
struct ipl_info ipl_info ;
EXPORT_SYMBOL_GPL ( ipl_info ) ;
2007-11-02 17:17:00 -04:00
static ssize_t ipl_type_show ( struct kobject * kobj , struct kobj_attribute * attr ,
char * page )
2006-09-20 15:58:49 +02:00
{
2007-04-27 16:01:49 +02:00
return sprintf ( page , " %s \n " , ipl_type_str ( ipl_info . type ) ) ;
2006-09-20 15:58:49 +02:00
}
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO ( ipl_type ) ;
2006-09-20 15:58:49 +02:00
2008-07-14 09:59:09 +02:00
/* VM IPL PARM routines */
2009-09-11 10:28:40 +02:00
size_t reipl_get_ascii_vmparm ( char * dest , size_t size ,
2008-07-14 09:59:09 +02:00
const struct ipl_parameter_block * ipb )
{
int i ;
2009-09-11 10:28:40 +02:00
size_t len ;
2008-07-14 09:59:09 +02:00
char has_lowercase = 0 ;
2009-09-11 10:28:40 +02:00
len = 0 ;
2008-07-14 09:59:09 +02:00
if ( ( ipb - > ipl_info . ccw . vm_flags & DIAG308_VM_FLAGS_VP_VALID ) & &
( ipb - > ipl_info . ccw . vm_parm_len > 0 ) ) {
2009-09-11 10:28:40 +02:00
len = min_t ( size_t , size - 1 , ipb - > ipl_info . ccw . vm_parm_len ) ;
2008-07-14 09:59:09 +02:00
memcpy ( dest , ipb - > ipl_info . ccw . vm_parm , len ) ;
/* If at least one character is lowercase, we assume mixed
* case ; otherwise we convert everything to lowercase .
*/
for ( i = 0 ; i < len ; i + + )
if ( ( dest [ i ] > 0x80 & & dest [ i ] < 0x8a ) | | /* a-i */
( dest [ i ] > 0x90 & & dest [ i ] < 0x9a ) | | /* j-r */
( dest [ i ] > 0xa1 & & dest [ i ] < 0xaa ) ) { /* s-z */
has_lowercase = 1 ;
break ;
}
if ( ! has_lowercase )
EBC_TOLOWER ( dest , len ) ;
EBCASC ( dest , len ) ;
}
dest [ len ] = 0 ;
2009-09-11 10:28:40 +02:00
return len ;
2008-07-14 09:59:09 +02:00
}
2009-09-11 10:28:40 +02:00
size_t append_ipl_vmparm ( char * dest , size_t size )
2008-07-14 09:59:09 +02:00
{
2009-09-11 10:28:40 +02:00
size_t rc ;
rc = 0 ;
2008-07-14 09:59:09 +02:00
if ( diag308_set_works & & ( ipl_block . hdr . pbt = = DIAG308_IPL_TYPE_CCW ) )
2009-09-11 10:28:40 +02:00
rc = reipl_get_ascii_vmparm ( dest , size , & ipl_block ) ;
2008-07-14 09:59:09 +02:00
else
dest [ 0 ] = 0 ;
2009-09-11 10:28:40 +02:00
return rc ;
2008-07-14 09:59:09 +02:00
}
static ssize_t ipl_vm_parm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
char parm [ DIAG308_VMPARM_SIZE + 1 ] = { } ;
2009-09-11 10:28:40 +02:00
append_ipl_vmparm ( parm , sizeof ( parm ) ) ;
2008-07-14 09:59:09 +02:00
return sprintf ( page , " %s \n " , parm ) ;
}
2009-09-11 10:28:40 +02:00
static size_t scpdata_length ( const char * buf , size_t count )
{
while ( count ) {
if ( buf [ count - 1 ] ! = ' \0 ' & & buf [ count - 1 ] ! = ' ' )
break ;
count - - ;
}
return count ;
}
size_t reipl_append_ascii_scpdata ( char * dest , size_t size ,
const struct ipl_parameter_block * ipb )
{
size_t count ;
size_t i ;
2009-09-11 10:28:41 +02:00
int has_lowercase ;
2009-09-11 10:28:40 +02:00
count = min ( size - 1 , scpdata_length ( ipb - > ipl_info . fcp . scp_data ,
ipb - > ipl_info . fcp . scp_data_len ) ) ;
if ( ! count )
goto out ;
2009-09-11 10:28:41 +02:00
has_lowercase = 0 ;
for ( i = 0 ; i < count ; i + + ) {
2009-09-11 10:28:40 +02:00
if ( ! isascii ( ipb - > ipl_info . fcp . scp_data [ i ] ) ) {
count = 0 ;
goto out ;
}
2009-09-11 10:28:41 +02:00
if ( ! has_lowercase & & islower ( ipb - > ipl_info . fcp . scp_data [ i ] ) )
has_lowercase = 1 ;
}
2009-09-11 10:28:40 +02:00
2009-09-11 10:28:41 +02:00
if ( has_lowercase )
memcpy ( dest , ipb - > ipl_info . fcp . scp_data , count ) ;
else
for ( i = 0 ; i < count ; i + + )
dest [ i ] = tolower ( ipb - > ipl_info . fcp . scp_data [ i ] ) ;
2009-09-11 10:28:40 +02:00
out :
dest [ count ] = ' \0 ' ;
return count ;
}
size_t append_ipl_scpdata ( char * dest , size_t len )
{
size_t rc ;
rc = 0 ;
if ( ipl_block . hdr . pbt = = DIAG308_IPL_TYPE_FCP )
rc = reipl_append_ascii_scpdata ( dest , len , & ipl_block ) ;
else
dest [ 0 ] = 0 ;
return rc ;
}
2008-07-14 09:59:09 +02:00
static struct kobj_attribute sys_ipl_vm_parm_attr =
__ATTR ( parm , S_IRUGO , ipl_vm_parm_show , NULL ) ;
2007-11-02 17:17:00 -04:00
static ssize_t sys_ipl_device_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
2006-09-20 15:58:49 +02:00
{
struct ipl_parameter_block * ipl = IPL_PARMBLOCK_START ;
2007-04-27 16:01:49 +02:00
switch ( ipl_info . type ) {
2006-09-20 15:58:49 +02:00
case IPL_TYPE_CCW :
return sprintf ( page , " 0.0.%04x \n " , ipl_devno ) ;
case IPL_TYPE_FCP :
2007-04-27 16:01:49 +02:00
case IPL_TYPE_FCP_DUMP :
2006-09-20 15:58:49 +02:00
return sprintf ( page , " 0.0.%04x \n " , ipl - > ipl_info . fcp . devno ) ;
default :
return 0 ;
}
}
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ipl_device_attr =
2006-09-20 15:58:49 +02:00
__ATTR ( device , S_IRUGO , sys_ipl_device_show , NULL ) ;
2007-07-15 21:01:22 +01:00
static ssize_t ipl_parameter_read ( struct kobject * kobj , struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
2006-09-20 15:58:49 +02:00
{
2008-07-14 09:59:17 +02:00
return memory_read_from_buffer ( buf , count , & off , IPL_PARMBLOCK_START ,
IPL_PARMBLOCK_SIZE ) ;
2006-09-20 15:58:49 +02:00
}
static struct bin_attribute ipl_parameter_attr = {
. attr = {
. name = " binary_parameter " ,
. mode = S_IRUGO ,
} ,
. size = PAGE_SIZE ,
. read = & ipl_parameter_read ,
} ;
2007-07-15 21:01:22 +01:00
static ssize_t ipl_scp_data_read ( struct kobject * kobj , struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
2006-09-20 15:58:49 +02:00
{
unsigned int size = IPL_PARMBLOCK_START - > ipl_info . fcp . scp_data_len ;
void * scp_data = & IPL_PARMBLOCK_START - > ipl_info . fcp . scp_data ;
2008-07-14 09:59:17 +02:00
return memory_read_from_buffer ( buf , count , & off , scp_data , size ) ;
2006-09-20 15:58:49 +02:00
}
static struct bin_attribute ipl_scp_data_attr = {
. attr = {
. name = " scp_data " ,
. mode = S_IRUGO ,
} ,
. size = PAGE_SIZE ,
2007-07-15 21:01:22 +01:00
. read = ipl_scp_data_read ,
2006-09-20 15:58:49 +02:00
} ;
/* FCP ipl device attributes */
DEFINE_IPL_ATTR_RO ( ipl_fcp , wwpn , " 0x%016llx \n " , ( unsigned long long )
IPL_PARMBLOCK_START - > ipl_info . fcp . wwpn ) ;
DEFINE_IPL_ATTR_RO ( ipl_fcp , lun , " 0x%016llx \n " , ( unsigned long long )
IPL_PARMBLOCK_START - > ipl_info . fcp . lun ) ;
DEFINE_IPL_ATTR_RO ( ipl_fcp , bootprog , " %lld \n " , ( unsigned long long )
IPL_PARMBLOCK_START - > ipl_info . fcp . bootprog ) ;
DEFINE_IPL_ATTR_RO ( ipl_fcp , br_lba , " %lld \n " , ( unsigned long long )
IPL_PARMBLOCK_START - > ipl_info . fcp . br_lba ) ;
static struct attribute * ipl_fcp_attrs [ ] = {
& sys_ipl_type_attr . attr ,
& sys_ipl_device_attr . attr ,
& sys_ipl_fcp_wwpn_attr . attr ,
& sys_ipl_fcp_lun_attr . attr ,
& sys_ipl_fcp_bootprog_attr . attr ,
& sys_ipl_fcp_br_lba_attr . attr ,
NULL ,
} ;
static struct attribute_group ipl_fcp_attr_group = {
. attrs = ipl_fcp_attrs ,
} ;
/* CCW ipl device attributes */
2007-11-02 17:17:00 -04:00
static ssize_t ipl_ccw_loadparm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
2006-12-04 15:39:58 +01:00
{
char loadparm [ LOADPARM_LEN + 1 ] = { } ;
2007-07-10 11:24:09 +02:00
if ( ! sclp_ipl_info . is_valid )
2006-12-04 15:39:58 +01:00
return sprintf ( page , " #unknown# \n " ) ;
2007-07-10 11:24:09 +02:00
memcpy ( loadparm , & sclp_ipl_info . loadparm , LOADPARM_LEN ) ;
2006-12-04 15:39:58 +01:00
EBCASC ( loadparm , LOADPARM_LEN ) ;
strstrip ( loadparm ) ;
return sprintf ( page , " %s \n " , loadparm ) ;
}
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_ipl_ccw_loadparm_attr =
2006-12-04 15:39:58 +01:00
__ATTR ( loadparm , 0444 , ipl_ccw_loadparm_show , NULL ) ;
2008-07-14 09:59:09 +02:00
static struct attribute * ipl_ccw_attrs_vm [ ] = {
& sys_ipl_type_attr . attr ,
& sys_ipl_device_attr . attr ,
& sys_ipl_ccw_loadparm_attr . attr ,
& sys_ipl_vm_parm_attr . attr ,
NULL ,
} ;
static struct attribute * ipl_ccw_attrs_lpar [ ] = {
2006-09-20 15:58:49 +02:00
& sys_ipl_type_attr . attr ,
& sys_ipl_device_attr . attr ,
2006-12-04 15:39:58 +01:00
& sys_ipl_ccw_loadparm_attr . attr ,
2006-09-20 15:58:49 +02:00
NULL ,
} ;
2008-07-14 09:59:09 +02:00
static struct attribute_group ipl_ccw_attr_group_vm = {
. attrs = ipl_ccw_attrs_vm ,
} ;
static struct attribute_group ipl_ccw_attr_group_lpar = {
. attrs = ipl_ccw_attrs_lpar
2006-09-20 15:58:49 +02:00
} ;
2007-02-05 21:18:24 +01:00
/* NSS ipl device attributes */
DEFINE_IPL_ATTR_RO ( ipl_nss , name , " %s \n " , kernel_nss_name ) ;
static struct attribute * ipl_nss_attrs [ ] = {
& sys_ipl_type_attr . attr ,
& sys_ipl_nss_name_attr . attr ,
2008-07-14 09:59:09 +02:00
& sys_ipl_ccw_loadparm_attr . attr ,
& sys_ipl_vm_parm_attr . attr ,
2007-02-05 21:18:24 +01:00
NULL ,
} ;
static struct attribute_group ipl_nss_attr_group = {
. attrs = ipl_nss_attrs ,
} ;
2006-09-20 15:58:49 +02:00
/* UNKNOWN ipl device attributes */
static struct attribute * ipl_unknown_attrs [ ] = {
& sys_ipl_type_attr . attr ,
NULL ,
} ;
static struct attribute_group ipl_unknown_attr_group = {
. attrs = ipl_unknown_attrs ,
} ;
2007-11-02 17:17:00 -04:00
static struct kset * ipl_kset ;
2006-09-20 15:58:49 +02:00
2008-01-26 14:11:11 +01:00
static int __init ipl_register_fcp_files ( void )
{
int rc ;
rc = sysfs_create_group ( & ipl_kset - > kobj , & ipl_fcp_attr_group ) ;
if ( rc )
goto out ;
rc = sysfs_create_bin_file ( & ipl_kset - > kobj , & ipl_parameter_attr ) ;
if ( rc )
goto out_ipl_parm ;
rc = sysfs_create_bin_file ( & ipl_kset - > kobj , & ipl_scp_data_attr ) ;
if ( ! rc )
goto out ;
sysfs_remove_bin_file ( & ipl_kset - > kobj , & ipl_parameter_attr ) ;
out_ipl_parm :
sysfs_remove_group ( & ipl_kset - > kobj , & ipl_fcp_attr_group ) ;
out :
return rc ;
}
static void ipl_run ( struct shutdown_trigger * trigger )
{
diag308 ( DIAG308_IPL , NULL ) ;
if ( MACHINE_IS_VM )
__cpcmd ( " IPL " , NULL , 0 , NULL ) ;
else if ( ipl_info . type = = IPL_TYPE_CCW )
reipl_ccw_dev ( & ipl_info . data . ccw . dev_id ) ;
}
2008-02-05 16:50:40 +01:00
static int __init ipl_init ( void )
2008-01-26 14:11:11 +01:00
{
int rc ;
ipl_kset = kset_create_and_add ( " ipl " , NULL , firmware_kobj ) ;
if ( ! ipl_kset ) {
rc = - ENOMEM ;
goto out ;
}
switch ( ipl_info . type ) {
case IPL_TYPE_CCW :
2008-07-14 09:59:09 +02:00
if ( MACHINE_IS_VM )
rc = sysfs_create_group ( & ipl_kset - > kobj ,
& ipl_ccw_attr_group_vm ) ;
else
rc = sysfs_create_group ( & ipl_kset - > kobj ,
& ipl_ccw_attr_group_lpar ) ;
2008-01-26 14:11:11 +01:00
break ;
case IPL_TYPE_FCP :
case IPL_TYPE_FCP_DUMP :
rc = ipl_register_fcp_files ( ) ;
break ;
case IPL_TYPE_NSS :
rc = sysfs_create_group ( & ipl_kset - > kobj , & ipl_nss_attr_group ) ;
break ;
default :
rc = sysfs_create_group ( & ipl_kset - > kobj ,
& ipl_unknown_attr_group ) ;
break ;
}
out :
if ( rc )
panic ( " ipl_init failed: rc = %i \n " , rc ) ;
return 0 ;
}
2008-02-05 16:50:40 +01:00
static struct shutdown_action __refdata ipl_action = {
. name = SHUTDOWN_ACTION_IPL_STR ,
. fn = ipl_run ,
. init = ipl_init ,
} ;
2008-01-26 14:11:11 +01:00
2006-09-20 15:58:49 +02:00
/*
2008-01-26 14:11:11 +01:00
* reipl shutdown action : Reboot Linux on shutdown .
2006-09-20 15:58:49 +02:00
*/
2008-07-14 09:59:09 +02:00
/* VM IPL PARM attributes */
static ssize_t reipl_generic_vmparm_show ( struct ipl_parameter_block * ipb ,
char * page )
{
char vmparm [ DIAG308_VMPARM_SIZE + 1 ] = { } ;
2009-09-11 10:28:40 +02:00
reipl_get_ascii_vmparm ( vmparm , sizeof ( vmparm ) , ipb ) ;
2008-07-14 09:59:09 +02:00
return sprintf ( page , " %s \n " , vmparm ) ;
}
static ssize_t reipl_generic_vmparm_store ( struct ipl_parameter_block * ipb ,
size_t vmparm_max ,
const char * buf , size_t len )
{
int i , ip_len ;
/* ignore trailing newline */
ip_len = len ;
if ( ( len > 0 ) & & ( buf [ len - 1 ] = = ' \n ' ) )
ip_len - - ;
if ( ip_len > vmparm_max )
return - EINVAL ;
/* parm is used to store kernel options, check for common chars */
for ( i = 0 ; i < ip_len ; i + + )
if ( ! ( isalnum ( buf [ i ] ) | | isascii ( buf [ i ] ) | | isprint ( buf [ i ] ) ) )
return - EINVAL ;
memset ( ipb - > ipl_info . ccw . vm_parm , 0 , DIAG308_VMPARM_SIZE ) ;
ipb - > ipl_info . ccw . vm_parm_len = ip_len ;
if ( ip_len > 0 ) {
ipb - > ipl_info . ccw . vm_flags | = DIAG308_VM_FLAGS_VP_VALID ;
memcpy ( ipb - > ipl_info . ccw . vm_parm , buf , ip_len ) ;
ASCEBC ( ipb - > ipl_info . ccw . vm_parm , ip_len ) ;
} else {
ipb - > ipl_info . ccw . vm_flags & = ~ DIAG308_VM_FLAGS_VP_VALID ;
}
return len ;
}
/* NSS wrapper */
static ssize_t reipl_nss_vmparm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return reipl_generic_vmparm_show ( reipl_block_nss , page ) ;
}
static ssize_t reipl_nss_vmparm_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return reipl_generic_vmparm_store ( reipl_block_nss , 56 , buf , len ) ;
}
/* CCW wrapper */
static ssize_t reipl_ccw_vmparm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return reipl_generic_vmparm_show ( reipl_block_ccw , page ) ;
}
static ssize_t reipl_ccw_vmparm_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return reipl_generic_vmparm_store ( reipl_block_ccw , 64 , buf , len ) ;
}
static struct kobj_attribute sys_reipl_nss_vmparm_attr =
__ATTR ( parm , S_IRUGO | S_IWUSR , reipl_nss_vmparm_show ,
reipl_nss_vmparm_store ) ;
static struct kobj_attribute sys_reipl_ccw_vmparm_attr =
__ATTR ( parm , S_IRUGO | S_IWUSR , reipl_ccw_vmparm_show ,
reipl_ccw_vmparm_store ) ;
2006-09-20 15:58:49 +02:00
/* FCP reipl device attributes */
2009-09-11 10:28:40 +02:00
static ssize_t reipl_fcp_scpdata_read ( struct kobject * kobj ,
struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
size_t size = reipl_block_fcp - > ipl_info . fcp . scp_data_len ;
void * scp_data = reipl_block_fcp - > ipl_info . fcp . scp_data ;
return memory_read_from_buffer ( buf , count , & off , scp_data , size ) ;
}
static ssize_t reipl_fcp_scpdata_write ( struct kobject * kobj ,
struct bin_attribute * attr ,
char * buf , loff_t off , size_t count )
{
size_t padding ;
size_t scpdata_len ;
if ( off < 0 )
return - EINVAL ;
if ( off > = DIAG308_SCPDATA_SIZE )
return - ENOSPC ;
if ( count > DIAG308_SCPDATA_SIZE - off )
count = DIAG308_SCPDATA_SIZE - off ;
memcpy ( reipl_block_fcp - > ipl_info . fcp . scp_data , buf + off , count ) ;
scpdata_len = off + count ;
if ( scpdata_len % 8 ) {
padding = 8 - ( scpdata_len % 8 ) ;
memset ( reipl_block_fcp - > ipl_info . fcp . scp_data + scpdata_len ,
0 , padding ) ;
scpdata_len + = padding ;
}
reipl_block_fcp - > ipl_info . fcp . scp_data_len = scpdata_len ;
reipl_block_fcp - > hdr . len = IPL_PARM_BLK_FCP_LEN + scpdata_len ;
reipl_block_fcp - > hdr . blk0_len = IPL_PARM_BLK0_FCP_LEN + scpdata_len ;
return count ;
}
static struct bin_attribute sys_reipl_fcp_scp_data_attr = {
. attr = {
. name = " scp_data " ,
. mode = S_IRUGO | S_IWUSR ,
} ,
. size = PAGE_SIZE ,
. read = reipl_fcp_scpdata_read ,
. write = reipl_fcp_scpdata_write ,
} ;
2006-09-20 15:58:49 +02:00
DEFINE_IPL_ATTR_RW ( reipl_fcp , wwpn , " 0x%016llx \n " , " %016llx \n " ,
reipl_block_fcp - > ipl_info . fcp . wwpn ) ;
DEFINE_IPL_ATTR_RW ( reipl_fcp , lun , " 0x%016llx \n " , " %016llx \n " ,
reipl_block_fcp - > ipl_info . fcp . lun ) ;
DEFINE_IPL_ATTR_RW ( reipl_fcp , bootprog , " %lld \n " , " %lld \n " ,
reipl_block_fcp - > ipl_info . fcp . bootprog ) ;
DEFINE_IPL_ATTR_RW ( reipl_fcp , br_lba , " %lld \n " , " %lld \n " ,
reipl_block_fcp - > ipl_info . fcp . br_lba ) ;
DEFINE_IPL_ATTR_RW ( reipl_fcp , device , " 0.0.%04llx \n " , " 0.0.%llx \n " ,
reipl_block_fcp - > ipl_info . fcp . devno ) ;
static struct attribute * reipl_fcp_attrs [ ] = {
& sys_reipl_fcp_device_attr . attr ,
& sys_reipl_fcp_wwpn_attr . attr ,
& sys_reipl_fcp_lun_attr . attr ,
& sys_reipl_fcp_bootprog_attr . attr ,
& sys_reipl_fcp_br_lba_attr . attr ,
NULL ,
} ;
static struct attribute_group reipl_fcp_attr_group = {
. attrs = reipl_fcp_attrs ,
} ;
/* CCW reipl device attributes */
DEFINE_IPL_ATTR_RW ( reipl_ccw , device , " 0.0.%04llx \n " , " 0.0.%llx \n " ,
reipl_block_ccw - > ipl_info . ccw . devno ) ;
2008-07-14 09:59:09 +02:00
static void reipl_get_ascii_loadparm ( char * loadparm ,
struct ipl_parameter_block * ibp )
2006-12-04 15:39:58 +01:00
{
2008-07-14 09:59:09 +02:00
memcpy ( loadparm , ibp - > ipl_info . ccw . load_parm , LOADPARM_LEN ) ;
2006-12-04 15:39:58 +01:00
EBCASC ( loadparm , LOADPARM_LEN ) ;
loadparm [ LOADPARM_LEN ] = 0 ;
strstrip ( loadparm ) ;
}
2008-07-14 09:59:09 +02:00
static ssize_t reipl_generic_loadparm_show ( struct ipl_parameter_block * ipb ,
char * page )
2006-12-04 15:39:58 +01:00
{
char buf [ LOADPARM_LEN + 1 ] ;
2008-07-14 09:59:09 +02:00
reipl_get_ascii_loadparm ( buf , ipb ) ;
2006-12-04 15:39:58 +01:00
return sprintf ( page , " %s \n " , buf ) ;
}
2008-07-14 09:59:09 +02:00
static ssize_t reipl_generic_loadparm_store ( struct ipl_parameter_block * ipb ,
const char * buf , size_t len )
2006-12-04 15:39:58 +01:00
{
int i , lp_len ;
/* ignore trailing newline */
lp_len = len ;
if ( ( len > 0 ) & & ( buf [ len - 1 ] = = ' \n ' ) )
lp_len - - ;
/* loadparm can have max 8 characters and must not start with a blank */
if ( ( lp_len > LOADPARM_LEN ) | | ( ( lp_len > 0 ) & & ( buf [ 0 ] = = ' ' ) ) )
return - EINVAL ;
/* loadparm can only contain "a-z,A-Z,0-9,SP,." */
for ( i = 0 ; i < lp_len ; i + + ) {
if ( isalpha ( buf [ i ] ) | | isdigit ( buf [ i ] ) | | ( buf [ i ] = = ' ' ) | |
( buf [ i ] = = ' . ' ) )
continue ;
return - EINVAL ;
}
/* initialize loadparm with blanks */
2008-07-14 09:59:09 +02:00
memset ( ipb - > ipl_info . ccw . load_parm , ' ' , LOADPARM_LEN ) ;
2006-12-04 15:39:58 +01:00
/* copy and convert to ebcdic */
2008-07-14 09:59:09 +02:00
memcpy ( ipb - > ipl_info . ccw . load_parm , buf , lp_len ) ;
ASCEBC ( ipb - > ipl_info . ccw . load_parm , LOADPARM_LEN ) ;
2006-12-04 15:39:58 +01:00
return len ;
}
2008-07-14 09:59:09 +02:00
/* NSS wrapper */
static ssize_t reipl_nss_loadparm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return reipl_generic_loadparm_show ( reipl_block_nss , page ) ;
}
static ssize_t reipl_nss_loadparm_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return reipl_generic_loadparm_store ( reipl_block_nss , buf , len ) ;
}
/* CCW wrapper */
static ssize_t reipl_ccw_loadparm_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return reipl_generic_loadparm_show ( reipl_block_ccw , page ) ;
}
static ssize_t reipl_ccw_loadparm_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return reipl_generic_loadparm_store ( reipl_block_ccw , buf , len ) ;
}
2007-11-02 17:17:00 -04:00
static struct kobj_attribute sys_reipl_ccw_loadparm_attr =
2008-07-14 09:59:09 +02:00
__ATTR ( loadparm , S_IRUGO | S_IWUSR , reipl_ccw_loadparm_show ,
reipl_ccw_loadparm_store ) ;
static struct attribute * reipl_ccw_attrs_vm [ ] = {
& sys_reipl_ccw_device_attr . attr ,
& sys_reipl_ccw_loadparm_attr . attr ,
& sys_reipl_ccw_vmparm_attr . attr ,
NULL ,
} ;
2006-12-04 15:39:58 +01:00
2008-07-14 09:59:09 +02:00
static struct attribute * reipl_ccw_attrs_lpar [ ] = {
2006-09-20 15:58:49 +02:00
& sys_reipl_ccw_device_attr . attr ,
2006-12-04 15:39:58 +01:00
& sys_reipl_ccw_loadparm_attr . attr ,
2006-09-20 15:58:49 +02:00
NULL ,
} ;
2008-07-14 09:59:09 +02:00
static struct attribute_group reipl_ccw_attr_group_vm = {
2006-09-20 15:58:49 +02:00
. name = IPL_CCW_STR ,
2008-07-14 09:59:09 +02:00
. attrs = reipl_ccw_attrs_vm ,
} ;
static struct attribute_group reipl_ccw_attr_group_lpar = {
. name = IPL_CCW_STR ,
. attrs = reipl_ccw_attrs_lpar ,
2006-09-20 15:58:49 +02:00
} ;
2007-02-05 21:18:24 +01:00
/* NSS reipl device attributes */
2008-07-14 09:59:09 +02:00
static void reipl_get_ascii_nss_name ( char * dst ,
struct ipl_parameter_block * ipb )
{
memcpy ( dst , ipb - > ipl_info . ccw . nss_name , NSS_NAME_SIZE ) ;
EBCASC ( dst , NSS_NAME_SIZE ) ;
dst [ NSS_NAME_SIZE ] = 0 ;
}
static ssize_t reipl_nss_name_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
char nss_name [ NSS_NAME_SIZE + 1 ] = { } ;
reipl_get_ascii_nss_name ( nss_name , reipl_block_nss ) ;
return sprintf ( page , " %s \n " , nss_name ) ;
}
static ssize_t reipl_nss_name_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
int nss_len ;
/* ignore trailing newline */
nss_len = len ;
if ( ( len > 0 ) & & ( buf [ len - 1 ] = = ' \n ' ) )
nss_len - - ;
2007-02-05 21:18:24 +01:00
2008-07-14 09:59:09 +02:00
if ( nss_len > NSS_NAME_SIZE )
return - EINVAL ;
memset ( reipl_block_nss - > ipl_info . ccw . nss_name , 0x40 , NSS_NAME_SIZE ) ;
if ( nss_len > 0 ) {
reipl_block_nss - > ipl_info . ccw . vm_flags | =
DIAG308_VM_FLAGS_NSS_VALID ;
memcpy ( reipl_block_nss - > ipl_info . ccw . nss_name , buf , nss_len ) ;
ASCEBC ( reipl_block_nss - > ipl_info . ccw . nss_name , nss_len ) ;
EBC_TOUPPER ( reipl_block_nss - > ipl_info . ccw . nss_name , nss_len ) ;
} else {
reipl_block_nss - > ipl_info . ccw . vm_flags & =
~ DIAG308_VM_FLAGS_NSS_VALID ;
}
return len ;
}
static struct kobj_attribute sys_reipl_nss_name_attr =
__ATTR ( name , S_IRUGO | S_IWUSR , reipl_nss_name_show ,
reipl_nss_name_store ) ;
static struct kobj_attribute sys_reipl_nss_loadparm_attr =
__ATTR ( loadparm , S_IRUGO | S_IWUSR , reipl_nss_loadparm_show ,
reipl_nss_loadparm_store ) ;
2007-02-05 21:18:24 +01:00
static struct attribute * reipl_nss_attrs [ ] = {
& sys_reipl_nss_name_attr . attr ,
2008-07-14 09:59:09 +02:00
& sys_reipl_nss_loadparm_attr . attr ,
& sys_reipl_nss_vmparm_attr . attr ,
2007-02-05 21:18:24 +01:00
NULL ,
} ;
static struct attribute_group reipl_nss_attr_group = {
. name = IPL_NSS_STR ,
. attrs = reipl_nss_attrs ,
} ;
2006-09-20 15:58:49 +02:00
/* reipl type */
static int reipl_set_type ( enum ipl_type type )
{
if ( ! ( reipl_capabilities & type ) )
return - EINVAL ;
switch ( type ) {
case IPL_TYPE_CCW :
2008-01-26 14:11:17 +01:00
if ( diag308_set_works )
reipl_method = REIPL_METHOD_CCW_DIAG ;
else if ( MACHINE_IS_VM )
2007-04-27 16:01:49 +02:00
reipl_method = REIPL_METHOD_CCW_VM ;
2006-09-20 15:58:49 +02:00
else
2007-04-27 16:01:49 +02:00
reipl_method = REIPL_METHOD_CCW_CIO ;
2009-03-26 15:23:43 +01:00
reipl_block_actual = reipl_block_ccw ;
2006-09-20 15:58:49 +02:00
break ;
case IPL_TYPE_FCP :
if ( diag308_set_works )
2007-04-27 16:01:49 +02:00
reipl_method = REIPL_METHOD_FCP_RW_DIAG ;
2006-09-20 15:58:49 +02:00
else if ( MACHINE_IS_VM )
2007-04-27 16:01:49 +02:00
reipl_method = REIPL_METHOD_FCP_RO_VM ;
2006-09-20 15:58:49 +02:00
else
2007-04-27 16:01:49 +02:00
reipl_method = REIPL_METHOD_FCP_RO_DIAG ;
2009-03-26 15:23:43 +01:00
reipl_block_actual = reipl_block_fcp ;
2007-04-27 16:01:49 +02:00
break ;
case IPL_TYPE_FCP_DUMP :
reipl_method = REIPL_METHOD_FCP_DUMP ;
2006-09-20 15:58:49 +02:00
break ;
2007-02-05 21:18:24 +01:00
case IPL_TYPE_NSS :
2008-07-14 09:59:09 +02:00
if ( diag308_set_works )
reipl_method = REIPL_METHOD_NSS_DIAG ;
else
reipl_method = REIPL_METHOD_NSS ;
2009-03-26 15:23:43 +01:00
reipl_block_actual = reipl_block_nss ;
2007-04-27 16:01:49 +02:00
break ;
case IPL_TYPE_UNKNOWN :
reipl_method = REIPL_METHOD_DEFAULT ;
2007-02-05 21:18:24 +01:00
break ;
2006-09-20 15:58:49 +02:00
default :
2007-04-27 16:01:49 +02:00
BUG ( ) ;
2006-09-20 15:58:49 +02:00
}
reipl_type = type ;
return 0 ;
}
2007-11-02 17:17:00 -04:00
static ssize_t reipl_type_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
2006-09-20 15:58:49 +02:00
{
return sprintf ( page , " %s \n " , ipl_type_str ( reipl_type ) ) ;
}
2007-11-02 17:17:00 -04:00
static ssize_t reipl_type_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
2006-09-20 15:58:49 +02:00
{
int rc = - EINVAL ;
if ( strncmp ( buf , IPL_CCW_STR , strlen ( IPL_CCW_STR ) ) = = 0 )
rc = reipl_set_type ( IPL_TYPE_CCW ) ;
else if ( strncmp ( buf , IPL_FCP_STR , strlen ( IPL_FCP_STR ) ) = = 0 )
rc = reipl_set_type ( IPL_TYPE_FCP ) ;
2007-02-05 21:18:24 +01:00
else if ( strncmp ( buf , IPL_NSS_STR , strlen ( IPL_NSS_STR ) ) = = 0 )
rc = reipl_set_type ( IPL_TYPE_NSS ) ;
2006-09-20 15:58:49 +02:00
return ( rc ! = 0 ) ? rc : len ;
}
2007-11-02 17:17:00 -04:00
static struct kobj_attribute reipl_type_attr =
2008-01-26 14:11:11 +01:00
__ATTR ( reipl_type , 0644 , reipl_type_show , reipl_type_store ) ;
2006-09-20 15:58:49 +02:00
2007-11-02 17:17:00 -04:00
static struct kset * reipl_kset ;
2009-09-11 10:28:40 +02:00
static struct kset * reipl_fcp_kset ;
2006-09-20 15:58:49 +02:00
2008-07-14 09:59:09 +02:00
static void get_ipl_string ( char * dst , struct ipl_parameter_block * ipb ,
const enum ipl_method m )
{
char loadparm [ LOADPARM_LEN + 1 ] = { } ;
char vmparm [ DIAG308_VMPARM_SIZE + 1 ] = { } ;
char nss_name [ NSS_NAME_SIZE + 1 ] = { } ;
size_t pos = 0 ;
reipl_get_ascii_loadparm ( loadparm , ipb ) ;
reipl_get_ascii_nss_name ( nss_name , ipb ) ;
2009-09-11 10:28:40 +02:00
reipl_get_ascii_vmparm ( vmparm , sizeof ( vmparm ) , ipb ) ;
2008-07-14 09:59:09 +02:00
switch ( m ) {
case REIPL_METHOD_CCW_VM :
pos = sprintf ( dst , " IPL %X CLEAR " , ipb - > ipl_info . ccw . devno ) ;
break ;
case REIPL_METHOD_NSS :
pos = sprintf ( dst , " IPL %s " , nss_name ) ;
break ;
default :
break ;
}
if ( strlen ( loadparm ) > 0 )
pos + = sprintf ( dst + pos , " LOADPARM '%s' " , loadparm ) ;
if ( strlen ( vmparm ) > 0 )
sprintf ( dst + pos , " PARM %s " , vmparm ) ;
}
2008-04-17 07:46:26 +02:00
static void reipl_run ( struct shutdown_trigger * trigger )
2006-09-20 15:58:49 +02:00
{
struct ccw_dev_id devid ;
2008-07-14 09:59:09 +02:00
static char buf [ 128 ] ;
2006-09-20 15:58:49 +02:00
switch ( reipl_method ) {
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_CCW_CIO :
2006-09-20 15:58:49 +02:00
devid . devno = reipl_block_ccw - > ipl_info . ccw . devno ;
devid . ssid = 0 ;
reipl_ccw_dev ( & devid ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_CCW_VM :
2008-07-14 09:59:09 +02:00
get_ipl_string ( buf , reipl_block_ccw , REIPL_METHOD_CCW_VM ) ;
2006-12-04 15:40:30 +01:00
__cpcmd ( buf , NULL , 0 , NULL ) ;
2006-09-20 15:58:49 +02:00
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_CCW_DIAG :
2006-09-20 15:58:49 +02:00
diag308 ( DIAG308_SET , reipl_block_ccw ) ;
diag308 ( DIAG308_IPL , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_FCP_RW_DIAG :
2006-09-20 15:58:49 +02:00
diag308 ( DIAG308_SET , reipl_block_fcp ) ;
diag308 ( DIAG308_IPL , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_FCP_RO_DIAG :
2006-09-20 15:58:49 +02:00
diag308 ( DIAG308_IPL , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_FCP_RO_VM :
2006-12-04 15:40:30 +01:00
__cpcmd ( " IPL " , NULL , 0 , NULL ) ;
2006-09-20 15:58:49 +02:00
break ;
2008-07-14 09:59:09 +02:00
case REIPL_METHOD_NSS_DIAG :
diag308 ( DIAG308_SET , reipl_block_nss ) ;
diag308 ( DIAG308_IPL , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_NSS :
2008-07-14 09:59:09 +02:00
get_ipl_string ( buf , reipl_block_nss , REIPL_METHOD_NSS ) ;
2007-02-05 21:18:24 +01:00
__cpcmd ( buf , NULL , 0 , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_DEFAULT :
2006-09-20 15:58:49 +02:00
if ( MACHINE_IS_VM )
2006-12-04 15:40:30 +01:00
__cpcmd ( " IPL " , NULL , 0 , NULL ) ;
2006-09-20 15:58:49 +02:00
diag308 ( DIAG308_IPL , NULL ) ;
break ;
2007-04-27 16:01:49 +02:00
case REIPL_METHOD_FCP_DUMP :
break ;
2006-09-20 15:58:49 +02:00
}
2008-03-05 12:37:15 +01:00
disabled_wait ( ( unsigned long ) __builtin_return_address ( 0 ) ) ;
2006-09-20 15:58:49 +02:00
}
2008-07-14 09:59:09 +02:00
static void reipl_block_ccw_init ( struct ipl_parameter_block * ipb )
2006-09-20 15:58:49 +02:00
{
2008-07-14 09:59:09 +02:00
ipb - > hdr . len = IPL_PARM_BLK_CCW_LEN ;
ipb - > hdr . version = IPL_PARM_BLOCK_VERSION ;
ipb - > hdr . blk0_len = IPL_PARM_BLK0_CCW_LEN ;
ipb - > hdr . pbt = DIAG308_IPL_TYPE_CCW ;
}
2006-09-20 15:58:49 +02:00
2008-07-14 09:59:09 +02:00
static void reipl_block_ccw_fill_parms ( struct ipl_parameter_block * ipb )
{
/* LOADPARM */
/* check if read scp info worked and set loadparm */
if ( sclp_ipl_info . is_valid )
memcpy ( ipb - > ipl_info . ccw . load_parm ,
& sclp_ipl_info . loadparm , LOADPARM_LEN ) ;
else
/* read scp info failed: set empty loadparm (EBCDIC blanks) */
memset ( ipb - > ipl_info . ccw . load_parm , 0x40 , LOADPARM_LEN ) ;
ipb - > hdr . flags = DIAG308_FLAGS_LP_VALID ;
/* VM PARM */
if ( MACHINE_IS_VM & & diag308_set_works & &
( ipl_block . ipl_info . ccw . vm_flags & DIAG308_VM_FLAGS_VP_VALID ) ) {
ipb - > ipl_info . ccw . vm_flags | = DIAG308_VM_FLAGS_VP_VALID ;
ipb - > ipl_info . ccw . vm_parm_len =
ipl_block . ipl_info . ccw . vm_parm_len ;
memcpy ( ipb - > ipl_info . ccw . vm_parm ,
ipl_block . ipl_info . ccw . vm_parm , DIAG308_VMPARM_SIZE ) ;
}
2006-09-20 15:58:49 +02:00
}
2008-01-26 14:11:11 +01:00
static int __init reipl_nss_init ( void )
2006-09-20 15:58:49 +02:00
{
int rc ;
2008-01-26 14:11:11 +01:00
if ( ! MACHINE_IS_VM )
return 0 ;
2008-07-14 09:59:09 +02:00
reipl_block_nss = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! reipl_block_nss )
return - ENOMEM ;
if ( ! diag308_set_works )
sys_reipl_nss_vmparm_attr . attr . mode = S_IRUGO ;
2008-01-26 14:11:11 +01:00
rc = sysfs_create_group ( & reipl_kset - > kobj , & reipl_nss_attr_group ) ;
2007-02-05 21:18:24 +01:00
if ( rc )
return rc ;
2008-07-14 09:59:09 +02:00
reipl_block_ccw_init ( reipl_block_nss ) ;
if ( ipl_info . type = = IPL_TYPE_NSS ) {
memset ( reipl_block_nss - > ipl_info . ccw . nss_name ,
' ' , NSS_NAME_SIZE ) ;
memcpy ( reipl_block_nss - > ipl_info . ccw . nss_name ,
kernel_nss_name , strlen ( kernel_nss_name ) ) ;
ASCEBC ( reipl_block_nss - > ipl_info . ccw . nss_name , NSS_NAME_SIZE ) ;
reipl_block_nss - > ipl_info . ccw . vm_flags | =
DIAG308_VM_FLAGS_NSS_VALID ;
reipl_block_ccw_fill_parms ( reipl_block_nss ) ;
}
2007-02-05 21:18:24 +01:00
reipl_capabilities | = IPL_TYPE_NSS ;
return 0 ;
}
2006-09-20 15:58:49 +02:00
static int __init reipl_ccw_init ( void )
{
int rc ;
reipl_block_ccw = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! reipl_block_ccw )
return - ENOMEM ;
2008-07-14 09:59:09 +02:00
if ( MACHINE_IS_VM ) {
if ( ! diag308_set_works )
sys_reipl_ccw_vmparm_attr . attr . mode = S_IRUGO ;
rc = sysfs_create_group ( & reipl_kset - > kobj ,
& reipl_ccw_attr_group_vm ) ;
} else {
if ( ! diag308_set_works )
sys_reipl_ccw_loadparm_attr . attr . mode = S_IRUGO ;
rc = sysfs_create_group ( & reipl_kset - > kobj ,
& reipl_ccw_attr_group_lpar ) ;
2006-09-20 15:58:49 +02:00
}
2008-07-14 09:59:09 +02:00
if ( rc )
return rc ;
reipl_block_ccw_init ( reipl_block_ccw ) ;
if ( ipl_info . type = = IPL_TYPE_CCW ) {
2006-09-20 15:58:49 +02:00
reipl_block_ccw - > ipl_info . ccw . devno = ipl_devno ;
2008-07-14 09:59:09 +02:00
reipl_block_ccw_fill_parms ( reipl_block_ccw ) ;
}
2006-09-20 15:58:49 +02:00
reipl_capabilities | = IPL_TYPE_CCW ;
return 0 ;
}
static int __init reipl_fcp_init ( void )
{
int rc ;
2009-03-26 15:24:51 +01:00
if ( ! diag308_set_works ) {
2009-09-11 10:28:40 +02:00
if ( ipl_info . type = = IPL_TYPE_FCP ) {
2009-03-26 15:24:51 +01:00
make_attrs_ro ( reipl_fcp_attrs ) ;
2009-09-11 10:28:40 +02:00
sys_reipl_fcp_scp_data_attr . attr . mode = S_IRUGO ;
} else
2009-03-26 15:24:51 +01:00
return 0 ;
}
2006-09-20 15:58:49 +02:00
reipl_block_fcp = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! reipl_block_fcp )
return - ENOMEM ;
2009-09-11 10:28:40 +02:00
/* sysfs: create fcp kset for mixing attr group and bin attrs */
reipl_fcp_kset = kset_create_and_add ( IPL_FCP_STR , NULL ,
& reipl_kset - > kobj ) ;
if ( ! reipl_kset ) {
free_page ( ( unsigned long ) reipl_block_fcp ) ;
return - ENOMEM ;
}
rc = sysfs_create_group ( & reipl_fcp_kset - > kobj , & reipl_fcp_attr_group ) ;
2006-09-20 15:58:49 +02:00
if ( rc ) {
2009-09-11 10:28:40 +02:00
kset_unregister ( reipl_fcp_kset ) ;
free_page ( ( unsigned long ) reipl_block_fcp ) ;
2006-09-20 15:58:49 +02:00
return rc ;
}
2009-09-11 10:28:40 +02:00
rc = sysfs_create_bin_file ( & reipl_fcp_kset - > kobj ,
& sys_reipl_fcp_scp_data_attr ) ;
if ( rc ) {
sysfs_remove_group ( & reipl_fcp_kset - > kobj , & reipl_fcp_attr_group ) ;
kset_unregister ( reipl_fcp_kset ) ;
free_page ( ( unsigned long ) reipl_block_fcp ) ;
return rc ;
}
if ( ipl_info . type = = IPL_TYPE_FCP )
2006-09-20 15:58:49 +02:00
memcpy ( reipl_block_fcp , IPL_PARMBLOCK_START , PAGE_SIZE ) ;
2009-09-11 10:28:40 +02:00
else {
2006-09-20 15:58:49 +02:00
reipl_block_fcp - > hdr . len = IPL_PARM_BLK_FCP_LEN ;
reipl_block_fcp - > hdr . version = IPL_PARM_BLOCK_VERSION ;
2007-03-19 13:19:03 +01:00
reipl_block_fcp - > hdr . blk0_len = IPL_PARM_BLK0_FCP_LEN ;
2006-09-20 15:58:49 +02:00
reipl_block_fcp - > hdr . pbt = DIAG308_IPL_TYPE_FCP ;
reipl_block_fcp - > ipl_info . fcp . opt = DIAG308_IPL_OPT_IPL ;
}
reipl_capabilities | = IPL_TYPE_FCP ;
return 0 ;
}
2008-02-05 16:50:40 +01:00
static int __init reipl_init ( void )
2006-09-20 15:58:49 +02:00
{
int rc ;
2007-11-05 13:16:15 -08:00
reipl_kset = kset_create_and_add ( " reipl " , NULL , firmware_kobj ) ;
2007-11-02 17:17:00 -04:00
if ( ! reipl_kset )
return - ENOMEM ;
rc = sysfs_create_file ( & reipl_kset - > kobj , & reipl_type_attr . attr ) ;
2006-09-20 15:58:49 +02:00
if ( rc ) {
2007-11-02 17:17:00 -04:00
kset_unregister ( reipl_kset ) ;
2006-09-20 15:58:49 +02:00
return rc ;
}
rc = reipl_ccw_init ( ) ;
if ( rc )
return rc ;
rc = reipl_fcp_init ( ) ;
2007-02-05 21:18:24 +01:00
if ( rc )
return rc ;
rc = reipl_nss_init ( ) ;
2006-09-20 15:58:49 +02:00
if ( rc )
return rc ;
2007-04-27 16:01:49 +02:00
rc = reipl_set_type ( ipl_info . type ) ;
2006-09-20 15:58:49 +02:00
if ( rc )
return rc ;
return 0 ;
}
2008-02-05 16:50:40 +01:00
static struct shutdown_action __refdata reipl_action = {
. name = SHUTDOWN_ACTION_REIPL_STR ,
. fn = reipl_run ,
. init = reipl_init ,
} ;
2008-01-26 14:11:11 +01:00
/*
* dump shutdown action : Dump Linux on shutdown .
*/
/* FCP dump device attributes */
DEFINE_IPL_ATTR_RW ( dump_fcp , wwpn , " 0x%016llx \n " , " %016llx \n " ,
dump_block_fcp - > ipl_info . fcp . wwpn ) ;
DEFINE_IPL_ATTR_RW ( dump_fcp , lun , " 0x%016llx \n " , " %016llx \n " ,
dump_block_fcp - > ipl_info . fcp . lun ) ;
DEFINE_IPL_ATTR_RW ( dump_fcp , bootprog , " %lld \n " , " %lld \n " ,
dump_block_fcp - > ipl_info . fcp . bootprog ) ;
DEFINE_IPL_ATTR_RW ( dump_fcp , br_lba , " %lld \n " , " %lld \n " ,
dump_block_fcp - > ipl_info . fcp . br_lba ) ;
DEFINE_IPL_ATTR_RW ( dump_fcp , device , " 0.0.%04llx \n " , " 0.0.%llx \n " ,
dump_block_fcp - > ipl_info . fcp . devno ) ;
static struct attribute * dump_fcp_attrs [ ] = {
& sys_dump_fcp_device_attr . attr ,
& sys_dump_fcp_wwpn_attr . attr ,
& sys_dump_fcp_lun_attr . attr ,
& sys_dump_fcp_bootprog_attr . attr ,
& sys_dump_fcp_br_lba_attr . attr ,
NULL ,
} ;
static struct attribute_group dump_fcp_attr_group = {
. name = IPL_FCP_STR ,
. attrs = dump_fcp_attrs ,
} ;
/* CCW dump device attributes */
DEFINE_IPL_ATTR_RW ( dump_ccw , device , " 0.0.%04llx \n " , " 0.0.%llx \n " ,
dump_block_ccw - > ipl_info . ccw . devno ) ;
static struct attribute * dump_ccw_attrs [ ] = {
& sys_dump_ccw_device_attr . attr ,
NULL ,
} ;
static struct attribute_group dump_ccw_attr_group = {
. name = IPL_CCW_STR ,
. attrs = dump_ccw_attrs ,
} ;
/* dump type */
static int dump_set_type ( enum dump_type type )
{
if ( ! ( dump_capabilities & type ) )
return - EINVAL ;
switch ( type ) {
case DUMP_TYPE_CCW :
2008-01-26 14:11:17 +01:00
if ( diag308_set_works )
dump_method = DUMP_METHOD_CCW_DIAG ;
else if ( MACHINE_IS_VM )
2008-01-26 14:11:11 +01:00
dump_method = DUMP_METHOD_CCW_VM ;
else
dump_method = DUMP_METHOD_CCW_CIO ;
break ;
case DUMP_TYPE_FCP :
dump_method = DUMP_METHOD_FCP_DIAG ;
break ;
default :
dump_method = DUMP_METHOD_NONE ;
}
dump_type = type ;
return 0 ;
}
static ssize_t dump_type_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return sprintf ( page , " %s \n " , dump_type_str ( dump_type ) ) ;
}
static ssize_t dump_type_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
int rc = - EINVAL ;
if ( strncmp ( buf , DUMP_NONE_STR , strlen ( DUMP_NONE_STR ) ) = = 0 )
rc = dump_set_type ( DUMP_TYPE_NONE ) ;
else if ( strncmp ( buf , DUMP_CCW_STR , strlen ( DUMP_CCW_STR ) ) = = 0 )
rc = dump_set_type ( DUMP_TYPE_CCW ) ;
else if ( strncmp ( buf , DUMP_FCP_STR , strlen ( DUMP_FCP_STR ) ) = = 0 )
rc = dump_set_type ( DUMP_TYPE_FCP ) ;
return ( rc ! = 0 ) ? rc : len ;
}
static struct kobj_attribute dump_type_attr =
__ATTR ( dump_type , 0644 , dump_type_show , dump_type_store ) ;
static struct kset * dump_kset ;
static void dump_run ( struct shutdown_trigger * trigger )
{
struct ccw_dev_id devid ;
static char buf [ 100 ] ;
switch ( dump_method ) {
case DUMP_METHOD_CCW_CIO :
smp_send_stop ( ) ;
devid . devno = dump_block_ccw - > ipl_info . ccw . devno ;
devid . ssid = 0 ;
reipl_ccw_dev ( & devid ) ;
break ;
case DUMP_METHOD_CCW_VM :
smp_send_stop ( ) ;
sprintf ( buf , " STORE STATUS " ) ;
__cpcmd ( buf , NULL , 0 , NULL ) ;
sprintf ( buf , " IPL %X " , dump_block_ccw - > ipl_info . ccw . devno ) ;
__cpcmd ( buf , NULL , 0 , NULL ) ;
break ;
case DUMP_METHOD_CCW_DIAG :
diag308 ( DIAG308_SET , dump_block_ccw ) ;
diag308 ( DIAG308_DUMP , NULL ) ;
break ;
case DUMP_METHOD_FCP_DIAG :
diag308 ( DIAG308_SET , dump_block_fcp ) ;
diag308 ( DIAG308_DUMP , NULL ) ;
break ;
case DUMP_METHOD_NONE :
return ;
}
printk ( KERN_EMERG " Dump failed! \n " ) ;
}
2006-09-20 15:58:49 +02:00
static int __init dump_ccw_init ( void )
{
int rc ;
dump_block_ccw = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! dump_block_ccw )
return - ENOMEM ;
2007-11-02 17:17:00 -04:00
rc = sysfs_create_group ( & dump_kset - > kobj , & dump_ccw_attr_group ) ;
2006-09-20 15:58:49 +02:00
if ( rc ) {
free_page ( ( unsigned long ) dump_block_ccw ) ;
return rc ;
}
dump_block_ccw - > hdr . len = IPL_PARM_BLK_CCW_LEN ;
dump_block_ccw - > hdr . version = IPL_PARM_BLOCK_VERSION ;
2007-03-19 13:19:03 +01:00
dump_block_ccw - > hdr . blk0_len = IPL_PARM_BLK0_CCW_LEN ;
2006-09-20 15:58:49 +02:00
dump_block_ccw - > hdr . pbt = DIAG308_IPL_TYPE_CCW ;
2007-04-27 16:01:49 +02:00
dump_capabilities | = DUMP_TYPE_CCW ;
2006-09-20 15:58:49 +02:00
return 0 ;
}
static int __init dump_fcp_init ( void )
{
int rc ;
2007-07-10 11:24:09 +02:00
if ( ! sclp_ipl_info . has_dump )
2006-09-20 15:58:49 +02:00
return 0 ; /* LDIPL DUMP is not installed */
if ( ! diag308_set_works )
return 0 ;
dump_block_fcp = ( void * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! dump_block_fcp )
return - ENOMEM ;
2007-11-02 17:17:00 -04:00
rc = sysfs_create_group ( & dump_kset - > kobj , & dump_fcp_attr_group ) ;
2006-09-20 15:58:49 +02:00
if ( rc ) {
free_page ( ( unsigned long ) dump_block_fcp ) ;
return rc ;
}
dump_block_fcp - > hdr . len = IPL_PARM_BLK_FCP_LEN ;
dump_block_fcp - > hdr . version = IPL_PARM_BLOCK_VERSION ;
2007-03-19 13:19:03 +01:00
dump_block_fcp - > hdr . blk0_len = IPL_PARM_BLK0_FCP_LEN ;
2006-09-20 15:58:49 +02:00
dump_block_fcp - > hdr . pbt = DIAG308_IPL_TYPE_FCP ;
dump_block_fcp - > ipl_info . fcp . opt = DIAG308_IPL_OPT_DUMP ;
2007-04-27 16:01:49 +02:00
dump_capabilities | = DUMP_TYPE_FCP ;
2006-09-20 15:58:49 +02:00
return 0 ;
}
2008-02-05 16:50:40 +01:00
static int __init dump_init ( void )
2006-09-20 15:58:49 +02:00
{
int rc ;
2007-11-05 13:16:15 -08:00
dump_kset = kset_create_and_add ( " dump " , NULL , firmware_kobj ) ;
2007-11-02 17:17:00 -04:00
if ( ! dump_kset )
return - ENOMEM ;
2008-01-26 14:11:11 +01:00
rc = sysfs_create_file ( & dump_kset - > kobj , & dump_type_attr . attr ) ;
2006-09-20 15:58:49 +02:00
if ( rc ) {
2007-11-02 17:17:00 -04:00
kset_unregister ( dump_kset ) ;
2006-09-20 15:58:49 +02:00
return rc ;
}
rc = dump_ccw_init ( ) ;
if ( rc )
return rc ;
rc = dump_fcp_init ( ) ;
if ( rc )
return rc ;
2007-04-27 16:01:49 +02:00
dump_set_type ( DUMP_TYPE_NONE ) ;
2006-09-20 15:58:49 +02:00
return 0 ;
}
2008-02-05 16:50:40 +01:00
static struct shutdown_action __refdata dump_action = {
. name = SHUTDOWN_ACTION_DUMP_STR ,
. fn = dump_run ,
. init = dump_init ,
} ;
2008-01-26 14:11:11 +01:00
2009-03-26 15:23:43 +01:00
static void dump_reipl_run ( struct shutdown_trigger * trigger )
{
preempt_disable ( ) ;
/*
* Bypass dynamic address translation ( DAT ) when storing IPL parameter
* information block address and checksum into the prefix area
* ( corresponding to absolute addresses 0 - 8191 ) .
* When enhanced DAT applies and the STE format control in one ,
* the absolute address is formed without prefixing . In this case a
* normal store ( stg / st ) into the prefix area would no more match to
* absolute addresses 0 - 8191.
*/
# ifdef CONFIG_64BIT
asm volatile ( " sturg %0,%1 "
: : " a " ( ( unsigned long ) reipl_block_actual ) ,
" a " ( & lowcore_ptr [ smp_processor_id ( ) ] - > ipib ) ) ;
# else
asm volatile ( " stura %0,%1 "
: : " a " ( ( unsigned long ) reipl_block_actual ) ,
" a " ( & lowcore_ptr [ smp_processor_id ( ) ] - > ipib ) ) ;
# endif
asm volatile ( " stura %0,%1 "
2009-03-26 15:24:45 +01:00
: : " a " ( csum_partial ( reipl_block_actual ,
reipl_block_actual - > hdr . len , 0 ) ) ,
2009-03-26 15:23:43 +01:00
" a " ( & lowcore_ptr [ smp_processor_id ( ) ] - > ipib_checksum ) ) ;
preempt_enable ( ) ;
dump_run ( trigger ) ;
}
static int __init dump_reipl_init ( void )
{
if ( ! diag308_set_works )
return - EOPNOTSUPP ;
else
return 0 ;
}
static struct shutdown_action __refdata dump_reipl_action = {
. name = SHUTDOWN_ACTION_DUMP_REIPL_STR ,
. fn = dump_reipl_run ,
. init = dump_reipl_init ,
} ;
2008-01-26 14:11:11 +01:00
/*
* vmcmd shutdown action : Trigger vm command on shutdown .
*/
static char vmcmd_on_reboot [ 128 ] ;
static char vmcmd_on_panic [ 128 ] ;
static char vmcmd_on_halt [ 128 ] ;
static char vmcmd_on_poff [ 128 ] ;
DEFINE_IPL_ATTR_STR_RW ( vmcmd , on_reboot , " %s \n " , " %s \n " , vmcmd_on_reboot ) ;
DEFINE_IPL_ATTR_STR_RW ( vmcmd , on_panic , " %s \n " , " %s \n " , vmcmd_on_panic ) ;
DEFINE_IPL_ATTR_STR_RW ( vmcmd , on_halt , " %s \n " , " %s \n " , vmcmd_on_halt ) ;
DEFINE_IPL_ATTR_STR_RW ( vmcmd , on_poff , " %s \n " , " %s \n " , vmcmd_on_poff ) ;
static struct attribute * vmcmd_attrs [ ] = {
& sys_vmcmd_on_reboot_attr . attr ,
& sys_vmcmd_on_panic_attr . attr ,
& sys_vmcmd_on_halt_attr . attr ,
& sys_vmcmd_on_poff_attr . attr ,
NULL ,
} ;
static struct attribute_group vmcmd_attr_group = {
. attrs = vmcmd_attrs ,
} ;
static struct kset * vmcmd_kset ;
static void vmcmd_run ( struct shutdown_trigger * trigger )
2006-09-20 15:58:49 +02:00
{
2008-01-26 14:11:11 +01:00
char * cmd , * next_cmd ;
if ( strcmp ( trigger - > name , ON_REIPL_STR ) = = 0 )
cmd = vmcmd_on_reboot ;
else if ( strcmp ( trigger - > name , ON_PANIC_STR ) = = 0 )
cmd = vmcmd_on_panic ;
else if ( strcmp ( trigger - > name , ON_HALT_STR ) = = 0 )
cmd = vmcmd_on_halt ;
else if ( strcmp ( trigger - > name , ON_POFF_STR ) = = 0 )
cmd = vmcmd_on_poff ;
else
return ;
if ( strlen ( cmd ) = = 0 )
return ;
do {
next_cmd = strchr ( cmd , ' \n ' ) ;
if ( next_cmd ) {
next_cmd [ 0 ] = 0 ;
next_cmd + = 1 ;
}
__cpcmd ( cmd , NULL , 0 , NULL ) ;
cmd = next_cmd ;
} while ( cmd ! = NULL ) ;
}
static int vmcmd_init ( void )
{
if ( ! MACHINE_IS_VM )
return - ENOTSUPP ;
vmcmd_kset = kset_create_and_add ( " vmcmd " , NULL , firmware_kobj ) ;
if ( ! vmcmd_kset )
return - ENOMEM ;
return sysfs_create_group ( & vmcmd_kset - > kobj , & vmcmd_attr_group ) ;
}
static struct shutdown_action vmcmd_action = { SHUTDOWN_ACTION_VMCMD_STR ,
vmcmd_run , vmcmd_init } ;
/*
* stop shutdown action : Stop Linux on shutdown .
*/
static void stop_run ( struct shutdown_trigger * trigger )
{
2008-01-26 14:11:12 +01:00
if ( strcmp ( trigger - > name , ON_PANIC_STR ) = = 0 )
disabled_wait ( ( unsigned long ) __builtin_return_address ( 0 ) ) ;
2009-10-29 15:04:13 +01:00
while ( signal_processor ( smp_processor_id ( ) , sigp_stop ) = = sigp_busy )
cpu_relax ( ) ;
for ( ; ; ) ;
2008-01-26 14:11:11 +01:00
}
static struct shutdown_action stop_action = { SHUTDOWN_ACTION_STOP_STR ,
stop_run , NULL } ;
/* action list */
static struct shutdown_action * shutdown_actions_list [ ] = {
2009-03-26 15:23:43 +01:00
& ipl_action , & reipl_action , & dump_reipl_action , & dump_action ,
& vmcmd_action , & stop_action } ;
2008-01-26 14:11:11 +01:00
# define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *))
/*
* Trigger section
*/
static struct kset * shutdown_actions_kset ;
static int set_trigger ( const char * buf , struct shutdown_trigger * trigger ,
size_t len )
{
int i ;
2009-03-26 15:23:43 +01:00
2008-01-26 14:11:11 +01:00
for ( i = 0 ; i < SHUTDOWN_ACTIONS_COUNT ; i + + ) {
2009-03-26 15:23:43 +01:00
if ( sysfs_streq ( buf , shutdown_actions_list [ i ] - > name ) ) {
2009-07-07 16:37:07 +02:00
if ( shutdown_actions_list [ i ] - > init_rc ) {
return shutdown_actions_list [ i ] - > init_rc ;
} else {
trigger - > action = shutdown_actions_list [ i ] ;
return len ;
}
2008-01-26 14:11:11 +01:00
}
}
return - EINVAL ;
}
/* on reipl */
static struct shutdown_trigger on_reboot_trigger = { ON_REIPL_STR ,
& reipl_action } ;
static ssize_t on_reboot_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return sprintf ( page , " %s \n " , on_reboot_trigger . action - > name ) ;
}
static ssize_t on_reboot_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return set_trigger ( buf , & on_reboot_trigger , len ) ;
}
static struct kobj_attribute on_reboot_attr =
__ATTR ( on_reboot , 0644 , on_reboot_show , on_reboot_store ) ;
static void do_machine_restart ( char * __unused )
{
smp_send_stop ( ) ;
on_reboot_trigger . action - > fn ( & on_reboot_trigger ) ;
reipl_run ( NULL ) ;
}
void ( * _machine_restart ) ( char * command ) = do_machine_restart ;
/* on panic */
static struct shutdown_trigger on_panic_trigger = { ON_PANIC_STR , & stop_action } ;
static ssize_t on_panic_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return sprintf ( page , " %s \n " , on_panic_trigger . action - > name ) ;
}
static ssize_t on_panic_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return set_trigger ( buf , & on_panic_trigger , len ) ;
}
static struct kobj_attribute on_panic_attr =
__ATTR ( on_panic , 0644 , on_panic_show , on_panic_store ) ;
static void do_panic ( void )
{
on_panic_trigger . action - > fn ( & on_panic_trigger ) ;
stop_run ( & on_panic_trigger ) ;
}
/* on halt */
static struct shutdown_trigger on_halt_trigger = { ON_HALT_STR , & stop_action } ;
static ssize_t on_halt_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return sprintf ( page , " %s \n " , on_halt_trigger . action - > name ) ;
}
static ssize_t on_halt_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return set_trigger ( buf , & on_halt_trigger , len ) ;
}
static struct kobj_attribute on_halt_attr =
__ATTR ( on_halt , 0644 , on_halt_show , on_halt_store ) ;
2006-09-20 15:58:49 +02:00
2008-01-26 14:11:11 +01:00
static void do_machine_halt ( void )
{
smp_send_stop ( ) ;
on_halt_trigger . action - > fn ( & on_halt_trigger ) ;
stop_run ( & on_halt_trigger ) ;
}
void ( * _machine_halt ) ( void ) = do_machine_halt ;
/* on power off */
static struct shutdown_trigger on_poff_trigger = { ON_POFF_STR , & stop_action } ;
static ssize_t on_poff_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * page )
{
return sprintf ( page , " %s \n " , on_poff_trigger . action - > name ) ;
}
static ssize_t on_poff_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t len )
{
return set_trigger ( buf , & on_poff_trigger , len ) ;
}
static struct kobj_attribute on_poff_attr =
__ATTR ( on_poff , 0644 , on_poff_show , on_poff_store ) ;
static void do_machine_power_off ( void )
{
smp_send_stop ( ) ;
on_poff_trigger . action - > fn ( & on_poff_trigger ) ;
stop_run ( & on_poff_trigger ) ;
}
void ( * _machine_power_off ) ( void ) = do_machine_power_off ;
static void __init shutdown_triggers_init ( void )
{
2007-11-02 17:17:00 -04:00
shutdown_actions_kset = kset_create_and_add ( " shutdown_actions " , NULL ,
2007-11-05 13:16:15 -08:00
firmware_kobj ) ;
2007-11-02 17:17:00 -04:00
if ( ! shutdown_actions_kset )
2008-01-26 14:11:11 +01:00
goto fail ;
if ( sysfs_create_file ( & shutdown_actions_kset - > kobj ,
& on_reboot_attr . attr ) )
goto fail ;
if ( sysfs_create_file ( & shutdown_actions_kset - > kobj ,
& on_panic_attr . attr ) )
goto fail ;
if ( sysfs_create_file ( & shutdown_actions_kset - > kobj ,
& on_halt_attr . attr ) )
goto fail ;
if ( sysfs_create_file ( & shutdown_actions_kset - > kobj ,
& on_poff_attr . attr ) )
goto fail ;
return ;
fail :
panic ( " shutdown_triggers_init failed \n " ) ;
}
static void __init shutdown_actions_init ( void )
{
int i ;
for ( i = 0 ; i < SHUTDOWN_ACTIONS_COUNT ; i + + ) {
if ( ! shutdown_actions_list [ i ] - > init )
continue ;
2009-07-07 16:37:07 +02:00
shutdown_actions_list [ i ] - > init_rc =
shutdown_actions_list [ i ] - > init ( ) ;
2006-09-20 15:58:49 +02:00
}
}
static int __init s390_ipl_init ( void )
{
2008-01-26 14:11:15 +01:00
sclp_get_ipl_info ( & sclp_ipl_info ) ;
2008-01-26 14:11:11 +01:00
shutdown_actions_init ( ) ;
shutdown_triggers_init ( ) ;
2006-09-20 15:58:49 +02:00
return 0 ;
}
__initcall ( s390_ipl_init ) ;
2006-12-04 15:40:26 +01:00
2008-01-26 14:11:11 +01:00
static void __init strncpy_skip_quote ( char * dst , char * src , int n )
{
int sx , dx ;
dx = 0 ;
for ( sx = 0 ; src [ sx ] ! = 0 ; sx + + ) {
if ( src [ sx ] = = ' " ' )
continue ;
dst [ dx + + ] = src [ sx ] ;
if ( dx > = n )
break ;
}
}
static int __init vmcmd_on_reboot_setup ( char * str )
{
if ( ! MACHINE_IS_VM )
return 1 ;
strncpy_skip_quote ( vmcmd_on_reboot , str , 127 ) ;
vmcmd_on_reboot [ 127 ] = 0 ;
on_reboot_trigger . action = & vmcmd_action ;
return 1 ;
}
__setup ( " vmreboot= " , vmcmd_on_reboot_setup ) ;
static int __init vmcmd_on_panic_setup ( char * str )
{
if ( ! MACHINE_IS_VM )
return 1 ;
strncpy_skip_quote ( vmcmd_on_panic , str , 127 ) ;
vmcmd_on_panic [ 127 ] = 0 ;
on_panic_trigger . action = & vmcmd_action ;
return 1 ;
}
__setup ( " vmpanic= " , vmcmd_on_panic_setup ) ;
static int __init vmcmd_on_halt_setup ( char * str )
{
if ( ! MACHINE_IS_VM )
return 1 ;
strncpy_skip_quote ( vmcmd_on_halt , str , 127 ) ;
vmcmd_on_halt [ 127 ] = 0 ;
on_halt_trigger . action = & vmcmd_action ;
return 1 ;
}
__setup ( " vmhalt= " , vmcmd_on_halt_setup ) ;
static int __init vmcmd_on_poff_setup ( char * str )
{
if ( ! MACHINE_IS_VM )
return 1 ;
strncpy_skip_quote ( vmcmd_on_poff , str , 127 ) ;
vmcmd_on_poff [ 127 ] = 0 ;
on_poff_trigger . action = & vmcmd_action ;
return 1 ;
}
__setup ( " vmpoff= " , vmcmd_on_poff_setup ) ;
static int on_panic_notify ( struct notifier_block * self ,
unsigned long event , void * data )
{
do_panic ( ) ;
return NOTIFY_OK ;
}
static struct notifier_block on_panic_nb = {
. notifier_call = on_panic_notify ,
2009-03-26 15:24:55 +01:00
. priority = INT_MIN ,
2008-01-26 14:11:11 +01:00
} ;
void __init setup_ipl ( void )
{
ipl_info . type = get_ipl_type ( ) ;
switch ( ipl_info . type ) {
case IPL_TYPE_CCW :
ipl_info . data . ccw . dev_id . devno = ipl_devno ;
ipl_info . data . ccw . dev_id . ssid = 0 ;
break ;
case IPL_TYPE_FCP :
case IPL_TYPE_FCP_DUMP :
ipl_info . data . fcp . dev_id . devno =
IPL_PARMBLOCK_START - > ipl_info . fcp . devno ;
ipl_info . data . fcp . dev_id . ssid = 0 ;
ipl_info . data . fcp . wwpn = IPL_PARMBLOCK_START - > ipl_info . fcp . wwpn ;
ipl_info . data . fcp . lun = IPL_PARMBLOCK_START - > ipl_info . fcp . lun ;
break ;
case IPL_TYPE_NSS :
strncpy ( ipl_info . data . nss . name , kernel_nss_name ,
sizeof ( ipl_info . data . nss . name ) ) ;
break ;
case IPL_TYPE_UNKNOWN :
/* We have no info to copy */
break ;
}
atomic_notifier_chain_register ( & panic_notifier_list , & on_panic_nb ) ;
}
2008-07-14 09:59:09 +02:00
void __init ipl_update_parameters ( void )
{
2008-08-01 16:39:10 +02:00
int rc ;
rc = diag308 ( DIAG308_STORE , & ipl_block ) ;
if ( ( rc = = DIAG308_RC_OK ) | | ( rc = = DIAG308_RC_NOCONFIG ) )
2008-07-14 09:59:09 +02:00
diag308_set_works = 1 ;
}
2007-04-27 16:01:25 +02:00
void __init ipl_save_parameters ( void )
{
struct cio_iplinfo iplinfo ;
unsigned int * ipl_ptr ;
void * src , * dst ;
if ( cio_get_iplinfo ( & iplinfo ) )
return ;
ipl_devno = iplinfo . devno ;
ipl_flags | = IPL_DEVNO_VALID ;
if ( ! iplinfo . is_qdio )
return ;
ipl_flags | = IPL_PARMBLOCK_VALID ;
ipl_ptr = ( unsigned int * ) __LC_IPL_PARMBLOCK_PTR ;
src = ( void * ) ( unsigned long ) * ipl_ptr ;
dst = ( void * ) IPL_PARMBLOCK_ORIGIN ;
memmove ( dst , src , PAGE_SIZE ) ;
* ipl_ptr = IPL_PARMBLOCK_ORIGIN ;
}
2006-12-04 15:40:26 +01:00
static LIST_HEAD ( rcall ) ;
static DEFINE_MUTEX ( rcall_mutex ) ;
void register_reset_call ( struct reset_call * reset )
{
mutex_lock ( & rcall_mutex ) ;
list_add ( & reset - > list , & rcall ) ;
mutex_unlock ( & rcall_mutex ) ;
}
EXPORT_SYMBOL_GPL ( register_reset_call ) ;
void unregister_reset_call ( struct reset_call * reset )
{
mutex_lock ( & rcall_mutex ) ;
list_del ( & reset - > list ) ;
mutex_unlock ( & rcall_mutex ) ;
}
EXPORT_SYMBOL_GPL ( unregister_reset_call ) ;
static void do_reset_calls ( void )
{
struct reset_call * reset ;
list_for_each_entry ( reset , & rcall , list )
reset - > fn ( ) ;
}
2007-03-05 23:35:43 +01:00
u32 dump_prefix_page ;
2006-12-04 15:40:26 +01:00
void s390_reset_system ( void )
{
struct _lowcore * lc ;
lc = ( struct _lowcore * ) ( unsigned long ) store_prefix ( ) ;
2006-12-15 17:18:22 +01:00
/* Stack for interrupt/machine check handler */
2006-12-04 15:40:26 +01:00
lc - > panic_stack = S390_lowcore . panic_stack ;
2006-12-15 17:18:27 +01:00
/* Save prefix page address for dump case */
2007-03-05 23:35:43 +01:00
dump_prefix_page = ( u32 ) ( unsigned long ) lc ;
2006-12-15 17:18:27 +01:00
2006-12-04 15:40:26 +01:00
/* Disable prefixing */
set_prefix ( 0 ) ;
/* Disable lowcore protection */
__ctl_clear_bit ( 0 , 28 ) ;
/* Set new machine check handler */
2007-02-05 21:18:17 +01:00
S390_lowcore . mcck_new_psw . mask = psw_kernel_bits & ~ PSW_MASK_MCHECK ;
2006-12-04 15:40:26 +01:00
S390_lowcore . mcck_new_psw . addr =
2007-02-05 21:18:37 +01:00
PSW_ADDR_AMODE | ( unsigned long ) s390_base_mcck_handler ;
2006-12-15 17:18:22 +01:00
/* Set new program check handler */
2007-02-05 21:18:17 +01:00
S390_lowcore . program_new_psw . mask = psw_kernel_bits & ~ PSW_MASK_MCHECK ;
2006-12-15 17:18:22 +01:00
S390_lowcore . program_new_psw . addr =
2007-02-05 21:18:37 +01:00
PSW_ADDR_AMODE | ( unsigned long ) s390_base_pgm_handler ;
2006-12-15 17:18:22 +01:00
2006-12-04 15:40:26 +01:00
do_reset_calls ( ) ;
}
2008-01-26 14:11:11 +01:00