s390/sclp: convert early sclp console code to C
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 8 Jul 2015 08:20:04 +0000 (10:20 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 29 Jul 2015 07:11:39 +0000 (09:11 +0200)
The 31-bit assembler code for the early sclp console is error
prone as git commit fde24b54d976cc123506695c17db01438a11b673
"s390/sclp: clear upper register halves in _sclp_print_early"
has shown.

Convert the assembler code to C.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/Makefile
arch/s390/include/asm/processor.h
arch/s390/include/asm/sclp.h
arch/s390/kernel/Makefile
arch/s390/kernel/head.S
arch/s390/kernel/sclp.S [deleted file]
arch/s390/kernel/sclp.c [new file with mode: 0644]

index 667b1bca5681fb0eea922ba892727abe5a31dc81..e8d4423e4f858da28b7658a04d47820b5ecbfb16 100644 (file)
@@ -33,6 +33,8 @@ mflags-$(CONFIG_MARCH_Z196)   := -march=z196
 mflags-$(CONFIG_MARCH_ZEC12)  := -march=zEC12
 mflags-$(CONFIG_MARCH_Z13)   := -march=z13
 
+export CC_FLAGS_MARCH := $(mflags-y)
+
 aflags-y += $(mflags-y)
 cflags-y += $(mflags-y)
 
index c417015c5304f9c30e78a3ba4e047ffe7f93e994..085fb0d3c54e944e2f846a2188136f7f729bef72 100644 (file)
@@ -232,6 +232,17 @@ static inline void __load_psw_mask (unsigned long mask)
                : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
 }
 
+/*
+ * Extract current PSW mask
+ */
+static inline unsigned long __extract_psw(void)
+{
+       unsigned int reg1, reg2;
+
+       asm volatile("epsw %0,%1" : "=d" (reg1), "=a" (reg2));
+       return (((unsigned long) reg1) << 32) | ((unsigned long) reg2);
+}
+
 /*
  * Rewind PSW instruction address by specified number of bytes.
  */
index f6ff06077631c8ebf4ede37bced142df25dbc7de..821dde5f425d0b3e97fbe32f5496fe968e4899fd 100644 (file)
@@ -79,6 +79,6 @@ int sclp_pci_configure(u32 fid);
 int sclp_pci_deconfigure(u32 fid);
 int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
 void sclp_early_detect(void);
-long _sclp_print_early(const char *);
+int _sclp_print_early(const char *);
 
 #endif /* _ASM_S390_SCLP_H */
index ffb87617a36c5992598e9f65bb2c84a06fac457e..b756c6348ac68ec20d9b67269823ab50db61f837 100644 (file)
@@ -28,6 +28,17 @@ CFLAGS_ptrace.o              += -DUTS_MACHINE='"$(UTS_MACHINE)"'
 
 CFLAGS_sysinfo.o += -w
 
+#
+# Use -march=z900 for sclp.c to be able to print an error message if
+# the kernel is started on a machine which is too old
+#
+CFLAGS_REMOVE_sclp.o = $(CC_FLAGS_FTRACE)
+ifneq ($(CC_FLAGS_MARCH),-march=z900)
+CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH)
+CFLAGS_sclp.o  += -march=z900
+endif
+GCOV_PROFILE_sclp.o := n
+
 obj-y  := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
 obj-y  += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
 obj-y  += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o
index 59b7c6470567d6d611daa28e310f4c420ffdedaf..63c77fdb619e8e8a52819da1d75b548c5ce0d367 100644 (file)
@@ -370,6 +370,7 @@ ENTRY(startup_kdump)
        xc      0x200(256),0x200        # partially clear lowcore
        xc      0x300(256),0x300
        xc      0xe00(256),0xe00
+       lctlg   %c0,%c15,0x200(%r0)     # initialize control registers
        stck    __LC_LAST_UPDATE_CLOCK
        spt     6f-.LPG0(%r13)
        mvc     __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13)
