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 15 char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data); 16 int sclp_init_state __section(.data) = sclp_init_state_uninitialized; 17 18 void sclp_early_wait_irq(void) 19 { 20 unsigned long psw_mask, addr; 21 psw_t psw_ext_save, psw_wait; 22 union ctlreg0 cr0, cr0_new; 23 24 __ctl_store(cr0.val, 0, 0); 25 cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; 26 cr0_new.lap = 0; 27 cr0_new.sssm = 1; 28 __ctl_load(cr0_new.val, 0, 0); 29 30 psw_ext_save = S390_lowcore.external_new_psw; 31 psw_mask = __extract_psw(); 32 S390_lowcore.external_new_psw.mask = psw_mask; 33 psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; 34 S390_lowcore.ext_int_code = 0; 35 36 do { 37 asm volatile( 38 " larl %[addr],0f\n" 39 " stg %[addr],%[psw_wait_addr]\n" 40 " stg %[addr],%[psw_ext_addr]\n" 41 " lpswe %[psw_wait]\n" 42 "0:\n" 43 : [addr] "=&d" (addr), 44 [psw_wait_addr] "=Q" (psw_wait.addr), 45 [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) 46 : [psw_wait] "Q" (psw_wait) 47 : "cc", "memory"); 48 } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); 49 50 S390_lowcore.external_new_psw = psw_ext_save; 51 __ctl_load(cr0.val, 0, 0); 52 } 53 54 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) 55 { 56 unsigned long flags; 57 int rc; 58 59 raw_local_irq_save(flags); 60 rc = sclp_service_call(cmd, sccb); 61 if (rc) 62 goto out; 63 sclp_early_wait_irq(); 64 out: 65 raw_local_irq_restore(flags); 66 return rc; 67 } 68 69 struct write_sccb { 70 struct sccb_header header; 71 struct msg_buf msg; 72 } __packed; 73 74 /* Output multi-line text using SCLP Message interface. */ 75 static void sclp_early_print_lm(const char *str, unsigned int len) 76 { 77 unsigned char *ptr, *end, ch; 78 unsigned int count, offset; 79 struct write_sccb *sccb; 80 struct msg_buf *msg; 81 struct mdb *mdb; 82 struct mto *mto; 83 struct go *go; 84 85 sccb = (struct write_sccb *) &sclp_early_sccb; 86 end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1; 87 memset(sccb, 0, sizeof(*sccb)); 88 ptr = (unsigned char *) &sccb->msg.mdb.mto; 89 offset = 0; 90 do { 91 for (count = sizeof(*mto); offset < len; count++) { 92 ch = str[offset++]; 93 if ((ch == 0x0a) || (ptr + count > end)) 94 break; 95 ptr[count] = _ascebc[ch]; 96 } 97 mto = (struct mto *) ptr; 98 memset(mto, 0, sizeof(*mto)); 99 mto->length = count; 100 mto->type = 4; 101 mto->line_type_flags = LNTPFLGS_ENDTEXT; 102 ptr += count; 103 } while ((offset < len) && (ptr + sizeof(*mto) <= end)); 104 len = ptr - (unsigned char *) sccb; 105 sccb->header.length = len - offsetof(struct write_sccb, header); 106 msg = &sccb->msg; 107 msg->header.type = EVTYP_MSG; 108 msg->header.length = len - offsetof(struct write_sccb, msg.header); 109 mdb = &msg->mdb; 110 mdb->header.type = 1; 111 mdb->header.tag = 0xD4C4C240; 112 mdb->header.revision_code = 1; 113 mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header); 114 go = &mdb->go; 115 go->length = sizeof(*go); 116 go->type = 1; 117 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 118 } 119 120 struct vt220_sccb { 121 struct sccb_header header; 122 struct { 123 struct evbuf_header header; 124 char data[]; 125 } msg; 126 } __packed; 127 128 /* Output multi-line text using SCLP VT220 interface. */ 129 static void sclp_early_print_vt220(const char *str, unsigned int len) 130 { 131 struct vt220_sccb *sccb; 132 133 sccb = (struct vt220_sccb *) &sclp_early_sccb; 134 if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb)) 135 len = sizeof(sclp_early_sccb) - sizeof(*sccb); 136 memset(sccb, 0, sizeof(*sccb)); 137 memcpy(&sccb->msg.data, str, len); 138 sccb->header.length = sizeof(*sccb) + len; 139 sccb->msg.header.length = sizeof(sccb->msg) + len; 140 sccb->msg.header.type = EVTYP_VT220MSG; 141 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 142 } 143 144 int sclp_early_set_event_mask(struct init_sccb *sccb, 145 unsigned long receive_mask, 146 unsigned long send_mask) 147 { 148 memset(sccb, 0, sizeof(*sccb)); 149 sccb->header.length = sizeof(*sccb); 150 sccb->mask_length = sizeof(sccb_mask_t); 151 sccb->receive_mask = receive_mask; 152 sccb->send_mask = send_mask; 153 if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb)) 154 return -EIO; 155 if (sccb->header.response_code != 0x20) 156 return -EIO; 157 return 0; 158 } 159 160 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) 161 { 162 if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) 163 return 0; 164 if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) 165 return 0; 166 return 1; 167 } 168 169 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220) 170 { 171 unsigned long receive_mask, send_mask; 172 struct init_sccb *sccb; 173 int rc; 174 175 *have_linemode = *have_vt220 = 0; 176 sccb = (struct init_sccb *) &sclp_early_sccb; 177 receive_mask = disable ? 0 : EVTYP_OPCMD_MASK; 178 send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK; 179 rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask); 180 if (rc) 181 return rc; 182 *have_linemode = sclp_early_con_check_linemode(sccb); 183 *have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK; 184 return rc; 185 } 186 187 /* 188 * Output one or more lines of text on the SCLP console (VT220 and / 189 * or line-mode). 190 */ 191 void __sclp_early_printk(const char *str, unsigned int len) 192 { 193 int have_linemode, have_vt220; 194 195 if (sclp_init_state != sclp_init_state_uninitialized) 196 return; 197 if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0) 198 return; 199 if (have_linemode) 200 sclp_early_print_lm(str, len); 201 if (have_vt220) 202 sclp_early_print_vt220(str, len); 203 sclp_early_setup(1, &have_linemode, &have_vt220); 204 } 205 206 void sclp_early_printk(const char *str) 207 { 208 __sclp_early_printk(str, strlen(str)); 209 } 210