xref: /openbmc/qemu/hw/char/sclpconsole.c (revision 9944d320)
1 /*
2  * SCLP event type
3  *    Ascii Console Data (VT220 Console)
4  *
5  * Copyright IBM, Corp. 2012
6  *
7  * Authors:
8  *  Heinz Graalfs <graalfs@de.ibm.com>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
11  * option) any later version.  See the COPYING file in the top-level directory.
12  *
13  */
14 
15 #include <hw/qdev.h>
16 #include "qemu/thread.h"
17 #include "qemu/error-report.h"
18 
19 #include "hw/s390x/sclp.h"
20 #include "hw/s390x/event-facility.h"
21 #include "char/char.h"
22 
23 typedef struct ASCIIConsoleData {
24     EventBufferHeader ebh;
25     char data[0];
26 } QEMU_PACKED ASCIIConsoleData;
27 
28 /* max size for ASCII data in 4K SCCB page */
29 #define SIZE_BUFFER_VT220 4080
30 
31 typedef struct SCLPConsole {
32     SCLPEvent event;
33     CharDriverState *chr;
34     /* io vector                                                       */
35     uint8_t *iov;           /* iov buffer pointer                      */
36     uint8_t *iov_sclp;      /* pointer to SCLP read offset             */
37     uint8_t *iov_bs;        /* pointer byte stream read offset         */
38     uint32_t iov_data_len;  /* length of byte stream in buffer         */
39     uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */
40     qemu_irq irq_read_vt220;
41 } SCLPConsole;
42 
43 /* character layer call-back functions */
44 
45 /* Return number of bytes that fit into iov buffer */
46 static int chr_can_read(void *opaque)
47 {
48     SCLPConsole *scon = opaque;
49 
50     return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0;
51 }
52 
53 /* Receive n bytes from character layer, save in iov buffer,
54  * and set event pending */
55 static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf,
56                                    int size)
57 {
58     assert(scon->iov);
59 
60     /* read data must fit into current buffer */
61     assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
62 
63     /* put byte-stream from character layer into buffer */
64     memcpy(scon->iov_bs, buf, size);
65     scon->iov_data_len += size;
66     scon->iov_sclp_rest += size;
67     scon->iov_bs += size;
68     scon->event.event_pending = true;
69 }
70 
71 /* Send data from a char device over to the guest */
72 static void chr_read(void *opaque, const uint8_t *buf, int size)
73 {
74     SCLPConsole *scon = opaque;
75 
76     assert(scon);
77 
78     receive_from_chr_layer(scon, buf, size);
79     /* trigger SCLP read operation */
80     qemu_irq_raise(scon->irq_read_vt220);
81 }
82 
83 static void chr_event(void *opaque, int event)
84 {
85     SCLPConsole *scon = opaque;
86 
87     switch (event) {
88     case CHR_EVENT_OPENED:
89         if (!scon->iov) {
90             scon->iov = g_malloc0(SIZE_BUFFER_VT220);
91             scon->iov_sclp = scon->iov;
92             scon->iov_bs = scon->iov;
93             scon->iov_data_len = 0;
94             scon->iov_sclp_rest = 0;
95         }
96         break;
97     case CHR_EVENT_CLOSED:
98         if (scon->iov) {
99             g_free(scon->iov);
100             scon->iov = NULL;
101         }
102         break;
103     }
104 }
105 
106 /* functions to be called by event facility */
107 
108 static int event_type(void)
109 {
110     return SCLP_EVENT_ASCII_CONSOLE_DATA;
111 }
112 
113 static unsigned int send_mask(void)
114 {
115     return SCLP_EVENT_MASK_MSG_ASCII;
116 }
117 
118 static unsigned int receive_mask(void)
119 {
120     return SCLP_EVENT_MASK_MSG_ASCII;
121 }
122 
123 /* triggered by SCLP's read_event_data -
124  * copy console data byte-stream into provided (SCLP) buffer
125  */
126 static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
127                              int avail)
128 {
129     SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event);
130 
131     /* first byte is hex 0 saying an ascii string follows */
132     *buf++ = '\0';
133     avail--;
134     /* if all data fit into provided SCLP buffer */
135     if (avail >= cons->iov_sclp_rest) {
136         /* copy character byte-stream to SCLP buffer */
137         memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest);
138         *size = cons->iov_sclp_rest + 1;
139         cons->iov_sclp = cons->iov;
140         cons->iov_bs = cons->iov;
141         cons->iov_data_len = 0;
142         cons->iov_sclp_rest = 0;
143         event->event_pending = false;
144         /* data provided and no more data pending */
145     } else {
146         /* if provided buffer is too small, just copy part */
147         memcpy(buf, cons->iov_sclp, avail);
148         *size = avail + 1;
149         cons->iov_sclp_rest -= avail;
150         cons->iov_sclp += avail;
151         /* more data pending */
152     }
153 }
154 
155 static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
156                            int *slen)
157 {
158     int avail;
159     size_t src_len;
160     uint8_t *to;
161     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
162 
163     if (!event->event_pending) {
164         /* no data pending */
165         return 0;
166     }
167 
168     to = (uint8_t *)&acd->data;
169     avail = *slen - sizeof(ASCIIConsoleData);
170     get_console_data(event, to, &src_len, avail);
171 
172     acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
173     acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
174     acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
175     *slen = avail - src_len;
176 
177     return 1;
178 }
179 
180 /* triggered by SCLP's write_event_data
181  *  - write console data to character layer
182  *  returns < 0 if an error occurred
183  */
184 static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
185                                   size_t len)
186 {
187     ssize_t ret = 0;
188     const uint8_t *iov_offset;
189     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
190 
191     if (!scon->chr) {
192         /* If there's no backend, we can just say we consumed all data. */
193         return len;
194     }
195 
196     iov_offset = buf;
197     while (len > 0) {
198         ret = qemu_chr_fe_write(scon->chr, buf, len);
199         if (ret == 0) {
200             /* a pty doesn't seem to be connected - no error */
201             len = 0;
202         } else if (ret == -EAGAIN || (ret > 0 && ret < len)) {
203             len -= ret;
204             iov_offset += ret;
205         } else {
206             len = 0;
207         }
208     }
209 
210     return ret;
211 }
212 
213 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
214 {
215     int rc;
216     int length;
217     ssize_t written;
218     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
219 
220     length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
221     written = write_console_data(event, (uint8_t *)acd->data, length);
222 
223     rc = SCLP_RC_NORMAL_COMPLETION;
224     /* set event buffer accepted flag */
225     evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
226 
227     /* written will be zero if a pty is not connected - don't treat as error */
228     if (written < 0) {
229         /* event buffer not accepted due to error in character layer */
230         evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
231         rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
232     }
233 
234     return rc;
235 }
236 
237 static void trigger_ascii_console_data(void *opaque, int n, int level)
238 {
239     sclp_service_interrupt(0);
240 }
241 
242 /* qemu object creation and initialization functions */
243 
244 /* tell character layer our call-back functions */
245 static int console_init(SCLPEvent *event)
246 {
247     static bool console_available;
248 
249     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
250 
251     if (console_available) {
252         error_report("Multiple VT220 operator consoles are not supported");
253         return -1;
254     }
255     console_available = true;
256     event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA;
257     if (scon->chr) {
258         qemu_chr_add_handlers(scon->chr, chr_can_read,
259                               chr_read, chr_event, scon);
260     }
261     scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data,
262                                                NULL, 1);
263 
264     return 0;
265 }
266 
267 static int console_exit(SCLPEvent *event)
268 {
269     return 0;
270 }
271 
272 static Property console_properties[] = {
273     DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
274     DEFINE_PROP_END_OF_LIST(),
275 };
276 
277 static void console_class_init(ObjectClass *klass, void *data)
278 {
279     DeviceClass *dc = DEVICE_CLASS(klass);
280     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
281 
282     dc->props = console_properties;
283     ec->init = console_init;
284     ec->exit = console_exit;
285     ec->get_send_mask = send_mask;
286     ec->get_receive_mask = receive_mask;
287     ec->event_type = event_type;
288     ec->read_event_data = read_event_data;
289     ec->write_event_data = write_event_data;
290 }
291 
292 static const TypeInfo sclp_console_info = {
293     .name          = "sclpconsole",
294     .parent        = TYPE_SCLP_EVENT,
295     .instance_size = sizeof(SCLPConsole),
296     .class_init    = console_class_init,
297     .class_size    = sizeof(SCLPEventClass),
298 };
299 
300 static void register_types(void)
301 {
302     type_register_static(&sclp_console_info);
303 }
304 
305 type_init(register_types)
306