2007-04-27 16:01:49 +02:00
/*
* zcore module to export memory content and register sets for creating system
* dumps on SCSI disks ( zfcpdump ) . The " zcore/mem " debugfs file shows the same
* dump format as s390 standalone dumps .
*
* For more information please refer to Documentation / s390 / zfcpdump . txt
*
2009-03-26 15:23:43 +01:00
* Copyright IBM Corp . 2003 , 2008
2007-04-27 16:01:49 +02:00
* Author ( s ) : Michael Holzheu
*/
2008-12-25 13:39:51 +01:00
# define KMSG_COMPONENT "zdump"
# define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
2007-04-27 16:01:49 +02:00
# include <linux/init.h>
# include <linux/miscdevice.h>
# include <linux/utsname.h>
# include <linux/debugfs.h>
# include <asm/ipl.h>
# include <asm/sclp.h>
# include <asm/setup.h>
# include <asm/sigp.h>
# include <asm/uaccess.h>
# include <asm/debug.h>
# include <asm/processor.h>
# include <asm/irqflags.h>
2007-05-10 15:45:46 +02:00
# include "sclp.h"
2007-04-27 16:01:49 +02:00
# define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
# define TO_USER 0
# define TO_KERNEL 1
2008-07-17 17:16:40 +02:00
# define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */
2007-04-27 16:01:49 +02:00
enum arch_id {
ARCH_S390 = 0 ,
ARCH_S390X = 1 ,
} ;
/* dump system info */
struct sys_info {
enum arch_id arch ;
unsigned long sa_base ;
u32 sa_size ;
int cpu_map [ NR_CPUS ] ;
unsigned long mem_size ;
union save_area lc_mask ;
} ;
2009-03-26 15:23:43 +01:00
struct ipib_info {
unsigned long ipib ;
u32 checksum ;
} __attribute__ ( ( packed ) ) ;
2007-04-27 16:01:49 +02:00
static struct sys_info sys_info ;
static struct debug_info * zcore_dbf ;
static int hsa_available ;
static struct dentry * zcore_dir ;
static struct dentry * zcore_file ;
2008-07-17 17:16:40 +02:00
static struct dentry * zcore_memmap_file ;
2009-03-26 15:23:43 +01:00
static struct dentry * zcore_reipl_file ;
static struct ipl_parameter_block * ipl_block ;
2007-04-27 16:01:49 +02:00
/*
* Copy memory from HSA to kernel or user memory ( not reentrant ) :
*
* @ dest : Kernel or user buffer where memory should be copied to
* @ src : Start address within HSA where data should be copied
* @ count : Size of buffer , which should be copied
* @ mode : Either TO_KERNEL or TO_USER
*/
static int memcpy_hsa ( void * dest , unsigned long src , size_t count , int mode )
{
int offs , blk_num ;
static char buf [ PAGE_SIZE ] __attribute__ ( ( __aligned__ ( PAGE_SIZE ) ) ) ;
if ( count = = 0 )
return 0 ;
/* copy first block */
offs = 0 ;
if ( ( src % PAGE_SIZE ) ! = 0 ) {
blk_num = src / PAGE_SIZE + 2 ;
if ( sclp_sdias_copy ( buf , blk_num , 1 ) ) {
TRACE ( " sclp_sdias_copy() failed \n " ) ;
return - EIO ;
}
offs = min ( ( PAGE_SIZE - ( src % PAGE_SIZE ) ) , count ) ;
if ( mode = = TO_USER ) {
if ( copy_to_user ( ( __force __user void * ) dest ,
buf + ( src % PAGE_SIZE ) , offs ) )
return - EFAULT ;
} else
memcpy ( dest , buf + ( src % PAGE_SIZE ) , offs ) ;
}
if ( offs = = count )
goto out ;
/* copy middle */
for ( ; ( offs + PAGE_SIZE ) < = count ; offs + = PAGE_SIZE ) {
blk_num = ( src + offs ) / PAGE_SIZE + 2 ;
if ( sclp_sdias_copy ( buf , blk_num , 1 ) ) {
TRACE ( " sclp_sdias_copy() failed \n " ) ;
return - EIO ;
}
if ( mode = = TO_USER ) {
if ( copy_to_user ( ( __force __user void * ) dest + offs ,
buf , PAGE_SIZE ) )
return - EFAULT ;
} else
memcpy ( dest + offs , buf , PAGE_SIZE ) ;
}
if ( offs = = count )
goto out ;
/* copy last block */
blk_num = ( src + offs ) / PAGE_SIZE + 2 ;
if ( sclp_sdias_copy ( buf , blk_num , 1 ) ) {
TRACE ( " sclp_sdias_copy() failed \n " ) ;
return - EIO ;
}
if ( mode = = TO_USER ) {
if ( copy_to_user ( ( __force __user void * ) dest + offs , buf ,
PAGE_SIZE ) )
return - EFAULT ;
} else
memcpy ( dest + offs , buf , count - offs ) ;
out :
return 0 ;
}
static int memcpy_hsa_user ( void __user * dest , unsigned long src , size_t count )
{
return memcpy_hsa ( ( void __force * ) dest , src , count , TO_USER ) ;
}
static int memcpy_hsa_kernel ( void * dest , unsigned long src , size_t count )
{
return memcpy_hsa ( dest , src , count , TO_KERNEL ) ;
}
static int memcpy_real ( void * dest , unsigned long src , size_t count )
{
unsigned long flags ;
int rc = - EFAULT ;
register unsigned long _dest asm ( " 2 " ) = ( unsigned long ) dest ;
register unsigned long _len1 asm ( " 3 " ) = ( unsigned long ) count ;
register unsigned long _src asm ( " 4 " ) = src ;
register unsigned long _len2 asm ( " 5 " ) = ( unsigned long ) count ;
if ( count = = 0 )
return 0 ;
2007-10-12 16:11:44 +02:00
flags = __raw_local_irq_stnsm ( 0xf8UL ) ; /* switch to real mode */
2007-04-27 16:01:49 +02:00
asm volatile (
" 0: mvcle %1,%2,0x0 \n "
" 1: jo 0b \n "
" lhi %0,0x0 \n "
" 2: \n "
EX_TABLE ( 1 b , 2 b )
2007-10-12 16:11:44 +02:00
: " +d " ( rc ) , " +d " ( _dest ) , " +d " ( _src ) , " +d " ( _len1 ) ,
" +d " ( _len2 ) , " =m " ( * ( ( long * ) dest ) )
: " m " ( * ( ( long * ) src ) )
2007-04-27 16:01:49 +02:00
: " cc " , " memory " ) ;
__raw_local_irq_ssm ( flags ) ;
return rc ;
}
2007-07-10 11:24:12 +02:00
static int memcpy_real_user ( void __user * dest , unsigned long src , size_t count )
2007-04-27 16:01:49 +02:00
{
static char buf [ 4096 ] ;
int offs = 0 , size ;
while ( offs < count ) {
size = min ( sizeof ( buf ) , count - offs ) ;
if ( memcpy_real ( buf , src + offs , size ) )
return - EFAULT ;
if ( copy_to_user ( dest + offs , buf , size ) )
return - EFAULT ;
offs + = size ;
}
return 0 ;
}
# ifdef __s390x__
/*
* Convert s390x ( 64 bit ) cpu info to s390 ( 32 bit ) cpu info
*/
static void __init s390x_to_s390_regs ( union save_area * out , union save_area * in ,
int cpu )
{
int i ;
for ( i = 0 ; i < 16 ; i + + ) {
out - > s390 . gp_regs [ i ] = in - > s390x . gp_regs [ i ] & 0x00000000ffffffff ;
out - > s390 . acc_regs [ i ] = in - > s390x . acc_regs [ i ] ;
out - > s390 . ctrl_regs [ i ] =
in - > s390x . ctrl_regs [ i ] & 0x00000000ffffffff ;
}
/* locore for 31 bit has only space for fpregs 0,2,4,6 */
out - > s390 . fp_regs [ 0 ] = in - > s390x . fp_regs [ 0 ] ;
out - > s390 . fp_regs [ 1 ] = in - > s390x . fp_regs [ 2 ] ;
out - > s390 . fp_regs [ 2 ] = in - > s390x . fp_regs [ 4 ] ;
out - > s390 . fp_regs [ 3 ] = in - > s390x . fp_regs [ 6 ] ;
memcpy ( & ( out - > s390 . psw [ 0 ] ) , & ( in - > s390x . psw [ 0 ] ) , 4 ) ;
out - > s390 . psw [ 1 ] | = 0x8 ; /* set bit 12 */
memcpy ( & ( out - > s390 . psw [ 4 ] ) , & ( in - > s390x . psw [ 12 ] ) , 4 ) ;
out - > s390 . psw [ 4 ] | = 0x80 ; /* set (31bit) addressing bit */
out - > s390 . pref_reg = in - > s390x . pref_reg ;
out - > s390 . timer = in - > s390x . timer ;
out - > s390 . clk_cmp = in - > s390x . clk_cmp ;
}
static void __init s390x_to_s390_save_areas ( void )
{
int i = 1 ;
static union save_area tmp ;
while ( zfcpdump_save_areas [ i ] ) {
s390x_to_s390_regs ( & tmp , zfcpdump_save_areas [ i ] , i ) ;
memcpy ( zfcpdump_save_areas [ i ] , & tmp , sizeof ( tmp ) ) ;
i + + ;
}
}
# endif /* __s390x__ */
static int __init init_cpu_info ( enum arch_id arch )
{
union save_area * sa ;
/* get info for boot cpu from lowcore, stored in the HSA */
sa = kmalloc ( sizeof ( * sa ) , GFP_KERNEL ) ;
2008-07-14 09:59:38 +02:00
if ( ! sa )
2007-04-27 16:01:49 +02:00
return - ENOMEM ;
if ( memcpy_hsa_kernel ( sa , sys_info . sa_base , sys_info . sa_size ) < 0 ) {
2008-07-14 09:59:38 +02:00
TRACE ( " could not copy from HSA \n " ) ;
2007-04-27 16:01:49 +02:00
kfree ( sa ) ;
return - EIO ;
}
zfcpdump_save_areas [ 0 ] = sa ;
# ifdef __s390x__
/* convert s390x regs to s390, if we are dumping an s390 Linux */
if ( arch = = ARCH_S390 )
s390x_to_s390_save_areas ( ) ;
# endif
return 0 ;
}
static DEFINE_MUTEX ( zcore_mutex ) ;
# define DUMP_VERSION 0x3
# define DUMP_MAGIC 0xa8190173618f23fdULL
# define DUMP_ARCH_S390X 2
# define DUMP_ARCH_S390 1
# define HEADER_SIZE 4096
/* dump header dumped according to s390 crash dump format */
struct zcore_header {
u64 magic ;
u32 version ;
u32 header_size ;
u32 dump_level ;
u32 page_size ;
u64 mem_size ;
u64 mem_start ;
u64 mem_end ;
u32 num_pages ;
u32 pad1 ;
u64 tod ;
cpuid_t cpu_id ;
u32 arch_id ;
2007-06-19 13:10:02 +02:00
u32 volnr ;
2007-04-27 16:01:49 +02:00
u32 build_arch ;
2007-06-19 13:10:02 +02:00
u64 rmem_size ;
2007-04-27 16:01:49 +02:00
char pad2 [ 4016 ] ;
} __attribute__ ( ( packed , __aligned__ ( 16 ) ) ) ;
static struct zcore_header zcore_header = {
. magic = DUMP_MAGIC ,
. version = DUMP_VERSION ,
. header_size = 4096 ,
. dump_level = 0 ,
. page_size = PAGE_SIZE ,
. mem_start = 0 ,
# ifdef __s390x__
. build_arch = DUMP_ARCH_S390X ,
# else
. build_arch = DUMP_ARCH_S390 ,
# endif
} ;
/*
* Copy lowcore info to buffer . Use map in order to copy only register parts .
*
* @ buf : User buffer
* @ sa : Pointer to save area
* @ sa_off : Offset in save area to copy
* @ len : Number of bytes to copy
*/
static int copy_lc ( void __user * buf , void * sa , int sa_off , int len )
{
int i ;
char * lc_mask = ( char * ) & sys_info . lc_mask ;
for ( i = 0 ; i < len ; i + + ) {
if ( ! lc_mask [ i + sa_off ] )
continue ;
if ( copy_to_user ( buf + i , sa + sa_off + i , 1 ) )
return - EFAULT ;
}
return 0 ;
}
/*
* Copy lowcores info to memory , if necessary
*
* @ buf : User buffer
* @ addr : Start address of buffer in dump memory
* @ count : Size of buffer
*/
static int zcore_add_lc ( char __user * buf , unsigned long start , size_t count )
{
unsigned long end ;
int i = 0 ;
if ( count = = 0 )
return 0 ;
end = start + count ;
while ( zfcpdump_save_areas [ i ] ) {
unsigned long cp_start , cp_end ; /* copy range */
unsigned long sa_start , sa_end ; /* save area range */
unsigned long prefix ;
unsigned long sa_off , len , buf_off ;
if ( sys_info . arch = = ARCH_S390 )
prefix = zfcpdump_save_areas [ i ] - > s390 . pref_reg ;
else
prefix = zfcpdump_save_areas [ i ] - > s390x . pref_reg ;
sa_start = prefix + sys_info . sa_base ;
sa_end = prefix + sys_info . sa_base + sys_info . sa_size ;
if ( ( end < sa_start ) | | ( start > sa_end ) )
goto next ;
cp_start = max ( start , sa_start ) ;
cp_end = min ( end , sa_end ) ;
buf_off = cp_start - start ;
sa_off = cp_start - sa_start ;
len = cp_end - cp_start ;
TRACE ( " copy_lc for: %lx \n " , start ) ;
if ( copy_lc ( buf + buf_off , zfcpdump_save_areas [ i ] , sa_off , len ) )
return - EFAULT ;
next :
i + + ;
}
return 0 ;
}
/*
* Read routine for zcore character device
* First 4 K are dump header
* Next 32 MB are HSA Memory
* Rest is read from absolute Memory
*/
static ssize_t zcore_read ( struct file * file , char __user * buf , size_t count ,
loff_t * ppos )
{
unsigned long mem_start ; /* Start address in memory */
size_t mem_offs ; /* Offset in dump memory */
size_t hdr_count ; /* Size of header part of output buffer */
size_t size ;
int rc ;
mutex_lock ( & zcore_mutex ) ;
if ( * ppos > ( sys_info . mem_size + HEADER_SIZE ) ) {
rc = - EINVAL ;
goto fail ;
}
count = min ( count , ( size_t ) ( sys_info . mem_size + HEADER_SIZE - * ppos ) ) ;
/* Copy dump header */
if ( * ppos < HEADER_SIZE ) {
size = min ( count , ( size_t ) ( HEADER_SIZE - * ppos ) ) ;
if ( copy_to_user ( buf , & zcore_header + * ppos , size ) ) {
rc = - EFAULT ;
goto fail ;
}
hdr_count = size ;
mem_start = 0 ;
} else {
hdr_count = 0 ;
mem_start = * ppos - HEADER_SIZE ;
}
mem_offs = 0 ;
/* Copy from HSA data */
if ( * ppos < ( ZFCPDUMP_HSA_SIZE + HEADER_SIZE ) ) {
size = min ( ( count - hdr_count ) , ( size_t ) ( ZFCPDUMP_HSA_SIZE
- mem_start ) ) ;
rc = memcpy_hsa_user ( buf + hdr_count , mem_start , size ) ;
if ( rc )
goto fail ;
mem_offs + = size ;
}
/* Copy from real mem */
size = count - mem_offs - hdr_count ;
rc = memcpy_real_user ( buf + hdr_count + mem_offs , mem_start + mem_offs ,
size ) ;
if ( rc )
goto fail ;
/*
* Since s390 dump analysis tools like lcrash or crash
* expect register sets in the prefix pages of the cpus ,
* we copy them into the read buffer , if necessary .
* buf + hdr_count : Start of memory part of output buffer
* mem_start : Start memory address to copy from
* count - hdr_count : Size of memory area to copy
*/
if ( zcore_add_lc ( buf + hdr_count , mem_start , count - hdr_count ) ) {
rc = - EFAULT ;
goto fail ;
}
* ppos + = count ;
fail :
mutex_unlock ( & zcore_mutex ) ;
return ( rc < 0 ) ? rc : count ;
}
static int zcore_open ( struct inode * inode , struct file * filp )
{
if ( ! hsa_available )
return - ENODATA ;
else
return capable ( CAP_SYS_RAWIO ) ? 0 : - EPERM ;
}
static int zcore_release ( struct inode * inode , struct file * filep )
{
diag308 ( DIAG308_REL_HSA , NULL ) ;
hsa_available = 0 ;
return 0 ;
}
static loff_t zcore_lseek ( struct file * file , loff_t offset , int orig )
{
loff_t rc ;
mutex_lock ( & zcore_mutex ) ;
switch ( orig ) {
case 0 :
file - > f_pos = offset ;
rc = file - > f_pos ;
break ;
case 1 :
file - > f_pos + = offset ;
rc = file - > f_pos ;
break ;
default :
rc = - EINVAL ;
}
mutex_unlock ( & zcore_mutex ) ;
return rc ;
}
2008-01-26 14:11:29 +01:00
static const struct file_operations zcore_fops = {
2007-04-27 16:01:49 +02:00
. owner = THIS_MODULE ,
. llseek = zcore_lseek ,
. read = zcore_read ,
. open = zcore_open ,
. release = zcore_release ,
} ;
2008-07-17 17:16:40 +02:00
static ssize_t zcore_memmap_read ( struct file * filp , char __user * buf ,
size_t count , loff_t * ppos )
{
return simple_read_from_buffer ( buf , count , ppos , filp - > private_data ,
MEMORY_CHUNKS * CHUNK_INFO_SIZE ) ;
}
static int zcore_memmap_open ( struct inode * inode , struct file * filp )
{
int i ;
char * buf ;
struct mem_chunk * chunk_array ;
chunk_array = kzalloc ( MEMORY_CHUNKS * sizeof ( struct mem_chunk ) ,
GFP_KERNEL ) ;
if ( ! chunk_array )
return - ENOMEM ;
detect_memory_layout ( chunk_array ) ;
buf = kzalloc ( MEMORY_CHUNKS * CHUNK_INFO_SIZE , GFP_KERNEL ) ;
if ( ! buf ) {
kfree ( chunk_array ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < MEMORY_CHUNKS ; i + + ) {
sprintf ( buf + ( i * CHUNK_INFO_SIZE ) , " %016llx %016llx " ,
( unsigned long long ) chunk_array [ i ] . addr ,
( unsigned long long ) chunk_array [ i ] . size ) ;
if ( chunk_array [ i ] . size = = 0 )
break ;
}
kfree ( chunk_array ) ;
filp - > private_data = buf ;
return 0 ;
}
static int zcore_memmap_release ( struct inode * inode , struct file * filp )
{
kfree ( filp - > private_data ) ;
return 0 ;
}
static const struct file_operations zcore_memmap_fops = {
. owner = THIS_MODULE ,
. read = zcore_memmap_read ,
. open = zcore_memmap_open ,
. release = zcore_memmap_release ,
} ;
2009-03-26 15:23:43 +01:00
static ssize_t zcore_reipl_write ( struct file * filp , const char __user * buf ,
size_t count , loff_t * ppos )
{
if ( ipl_block ) {
diag308 ( DIAG308_SET , ipl_block ) ;
diag308 ( DIAG308_IPL , NULL ) ;
}
return count ;
}
static int zcore_reipl_open ( struct inode * inode , struct file * filp )
{
return 0 ;
}
static int zcore_reipl_release ( struct inode * inode , struct file * filp )
{
return 0 ;
}
static const struct file_operations zcore_reipl_fops = {
. owner = THIS_MODULE ,
. write = zcore_reipl_write ,
. open = zcore_reipl_open ,
. release = zcore_reipl_release ,
} ;
2007-04-27 16:01:49 +02:00
static void __init set_s390_lc_mask ( union save_area * map )
{
memset ( & map - > s390 . ext_save , 0xff , sizeof ( map - > s390 . ext_save ) ) ;
memset ( & map - > s390 . timer , 0xff , sizeof ( map - > s390 . timer ) ) ;
memset ( & map - > s390 . clk_cmp , 0xff , sizeof ( map - > s390 . clk_cmp ) ) ;
memset ( & map - > s390 . psw , 0xff , sizeof ( map - > s390 . psw ) ) ;
memset ( & map - > s390 . pref_reg , 0xff , sizeof ( map - > s390 . pref_reg ) ) ;
memset ( & map - > s390 . acc_regs , 0xff , sizeof ( map - > s390 . acc_regs ) ) ;
memset ( & map - > s390 . fp_regs , 0xff , sizeof ( map - > s390 . fp_regs ) ) ;
memset ( & map - > s390 . gp_regs , 0xff , sizeof ( map - > s390 . gp_regs ) ) ;
memset ( & map - > s390 . ctrl_regs , 0xff , sizeof ( map - > s390 . ctrl_regs ) ) ;
}
static void __init set_s390x_lc_mask ( union save_area * map )
{
memset ( & map - > s390x . fp_regs , 0xff , sizeof ( map - > s390x . fp_regs ) ) ;
memset ( & map - > s390x . gp_regs , 0xff , sizeof ( map - > s390x . gp_regs ) ) ;
memset ( & map - > s390x . psw , 0xff , sizeof ( map - > s390x . psw ) ) ;
memset ( & map - > s390x . pref_reg , 0xff , sizeof ( map - > s390x . pref_reg ) ) ;
memset ( & map - > s390x . fp_ctrl_reg , 0xff , sizeof ( map - > s390x . fp_ctrl_reg ) ) ;
memset ( & map - > s390x . tod_reg , 0xff , sizeof ( map - > s390x . tod_reg ) ) ;
memset ( & map - > s390x . timer , 0xff , sizeof ( map - > s390x . timer ) ) ;
memset ( & map - > s390x . clk_cmp , 0xff , sizeof ( map - > s390x . clk_cmp ) ) ;
memset ( & map - > s390x . acc_regs , 0xff , sizeof ( map - > s390x . acc_regs ) ) ;
memset ( & map - > s390x . ctrl_regs , 0xff , sizeof ( map - > s390x . ctrl_regs ) ) ;
}
/*
* Initialize dump globals for a given architecture
*/
static int __init sys_info_init ( enum arch_id arch )
{
2008-07-14 09:59:38 +02:00
int rc ;
2007-04-27 16:01:49 +02:00
switch ( arch ) {
case ARCH_S390X :
2008-12-25 13:39:51 +01:00
pr_alert ( " DETECTED 'S390X (64 bit) OS' \n " ) ;
2007-04-27 16:01:49 +02:00
sys_info . sa_base = SAVE_AREA_BASE_S390X ;
sys_info . sa_size = sizeof ( struct save_area_s390x ) ;
set_s390x_lc_mask ( & sys_info . lc_mask ) ;
break ;
case ARCH_S390 :
2008-12-25 13:39:51 +01:00
pr_alert ( " DETECTED 'S390 (32 bit) OS' \n " ) ;
2007-04-27 16:01:49 +02:00
sys_info . sa_base = SAVE_AREA_BASE_S390 ;
sys_info . sa_size = sizeof ( struct save_area_s390 ) ;
set_s390_lc_mask ( & sys_info . lc_mask ) ;
break ;
default :
2008-12-25 13:39:51 +01:00
pr_alert ( " 0x%x is an unknown architecture. \n " , arch ) ;
2007-04-27 16:01:49 +02:00
return - EINVAL ;
}
sys_info . arch = arch ;
2008-07-14 09:59:38 +02:00
rc = init_cpu_info ( arch ) ;
if ( rc )
return rc ;
2007-04-27 16:01:49 +02:00
sys_info . mem_size = real_memory_size ;
return 0 ;
}
static int __init check_sdias ( void )
{
int rc , act_hsa_size ;
rc = sclp_sdias_blk_count ( ) ;
if ( rc < 0 ) {
2008-07-14 09:59:38 +02:00
TRACE ( " Could not determine HSA size \n " ) ;
2007-04-27 16:01:49 +02:00
return rc ;
}
act_hsa_size = ( rc - 1 ) * PAGE_SIZE ;
if ( act_hsa_size < ZFCPDUMP_HSA_SIZE ) {
2008-07-14 09:59:38 +02:00
TRACE ( " HSA size too small: %i \n " , act_hsa_size ) ;
2007-04-27 16:01:49 +02:00
return - EINVAL ;
}
return 0 ;
}
2008-07-17 17:16:40 +02:00
static int __init get_mem_size ( unsigned long * mem )
{
int i ;
struct mem_chunk * chunk_array ;
chunk_array = kzalloc ( MEMORY_CHUNKS * sizeof ( struct mem_chunk ) ,
GFP_KERNEL ) ;
if ( ! chunk_array )
return - ENOMEM ;
detect_memory_layout ( chunk_array ) ;
for ( i = 0 ; i < MEMORY_CHUNKS ; i + + ) {
if ( chunk_array [ i ] . size = = 0 )
break ;
* mem + = chunk_array [ i ] . size ;
}
kfree ( chunk_array ) ;
return 0 ;
}
static int __init zcore_header_init ( int arch , struct zcore_header * hdr )
2007-04-27 16:01:49 +02:00
{
2008-07-17 17:16:40 +02:00
int rc ;
unsigned long memory = 0 ;
2007-04-27 16:01:49 +02:00
if ( arch = = ARCH_S390X )
hdr - > arch_id = DUMP_ARCH_S390X ;
else
hdr - > arch_id = DUMP_ARCH_S390 ;
2008-07-17 17:16:40 +02:00
rc = get_mem_size ( & memory ) ;
if ( rc )
return rc ;
hdr - > mem_size = memory ;
hdr - > rmem_size = memory ;
2007-04-27 16:01:49 +02:00
hdr - > mem_end = sys_info . mem_size ;
2008-07-17 17:16:40 +02:00
hdr - > num_pages = memory / PAGE_SIZE ;
2007-04-27 16:01:49 +02:00
hdr - > tod = get_clock ( ) ;
get_cpu_id ( & hdr - > cpu_id ) ;
2008-07-17 17:16:40 +02:00
return 0 ;
2007-04-27 16:01:49 +02:00
}
2009-03-26 15:23:43 +01:00
/*
* Provide IPL parameter information block from either HSA or memory
* for future reipl
*/
static int __init zcore_reipl_init ( void )
{
struct ipib_info ipib_info ;
int rc ;
rc = memcpy_hsa_kernel ( & ipib_info , __LC_DUMP_REIPL , sizeof ( ipib_info ) ) ;
if ( rc )
return rc ;
if ( ipib_info . ipib = = 0 )
return 0 ;
ipl_block = ( void * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! ipl_block )
return - ENOMEM ;
if ( ipib_info . ipib < ZFCPDUMP_HSA_SIZE )
rc = memcpy_hsa_kernel ( ipl_block , ipib_info . ipib , PAGE_SIZE ) ;
else
rc = memcpy_real ( ipl_block , ipib_info . ipib , PAGE_SIZE ) ;
if ( rc ) {
free_page ( ( unsigned long ) ipl_block ) ;
return rc ;
}
if ( cksm ( ipl_block , ipl_block - > hdr . len ) ! = ipib_info . checksum ) {
TRACE ( " Checksum does not match \n " ) ;
free_page ( ( unsigned long ) ipl_block ) ;
ipl_block = NULL ;
}
return 0 ;
}
2007-04-27 16:01:49 +02:00
static int __init zcore_init ( void )
{
unsigned char arch ;
int rc ;
if ( ipl_info . type ! = IPL_TYPE_FCP_DUMP )
return - ENODATA ;
zcore_dbf = debug_register ( " zcore " , 4 , 1 , 4 * sizeof ( long ) ) ;
debug_register_view ( zcore_dbf , & debug_sprintf_view ) ;
debug_set_level ( zcore_dbf , 6 ) ;
TRACE ( " devno: %x \n " , ipl_info . data . fcp . dev_id . devno ) ;
TRACE ( " wwpn: %llx \n " , ( unsigned long long ) ipl_info . data . fcp . wwpn ) ;
TRACE ( " lun: %llx \n " , ( unsigned long long ) ipl_info . data . fcp . lun ) ;
2007-05-10 15:45:46 +02:00
rc = sclp_sdias_init ( ) ;
2007-04-27 16:01:49 +02:00
if ( rc )
goto fail ;
rc = check_sdias ( ) ;
2008-07-14 09:59:38 +02:00
if ( rc )
2007-04-27 16:01:49 +02:00
goto fail ;
rc = memcpy_hsa_kernel ( & arch , __LC_AR_MODE_ID , 1 ) ;
2008-07-14 09:59:38 +02:00
if ( rc )
2007-04-27 16:01:49 +02:00
goto fail ;
# ifndef __s390x__
if ( arch = = ARCH_S390X ) {
2008-12-25 13:39:51 +01:00
pr_alert ( " The 32-bit dump tool cannot be used for a "
" 64-bit system \n " ) ;
2007-04-27 16:01:49 +02:00
rc = - EINVAL ;
goto fail ;
}
# endif
rc = sys_info_init ( arch ) ;
2008-07-14 09:59:38 +02:00
if ( rc )
2007-04-27 16:01:49 +02:00
goto fail ;
2008-07-17 17:16:40 +02:00
rc = zcore_header_init ( arch , & zcore_header ) ;
if ( rc )
goto fail ;
2007-04-27 16:01:49 +02:00
2009-03-26 15:23:43 +01:00
rc = zcore_reipl_init ( ) ;
if ( rc )
goto fail ;
2007-04-27 16:01:49 +02:00
zcore_dir = debugfs_create_dir ( " zcore " , NULL ) ;
if ( ! zcore_dir ) {
rc = - ENOMEM ;
goto fail ;
}
zcore_file = debugfs_create_file ( " mem " , S_IRUSR , zcore_dir , NULL ,
& zcore_fops ) ;
if ( ! zcore_file ) {
rc = - ENOMEM ;
2008-07-17 17:16:40 +02:00
goto fail_dir ;
}
zcore_memmap_file = debugfs_create_file ( " memmap " , S_IRUSR , zcore_dir ,
NULL , & zcore_memmap_fops ) ;
if ( ! zcore_memmap_file ) {
rc = - ENOMEM ;
goto fail_file ;
2007-04-27 16:01:49 +02:00
}
2009-03-26 15:23:43 +01:00
zcore_reipl_file = debugfs_create_file ( " reipl " , S_IRUSR , zcore_dir ,
NULL , & zcore_reipl_fops ) ;
if ( ! zcore_reipl_file ) {
rc = - ENOMEM ;
goto fail_memmap_file ;
}
2007-04-27 16:01:49 +02:00
hsa_available = 1 ;
return 0 ;
2009-03-26 15:23:43 +01:00
fail_memmap_file :
debugfs_remove ( zcore_memmap_file ) ;
2008-07-17 17:16:40 +02:00
fail_file :
debugfs_remove ( zcore_file ) ;
fail_dir :
debugfs_remove ( zcore_dir ) ;
2007-04-27 16:01:49 +02:00
fail :
diag308 ( DIAG308_REL_HSA , NULL ) ;
return rc ;
}
static void __exit zcore_exit ( void )
{
debug_unregister ( zcore_dbf ) ;
2007-05-10 15:45:46 +02:00
sclp_sdias_exit ( ) ;
2009-03-26 15:23:43 +01:00
free_page ( ( unsigned long ) ipl_block ) ;
debugfs_remove ( zcore_reipl_file ) ;
debugfs_remove ( zcore_memmap_file ) ;
debugfs_remove ( zcore_file ) ;
debugfs_remove ( zcore_dir ) ;
2007-04-27 16:01:49 +02:00
diag308 ( DIAG308_REL_HSA , NULL ) ;
}
2009-03-26 15:23:43 +01:00
MODULE_AUTHOR ( " Copyright IBM Corp. 2003,2008 " ) ;
2007-04-27 16:01:49 +02:00
MODULE_DESCRIPTION ( " zcore module for zfcpdump support " ) ;
MODULE_LICENSE ( " GPL " ) ;
subsys_initcall ( zcore_init ) ;
module_exit ( zcore_exit ) ;