2019-08-08 21:09:08 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/kernel.h>
2021-08-02 23:40:32 +03:00
# include <linux/stdarg.h>
2019-08-08 21:09:08 +03:00
# include <linux/string.h>
2020-11-10 19:05:26 +03:00
# include <linux/ctype.h>
2020-11-10 19:05:35 +03:00
# include <asm/stacktrace.h>
2020-11-11 01:30:23 +03:00
# include <asm/boot_data.h>
2019-08-08 21:09:08 +03:00
# include <asm/lowcore.h>
2019-08-11 21:23:56 +03:00
# include <asm/setup.h>
2019-08-08 21:09:08 +03:00
# include <asm/sclp.h>
2020-11-11 01:30:23 +03:00
# include <asm/uv.h>
2019-08-08 21:09:08 +03:00
# include "boot.h"
const char hex_asc [ ] = " 0123456789abcdef " ;
2020-11-10 19:05:26 +03:00
static char * as_hex ( char * dst , unsigned long val , int pad )
2019-08-08 21:09:08 +03:00
{
2020-11-10 19:05:26 +03:00
char * p , * end = p = dst + max ( pad , ( int ) __fls ( val | 1 ) / 4 + 1 ) ;
2019-08-08 21:09:08 +03:00
2020-11-10 19:05:26 +03:00
for ( * p - - = 0 ; p > = dst ; val > > = 4 )
* p - - = hex_asc [ val & 0x0f ] ;
return end ;
2019-08-08 21:09:08 +03:00
}
2020-11-11 12:59:40 +03:00
static char * symstart ( char * p )
{
while ( * p )
p - - ;
return p + 1 ;
}
static noinline char * findsym ( unsigned long ip , unsigned short * off , unsigned short * len )
{
/* symbol entries are in a form "10000 c4 startup\0" */
char * a = _decompressor_syms_start ;
char * b = _decompressor_syms_end ;
unsigned long start ;
unsigned long size ;
char * pivot ;
char * endp ;
while ( a < b ) {
pivot = symstart ( a + ( b - a ) / 2 ) ;
start = simple_strtoull ( pivot , & endp , 16 ) ;
size = simple_strtoull ( endp + 1 , & endp , 16 ) ;
if ( ip < start ) {
b = pivot ;
continue ;
}
if ( ip > start + size ) {
a = pivot + strlen ( pivot ) + 1 ;
continue ;
}
* off = ip - start ;
* len = size ;
return endp + 1 ;
}
return NULL ;
}
static noinline char * strsym ( void * ip )
{
static char buf [ 64 ] ;
unsigned short off ;
unsigned short len ;
char * p ;
p = findsym ( ( unsigned long ) ip , & off , & len ) ;
if ( p ) {
strncpy ( buf , p , sizeof ( buf ) ) ;
/* reserve 15 bytes for offset/len in symbol+0x1234/0x1234 */
p = buf + strnlen ( buf , sizeof ( buf ) - 15 ) ;
strcpy ( p , " +0x " ) ;
p = as_hex ( p + 3 , off , 0 ) ;
strcpy ( p , " /0x " ) ;
as_hex ( p + 3 , len , 0 ) ;
} else {
as_hex ( buf , ( unsigned long ) ip , 16 ) ;
}
return buf ;
}
2020-11-10 19:05:26 +03:00
void decompressor_printk ( const char * fmt , . . . )
2019-08-08 21:09:08 +03:00
{
2020-11-10 19:05:26 +03:00
char buf [ 1024 ] = { 0 } ;
char * end = buf + sizeof ( buf ) - 1 ; /* make sure buf is 0 terminated */
unsigned long pad ;
char * p = buf ;
va_list args ;
2019-08-08 21:09:08 +03:00
2020-11-10 19:05:26 +03:00
va_start ( args , fmt ) ;
for ( ; p < end & & * fmt ; fmt + + ) {
if ( * fmt ! = ' % ' ) {
* p + + = * fmt ;
continue ;
}
pad = isdigit ( * + + fmt ) ? simple_strtol ( fmt , ( char * * ) & fmt , 10 ) : 0 ;
switch ( * fmt ) {
case ' s ' :
p = buf + strlcat ( buf , va_arg ( args , char * ) , sizeof ( buf ) ) ;
break ;
2020-11-11 12:59:40 +03:00
case ' p ' :
if ( * + + fmt ! = ' S ' )
goto out ;
p = buf + strlcat ( buf , strsym ( va_arg ( args , void * ) ) , sizeof ( buf ) ) ;
break ;
2020-11-10 19:05:26 +03:00
case ' l ' :
if ( * + + fmt ! = ' x ' | | end - p < = max ( sizeof ( long ) * 2 , pad ) )
goto out ;
p = as_hex ( p , va_arg ( args , unsigned long ) , pad ) ;
break ;
case ' x ' :
if ( end - p < = max ( sizeof ( int ) * 2 , pad ) )
goto out ;
p = as_hex ( p , va_arg ( args , unsigned int ) , pad ) ;
break ;
default :
goto out ;
}
2019-08-11 21:23:56 +03:00
}
2020-11-10 19:05:26 +03:00
out :
va_end ( args ) ;
2019-08-08 21:09:08 +03:00
sclp_early_printk ( buf ) ;
2020-11-10 19:05:26 +03:00
}
2019-08-08 21:09:08 +03:00
2020-11-10 19:05:35 +03:00
static noinline void print_stacktrace ( void )
{
2021-06-30 18:17:53 +03:00
struct stack_info boot_stack = { STACK_TYPE_TASK , ( unsigned long ) _stack_start ,
( unsigned long ) _stack_end } ;
2020-11-10 19:05:35 +03:00
unsigned long sp = S390_lowcore . gpregs_save_area [ 15 ] ;
bool first = true ;
decompressor_printk ( " Call Trace: \n " ) ;
while ( ! ( sp & 0x7 ) & & on_stack ( & boot_stack , sp , sizeof ( struct stack_frame ) ) ) {
struct stack_frame * sf = ( struct stack_frame * ) sp ;
decompressor_printk ( first ? " (sp:%016lx [<%016lx>] %pS) \n " :
" sp:%016lx [<%016lx>] %pS \n " ,
sp , sf - > gprs [ 8 ] , ( void * ) sf - > gprs [ 8 ] ) ;
if ( sf - > back_chain < = sp )
break ;
sp = sf - > back_chain ;
first = false ;
}
}
2020-11-10 19:05:26 +03:00
void print_pgm_check_info ( void )
{
unsigned long * gpregs = ( unsigned long * ) S390_lowcore . gpregs_save_area ;
struct psw_bits * psw = & psw_bits ( S390_lowcore . psw_save_area ) ;
2019-08-08 21:09:08 +03:00
2020-11-10 19:05:26 +03:00
decompressor_printk ( " Linux version %s \n " , kernel_version ) ;
2020-11-11 01:30:23 +03:00
if ( ! is_prot_virt_guest ( ) & & early_command_line [ 0 ] )
decompressor_printk ( " Kernel command line: %s \n " , early_command_line ) ;
2020-11-10 19:05:26 +03:00
decompressor_printk ( " Kernel fault: interruption code %04x ilc:%x \n " ,
S390_lowcore . pgm_code , S390_lowcore . pgm_ilc > > 1 ) ;
if ( kaslr_enabled )
decompressor_printk ( " Kernel random base: %lx \n " , __kaslr_offset ) ;
2020-11-11 12:59:40 +03:00
decompressor_printk ( " PSW : %016lx %016lx (%pS) \n " ,
2020-11-10 19:05:26 +03:00
S390_lowcore . psw_save_area . mask ,
2020-11-11 12:59:40 +03:00
S390_lowcore . psw_save_area . addr ,
( void * ) S390_lowcore . psw_save_area . addr ) ;
2020-11-10 19:05:26 +03:00
decompressor_printk (
" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x P:%x AS:%x CC:%x PM:%x RI:%x EA:%x \n " ,
psw - > per , psw - > dat , psw - > io , psw - > ext , psw - > key , psw - > mcheck ,
psw - > wait , psw - > pstate , psw - > as , psw - > cc , psw - > pm , psw - > ri ,
psw - > eaba ) ;
decompressor_printk ( " GPRS: %016lx %016lx %016lx %016lx \n " ,
gpregs [ 0 ] , gpregs [ 1 ] , gpregs [ 2 ] , gpregs [ 3 ] ) ;
decompressor_printk ( " %016lx %016lx %016lx %016lx \n " ,
gpregs [ 4 ] , gpregs [ 5 ] , gpregs [ 6 ] , gpregs [ 7 ] ) ;
decompressor_printk ( " %016lx %016lx %016lx %016lx \n " ,
gpregs [ 8 ] , gpregs [ 9 ] , gpregs [ 10 ] , gpregs [ 11 ] ) ;
decompressor_printk ( " %016lx %016lx %016lx %016lx \n " ,
gpregs [ 12 ] , gpregs [ 13 ] , gpregs [ 14 ] , gpregs [ 15 ] ) ;
2020-11-10 19:05:35 +03:00
print_stacktrace ( ) ;
2020-11-11 01:30:23 +03:00
decompressor_printk ( " Last Breaking-Event-Address: \n " ) ;
decompressor_printk ( " [<%016lx>] %pS \n " , ( unsigned long ) S390_lowcore . breaking_event_addr ,
( void * ) S390_lowcore . breaking_event_addr ) ;
2019-08-08 21:09:08 +03:00
}