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