linux/drivers/s390/char/sclp_early_core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *    Copyright IBM Corp. 2015
   4 *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
   5 */
   6
   7#include <linux/kernel.h>
   8#include <asm/processor.h>
   9#include <asm/lowcore.h>
  10#include <asm/ebcdic.h>
  11#include <asm/irq.h>
  12#include "sclp.h"
  13#include "sclp_rw.h"
  14
  15char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
  16int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
  17/*
  18 * Used to keep track of the size of the event masks. Qemu until version 2.11
  19 * only supports 4 and needs a workaround.
  20 */
  21bool sclp_mask_compat_mode __section(.data);
  22
  23void sclp_early_wait_irq(void)
  24{
  25        unsigned long psw_mask, addr;
  26        psw_t psw_ext_save, psw_wait;
  27        union ctlreg0 cr0, cr0_new;
  28
  29        __ctl_store(cr0.val, 0, 0);
  30        cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
  31        cr0_new.lap = 0;
  32        cr0_new.sssm = 1;
  33        __ctl_load(cr0_new.val, 0, 0);
  34
  35        psw_ext_save = S390_lowcore.external_new_psw;
  36        psw_mask = __extract_psw();
  37        S390_lowcore.external_new_psw.mask = psw_mask;
  38        psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
  39        S390_lowcore.ext_int_code = 0;
  40
  41        do {
  42                asm volatile(
  43                        "       larl    %[addr],0f\n"
  44                        "       stg     %[addr],%[psw_wait_addr]\n"
  45                        "       stg     %[addr],%[psw_ext_addr]\n"
  46                        "       lpswe   %[psw_wait]\n"
  47                        "0:\n"
  48                        : [addr] "=&d" (addr),
  49                          [psw_wait_addr] "=Q" (psw_wait.addr),
  50                          [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
  51                        : [psw_wait] "Q" (psw_wait)
  52                        : "cc", "memory");
  53        } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
  54
  55        S390_lowcore.external_new_psw = psw_ext_save;
  56        __ctl_load(cr0.val, 0, 0);
  57}
  58
  59int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
  60{
  61        unsigned long flags;
  62        int rc;
  63
  64        raw_local_irq_save(flags);
  65        rc = sclp_service_call(cmd, sccb);
  66        if (rc)
  67                goto out;
  68        sclp_early_wait_irq();
  69out:
  70        raw_local_irq_restore(flags);
  71        return rc;
  72}
  73
  74struct write_sccb {
  75        struct sccb_header header;
  76        struct msg_buf msg;
  77} __packed;
  78
  79/* Output multi-line text using SCLP Message interface. */
  80static void sclp_early_print_lm(const char *str, unsigned int len)
  81{
  82        unsigned char *ptr, *end, ch;
  83        unsigned int count, offset;
  84        struct write_sccb *sccb;
  85        struct msg_buf *msg;
  86        struct mdb *mdb;
  87        struct mto *mto;
  88        struct go *go;
  89
  90        sccb = (struct write_sccb *) &sclp_early_sccb;
  91        end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1;
  92        memset(sccb, 0, sizeof(*sccb));
  93        ptr = (unsigned char *) &sccb->msg.mdb.mto;
  94        offset = 0;
  95        do {
  96                for (count = sizeof(*mto); offset < len; count++) {
  97                        ch = str[offset++];
  98                        if ((ch == 0x0a) || (ptr + count > end))
  99                                break;
 100                        ptr[count] = _ascebc[ch];
 101                }
 102                mto = (struct mto *) ptr;
 103                memset(mto, 0, sizeof(*mto));
 104                mto->length = count;
 105                mto->type = 4;
 106                mto->line_type_flags = LNTPFLGS_ENDTEXT;
 107                ptr += count;
 108        } while ((offset < len) && (ptr + sizeof(*mto) <= end));
 109        len = ptr - (unsigned char *) sccb;
 110        sccb->header.length = len - offsetof(struct write_sccb, header);
 111        msg = &sccb->msg;
 112        msg->header.type = EVTYP_MSG;
 113        msg->header.length = len - offsetof(struct write_sccb, msg.header);
 114        mdb = &msg->mdb;
 115        mdb->header.type = 1;
 116        mdb->header.tag = 0xD4C4C240;
 117        mdb->header.revision_code = 1;
 118        mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
 119        go = &mdb->go;
 120        go->length = sizeof(*go);
 121        go->type = 1;
 122        sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 123}
 124
 125struct vt220_sccb {
 126        struct sccb_header header;
 127        struct {
 128                struct evbuf_header header;
 129                char data[];
 130        } msg;
 131} __packed;
 132
 133/* Output multi-line text using SCLP VT220 interface. */
 134static void sclp_early_print_vt220(const char *str, unsigned int len)
 135{
 136        struct vt220_sccb *sccb;
 137
 138        sccb = (struct vt220_sccb *) &sclp_early_sccb;
 139        if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb))
 140                len = sizeof(sclp_early_sccb) - sizeof(*sccb);
 141        memset(sccb, 0, sizeof(*sccb));
 142        memcpy(&sccb->msg.data, str, len);
 143        sccb->header.length = sizeof(*sccb) + len;
 144        sccb->msg.header.length = sizeof(sccb->msg) + len;
 145        sccb->msg.header.type = EVTYP_VT220MSG;
 146        sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 147}
 148
 149int sclp_early_set_event_mask(struct init_sccb *sccb,
 150                              sccb_mask_t receive_mask,
 151                              sccb_mask_t send_mask)
 152{
 153retry:
 154        memset(sccb, 0, sizeof(*sccb));
 155        sccb->header.length = sizeof(*sccb);
 156        if (sclp_mask_compat_mode)
 157                sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
 158        else
 159                sccb->mask_length = sizeof(sccb_mask_t);
 160        sccb_set_recv_mask(sccb, receive_mask);
 161        sccb_set_send_mask(sccb, send_mask);
 162        if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
 163                return -EIO;
 164        if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
 165                sclp_mask_compat_mode = true;
 166                goto retry;
 167        }
 168        if (sccb->header.response_code != 0x20)
 169                return -EIO;
 170        return 0;
 171}
 172
 173unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
 174{
 175        if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
 176                return 0;
 177        if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
 178                return 0;
 179        return 1;
 180}
 181
 182unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
 183{
 184        if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
 185                return 1;
 186        return 0;
 187}
 188
 189static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
 190{
 191        unsigned long receive_mask, send_mask;
 192        struct init_sccb *sccb;
 193        int rc;
 194
 195        BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
 196
 197        *have_linemode = *have_vt220 = 0;
 198        sccb = (struct init_sccb *) &sclp_early_sccb;
 199        receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
 200        send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
 201        rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
 202        if (rc)
 203                return rc;
 204        *have_linemode = sclp_early_con_check_linemode(sccb);
 205        *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
 206        return rc;
 207}
 208
 209/*
 210 * Output one or more lines of text on the SCLP console (VT220 and /
 211 * or line-mode).
 212 */
 213void __sclp_early_printk(const char *str, unsigned int len)
 214{
 215        int have_linemode, have_vt220;
 216
 217        if (sclp_init_state != sclp_init_state_uninitialized)
 218                return;
 219        if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
 220                return;
 221        if (have_linemode)
 222                sclp_early_print_lm(str, len);
 223        if (have_vt220)
 224                sclp_early_print_vt220(str, len);
 225        sclp_early_setup(1, &have_linemode, &have_vt220);
 226}
 227
 228void sclp_early_printk(const char *str)
 229{
 230        __sclp_early_printk(str, strlen(str));
 231}
 232