xref: /openbmc/qemu/hw/char/sclpconsole.c (revision 56c4bfb3)
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     /* 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     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
188 
189     if (!scon->chr) {
190         /* If there's no backend, we can just say we consumed all data. */
191         return len;
192     }
193 
194     return qemu_chr_fe_write_all(scon->chr, buf, len);
195 }
196 
197 static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
198 {
199     int rc;
200     int length;
201     ssize_t written;
202     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
203 
204     length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
205     written = write_console_data(event, (uint8_t *)acd->data, length);
206 
207     rc = SCLP_RC_NORMAL_COMPLETION;
208     /* set event buffer accepted flag */
209     evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
210 
211     /* written will be zero if a pty is not connected - don't treat as error */
212     if (written < 0) {
213         /* event buffer not accepted due to error in character layer */
214         evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
215         rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
216     }
217 
218     return rc;
219 }
220 
221 static void trigger_ascii_console_data(void *opaque, int n, int level)
222 {
223     sclp_service_interrupt(0);
224 }
225 
226 /* qemu object creation and initialization functions */
227 
228 /* tell character layer our call-back functions */
229 static int console_init(SCLPEvent *event)
230 {
231     static bool console_available;
232 
233     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
234 
235     if (console_available) {
236         error_report("Multiple VT220 operator consoles are not supported");
237         return -1;
238     }
239     console_available = true;
240     event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA;
241     if (scon->chr) {
242         qemu_chr_add_handlers(scon->chr, chr_can_read,
243                               chr_read, chr_event, scon);
244     }
245     scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data,
246                                                NULL, 1);
247 
248     return 0;
249 }
250 
251 static int console_exit(SCLPEvent *event)
252 {
253     return 0;
254 }
255 
256 static Property console_properties[] = {
257     DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
258     DEFINE_PROP_END_OF_LIST(),
259 };
260 
261 static void console_class_init(ObjectClass *klass, void *data)
262 {
263     DeviceClass *dc = DEVICE_CLASS(klass);
264     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
265 
266     dc->props = console_properties;
267     ec->init = console_init;
268     ec->exit = console_exit;
269     ec->get_send_mask = send_mask;
270     ec->get_receive_mask = receive_mask;
271     ec->event_type = event_type;
272     ec->read_event_data = read_event_data;
273     ec->write_event_data = write_event_data;
274 }
275 
276 static const TypeInfo sclp_console_info = {
277     .name          = "sclpconsole",
278     .parent        = TYPE_SCLP_EVENT,
279     .instance_size = sizeof(SCLPConsole),
280     .class_init    = console_class_init,
281     .class_size    = sizeof(SCLPEventClass),
282 };
283 
284 static void register_types(void)
285 {
286     type_register_static(&sclp_console_info);
287 }
288 
289 type_init(register_types)
290