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