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 <asm/sections.h> 13 #include <asm/mem_detect.h> 14 #include "sclp.h" 15 #include "sclp_rw.h" 16 17 static struct read_info_sccb __bootdata(sclp_info_sccb); 18 static int __bootdata(sclp_info_sccb_valid); 19 char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data); 20 int sclp_init_state __section(.data) = sclp_init_state_uninitialized; 21 /* 22 * Used to keep track of the size of the event masks. Qemu until version 2.11 23 * only supports 4 and needs a workaround. 24 */ 25 bool sclp_mask_compat_mode __section(.data); 26 27 void sclp_early_wait_irq(void) 28 { 29 unsigned long psw_mask, addr; 30 psw_t psw_ext_save, psw_wait; 31 union ctlreg0 cr0, cr0_new; 32 33 __ctl_store(cr0.val, 0, 0); 34 cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK; 35 cr0_new.lap = 0; 36 cr0_new.sssm = 1; 37 __ctl_load(cr0_new.val, 0, 0); 38 39 psw_ext_save = S390_lowcore.external_new_psw; 40 psw_mask = __extract_psw(); 41 S390_lowcore.external_new_psw.mask = psw_mask; 42 psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; 43 S390_lowcore.ext_int_code = 0; 44 45 do { 46 asm volatile( 47 " larl %[addr],0f\n" 48 " stg %[addr],%[psw_wait_addr]\n" 49 " stg %[addr],%[psw_ext_addr]\n" 50 " lpswe %[psw_wait]\n" 51 "0:\n" 52 : [addr] "=&d" (addr), 53 [psw_wait_addr] "=Q" (psw_wait.addr), 54 [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) 55 : [psw_wait] "Q" (psw_wait) 56 : "cc", "memory"); 57 } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); 58 59 S390_lowcore.external_new_psw = psw_ext_save; 60 __ctl_load(cr0.val, 0, 0); 61 } 62 63 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) 64 { 65 unsigned long flags; 66 int rc; 67 68 raw_local_irq_save(flags); 69 rc = sclp_service_call(cmd, sccb); 70 if (rc) 71 goto out; 72 sclp_early_wait_irq(); 73 out: 74 raw_local_irq_restore(flags); 75 return rc; 76 } 77 78 struct write_sccb { 79 struct sccb_header header; 80 struct msg_buf msg; 81 } __packed; 82 83 /* Output multi-line text using SCLP Message interface. */ 84 static void sclp_early_print_lm(const char *str, unsigned int len) 85 { 86 unsigned char *ptr, *end, ch; 87 unsigned int count, offset; 88 struct write_sccb *sccb; 89 struct msg_buf *msg; 90 struct mdb *mdb; 91 struct mto *mto; 92 struct go *go; 93 94 sccb = (struct write_sccb *) &sclp_early_sccb; 95 end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1; 96 memset(sccb, 0, sizeof(*sccb)); 97 ptr = (unsigned char *) &sccb->msg.mdb.mto; 98 offset = 0; 99 do { 100 for (count = sizeof(*mto); offset < len; count++) { 101 ch = str[offset++]; 102 if ((ch == 0x0a) || (ptr + count > end)) 103 break; 104 ptr[count] = _ascebc[ch]; 105 } 106 mto = (struct mto *) ptr; 107 memset(mto, 0, sizeof(*mto)); 108 mto->length = count; 109 mto->type = 4; 110 mto->line_type_flags = LNTPFLGS_ENDTEXT; 111 ptr += count; 112 } while ((offset < len) && (ptr + sizeof(*mto) <= end)); 113 len = ptr - (unsigned char *) sccb; 114 sccb->header.length = len - offsetof(struct write_sccb, header); 115 msg = &sccb->msg; 116 msg->header.type = EVTYP_MSG; 117 msg->header.length = len - offsetof(struct write_sccb, msg.header); 118 mdb = &msg->mdb; 119 mdb->header.type = 1; 120 mdb->header.tag = 0xD4C4C240; 121 mdb->header.revision_code = 1; 122 mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header); 123 go = &mdb->go; 124 go->length = sizeof(*go); 125 go->type = 1; 126 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 127 } 128 129 struct vt220_sccb { 130 struct sccb_header header; 131 struct { 132 struct evbuf_header header; 133 char data[]; 134 } msg; 135 } __packed; 136 137 /* Output multi-line text using SCLP VT220 interface. */ 138 static void sclp_early_print_vt220(const char *str, unsigned int len) 139 { 140 struct vt220_sccb *sccb; 141 142 sccb = (struct vt220_sccb *) &sclp_early_sccb; 143 if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb)) 144 len = sizeof(sclp_early_sccb) - sizeof(*sccb); 145 memset(sccb, 0, sizeof(*sccb)); 146 memcpy(&sccb->msg.data, str, len); 147 sccb->header.length = sizeof(*sccb) + len; 148 sccb->msg.header.length = sizeof(sccb->msg) + len; 149 sccb->msg.header.type = EVTYP_VT220MSG; 150 sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb); 151 } 152 153 int sclp_early_set_event_mask(struct init_sccb *sccb, 154 sccb_mask_t receive_mask, 155 sccb_mask_t send_mask) 156 { 157 retry: 158 memset(sccb, 0, sizeof(*sccb)); 159 sccb->header.length = sizeof(*sccb); 160 if (sclp_mask_compat_mode) 161 sccb->mask_length = SCLP_MASK_SIZE_COMPAT; 162 else 163 sccb->mask_length = sizeof(sccb_mask_t); 164 sccb_set_recv_mask(sccb, receive_mask); 165 sccb_set_send_mask(sccb, send_mask); 166 if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb)) 167 return -EIO; 168 if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) { 169 sclp_mask_compat_mode = true; 170 goto retry; 171 } 172 if (sccb->header.response_code != 0x20) 173 return -EIO; 174 return 0; 175 } 176 177 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) 178 { 179 if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK)) 180 return 0; 181 if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) 182 return 0; 183 return 1; 184 } 185 186 unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb) 187 { 188 if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK) 189 return 1; 190 return 0; 191 } 192 193 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220) 194 { 195 unsigned long receive_mask, send_mask; 196 struct init_sccb *sccb; 197 int rc; 198 199 BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE); 200 201 *have_linemode = *have_vt220 = 0; 202 sccb = (struct init_sccb *) &sclp_early_sccb; 203 receive_mask = disable ? 0 : EVTYP_OPCMD_MASK; 204 send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK; 205 rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask); 206 if (rc) 207 return rc; 208 *have_linemode = sclp_early_con_check_linemode(sccb); 209 *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK); 210 return rc; 211 } 212 213 /* 214 * Output one or more lines of text on the SCLP console (VT220 and / 215 * or line-mode). 216 */ 217 void __sclp_early_printk(const char *str, unsigned int len, unsigned int force) 218 { 219 int have_linemode, have_vt220; 220 221 if (!force && sclp_init_state != sclp_init_state_uninitialized) 222 return; 223 if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0) 224 return; 225 if (have_linemode) 226 sclp_early_print_lm(str, len); 227 if (have_vt220) 228 sclp_early_print_vt220(str, len); 229 sclp_early_setup(1, &have_linemode, &have_vt220); 230 } 231 232 void sclp_early_printk(const char *str) 233 { 234 __sclp_early_printk(str, strlen(str), 0); 235 } 236 237 void sclp_early_printk_force(const char *str) 238 { 239 __sclp_early_printk(str, strlen(str), 1); 240 } 241 242 int __init sclp_early_read_info(void) 243 { 244 int i; 245 struct read_info_sccb *sccb = &sclp_info_sccb; 246 sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, 247 SCLP_CMDW_READ_SCP_INFO}; 248 249 for (i = 0; i < ARRAY_SIZE(commands); i++) { 250 memset(sccb, 0, sizeof(*sccb)); 251 sccb->header.length = sizeof(*sccb); 252 sccb->header.function_code = 0x80; 253 sccb->header.control_mask[2] = 0x80; 254 if (sclp_early_cmd(commands[i], sccb)) 255 break; 256 if (sccb->header.response_code == 0x10) { 257 sclp_info_sccb_valid = 1; 258 return 0; 259 } 260 if (sccb->header.response_code != 0x1f0) 261 break; 262 } 263 return -EIO; 264 } 265 266 int __init sclp_early_get_info(struct read_info_sccb *info) 267 { 268 if (!sclp_info_sccb_valid) 269 return -EIO; 270 271 *info = sclp_info_sccb; 272 return 0; 273 } 274 275 int __init sclp_early_get_memsize(unsigned long *mem) 276 { 277 unsigned long rnmax; 278 unsigned long rnsize; 279 struct read_info_sccb *sccb = &sclp_info_sccb; 280 281 if (!sclp_info_sccb_valid) 282 return -EIO; 283 284 rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; 285 rnsize = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; 286 rnsize <<= 20; 287 *mem = rnsize * rnmax; 288 return 0; 289 } 290 291 int __init sclp_early_get_hsa_size(unsigned long *hsa_size) 292 { 293 if (!sclp_info_sccb_valid) 294 return -EIO; 295 296 *hsa_size = 0; 297 if (sclp_info_sccb.hsa_size) 298 *hsa_size = (sclp_info_sccb.hsa_size - 1) * PAGE_SIZE; 299 return 0; 300 } 301 302 #define SCLP_STORAGE_INFO_FACILITY 0x0000400000000000UL 303 304 void __weak __init add_mem_detect_block(u64 start, u64 end) {} 305 int __init sclp_early_read_storage_info(void) 306 { 307 struct read_storage_sccb *sccb = (struct read_storage_sccb *)&sclp_early_sccb; 308 int rc, id, max_id = 0; 309 unsigned long rn, rzm; 310 sclp_cmdw_t command; 311 u16 sn; 312 313 if (!sclp_info_sccb_valid) 314 return -EIO; 315 316 if (!(sclp_info_sccb.facilities & SCLP_STORAGE_INFO_FACILITY)) 317 return -EOPNOTSUPP; 318 319 rzm = sclp_info_sccb.rnsize ?: sclp_info_sccb.rnsize2; 320 rzm <<= 20; 321 322 for (id = 0; id <= max_id; id++) { 323 memset(sclp_early_sccb, 0, sizeof(sclp_early_sccb)); 324 sccb->header.length = sizeof(sclp_early_sccb); 325 command = SCLP_CMDW_READ_STORAGE_INFO | (id << 8); 326 rc = sclp_early_cmd(command, sccb); 327 if (rc) 328 goto fail; 329 330 max_id = sccb->max_id; 331 switch (sccb->header.response_code) { 332 case 0x0010: 333 for (sn = 0; sn < sccb->assigned; sn++) { 334 if (!sccb->entries[sn]) 335 continue; 336 rn = sccb->entries[sn] >> 16; 337 add_mem_detect_block((rn - 1) * rzm, rn * rzm); 338 } 339 break; 340 case 0x0310: 341 case 0x0410: 342 break; 343 default: 344 goto fail; 345 } 346 } 347 348 return 0; 349 fail: 350 mem_detect.count = 0; 351 return -EIO; 352 } 353