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