Linux 5.1.15
[linux/fpc-iii.git] / drivers / s390 / char / sclp_early_core.c
blob387c114ded3f776c843674ed1bb651bfe3233e8a
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright IBM Corp. 2015
4 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
5 */
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"
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;
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.
25 bool sclp_mask_compat_mode __section(.data);
27 void sclp_early_wait_irq(void)
29 unsigned long psw_mask, addr;
30 psw_t psw_ext_save, psw_wait;
31 union ctlreg0 cr0, cr0_new;
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);
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;
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);
59 S390_lowcore.external_new_psw = psw_ext_save;
60 __ctl_load(cr0.val, 0, 0);
63 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
65 unsigned long flags;
66 int rc;
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;
78 struct write_sccb {
79 struct sccb_header header;
80 struct msg_buf msg;
81 } __packed;
83 /* Output multi-line text using SCLP Message interface. */
84 static void sclp_early_print_lm(const char *str, unsigned int len)
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;
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];
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);
129 struct vt220_sccb {
130 struct sccb_header header;
131 struct {
132 struct evbuf_header header;
133 char data[];
134 } msg;
135 } __packed;
137 /* Output multi-line text using SCLP VT220 interface. */
138 static void sclp_early_print_vt220(const char *str, unsigned int len)
140 struct vt220_sccb *sccb;
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);
153 int sclp_early_set_event_mask(struct init_sccb *sccb,
154 sccb_mask_t receive_mask,
155 sccb_mask_t send_mask)
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;
172 if (sccb->header.response_code != 0x20)
173 return -EIO;
174 return 0;
177 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
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;
186 unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
188 if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
189 return 1;
190 return 0;
193 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
195 unsigned long receive_mask, send_mask;
196 struct init_sccb *sccb;
197 int rc;
199 BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
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;
214 * Output one or more lines of text on the SCLP console (VT220 and /
215 * or line-mode).
217 void __sclp_early_printk(const char *str, unsigned int len, unsigned int force)
219 int have_linemode, have_vt220;
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);
232 void sclp_early_printk(const char *str)
234 __sclp_early_printk(str, strlen(str), 0);
237 void sclp_early_printk_force(const char *str)
239 __sclp_early_printk(str, strlen(str), 1);
242 int __init sclp_early_read_info(void)
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};
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;
260 if (sccb->header.response_code != 0x1f0)
261 break;
263 return -EIO;
266 int __init sclp_early_get_info(struct read_info_sccb *info)
268 if (!sclp_info_sccb_valid)
269 return -EIO;
271 *info = sclp_info_sccb;
272 return 0;
275 int __init sclp_early_get_memsize(unsigned long *mem)
277 unsigned long rnmax;
278 unsigned long rnsize;
279 struct read_info_sccb *sccb = &sclp_info_sccb;
281 if (!sclp_info_sccb_valid)
282 return -EIO;
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;
291 int __init sclp_early_get_hsa_size(unsigned long *hsa_size)
293 if (!sclp_info_sccb_valid)
294 return -EIO;
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;
302 #define SCLP_STORAGE_INFO_FACILITY 0x0000400000000000UL
304 void __weak __init add_mem_detect_block(u64 start, u64 end) {}
305 int __init sclp_early_read_storage_info(void)
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;
313 if (!sclp_info_sccb_valid)
314 return -EIO;
316 if (!(sclp_info_sccb.facilities & SCLP_STORAGE_INFO_FACILITY))
317 return -EOPNOTSUPP;
319 rzm = sclp_info_sccb.rnsize ?: sclp_info_sccb.rnsize2;
320 rzm <<= 20;
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;
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);
339 break;
340 case 0x0310:
341 case 0x0410:
342 break;
343 default:
344 goto fail;
348 return 0;
349 fail:
350 mem_detect.count = 0;
351 return -EIO;