1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * driver: reading from and writing to system console on S/390 via SCLP |
4 | * |
5 | * Copyright IBM Corp. 1999, 2009 |
6 | * |
7 | * Author(s): Martin Peschke <mpeschke@de.ibm.com> |
8 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
9 | */ |
10 | |
11 | #include <linux/kmod.h> |
12 | #include <linux/types.h> |
13 | #include <linux/err.h> |
14 | #include <linux/string.h> |
15 | #include <linux/spinlock.h> |
16 | #include <linux/ctype.h> |
17 | #include <linux/uaccess.h> |
18 | |
19 | #include "sclp.h" |
20 | #include "sclp_rw.h" |
21 | |
22 | /* |
23 | * The room for the SCCB (only for writing) is not equal to a pages size |
24 | * (as it is specified as the maximum size in the SCLP documentation) |
25 | * because of the additional data structure described above. |
26 | */ |
27 | #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) |
28 | |
29 | /* Event type structure for write message and write priority message */ |
30 | static struct sclp_register sclp_rw_event = { |
31 | .send_mask = EVTYP_MSG_MASK, |
32 | }; |
33 | |
34 | /* |
35 | * Setup a sclp write buffer. Gets a page as input (4K) and returns |
36 | * a pointer to a struct sclp_buffer structure that is located at the |
37 | * end of the input page. This reduces the buffer space by a few |
38 | * bytes but simplifies things. |
39 | */ |
40 | struct sclp_buffer * |
41 | sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) |
42 | { |
43 | struct sclp_buffer *buffer; |
44 | struct sccb_header *sccb; |
45 | |
46 | sccb = (struct sccb_header *) page; |
47 | /* |
48 | * We keep the struct sclp_buffer structure at the end |
49 | * of the sccb page. |
50 | */ |
51 | buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1; |
52 | buffer->sccb = sccb; |
53 | buffer->retry_count = 0; |
54 | buffer->messages = 0; |
55 | buffer->char_sum = 0; |
56 | buffer->current_line = NULL; |
57 | buffer->current_length = 0; |
58 | buffer->columns = columns; |
59 | buffer->htab = htab; |
60 | |
61 | /* initialize sccb */ |
62 | memset(sccb, 0, sizeof(struct sccb_header)); |
63 | sccb->length = sizeof(struct sccb_header); |
64 | |
65 | return buffer; |
66 | } |
67 | |
68 | /* |
69 | * Return a pointer to the original page that has been used to create |
70 | * the buffer. |
71 | */ |
72 | void * |
73 | sclp_unmake_buffer(struct sclp_buffer *buffer) |
74 | { |
75 | return buffer->sccb; |
76 | } |
77 | |
78 | /* |
79 | * Initialize a new message the end of the provided buffer with |
80 | * enough room for max_len characters. Return 0 on success. |
81 | */ |
82 | static int |
83 | sclp_initialize_mto(struct sclp_buffer *buffer, int max_len) |
84 | { |
85 | struct sccb_header *sccb; |
86 | struct msg_buf *msg; |
87 | struct mdb *mdb; |
88 | struct go *go; |
89 | struct mto *mto; |
90 | int msg_size; |
91 | |
92 | /* max size of new message including message text */ |
93 | msg_size = sizeof(struct msg_buf) + max_len; |
94 | |
95 | /* check if current buffer sccb can contain the mto */ |
96 | sccb = buffer->sccb; |
97 | if ((MAX_SCCB_ROOM - sccb->length) < msg_size) |
98 | return -ENOMEM; |
99 | |
100 | msg = (struct msg_buf *)((addr_t) sccb + sccb->length); |
101 | memset(msg, 0, sizeof(struct msg_buf)); |
102 | msg->header.length = sizeof(struct msg_buf); |
103 | msg->header.type = EVTYP_MSG; |
104 | |
105 | mdb = &msg->mdb; |
106 | mdb->header.length = sizeof(struct mdb); |
107 | mdb->header.type = 1; |
108 | mdb->header.tag = 0xD4C4C240; /* ebcdic "MDB " */ |
109 | mdb->header.revision_code = 1; |
110 | |
111 | go = &mdb->go; |
112 | go->length = sizeof(struct go); |
113 | go->type = 1; |
114 | |
115 | mto = &mdb->mto; |
116 | mto->length = sizeof(struct mto); |
117 | mto->type = 4; /* message text object */ |
118 | mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */ |
119 | |
120 | /* set pointer to first byte after struct mto. */ |
121 | buffer->current_msg = msg; |
122 | buffer->current_line = (char *) (mto + 1); |
123 | buffer->current_length = 0; |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | /* |
129 | * Finalize message initialized by sclp_initialize_mto(), |
130 | * updating the sizes of MTO, enclosing MDB, event buffer and SCCB. |
131 | */ |
132 | static void |
133 | sclp_finalize_mto(struct sclp_buffer *buffer) |
134 | { |
135 | struct sccb_header *sccb; |
136 | struct msg_buf *msg; |
137 | |
138 | /* |
139 | * update values of sizes |
140 | * (SCCB, Event(Message) Buffer, Message Data Block) |
141 | */ |
142 | sccb = buffer->sccb; |
143 | msg = buffer->current_msg; |
144 | msg->header.length += buffer->current_length; |
145 | msg->mdb.header.length += buffer->current_length; |
146 | msg->mdb.mto.length += buffer->current_length; |
147 | sccb->length += msg->header.length; |
148 | |
149 | /* |
150 | * count number of buffered messages (= number of Message Text |
151 | * Objects) and number of buffered characters |
152 | * for the SCCB currently used for buffering and at all |
153 | */ |
154 | buffer->messages++; |
155 | buffer->char_sum += buffer->current_length; |
156 | |
157 | buffer->current_line = NULL; |
158 | buffer->current_length = 0; |
159 | buffer->current_msg = NULL; |
160 | } |
161 | |
162 | /* |
163 | * processing of a message including escape characters, |
164 | * returns number of characters written to the output sccb |
165 | * ("processed" means that is not guaranteed that the character have already |
166 | * been sent to the SCLP but that it will be done at least next time the SCLP |
167 | * is not busy) |
168 | */ |
169 | int |
170 | sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count) |
171 | { |
172 | int spaces, i_msg; |
173 | int rc; |
174 | |
175 | /* |
176 | * parse msg for escape sequences (\t,\v ...) and put formated |
177 | * msg into an mto (created by sclp_initialize_mto). |
178 | * |
179 | * We have to do this work ourselfs because there is no support for |
180 | * these characters on the native machine and only partial support |
181 | * under VM (Why does VM interpret \n but the native machine doesn't ?) |
182 | * |
183 | * Depending on i/o-control setting the message is always written |
184 | * immediately or we wait for a final new line maybe coming with the |
185 | * next message. Besides we avoid a buffer overrun by writing its |
186 | * content. |
187 | * |
188 | * RESTRICTIONS: |
189 | * |
190 | * \r and \b work within one line because we are not able to modify |
191 | * previous output that have already been accepted by the SCLP. |
192 | * |
193 | * \t combined with following \r is not correctly represented because |
194 | * \t is expanded to some spaces but \r does not know about a |
195 | * previous \t and decreases the current position by one column. |
196 | * This is in order to a slim and quick implementation. |
197 | */ |
198 | for (i_msg = 0; i_msg < count; i_msg++) { |
199 | switch (msg[i_msg]) { |
200 | case '\n': /* new line, line feed (ASCII) */ |
201 | /* check if new mto needs to be created */ |
202 | if (buffer->current_line == NULL) { |
203 | rc = sclp_initialize_mto(buffer, max_len: 0); |
204 | if (rc) |
205 | return i_msg; |
206 | } |
207 | sclp_finalize_mto(buffer); |
208 | break; |
209 | case '\a': /* bell, one for several times */ |
210 | /* set SCLP sound alarm bit in General Object */ |
211 | if (buffer->current_line == NULL) { |
212 | rc = sclp_initialize_mto(buffer, |
213 | max_len: buffer->columns); |
214 | if (rc) |
215 | return i_msg; |
216 | } |
217 | buffer->current_msg->mdb.go.general_msg_flags |= |
218 | GNRLMSGFLGS_SNDALRM; |
219 | break; |
220 | case '\t': /* horizontal tabulator */ |
221 | /* check if new mto needs to be created */ |
222 | if (buffer->current_line == NULL) { |
223 | rc = sclp_initialize_mto(buffer, |
224 | max_len: buffer->columns); |
225 | if (rc) |
226 | return i_msg; |
227 | } |
228 | /* "go to (next htab-boundary + 1, same line)" */ |
229 | do { |
230 | if (buffer->current_length >= buffer->columns) |
231 | break; |
232 | /* ok, add a blank */ |
233 | *buffer->current_line++ = 0x40; |
234 | buffer->current_length++; |
235 | } while (buffer->current_length % buffer->htab); |
236 | break; |
237 | case '\f': /* form feed */ |
238 | case '\v': /* vertical tabulator */ |
239 | /* "go to (actual column, actual line + 1)" */ |
240 | /* = new line, leading spaces */ |
241 | if (buffer->current_line != NULL) { |
242 | spaces = buffer->current_length; |
243 | sclp_finalize_mto(buffer); |
244 | rc = sclp_initialize_mto(buffer, |
245 | max_len: buffer->columns); |
246 | if (rc) |
247 | return i_msg; |
248 | memset(buffer->current_line, 0x40, spaces); |
249 | buffer->current_line += spaces; |
250 | buffer->current_length = spaces; |
251 | } else { |
252 | /* one an empty line this is the same as \n */ |
253 | rc = sclp_initialize_mto(buffer, |
254 | max_len: buffer->columns); |
255 | if (rc) |
256 | return i_msg; |
257 | sclp_finalize_mto(buffer); |
258 | } |
259 | break; |
260 | case '\b': /* backspace */ |
261 | /* "go to (actual column - 1, actual line)" */ |
262 | /* decrement counter indicating position, */ |
263 | /* do not remove last character */ |
264 | if (buffer->current_line != NULL && |
265 | buffer->current_length > 0) { |
266 | buffer->current_length--; |
267 | buffer->current_line--; |
268 | } |
269 | break; |
270 | case 0x00: /* end of string */ |
271 | /* transfer current line to SCCB */ |
272 | if (buffer->current_line != NULL) |
273 | sclp_finalize_mto(buffer); |
274 | /* skip the rest of the message including the 0 byte */ |
275 | i_msg = count - 1; |
276 | break; |
277 | default: /* no escape character */ |
278 | /* do not output unprintable characters */ |
279 | if (!isprint(msg[i_msg])) |
280 | break; |
281 | /* check if new mto needs to be created */ |
282 | if (buffer->current_line == NULL) { |
283 | rc = sclp_initialize_mto(buffer, |
284 | max_len: buffer->columns); |
285 | if (rc) |
286 | return i_msg; |
287 | } |
288 | *buffer->current_line++ = sclp_ascebc(ch: msg[i_msg]); |
289 | buffer->current_length++; |
290 | break; |
291 | } |
292 | /* check if current mto is full */ |
293 | if (buffer->current_line != NULL && |
294 | buffer->current_length >= buffer->columns) |
295 | sclp_finalize_mto(buffer); |
296 | } |
297 | |
298 | /* return number of processed characters */ |
299 | return i_msg; |
300 | } |
301 | |
302 | /* |
303 | * Return the number of free bytes in the sccb |
304 | */ |
305 | int |
306 | sclp_buffer_space(struct sclp_buffer *buffer) |
307 | { |
308 | struct sccb_header *sccb; |
309 | int count; |
310 | |
311 | sccb = buffer->sccb; |
312 | count = MAX_SCCB_ROOM - sccb->length; |
313 | if (buffer->current_line != NULL) |
314 | count -= sizeof(struct msg_buf) + buffer->current_length; |
315 | return count; |
316 | } |
317 | |
318 | /* |
319 | * Return number of characters in buffer |
320 | */ |
321 | unsigned int |
322 | sclp_chars_in_buffer(struct sclp_buffer *buffer) |
323 | { |
324 | unsigned int count; |
325 | |
326 | count = buffer->char_sum; |
327 | if (buffer->current_line != NULL) |
328 | count += buffer->current_length; |
329 | return count; |
330 | } |
331 | |
332 | /* |
333 | * called by sclp_console_init and/or sclp_tty_init |
334 | */ |
335 | int |
336 | sclp_rw_init(void) |
337 | { |
338 | static int init_done = 0; |
339 | int rc; |
340 | |
341 | if (init_done) |
342 | return 0; |
343 | |
344 | rc = sclp_register(reg: &sclp_rw_event); |
345 | if (rc == 0) |
346 | init_done = 1; |
347 | return rc; |
348 | } |
349 | |
350 | #define SCLP_BUFFER_MAX_RETRY 1 |
351 | |
352 | /* |
353 | * second half of Write Event Data-function that has to be done after |
354 | * interruption indicating completion of Service Call. |
355 | */ |
356 | static void |
357 | sclp_writedata_callback(struct sclp_req *request, void *data) |
358 | { |
359 | int rc; |
360 | struct sclp_buffer *buffer; |
361 | struct sccb_header *sccb; |
362 | |
363 | buffer = (struct sclp_buffer *) data; |
364 | sccb = buffer->sccb; |
365 | |
366 | if (request->status == SCLP_REQ_FAILED) { |
367 | if (buffer->callback != NULL) |
368 | buffer->callback(buffer, -EIO); |
369 | return; |
370 | } |
371 | /* check SCLP response code and choose suitable action */ |
372 | switch (sccb->response_code) { |
373 | case 0x0020 : |
374 | /* Normal completion, buffer processed, message(s) sent */ |
375 | rc = 0; |
376 | break; |
377 | |
378 | case 0x0340: /* Contained SCLP equipment check */ |
379 | if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) { |
380 | rc = -EIO; |
381 | break; |
382 | } |
383 | /* remove processed buffers and requeue rest */ |
384 | if (sclp_remove_processed(sccb: (struct sccb_header *) sccb) > 0) { |
385 | /* not all buffers were processed */ |
386 | sccb->response_code = 0x0000; |
387 | buffer->request.status = SCLP_REQ_FILLED; |
388 | rc = sclp_add_request(req: request); |
389 | if (rc == 0) |
390 | return; |
391 | } else |
392 | rc = 0; |
393 | break; |
394 | |
395 | case 0x0040: /* SCLP equipment check */ |
396 | case 0x05f0: /* Target resource in improper state */ |
397 | if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) { |
398 | rc = -EIO; |
399 | break; |
400 | } |
401 | /* retry request */ |
402 | sccb->response_code = 0x0000; |
403 | buffer->request.status = SCLP_REQ_FILLED; |
404 | rc = sclp_add_request(req: request); |
405 | if (rc == 0) |
406 | return; |
407 | break; |
408 | default: |
409 | if (sccb->response_code == 0x71f0) |
410 | rc = -ENOMEM; |
411 | else |
412 | rc = -EINVAL; |
413 | break; |
414 | } |
415 | if (buffer->callback != NULL) |
416 | buffer->callback(buffer, rc); |
417 | } |
418 | |
419 | /* |
420 | * Setup the request structure in the struct sclp_buffer to do SCLP Write |
421 | * Event Data and pass the request to the core SCLP loop. Return zero on |
422 | * success, non-zero otherwise. |
423 | */ |
424 | int |
425 | sclp_emit_buffer(struct sclp_buffer *buffer, |
426 | void (*callback)(struct sclp_buffer *, int)) |
427 | { |
428 | /* add current line if there is one */ |
429 | if (buffer->current_line != NULL) |
430 | sclp_finalize_mto(buffer); |
431 | |
432 | /* Are there messages in the output buffer ? */ |
433 | if (buffer->messages == 0) |
434 | return -EIO; |
435 | |
436 | buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; |
437 | buffer->request.status = SCLP_REQ_FILLED; |
438 | buffer->request.callback = sclp_writedata_callback; |
439 | buffer->request.callback_data = buffer; |
440 | buffer->request.sccb = buffer->sccb; |
441 | buffer->callback = callback; |
442 | return sclp_add_request(req: &buffer->request); |
443 | } |
444 | |