2015-07-08 10:20:04 +02:00
/*
* Copyright IBM Corp . 2015
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com >
*/
2017-01-24 15:58:52 +01:00
2015-07-08 10:20:04 +02:00
# include <linux/kernel.h>
2017-01-24 15:58:52 +01:00
# include <asm/processor.h>
# include <asm/lowcore.h>
2015-07-08 10:20:04 +02:00
# include <asm/ebcdic.h>
# include <asm/irq.h>
2017-01-24 15:58:52 +01:00
# include "sclp.h"
# include "sclp_rw.h"
2015-07-08 10:20:04 +02:00
2017-01-24 15:58:52 +01:00
char sclp_early_sccb [ PAGE_SIZE ] __aligned ( PAGE_SIZE ) __section ( data ) ;
2017-01-27 15:54:57 +01:00
int sclp_init_state __section ( data ) = sclp_init_state_uninitialized ;
2017-01-24 15:58:52 +01:00
void sclp_early_wait_irq ( void )
2015-07-08 10:20:04 +02:00
{
2017-01-24 15:58:52 +01:00
unsigned long psw_mask , addr ;
2015-07-08 10:20:04 +02:00
psw_t psw_ext_save , psw_wait ;
2017-01-11 09:14:45 +01:00
union ctlreg0 cr0 , cr0_new ;
2015-07-08 10:20:04 +02:00
2017-01-11 09:14:45 +01:00
__ctl_store ( cr0 . val , 0 , 0 ) ;
cr0_new . val = cr0 . val & ~ CR0_IRQ_SUBCLASS_MASK ;
cr0_new . lap = 0 ;
cr0_new . sssm = 1 ;
__ctl_load ( cr0_new . val , 0 , 0 ) ;
2015-07-08 10:20:04 +02:00
psw_ext_save = S390_lowcore . external_new_psw ;
2015-11-04 14:16:57 +01:00
psw_mask = __extract_psw ( ) ;
2015-07-08 10:20:04 +02:00
S390_lowcore . external_new_psw . mask = psw_mask ;
psw_wait . mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT ;
S390_lowcore . ext_int_code = 0 ;
do {
asm volatile (
" larl %[addr],0f \n "
" stg %[addr],%[psw_wait_addr] \n "
" stg %[addr],%[psw_ext_addr] \n "
" lpswe %[psw_wait] \n "
" 0: \n "
: [ addr ] " =&d " ( addr ) ,
[ psw_wait_addr ] " =Q " ( psw_wait . addr ) ,
[ psw_ext_addr ] " =Q " ( S390_lowcore . external_new_psw . addr )
: [ psw_wait ] " Q " ( psw_wait )
: " cc " , " memory " ) ;
} while ( S390_lowcore . ext_int_code ! = EXT_IRQ_SERVICE_SIG ) ;
S390_lowcore . external_new_psw = psw_ext_save ;
2017-01-11 09:14:45 +01:00
__ctl_load ( cr0 . val , 0 , 0 ) ;
2015-07-08 10:20:04 +02:00
}
2017-01-24 15:58:52 +01:00
int sclp_early_cmd_sync ( sclp_cmdw_t cmd , void * sccb )
2015-07-08 10:20:04 +02:00
{
2017-01-24 15:58:52 +01:00
unsigned long flags ;
int rc ;
2015-07-08 10:20:04 +02:00
2017-01-24 15:58:52 +01:00
raw_local_irq_save ( flags ) ;
rc = sclp_service_call ( cmd , sccb ) ;
if ( rc )
goto out ;
sclp_early_wait_irq ( ) ;
out :
raw_local_irq_restore ( flags ) ;
return rc ;
2015-07-08 10:20:04 +02:00
}
2017-01-24 15:58:52 +01:00
int sclp_early_cmd ( sclp_cmdw_t cmd , void * sccb )
2015-07-08 10:20:04 +02:00
{
int rc ;
2017-01-24 15:58:52 +01:00
do {
rc = sclp_early_cmd_sync ( cmd , sccb ) ;
} while ( rc = = - EBUSY ) ;
2015-07-08 10:20:04 +02:00
if ( rc )
2017-01-24 15:58:52 +01:00
return - EIO ;
if ( ( ( struct sccb_header * ) sccb ) - > response_code ! = 0x0020 )
return - EIO ;
2015-07-08 10:20:04 +02:00
return 0 ;
}
2017-01-24 15:58:52 +01:00
struct write_sccb {
struct sccb_header header ;
struct msg_buf msg ;
} __packed ;
2015-11-24 16:28:55 +01:00
/* Output multi-line text using SCLP Message interface. */
2017-01-24 15:58:52 +01:00
static void sclp_early_print_lm ( const char * str , unsigned int len )
2015-07-08 10:20:04 +02:00
{
2017-01-24 15:58:52 +01:00
unsigned char * ptr , * end , ch ;
unsigned int count , offset ;
struct write_sccb * sccb ;
struct msg_buf * msg ;
struct mdb * mdb ;
struct mto * mto ;
struct go * go ;
sccb = ( struct write_sccb * ) & sclp_early_sccb ;
end = ( unsigned char * ) sccb + sizeof ( sclp_early_sccb ) - 1 ;
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
ptr = ( unsigned char * ) & sccb - > msg . mdb . mto ;
offset = 0 ;
2015-07-08 10:20:04 +02:00
do {
2017-01-24 15:58:52 +01:00
for ( count = sizeof ( * mto ) ; offset < len ; count + + ) {
ch = str [ offset + + ] ;
if ( ( ch = = 0x0a ) | | ( ptr + count > end ) )
2017-01-11 11:07:39 +01:00
break ;
2015-07-08 10:20:04 +02:00
ptr [ count ] = _ascebc [ ch ] ;
}
2017-01-24 15:58:52 +01:00
mto = ( struct mto * ) ptr ;
memset ( mto , 0 , sizeof ( * mto ) ) ;
mto - > length = count ;
mto - > type = 4 ;
mto - > line_type_flags = LNTPFLGS_ENDTEXT ;
2015-07-08 10:20:04 +02:00
ptr + = count ;
2017-01-24 15:58:52 +01:00
} while ( ( offset < len ) & & ( ptr + sizeof ( * mto ) < = end ) ) ;
len = ptr - ( unsigned char * ) sccb ;
sccb - > header . length = len - offsetof ( struct write_sccb , header ) ;
msg = & sccb - > msg ;
msg - > header . type = EVTYP_MSG ;
msg - > header . length = len - offsetof ( struct write_sccb , msg . header ) ;
mdb = & msg - > mdb ;
mdb - > header . type = 1 ;
mdb - > header . tag = 0xD4C4C240 ;
mdb - > header . revision_code = 1 ;
mdb - > header . length = len - offsetof ( struct write_sccb , msg . mdb . header ) ;
go = & mdb - > go ;
go - > length = sizeof ( * go ) ;
go - > type = 1 ;
sclp_early_cmd ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) ;
2015-07-08 10:20:04 +02:00
}
2017-01-24 15:58:52 +01:00
struct vt220_sccb {
struct sccb_header header ;
struct {
struct evbuf_header header ;
char data [ ] ;
} msg ;
} __packed ;
2015-11-24 16:28:55 +01:00
/* Output multi-line text (plus a newline) using SCLP VT220
* interface .
*/
2017-01-24 15:58:52 +01:00
static void sclp_early_print_vt220 ( const char * str , unsigned int len )
{
struct vt220_sccb * sccb ;
sccb = ( struct vt220_sccb * ) & sclp_early_sccb ;
if ( sizeof ( * sccb ) + len > = sizeof ( sclp_early_sccb ) )
len = sizeof ( sclp_early_sccb ) - sizeof ( * sccb ) - 1 ;
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
memcpy ( & sccb - > msg . data , str , len ) ;
sccb - > msg . data [ len ] = ' \n ' ;
sccb - > header . length = sizeof ( * sccb ) + len + 1 ;
sccb - > msg . header . length = sizeof ( sccb - > msg ) + len + 1 ;
sccb - > msg . header . type = EVTYP_VT220MSG ;
sclp_early_cmd ( SCLP_CMDW_WRITE_EVENT_DATA , sccb ) ;
}
int sclp_early_set_event_mask ( struct init_sccb * sccb ,
unsigned long receive_mask ,
unsigned long send_mask )
{
memset ( sccb , 0 , sizeof ( * sccb ) ) ;
sccb - > header . length = sizeof ( * sccb ) ;
sccb - > mask_length = sizeof ( sccb_mask_t ) ;
sccb - > receive_mask = receive_mask ;
sccb - > send_mask = send_mask ;
return sclp_early_cmd ( SCLP_CMDW_WRITE_EVENT_MASK , sccb ) ;
}
unsigned int sclp_early_con_check_linemode ( struct init_sccb * sccb )
2015-07-08 10:20:04 +02:00
{
2017-01-24 15:58:52 +01:00
if ( ! ( sccb - > sclp_send_mask & EVTYP_OPCMD_MASK ) )
return 0 ;
if ( ! ( sccb - > sclp_receive_mask & ( EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK ) ) )
return 0 ;
return 1 ;
}
static int sclp_early_setup ( int disable , int * have_linemode , int * have_vt220 )
{
unsigned long receive_mask , send_mask ;
struct init_sccb * sccb ;
int rc ;
* have_linemode = * have_vt220 = 0 ;
sccb = ( struct init_sccb * ) & sclp_early_sccb ;
receive_mask = disable ? 0 : EVTYP_OPCMD_MASK ;
send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK ;
rc = sclp_early_set_event_mask ( sccb , receive_mask , send_mask ) ;
if ( rc )
return rc ;
* have_linemode = sclp_early_con_check_linemode ( sccb ) ;
* have_vt220 = sccb - > send_mask & EVTYP_VT220MSG_MASK ;
return rc ;
2015-11-24 16:28:55 +01:00
}
/* Output one or more lines of text on the SCLP console (VT220 and /
* or line - mode ) . All lines get terminated ; no need for a trailing LF .
*/
2017-01-24 15:58:52 +01:00
void __sclp_early_printk ( const char * str , unsigned int len )
2015-11-24 16:28:55 +01:00
{
2017-01-24 15:58:52 +01:00
int have_linemode , have_vt220 ;
2017-01-27 15:54:57 +01:00
if ( sclp_init_state ! = sclp_init_state_uninitialized )
return ;
2017-01-24 15:58:52 +01:00
if ( sclp_early_setup ( 0 , & have_linemode , & have_vt220 ) ! = 0 )
2015-11-24 16:28:55 +01:00
return ;
if ( have_linemode )
2017-01-24 15:58:52 +01:00
sclp_early_print_lm ( str , len ) ;
2015-11-24 16:28:55 +01:00
if ( have_vt220 )
2017-01-24 15:58:52 +01:00
sclp_early_print_vt220 ( str , len ) ;
sclp_early_setup ( 1 , & have_linemode , & have_vt220 ) ;
2015-07-08 10:20:04 +02:00
}
2017-01-11 09:14:52 +01:00
2017-01-24 15:58:52 +01:00
void sclp_early_printk ( const char * str )
2017-01-11 09:14:52 +01:00
{
2017-01-24 15:58:52 +01:00
__sclp_early_printk ( str , strlen ( str ) ) ;
2017-01-11 09:14:52 +01:00
}