xref: /openbmc/qemu/hw/char/sclpconsole-lm.c (revision 8692aa29)
1 /*
2  * SCLP event types
3  *    Operations Command - Line Mode input
4  *    Message            - Line Mode output
5  *
6  * Copyright IBM, Corp. 2013
7  *
8  * Authors:
9  *  Heinz Graalfs <graalfs@linux.vnet.ibm.com>
10  *
11  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
12  * option) any later version.  See the COPYING file in the top-level directory.
13  *
14  */
15 
16 #include "qemu/osdep.h"
17 #include "hw/qdev.h"
18 #include "qemu/thread.h"
19 #include "qemu/error-report.h"
20 #include "sysemu/char.h"
21 
22 #include "hw/s390x/sclp.h"
23 #include "hw/s390x/event-facility.h"
24 #include "hw/s390x/ebcdic.h"
25 
26 #define SIZE_BUFFER 4096
27 #define NEWLINE     "\n"
28 
29 typedef struct OprtnsCommand {
30     EventBufferHeader header;
31     MDMSU message_unit;
32     char data[0];
33 } QEMU_PACKED OprtnsCommand;
34 
35 /* max size for line-mode data in 4K SCCB page */
36 #define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand))
37 
38 typedef struct SCLPConsoleLM {
39     SCLPEvent event;
40     CharDriverState *chr;
41     bool echo;                  /* immediate echo of input if true        */
42     uint32_t write_errors;      /* errors writing to char layer           */
43     uint32_t length;            /* length of byte stream in buffer        */
44     uint8_t buf[SIZE_CONSOLE_BUFFER];
45 } SCLPConsoleLM;
46 
47 #define TYPE_SCLPLM_CONSOLE "sclplmconsole"
48 #define SCLPLM_CONSOLE(obj) \
49     OBJECT_CHECK(SCLPConsoleLM, (obj), TYPE_SCLPLM_CONSOLE)
50 
51 /*
52 *  Character layer call-back functions
53  *
54  * Allow 1 character at a time
55  *
56  * Accumulate bytes from character layer in console buffer,
57  * event_pending is set when a newline character is encountered
58  *
59  * The maximum command line length is limited by the maximum
60  * space available in an SCCB. Line mode console input is sent
61  * truncated to the guest in case it doesn't fit into the SCCB.
62  */
63 
64 static int chr_can_read(void *opaque)
65 {
66     SCLPConsoleLM *scon = opaque;
67 
68     if (scon->event.event_pending) {
69         return 0;
70     }
71     return 1;
72 }
73 
74 static void chr_read(void *opaque, const uint8_t *buf, int size)
75 {
76     SCLPConsoleLM *scon = opaque;
77 
78     assert(size == 1);
79 
80     if (*buf == '\r' || *buf == '\n') {
81         scon->event.event_pending = true;
82         sclp_service_interrupt(0);
83         return;
84     }
85     if (scon->length == SIZE_CONSOLE_BUFFER) {
86         /* Eat the character, but still process CR and LF.  */
87         return;
88     }
89     scon->buf[scon->length] = *buf;
90     scon->length += 1;
91     if (scon->echo) {
92         qemu_chr_fe_write(scon->chr, buf, size);
93     }
94 }
95 
96 /* functions to be called by event facility */
97 
98 static bool can_handle_event(uint8_t type)
99 {
100     return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD;
101 }
102 
103 static unsigned int send_mask(void)
104 {
105     return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD;
106 }
107 
108 static unsigned int receive_mask(void)
109 {
110     return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD;
111 }
112 
113 /*
114  * Triggered by SCLP's read_event_data
115  * - convert ASCII byte stream to EBCDIC and
116  * - copy converted data into provided (SCLP) buffer
117  */
118 static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
119                             int avail)
120 {
121     int len;
122 
123     SCLPConsoleLM *cons = SCLPLM_CONSOLE(event);
124 
125     len = cons->length;
126     /* data need to fit into provided SCLP buffer */
127     if (len > avail) {
128         return 1;
129     }
130 
131     ebcdic_put(buf, (char *)&cons->buf, len);
132     *size = len;
133     cons->length = 0;
134     /* data provided and no more data pending */
135     event->event_pending = false;
136     qemu_notify_event();
137     return 0;
138 }
139 
140 static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
141                            int *slen)
142 {
143     int avail, rc;
144     size_t src_len;
145     uint8_t *to;
146     OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr;
147 
148     if (!event->event_pending) {
149         /* no data pending */
150         return 0;
151     }
152 
153     to = (uint8_t *)&oc->data;
154     avail = *slen - sizeof(OprtnsCommand);
155     rc = get_console_data(event, to, &src_len, avail);
156     if (rc) {
157         /* data didn't fit, try next SCCB */
158         return 1;
159     }
160 
161     oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU;
162     oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU));
163 
164     oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU;
165     oc->message_unit.cpmsu.length =
166         cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector));
167 
168     oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD;
169     oc->message_unit.text_command.length =
170         cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector)));
171 
172     oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG;
173     oc->message_unit.self_def_text_message.length =
174         cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector)));
175 
176     oc->message_unit.text_message.key = GDS_KEY_TEXTMSG;
177     oc->message_unit.text_message.length =
178         cpu_to_be16(sizeof(GdsSubvector) + src_len);
179 
180     oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len);
181     oc->header.type = SCLP_EVENT_OPRTNS_COMMAND;
182     *slen = avail - src_len;
183 
184     return 1;
185 }
186 
187 /*
188  * Triggered by SCLP's write_event_data
189  *  - write console data to character layer
190  *  returns < 0 if an error occurred
191  */
192 static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len)
193 {
194     int ret = 0;
195     const uint8_t *buf_offset;
196 
197     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event);
198 
199     if (!scon->chr) {
200         /* If there's no backend, we can just say we consumed all data. */
201         return len;
202     }
203 
204     buf_offset = buf;
205     while (len > 0) {
206         ret = qemu_chr_fe_write(scon->chr, buf, len);
207         if (ret == 0) {
208             /* a pty doesn't seem to be connected - no error */
209             len = 0;
210         } else if (ret == -EAGAIN || (ret > 0 && ret < len)) {
211             len -= ret;
212             buf_offset += ret;
213         } else {
214             len = 0;
215         }
216     }
217 
218     return ret;
219 }
220 
221 static int process_mdb(SCLPEvent *event, MDBO *mdbo)
222 {
223     int rc;
224     int len;
225     uint8_t buffer[SIZE_BUFFER];
226 
227     len = be16_to_cpu(mdbo->length);
228     len -= sizeof(mdbo->length) + sizeof(mdbo->type)
229             + sizeof(mdbo->mto.line_type_flags)
230             + sizeof(mdbo->mto.alarm_control)
231             + sizeof(mdbo->mto._reserved);
232 
233     assert(len <= SIZE_BUFFER);
234 
235     /* convert EBCDIC SCLP contents to ASCII console message */
236     ascii_put(buffer, mdbo->mto.message, len);
237     rc = write_console_data(event, (uint8_t *)NEWLINE, 1);
238     if (rc < 0) {
239         return rc;
240     }
241     return write_console_data(event, buffer, len);
242 }
243 
244 static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh)
245 {
246     int len;
247     int written;
248     int errors = 0;
249     MDBO *mdbo;
250     SclpMsg *data = (SclpMsg *) ebh;
251     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event);
252 
253     len = be16_to_cpu(data->mdb.header.length);
254     if (len < sizeof(data->mdb.header)) {
255         return SCLP_RC_INCONSISTENT_LENGTHS;
256     }
257     len -= sizeof(data->mdb.header);
258 
259     /* first check message buffers */
260     mdbo = data->mdb.mdbo;
261     while (len > 0) {
262         if (be16_to_cpu(mdbo->length) > len
263                 || be16_to_cpu(mdbo->length) == 0) {
264             return SCLP_RC_INCONSISTENT_LENGTHS;
265         }
266         len -= be16_to_cpu(mdbo->length);
267         mdbo = (void *) mdbo + be16_to_cpu(mdbo->length);
268     }
269 
270     /* then execute */
271     len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header);
272     mdbo = data->mdb.mdbo;
273     while (len > 0) {
274         switch (be16_to_cpu(mdbo->type)) {
275         case MESSAGE_TEXT:
276             /* message text object */
277             written = process_mdb(event, mdbo);
278             if (written < 0) {
279                 /* character layer error */
280                 errors++;
281             }
282             break;
283         default: /* ignore */
284             break;
285         }
286         len -= be16_to_cpu(mdbo->length);
287         mdbo = (void *) mdbo + be16_to_cpu(mdbo->length);
288     }
289     if (errors) {
290         scon->write_errors += errors;
291     }
292     data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED;
293 
294     return SCLP_RC_NORMAL_COMPLETION;
295 }
296 
297 /* functions for live migration */
298 
299 static const VMStateDescription vmstate_sclplmconsole = {
300     .name = "sclplmconsole",
301     .version_id = 0,
302     .minimum_version_id = 0,
303     .fields = (VMStateField[]) {
304         VMSTATE_BOOL(event.event_pending, SCLPConsoleLM),
305         VMSTATE_UINT32(write_errors, SCLPConsoleLM),
306         VMSTATE_UINT32(length, SCLPConsoleLM),
307         VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER),
308         VMSTATE_END_OF_LIST()
309      }
310 };
311 
312 /* qemu object creation and initialization functions */
313 
314 /* tell character layer our call-back functions */
315 
316 static int console_init(SCLPEvent *event)
317 {
318     static bool console_available;
319 
320     SCLPConsoleLM *scon = SCLPLM_CONSOLE(event);
321 
322     if (console_available) {
323         error_report("Multiple line-mode operator consoles are not supported");
324         return -1;
325     }
326     console_available = true;
327 
328     if (scon->chr) {
329         qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon);
330     }
331 
332     return 0;
333 }
334 
335 static int console_exit(SCLPEvent *event)
336 {
337     return 0;
338 }
339 
340 static void console_reset(DeviceState *dev)
341 {
342    SCLPEvent *event = SCLP_EVENT(dev);
343    SCLPConsoleLM *scon = SCLPLM_CONSOLE(event);
344 
345    event->event_pending = false;
346    scon->length = 0;
347    scon->write_errors = 0;
348 }
349 
350 static Property console_properties[] = {
351     DEFINE_PROP_CHR("chardev", SCLPConsoleLM, chr),
352     DEFINE_PROP_UINT32("write_errors", SCLPConsoleLM, write_errors, 0),
353     DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true),
354     DEFINE_PROP_END_OF_LIST(),
355 };
356 
357 static void console_class_init(ObjectClass *klass, void *data)
358 {
359     DeviceClass *dc = DEVICE_CLASS(klass);
360     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
361 
362     dc->props = console_properties;
363     dc->reset = console_reset;
364     dc->vmsd = &vmstate_sclplmconsole;
365     ec->init = console_init;
366     ec->exit = console_exit;
367     ec->get_send_mask = send_mask;
368     ec->get_receive_mask = receive_mask;
369     ec->can_handle_event = can_handle_event;
370     ec->read_event_data = read_event_data;
371     ec->write_event_data = write_event_data;
372     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
373 }
374 
375 static const TypeInfo sclp_console_info = {
376     .name          = "sclplmconsole",
377     .parent        = TYPE_SCLP_EVENT,
378     .instance_size = sizeof(SCLPConsoleLM),
379     .class_init    = console_class_init,
380     .class_size    = sizeof(SCLPEventClass),
381 };
382 
383 static void register_types(void)
384 {
385     type_register_static(&sclp_console_info);
386 }
387 
388 type_init(register_types)
389