@ -43,27 +43,14 @@ enum arch_id {
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 ;
struct save_area lc_mask ;
} ;
struct ipib_info {
unsigned long ipib ;
u32 checksum ;
} __attribute__ ( ( packed ) ) ;
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 ;
static struct dentry * zcore_memmap_file ;
static struct dentry * zcore_reipl_file ;
static struct dentry * zcore_hsa_file ;
@ -149,171 +136,22 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
return memcpy_hsa ( dest , src , count , TO_KERNEL ) ;
}
static int __init init_cpu_info ( enum arch_id arch )
static int __init init_cpu_info ( void )
{
struct save_area_ext * sa_ext ;
struct _lowcore * lc ;
void * ptr ;
int i ;
/* get info for boot cpu from lowcore, stored in the HSA */
sa_ext = dump_save_areas . areas [ 0 ] ;
if ( ! sa_ext )
return - ENOMEM ;
if ( memcpy_hsa_kernel ( & sa_ext - > sa , sys_info . sa_base ,
sys_info . sa_size ) < 0 ) {
if ( memcpy_hsa_kernel ( & sa_ext - > sa , SAVE_AREA_BASE ,
sizeof ( struct save_area ) ) < 0 ) {
TRACE ( " could not copy from HSA \n " ) ;
return - EIO ;
}
if ( ! MACHINE_HAS_VX )
return 0 ;
save_vx_regs_safe ( sa_ext - > vx_regs ) ;
/* Get address of the vector register save area for each CPU */
for ( i = 0 ; i < dump_save_areas . count ; i + + ) {
sa_ext = dump_save_areas . areas [ i ] ;
lc = ( struct _lowcore * ) ( unsigned long ) sa_ext - > sa . pref_reg ;
ptr = & lc - > vector_save_area_addr ;
copy_from_oldmem ( & sa_ext - > vx_sa_addr , ptr ,
sizeof ( sa_ext - > vx_sa_addr ) ) ;
}
return 0 ;
}
static DEFINE_MUTEX ( zcore_mutex ) ;
# define DUMP_VERSION 0x5
# 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 ;
struct cpuid cpu_id ;
u32 arch_id ;
u32 volnr ;
u32 build_arch ;
u64 rmem_size ;
u8 mvdump ;
u16 cpu_cnt ;
u16 real_cpu_cnt ;
u8 end_pad1 [ 0x200 - 0x061 ] ;
u64 mvdump_sign ;
u64 mvdump_zipl_time ;
u8 end_pad2 [ 0x800 - 0x210 ] ;
u32 lc_vec [ 512 ] ;
} __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 ,
. build_arch = DUMP_ARCH_S390X ,
} ;
/*
* 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 )
{
struct save_area_ext * sa_ext ;
struct save_area * sa ;
unsigned long end ;
int i ;
if ( count = = 0 )
return 0 ;
end = start + count ;
for ( i = 0 ; i < dump_save_areas . count ; i + + ) {
unsigned long cp_start , cp_end ; /* copy range */
unsigned long sa_start , sa_end ; /* save area range */
unsigned long sa_off , len , buf_off ;
sa_ext = dump_save_areas . areas [ i ] ;
sa = & sa_ext - > sa ;
/* Copy the 512 bytes lowcore save area 0x1200 - 0x1400 */
sa_start = sa - > pref_reg + sys_info . sa_base ;
sa_end = sa_start + sys_info . sa_size ;
if ( end > = sa_start & & start < sa_end ) {
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: %lx-%lx \n " , cp_start , cp_end ) ;
if ( copy_lc ( buf + buf_off , sa , sa_off , len ) )
return - EFAULT ;
}
if ( ! MACHINE_HAS_VX )
continue ;
/* Copy the 512 bytes vector save area */
sa_start = sa_ext - > vx_sa_addr & - 1024UL ;
sa_end = sa_start + 512 ;
if ( end > = sa_start & & start < sa_end ) {
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 vxrs: %lx-%lx \n " , cp_start , cp_end ) ;
if ( copy_to_user ( buf + buf_off ,
( void * ) & sa_ext - > vx_regs + sa_off ,
len ) )
return - EFAULT ;
}
}
if ( MACHINE_HAS_VX )
save_vx_regs_safe ( sa_ext - > vx_regs ) ;
return 0 ;
}
@ -326,126 +164,6 @@ static void release_hsa(void)
hsa_available = 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 < sclp . hsa_size + HEADER_SIZE ) {
size = min ( ( count - hdr_count ) ,
( size_t ) ( sclp . 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 = copy_to_user_real ( buf + hdr_count + mem_offs ,
( void * ) 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 )
{
if ( hsa_available )
release_hsa ( ) ;
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 ;
}
static const struct file_operations zcore_fops = {
. owner = THIS_MODULE ,
. llseek = zcore_lseek ,
. read = zcore_read ,
. open = zcore_open ,
. release = zcore_release ,
} ;
static ssize_t zcore_memmap_read ( struct file * filp , char __user * buf ,
size_t count , loff_t * ppos )
{
@ -549,50 +267,6 @@ static const struct file_operations zcore_hsa_fops = {
. llseek = no_llseek ,
} ;
static void __init set_lc_mask ( struct save_area * map )
{
memset ( & map - > fp_regs , 0xff , sizeof ( map - > fp_regs ) ) ;
memset ( & map - > gp_regs , 0xff , sizeof ( map - > gp_regs ) ) ;
memset ( & map - > psw , 0xff , sizeof ( map - > psw ) ) ;
memset ( & map - > pref_reg , 0xff , sizeof ( map - > pref_reg ) ) ;
memset ( & map - > fp_ctrl_reg , 0xff , sizeof ( map - > fp_ctrl_reg ) ) ;
memset ( & map - > tod_reg , 0xff , sizeof ( map - > tod_reg ) ) ;
memset ( & map - > timer , 0xff , sizeof ( map - > timer ) ) ;
memset ( & map - > clk_cmp , 0xff , sizeof ( map - > clk_cmp ) ) ;
memset ( & map - > acc_regs , 0xff , sizeof ( map - > acc_regs ) ) ;
memset ( & map - > ctrl_regs , 0xff , sizeof ( map - > ctrl_regs ) ) ;
}
/*
* Initialize dump globals for a given architecture
*/
static int __init sys_info_init ( enum arch_id arch , unsigned long mem_end )
{
int rc ;
switch ( arch ) {
case ARCH_S390X :
pr_alert ( " DETECTED 'S390X (64 bit) OS' \n " ) ;
break ;
case ARCH_S390 :
pr_alert ( " DETECTED 'S390 (32 bit) OS' \n " ) ;
break ;
default :
pr_alert ( " 0x%x is an unknown architecture. \n " , arch ) ;
return - EINVAL ;
}
sys_info . sa_base = SAVE_AREA_BASE ;
sys_info . sa_size = sizeof ( struct save_area ) ;
sys_info . arch = arch ;
set_lc_mask ( & sys_info . lc_mask ) ;
rc = init_cpu_info ( arch ) ;
if ( rc )
return rc ;
sys_info . mem_size = mem_end ;
return 0 ;
}
static int __init check_sdias ( void )
{
if ( ! sclp . hsa_size ) {
@ -602,43 +276,6 @@ static int __init check_sdias(void)
return 0 ;
}
static int __init get_mem_info ( unsigned long * mem , unsigned long * end )
{
struct memblock_region * reg ;
for_each_memblock ( memory , reg ) {
* mem + = reg - > size ;
* end = max_t ( unsigned long , * end , reg - > base + reg - > size ) ;
}
return 0 ;
}
static void __init zcore_header_init ( int arch , struct zcore_header * hdr ,
unsigned long mem_size )
{
u32 prefix ;
int i ;
if ( arch = = ARCH_S390X )
hdr - > arch_id = DUMP_ARCH_S390X ;
else
hdr - > arch_id = DUMP_ARCH_S390 ;
hdr - > mem_size = mem_size ;
hdr - > rmem_size = mem_size ;
hdr - > mem_end = sys_info . mem_size ;
hdr - > num_pages = mem_size / PAGE_SIZE ;
hdr - > tod = get_tod_clock ( ) ;
get_cpu_id ( & hdr - > cpu_id ) ;
for ( i = 0 ; i < dump_save_areas . count ; i + + ) {
prefix = dump_save_areas . areas [ i ] - > sa . pref_reg ;
hdr - > real_cpu_cnt + + ;
if ( ! prefix )
continue ;
hdr - > lc_vec [ hdr - > cpu_cnt ] = prefix ;
hdr - > cpu_cnt + + ;
}
}
/*
* Provide IPL parameter information block from either HSA or memory
* for future reipl
@ -671,11 +308,9 @@ static int __init zcore_reipl_init(void)
static int __init zcore_init ( void )
{
unsigned long mem_size , mem_end ;
unsigned char arch ;
int rc ;
mem_size = mem_end = 0 ;
if ( ipl_info . type ! = IPL_TYPE_FCP_DUMP )
return - ENODATA ;
if ( OLDMEM_BASE )
@ -709,15 +344,11 @@ static int __init zcore_init(void)
goto fail ;
}
rc = get_mem_info ( & mem_size , & mem_end ) ;
pr_alert ( " DETECTED 'S390X (64 bit) OS' \n " ) ;
rc = init_cpu_info ( ) ;
if ( rc )
goto fail ;
rc = sys_info_init ( arch , mem_end ) ;
if ( rc )
goto fail ;
zcore_header_init ( arch , & zcore_header , mem_size ) ;
rc = zcore_reipl_init ( ) ;
if ( rc )
goto fail ;
@ -727,17 +358,11 @@ static int __init zcore_init(void)
rc = - ENOMEM ;
goto fail ;
}
zcore_file = debugfs_create_file ( " mem " , S_IRUSR , zcore_dir , NULL ,
& zcore_fops ) ;
if ( ! zcore_file ) {
rc = - ENOMEM ;
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 ;
goto fail_dir ;
}
zcore_reipl_file = debugfs_create_file ( " reipl " , S_IRUSR , zcore_dir ,
NULL , & zcore_reipl_fops ) ;
@ -757,8 +382,6 @@ fail_reipl_file:
debugfs_remove ( zcore_reipl_file ) ;
fail_memmap_file :
debugfs_remove ( zcore_memmap_file ) ;
fail_file :
debugfs_remove ( zcore_file ) ;
fail_dir :
debugfs_remove ( zcore_dir ) ;
fail :
@ -774,7 +397,6 @@ static void __exit zcore_exit(void)
debugfs_remove ( zcore_hsa_file ) ;
debugfs_remove ( zcore_reipl_file ) ;
debugfs_remove ( zcore_memmap_file ) ;
debugfs_remove ( zcore_file ) ;
debugfs_remove ( zcore_dir ) ;
diag308 ( DIAG308_REL_HSA , NULL ) ;
}