xref: /openbmc/qemu/hw/char/sclpconsole.c (revision 77a8257e)
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 "sysemu/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     uint8_t iov[SIZE_BUFFER_VT220];
35     uint32_t iov_sclp;      /* offset in buf for SCLP read operation       */
36     uint32_t iov_bs;        /* offset in buf for char layer read operation */
37     uint32_t iov_data_len;  /* length of byte stream in buffer             */
38     uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP     */
39     bool notify;            /* qemu_notify_event() req'd if true           */
40 } SCLPConsole;
41 
42 /* character layer call-back functions */
43 
44 /* Return number of bytes that fit into iov buffer */
45 static int chr_can_read(void *opaque)
46 {
47     SCLPConsole *scon = opaque;
48     int avail = SIZE_BUFFER_VT220 - scon->iov_data_len;
49 
50     if (avail == 0) {
51         scon->notify = true;
52     }
53     return avail;
54 }
55 
56 /* Send data from a char device over to the guest */
57 static void chr_read(void *opaque, const uint8_t *buf, int size)
58 {
59     SCLPConsole *scon = opaque;
60 
61     assert(scon);
62     /* read data must fit into current buffer */
63     assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
64 
65     /* put byte-stream from character layer into buffer */
66     memcpy(&scon->iov[scon->iov_bs], buf, size);
67     scon->iov_data_len += size;
68     scon->iov_sclp_rest += size;
69     scon->iov_bs += size;
70     scon->event.event_pending = true;
71     sclp_service_interrupt(0);
72 }
73 
74 /* functions to be called by event facility */
75 
76 static bool can_handle_event(uint8_t type)
77 {
78     return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
79 }
80 
81 static unsigned int send_mask(void)
82 {
83     return SCLP_EVENT_MASK_MSG_ASCII;
84 }
85 
86 static unsigned int receive_mask(void)
87 {
88     return SCLP_EVENT_MASK_MSG_ASCII;
89 }
90 
91 /* triggered by SCLP's read_event_data -
92  * copy console data byte-stream into provided (SCLP) buffer
93  */
94 static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
95                              int avail)
96 {
97     SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event);
98 
99     /* first byte is hex 0 saying an ascii string follows */
100     *buf++ = '\0';
101     avail--;
102     /* if all data fit into provided SCLP buffer */
103     if (avail >= cons->iov_sclp_rest) {
104         /* copy character byte-stream to SCLP buffer */
105         memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest);
106         *size = cons->iov_sclp_rest + 1;
107         cons->iov_sclp = 0;
108         cons->iov_bs = 0;
109         cons->iov_data_len = 0;
110         cons->iov_sclp_rest = 0;
111         event->event_pending = false;
112         /* data provided and no more data pending */
113     } else {
114         /* if provided buffer is too small, just copy part */
115         memcpy(buf, &cons->iov[cons->iov_sclp], avail);
116         *size = avail + 1;
117         cons->iov_sclp_rest -= avail;
118         cons->iov_sclp += avail;
119         /* more data pending */
120     }
121     if (cons->notify) {
122         cons->notify = false;
123         qemu_notify_event();
124     }
125 }
126 
127 static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
128                            int *slen)
129 {
130     int avail;
131     size_t src_len;
132     uint8_t *to;
133     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
134 
135     if (!event->event_pending) {
136         /* no data pending */
137         return 0;
138     }
139 
140     to = (uint8_t *)&acd->data;
141     avail = *slen - sizeof(ASCIIConsoleData);
142     get_console_data(event, to, &src_len, avail);
143 
144     acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
145     acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
146     acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
147     *slen = avail - src_len;
148 
149     return 1;
150 }
151 
152 /* triggered by SCLP's write_event_data
153  *  - write console data to character layer
154  *  returns < 0 if an error occurred
155  */
156 static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
157                                   size_t len)
158 {
159     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
160 
161     if (!scon->chr) {
162         /* If there's no backend, we can just say we consumed all data. */
163         return len;
164     }
165 
166     return qemu_chr_fe_write_all(scon->chr, buf, len);
167 }
168 
169 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
170 {
171     int rc;
172     int length;
173     ssize_t written;
174     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
175 
176     length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
177     written = write_console_data(event, (uint8_t *)acd->data, length);
178 
179     rc = SCLP_RC_NORMAL_COMPLETION;
180     /* set event buffer accepted flag */
181     evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
182 
183     /* written will be zero if a pty is not connected - don't treat as error */
184     if (written < 0) {
185         /* event buffer not accepted due to error in character layer */
186         evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
187         rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
188     }
189 
190     return rc;
191 }
192 
193 static const VMStateDescription vmstate_sclpconsole = {
194     .name = "sclpconsole",
195     .version_id = 0,
196     .minimum_version_id = 0,
197     .fields = (VMStateField[]) {
198         VMSTATE_BOOL(event.event_pending, SCLPConsole),
199         VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220),
200         VMSTATE_UINT32(iov_sclp, SCLPConsole),
201         VMSTATE_UINT32(iov_bs, SCLPConsole),
202         VMSTATE_UINT32(iov_data_len, SCLPConsole),
203         VMSTATE_UINT32(iov_sclp_rest, SCLPConsole),
204         VMSTATE_END_OF_LIST()
205      }
206 };
207 
208 /* qemu object creation and initialization functions */
209 
210 /* tell character layer our call-back functions */
211 
212 static int console_init(SCLPEvent *event)
213 {
214     static bool console_available;
215 
216     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
217 
218     if (console_available) {
219         error_report("Multiple VT220 operator consoles are not supported");
220         return -1;
221     }
222     console_available = true;
223     if (scon->chr) {
224         qemu_chr_add_handlers(scon->chr, chr_can_read,
225                               chr_read, NULL, scon);
226     }
227 
228     return 0;
229 }
230 
231 static void console_reset(DeviceState *dev)
232 {
233    SCLPEvent *event = SCLP_EVENT(dev);
234    SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
235 
236    event->event_pending = false;
237    scon->iov_sclp = 0;
238    scon->iov_bs = 0;
239    scon->iov_data_len = 0;
240    scon->iov_sclp_rest = 0;
241    scon->notify = false;
242 }
243 
244 static int console_exit(SCLPEvent *event)
245 {
246     return 0;
247 }
248 
249 static Property console_properties[] = {
250     DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
251     DEFINE_PROP_END_OF_LIST(),
252 };
253 
254 static void console_class_init(ObjectClass *klass, void *data)
255 {
256     DeviceClass *dc = DEVICE_CLASS(klass);
257     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
258 
259     dc->props = console_properties;
260     dc->reset = console_reset;
261     dc->vmsd = &vmstate_sclpconsole;
262     ec->init = console_init;
263     ec->exit = console_exit;
264     ec->get_send_mask = send_mask;
265     ec->get_receive_mask = receive_mask;
266     ec->can_handle_event = can_handle_event;
267     ec->read_event_data = read_event_data;
268     ec->write_event_data = write_event_data;
269 }
270 
271 static const TypeInfo sclp_console_info = {
272     .name          = "sclpconsole",
273     .parent        = TYPE_SCLP_EVENT,
274     .instance_size = sizeof(SCLPConsole),
275     .class_init    = console_class_init,
276     .class_size    = sizeof(SCLPEventClass),
277 };
278 
279 static void register_types(void)
280 {
281     type_register_static(&sclp_console_info);
282 }
283 
284 type_init(register_types)
285