]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
22362a0e MS |
2 | /* |
3 | * Copyright IBM Corp. 2015 | |
4 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | |
5 | */ | |
d5ab7a34 | 6 | |
22362a0e | 7 | #include <linux/kernel.h> |
d5ab7a34 HC |
8 | #include <asm/processor.h> |
9 | #include <asm/lowcore.h> | |
22362a0e MS |
10 | #include <asm/ebcdic.h> |
11 | #include <asm/irq.h> | |
d5ab7a34 HC |
12 | #include "sclp.h" |
13 | #include "sclp_rw.h" | |
22362a0e | 14 | |
d5ab7a34 | 15 | char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(data); |
76fdf141 HC |
16 | int sclp_init_state __section(data) = sclp_init_state_uninitialized; |
17 | ||
d5ab7a34 | 18 | void sclp_early_wait_irq(void) |
22362a0e | 19 | { |
d5ab7a34 | 20 | unsigned long psw_mask, addr; |
22362a0e | 21 | psw_t psw_ext_save, psw_wait; |
742dc577 | 22 | union ctlreg0 cr0, cr0_new; |
22362a0e | 23 | |
742dc577 HC |
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); | |
22362a0e MS |
29 | |
30 | psw_ext_save = S390_lowcore.external_new_psw; | |
f07f21b3 | 31 | psw_mask = __extract_psw(); |
22362a0e MS |
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 | ||
22362a0e | 50 | S390_lowcore.external_new_psw = psw_ext_save; |
742dc577 | 51 | __ctl_load(cr0.val, 0, 0); |
22362a0e MS |
52 | } |
53 | ||
f694bb3a | 54 | int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb) |
22362a0e | 55 | { |
d5ab7a34 HC |
56 | unsigned long flags; |
57 | int rc; | |
22362a0e | 58 | |
d5ab7a34 HC |
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; | |
22362a0e MS |
67 | } |
68 | ||
d5ab7a34 HC |
69 | struct write_sccb { |
70 | struct sccb_header header; | |
71 | struct msg_buf msg; | |
72 | } __packed; | |
73 | ||
3f975df6 | 74 | /* Output multi-line text using SCLP Message interface. */ |
d5ab7a34 | 75 | static void sclp_early_print_lm(const char *str, unsigned int len) |
22362a0e | 76 | { |
d5ab7a34 HC |
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; | |
22362a0e | 90 | do { |
d5ab7a34 HC |
91 | for (count = sizeof(*mto); offset < len; count++) { |
92 | ch = str[offset++]; | |
93 | if ((ch == 0x0a) || (ptr + count > end)) | |
f0319748 | 94 | break; |
22362a0e MS |
95 | ptr[count] = _ascebc[ch]; |
96 | } | |
d5ab7a34 HC |
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; | |
22362a0e | 102 | ptr += count; |
d5ab7a34 HC |
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); | |
22362a0e MS |
118 | } |
119 | ||
d5ab7a34 HC |
120 | struct vt220_sccb { |
121 | struct sccb_header header; | |
122 | struct { | |
123 | struct evbuf_header header; | |
124 | char data[]; | |
125 | } msg; | |
126 | } __packed; | |
127 | ||
02407baa | 128 | /* Output multi-line text using SCLP VT220 interface. */ |
d5ab7a34 HC |
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)) | |
02407baa | 135 | len = sizeof(sclp_early_sccb) - sizeof(*sccb); |
d5ab7a34 HC |
136 | memset(sccb, 0, sizeof(*sccb)); |
137 | memcpy(&sccb->msg.data, str, len); | |
02407baa HC |
138 | sccb->header.length = sizeof(*sccb) + len; |
139 | sccb->msg.header.length = sizeof(sccb->msg) + len; | |
d5ab7a34 HC |
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; | |
f694bb3a HC |
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; | |
d5ab7a34 HC |
158 | } |
159 | ||
160 | unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb) | |
22362a0e | 161 | { |
d5ab7a34 HC |
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; | |
3f975df6 SS |
185 | } |
186 | ||
02407baa HC |
187 | /* |
188 | * Output one or more lines of text on the SCLP console (VT220 and / | |
189 | * or line-mode). | |
3f975df6 | 190 | */ |
d5ab7a34 | 191 | void __sclp_early_printk(const char *str, unsigned int len) |
3f975df6 | 192 | { |
d5ab7a34 HC |
193 | int have_linemode, have_vt220; |
194 | ||
76fdf141 HC |
195 | if (sclp_init_state != sclp_init_state_uninitialized) |
196 | return; | |
d5ab7a34 | 197 | if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0) |
3f975df6 SS |
198 | return; |
199 | if (have_linemode) | |
d5ab7a34 | 200 | sclp_early_print_lm(str, len); |
3f975df6 | 201 | if (have_vt220) |
d5ab7a34 HC |
202 | sclp_early_print_vt220(str, len); |
203 | sclp_early_setup(1, &have_linemode, &have_vt220); | |
22362a0e | 204 | } |
89175cf7 | 205 | |
d5ab7a34 | 206 | void sclp_early_printk(const char *str) |
89175cf7 | 207 | { |
d5ab7a34 | 208 | __sclp_early_printk(str, strlen(str)); |
89175cf7 | 209 | } |