diff --git a/arch/s390/kernel/sclp.S b/arch/s390/kernel/sclp.S
deleted file mode 100644 (file)
index ada0c07..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Mini SCLP driver.
- *
- * Copyright IBM Corp. 2004, 2009
- *
- *   Author(s):        Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>,
- *             Heiko Carstens <heiko.carstens@de.ibm.com>,
- *
- */
-
-#include <linux/linkage.h>
-#include <asm/irq.h>
-
-LC_EXT_NEW_PSW         = 0x58                  # addr of ext int handler
-LC_EXT_NEW_PSW_64      = 0x1b0                 # addr of ext int handler 64 bit
-LC_EXT_INT_PARAM       = 0x80                  # addr of ext int parameter
-LC_EXT_INT_CODE                = 0x86                  # addr of ext int code
-LC_AR_MODE_ID          = 0xa3
-
-#
-# Subroutine which waits synchronously until either an external interruption
-# or a timeout occurs.
-#
-# Parameters:
-#   R2 = 0 for no timeout, non-zero for timeout in (approximated) seconds
-#
-# Returns:
-#   R2 = 0 on interrupt, 2 on timeout
-#   R3 = external interruption parameter if R2=0
-#
-
-_sclp_wait_int:
-       stm     %r6,%r15,24(%r15)               # save registers
-       basr    %r13,0                          # get base register
-.LbaseS1:
-       ahi     %r15,-96                        # create stack frame
-       la      %r8,LC_EXT_NEW_PSW              # register int handler
-       la      %r9,.LextpswS1-.LbaseS1(%r13)
-       tm      LC_AR_MODE_ID,1
-       jno     .Lesa1
-       la      %r8,LC_EXT_NEW_PSW_64           # register int handler 64 bit
-       la      %r9,.LextpswS1_64-.LbaseS1(%r13)
-.Lesa1:
-       mvc     .LoldpswS1-.LbaseS1(16,%r13),0(%r8)
-       mvc     0(16,%r8),0(%r9)
-       epsw    %r6,%r7                         # set current addressing mode
-       nill    %r6,0x1                         # in new psw (31 or 64 bit mode)
-       nilh    %r7,0x8000
-       stm     %r6,%r7,0(%r8)
-       lhi     %r6,0x0200                      # cr mask for ext int (cr0.54)
-       ltr     %r2,%r2
-       jz      .LsetctS1
-       ahi     %r6,0x0800                      # cr mask for clock int (cr0.52)
-       stck    .LtimeS1-.LbaseS1(%r13)         # initiate timeout
-       al      %r2,.LtimeS1-.LbaseS1(%r13)
-       st      %r2,.LtimeS1-.LbaseS1(%r13)
-       sckc    .LtimeS1-.LbaseS1(%r13)
-
-.LsetctS1:
-       stctl   %c0,%c0,.LctlS1-.LbaseS1(%r13)  # enable required interrupts
-       l       %r0,.LctlS1-.LbaseS1(%r13)
-       lhi     %r1,~(0x200 | 0x800)            # clear old values
-       nr      %r1,%r0
-       or      %r1,%r6                         # set new value
-       st      %r1,.LctlS1-.LbaseS1(%r13)
-       lctl    %c0,%c0,.LctlS1-.LbaseS1(%r13)
-       st      %r0,.LctlS1-.LbaseS1(%r13)
-       lhi     %r2,2                           # return code for timeout
-.LloopS1:
-       lpsw    .LwaitpswS1-.LbaseS1(%r13)      # wait until interrupt
-.LwaitS1:
-       lh      %r7,LC_EXT_INT_CODE
-       chi     %r7,EXT_IRQ_CLK_COMP            # timeout?
-       je      .LtimeoutS1
-       chi     %r7,EXT_IRQ_SERVICE_SIG         # service int?
-       jne     .LloopS1
-       sr      %r2,%r2
-       l       %r3,LC_EXT_INT_PARAM
-.LtimeoutS1:
-       lctl    %c0,%c0,.LctlS1-.LbaseS1(%r13)  # restore interrupt setting
-       # restore old handler
-       mvc     0(16,%r8),.LoldpswS1-.LbaseS1(%r13)
-       lm      %r6,%r15,120(%r15)              # restore registers
-       br      %r14                            # return to caller
-
-       .align  8
-.LoldpswS1:
-       .long   0, 0, 0, 0                      # old ext int PSW
-.LextpswS1:
-       .long   0x00080000, 0x80000000+.LwaitS1 # PSW to handle ext int
-.LextpswS1_64:
-       .quad   0, .LwaitS1                     # PSW to handle ext int, 64 bit
-.LwaitpswS1:
-       .long   0x010a0000, 0x00000000+.LloopS1 # PSW to wait for ext int
-.LtimeS1:
-       .quad   0                               # current time
-.LctlS1:
-       .long   0                               # CT0 contents
-
-#
-# Subroutine to synchronously issue a service call.
-#
-# Parameters:
-#   R2 = command word
-#   R3 = sccb address
-#
-# Returns:
-#   R2 = 0 on success, 1 on failure
-#   R3 = sccb response code if R2 = 0
-#
-
-_sclp_servc:
-       stm     %r6,%r15,24(%r15)               # save registers
-       ahi     %r15,-96                        # create stack frame
-       lr      %r6,%r2                         # save command word
-       lr      %r7,%r3                         # save sccb address
-.LretryS2:
-       lhi     %r2,1                           # error return code
-       .insn   rre,0xb2200000,%r6,%r7          # servc
-       brc     1,.LendS2                       # exit if not operational
-       brc     8,.LnotbusyS2                   # go on if not busy
-       sr      %r2,%r2                         # wait until no longer busy
-       bras    %r14,_sclp_wait_int
-       j       .LretryS2                       # retry
-.LnotbusyS2:
-       sr      %r2,%r2                         # wait until result
-       bras    %r14,_sclp_wait_int
-       sr      %r2,%r2
-       lh      %r3,6(%r7)
-.LendS2:
-       lm      %r6,%r15,120(%r15)              # restore registers
-       br      %r14
-
-#
-# Subroutine to set up the SCLP interface.
-#
-# Parameters:
-#   R2 = 0 to activate, non-zero to deactivate
-#
-# Returns:
-#   R2 = 0 on success, non-zero on failure
-#
-
-_sclp_setup:
-       stm     %r6,%r15,24(%r15)               # save registers
-       ahi     %r15,-96                        # create stack frame
-       basr    %r13,0                          # get base register
-.LbaseS3:
-       l       %r6,.LsccbS0-.LbaseS3(%r13)     # prepare init mask sccb
-       mvc     0(.LinitendS3-.LinitsccbS3,%r6),.LinitsccbS3-.LbaseS3(%r13)
-       ltr     %r2,%r2                         # initialization?
-       jz      .LdoinitS3                      # go ahead
-       # clear masks
-       xc      .LinitmaskS3-.LinitsccbS3(8,%r6),.LinitmaskS3-.LinitsccbS3(%r6)
-.LdoinitS3:
-       l       %r2,.LwritemaskS3-.LbaseS3(%r13)# get command word
-       lr      %r3,%r6                         # get sccb address
-       bras    %r14,_sclp_servc                # issue service call
-       ltr     %r2,%r2                         # servc successful?
-       jnz     .LerrorS3
-       chi     %r3,0x20                        # write mask successful?
-       jne     .LerrorS3
-       # check masks
-       la      %r2,.LinitmaskS3-.LinitsccbS3(%r6)
-       l       %r1,0(%r2)                      # receive mask ok?
-       n       %r1,12(%r2)
-       cl      %r1,0(%r2)
-       jne     .LerrorS3
-       l       %r1,4(%r2)                      # send mask ok?
-       n       %r1,8(%r2)
-       cl      %r1,4(%r2)
-       sr      %r2,%r2
-       je      .LendS3
-.LerrorS3:
-       lhi     %r2,1                           # error return code
-.LendS3:
-       lm      %r6,%r15,120(%r15)              # restore registers
-       br      %r14
-.LwritemaskS3:
-       .long   0x00780005                      # SCLP command for write mask
-.LinitsccbS3:
-       .word   .LinitendS3-.LinitsccbS3
-       .byte   0,0,0,0
-       .word   0
-       .word   0
-       .word   4
-.LinitmaskS3:
-       .long   0x80000000
-       .long   0x40000000
-       .long   0
-       .long   0
-.LinitendS3:
-
-#
-# Subroutine which prints a given text to the SCLP console.
-#
-# Parameters:
-#   R2 = address of nil-terminated ASCII text
-#
-# Returns:
-#   R2 = 0 on success, 1 on failure
-#
-
-_sclp_print:
-       stm     %r6,%r15,24(%r15)               # save registers
-       ahi     %r15,-96                        # create stack frame
-       basr    %r13,0                          # get base register
-.LbaseS4:
-       l       %r8,.LsccbS0-.LbaseS4(%r13)     # prepare write data sccb
-       mvc     0(.LmtoS4-.LwritesccbS4,%r8),.LwritesccbS4-.LbaseS4(%r13)
-       la      %r7,.LmtoS4-.LwritesccbS4(%r8)  # current mto addr
-       sr      %r0,%r0
-       l       %r10,.Lascebc-.LbaseS4(%r13)    # address of translation table
-.LinitmtoS4:
-       # initialize mto
-       mvc     0(.LmtoendS4-.LmtoS4,%r7),.LmtoS4-.LbaseS4(%r13)
-       lhi     %r6,.LmtoendS4-.LmtoS4          # current mto length
-.LloopS4:
-       ic      %r0,0(%r2)                      # get character
-       ahi     %r2,1
-       ltr     %r0,%r0                         # end of string?
-       jz      .LfinalizemtoS4
-       chi     %r0,0x0a                        # end of line (NL)?
-       jz      .LfinalizemtoS4
-       stc     %r0,0(%r6,%r7)                  # copy to mto
-       la      %r11,0(%r6,%r7)
-       tr      0(1,%r11),0(%r10)               # translate to EBCDIC
-       ahi     %r6,1
-       j       .LloopS4
-.LfinalizemtoS4:
-       sth     %r6,0(%r7)                      # update mto length
-       lh      %r9,.LmdbS4-.LwritesccbS4(%r8)  # update mdb length
-       ar      %r9,%r6
-       sth     %r9,.LmdbS4-.LwritesccbS4(%r8)
-       lh      %r9,.LevbufS4-.LwritesccbS4(%r8)# update evbuf length
-       ar      %r9,%r6
-       sth     %r9,.LevbufS4-.LwritesccbS4(%r8)
-       lh      %r9,0(%r8)                      # update sccb length
-       ar      %r9,%r6
-       sth     %r9,0(%r8)
-       ar      %r7,%r6                         # update current mto address
-       ltr     %r0,%r0                         # more characters?
-       jnz     .LinitmtoS4
-       l       %r2,.LwritedataS4-.LbaseS4(%r13)# write data
-       lr      %r3,%r8
-       bras    %r14,_sclp_servc
-       ltr     %r2,%r2                         # servc successful?
-       jnz     .LendS4
-       chi     %r3,0x20                        # write data successful?
-       je      .LendS4
-       lhi     %r2,1                           # error return code
-.LendS4:
-       lm      %r6,%r15,120(%r15)              # restore registers
-       br      %r14
-
-#
-# Function which prints a given text to the SCLP console.
-#
-# Parameters:
-#   R2 = address of nil-terminated ASCII text
-#
-# Returns:
-#   R2 = 0 on success, 1 on failure
-#
-
-ENTRY(_sclp_print_early)
-       stm     %r6,%r15,24(%r15)               # save registers
-       ahi     %r15,-96                        # create stack frame
-       tm      LC_AR_MODE_ID,1
-       jno     .Lesa2
-       ahi     %r15,-80
-       stmh    %r6,%r15,96(%r15)               # store upper register halves
-       basr    %r13,0
-       lmh     %r0,%r15,.Lzeroes-.(%r13)       # clear upper register halves
-.Lesa2:
-       lr      %r10,%r2                        # save string pointer
-       lhi     %r2,0
-       bras    %r14,_sclp_setup                # enable console
-       ltr     %r2,%r2
-       jnz     .LendS5
-       lr      %r2,%r10
-       bras    %r14,_sclp_print                # print string
-       ltr     %r2,%r2
-       jnz     .LendS5
-       lhi     %r2,1
-       bras    %r14,_sclp_setup                # disable console
-.LendS5:
-       tm      LC_AR_MODE_ID,1
-       jno     .Lesa3
-       lgfr    %r2,%r2                         # sign extend return value
-       lmh     %r6,%r15,96(%r15)               # restore upper register halves
-       ahi     %r15,80
-.Lesa3:
-       lm      %r6,%r15,120(%r15)              # restore registers
-       br      %r14
-.Lzeroes:
-       .fill   64,4,0
-
-.LwritedataS4:
-       .long   0x00760005                      # SCLP command for write data
-.LwritesccbS4:
-       # sccb
-       .word   .LmtoS4-.LwritesccbS4
-       .byte   0
-       .byte   0,0,0
-       .word   0
-
-       # evbuf
-.LevbufS4:
-       .word   .LmtoS4-.LevbufS4
-       .byte   0x02
-       .byte   0
-       .word   0
-
-.LmdbS4:
-       # mdb
-       .word   .LmtoS4-.LmdbS4
-       .word   1
-       .long   0xd4c4c240
-       .long   1
-
-       # go
-.LgoS4:
-       .word   .LmtoS4-.LgoS4
-       .word   1
-       .long   0
-       .byte   0,0,0,0,0,0,0,0
-       .byte   0,0,0
-       .byte   0
-       .byte   0,0,0,0,0,0,0
-       .byte   0
-       .word   0
-       .byte   0,0,0,0,0,0,0,0,0,0
-       .byte   0,0,0,0,0,0,0,0
-       .byte   0,0,0,0,0,0,0,0
-
-.LmtoS4:
-       .word   .LmtoendS4-.LmtoS4
-       .word   4
-       .word   0x1000
-       .byte   0
-       .byte   0,0,0
-.LmtoendS4:
-
-       # Global constants
-.LsccbS0:
-       .long   _sclp_work_area
-.Lascebc:
-       .long   _ascebc
-
-.section .data,"aw",@progbits
-       .balign 4096
-_sclp_work_area:
-       .fill   4096
-.previous
diff --git a/arch/s390/kernel/sclp.c b/arch/s390/kernel/sclp.c
new file mode 100644 (file)
index 0000000..fa0bdff
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *    Copyright IBM Corp. 2015
+ *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+#include <linux/kernel.h>
+#include <asm/ebcdic.h>
+#include <asm/irq.h>
+#include <asm/lowcore.h>
+#include <asm/processor.h>
+#include <asm/sclp.h>
+
+static char _sclp_work_area[4096] __aligned(PAGE_SIZE);
+
+static void _sclp_wait_int(void)
+{
+       unsigned long cr0, cr0_new, psw_mask, addr;
+       psw_t psw_ext_save, psw_wait;
+
+       __ctl_store(cr0, 0, 0);
+       cr0_new = cr0 | 0x200;
+       __ctl_load(cr0_new, 0, 0);
+
+       psw_ext_save = S390_lowcore.external_new_psw;
+       psw_mask = __extract_psw() & (PSW_MASK_EA | PSW_MASK_BA);
+       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);
+
+       __ctl_load(cr0, 0, 0);
+       S390_lowcore.external_new_psw = psw_ext_save;
+}
+
+static int _sclp_servc(unsigned int cmd, char *sccb)
+{
+       unsigned int cc;
+
+       do {
+               asm volatile(
+                       "       .insn   rre,0xb2200000,%1,%2\n"
+                       "       ipm     %0\n"
+                       : "=d" (cc) : "d" (cmd), "a" (sccb)
+                       : "cc", "memory");
+               cc >>= 28;
+               if (cc == 3)
+                       return -EINVAL;
+               _sclp_wait_int();
+       } while (cc != 0);
+       return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO;
+}
+
+static int _sclp_setup(int disable)
+{
+       static unsigned char init_sccb[] = {
+               0x00, 0x1c,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x04,
+               0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+       unsigned int *masks;
+       int rc;
+
+       memcpy(_sclp_work_area, init_sccb, 28);
+       masks = (unsigned int *)(_sclp_work_area + 12);
+       if (disable)
+               memset(masks, 0, 16);
+       /* SCLP write mask */
+       rc = _sclp_servc(0x00780005, _sclp_work_area);
+       if (rc)
+               return rc;
+       if ((masks[0] & masks[3]) != masks[0] ||
+           (masks[1] & masks[2]) != masks[1])
+               return -EIO;
+       return 0;
+}
+
+static int _sclp_print(const char *str)
+{
+       static unsigned char write_head[] = {
+               /* sccb header */
+               0x00, 0x52,                                     /* 0 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,             /* 2 */
+               /* evbuf */
+               0x00, 0x4a,                                     /* 8 */
+               0x02, 0x00, 0x00, 0x00,                         /* 10 */
+               /* mdb */
+               0x00, 0x44,                                     /* 14 */
+               0x00, 0x01,                                     /* 16 */
+               0xd4, 0xc4, 0xc2, 0x40,                         /* 18 */
+               0x00, 0x00, 0x00, 0x01,                         /* 22 */
+               /* go */
+               0x00, 0x38,                                     /* 26 */
+               0x00, 0x01,                                     /* 28 */
+               0x00, 0x00, 0x00, 0x00,                         /* 30 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */
+               0x00, 0x00, 0x00, 0x00,                         /* 50 */
+               0x00, 0x00,                                     /* 54 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */
+               0x00, 0x00,                                     /* 80 */
+       };
+       static unsigned char write_mto[] = {
+               /* mto  */
+               0x00, 0x0a,                                     /* 0 */
+               0x00, 0x04,                                     /* 2 */
+               0x10, 0x00,                                     /* 4 */
+               0x00, 0x00, 0x00, 0x00                          /* 6 */
+       };
+       unsigned char *ptr, ch;
+       unsigned int count;
+
+       memcpy(_sclp_work_area, write_head, sizeof(write_head));
+       ptr = _sclp_work_area + sizeof(write_head);
+       do {
+               memcpy(ptr, write_mto, sizeof(write_mto));
+               for (count = sizeof(write_mto); (ch = *str++) != 0; count++) {
+                       if (ch == 0x0a)
+                               break;
+                       ptr[count] = _ascebc[ch];
+               }
+               /* Update length fields in mto, mdb, evbuf and sccb */
+               *(unsigned short *) ptr = count;
+               *(unsigned short *)(_sclp_work_area + 14) += count;
+               *(unsigned short *)(_sclp_work_area + 8) += count;
+               *(unsigned short *)(_sclp_work_area + 0) += count;
+               ptr += count;
+       } while (ch != 0);
+
+       /* SCLP write data */
+       return _sclp_servc(0x00760005, _sclp_work_area);
+}
+
+int _sclp_print_early(const char *str)
+{
+       int rc;
+
+       rc = _sclp_setup(0);
+       if (rc)
+               return rc;
+       rc = _sclp_print(str);
+       if (rc)
+               return rc;
+       return _sclp_setup(1);
+}