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