xref: /qemu/hw/char/sclpconsole.c (revision ea9ad3e945526c56935c245a268731878c74e570)
1130c57c0SHeinz Graalfs /*
2130c57c0SHeinz Graalfs  * SCLP event type
3130c57c0SHeinz Graalfs  *    Ascii Console Data (VT220 Console)
4130c57c0SHeinz Graalfs  *
5130c57c0SHeinz Graalfs  * Copyright IBM, Corp. 2012
6130c57c0SHeinz Graalfs  *
7130c57c0SHeinz Graalfs  * Authors:
8130c57c0SHeinz Graalfs  *  Heinz Graalfs <graalfs@de.ibm.com>
9130c57c0SHeinz Graalfs  *
10130c57c0SHeinz Graalfs  * This work is licensed under the terms of the GNU GPL, version 2 or (at your
11130c57c0SHeinz Graalfs  * option) any later version.  See the COPYING file in the top-level directory.
12130c57c0SHeinz Graalfs  *
13130c57c0SHeinz Graalfs  */
14130c57c0SHeinz Graalfs 
15130c57c0SHeinz Graalfs #include <hw/qdev.h>
161de7afc9SPaolo Bonzini #include "qemu/thread.h"
17b4a42f81SPaolo Bonzini #include "qemu/error-report.h"
18130c57c0SHeinz Graalfs 
1983c9f4caSPaolo Bonzini #include "hw/s390x/sclp.h"
2083c9f4caSPaolo Bonzini #include "hw/s390x/event-facility.h"
21dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
22130c57c0SHeinz Graalfs 
23130c57c0SHeinz Graalfs typedef struct ASCIIConsoleData {
24130c57c0SHeinz Graalfs     EventBufferHeader ebh;
25130c57c0SHeinz Graalfs     char data[0];
26130c57c0SHeinz Graalfs } QEMU_PACKED ASCIIConsoleData;
27130c57c0SHeinz Graalfs 
28130c57c0SHeinz Graalfs /* max size for ASCII data in 4K SCCB page */
29130c57c0SHeinz Graalfs #define SIZE_BUFFER_VT220 4080
30130c57c0SHeinz Graalfs 
31130c57c0SHeinz Graalfs typedef struct SCLPConsole {
32130c57c0SHeinz Graalfs     SCLPEvent event;
33130c57c0SHeinz Graalfs     CharDriverState *chr;
34*ea9ad3e9SHeinz Graalfs     uint8_t iov[SIZE_BUFFER_VT220];
35*ea9ad3e9SHeinz Graalfs     uint32_t iov_sclp;      /* offset in buf for SCLP read operation       */
36*ea9ad3e9SHeinz Graalfs     uint32_t iov_bs;        /* offset in buf for char layer read operation */
37130c57c0SHeinz Graalfs     uint32_t iov_data_len;  /* length of byte stream in buffer             */
38130c57c0SHeinz Graalfs     uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP     */
39130c57c0SHeinz Graalfs     qemu_irq irq_read_vt220;
40130c57c0SHeinz Graalfs } SCLPConsole;
41130c57c0SHeinz Graalfs 
42130c57c0SHeinz Graalfs /* character layer call-back functions */
43130c57c0SHeinz Graalfs 
44130c57c0SHeinz Graalfs /* Return number of bytes that fit into iov buffer */
45130c57c0SHeinz Graalfs static int chr_can_read(void *opaque)
46130c57c0SHeinz Graalfs {
47130c57c0SHeinz Graalfs     SCLPConsole *scon = opaque;
48130c57c0SHeinz Graalfs 
49*ea9ad3e9SHeinz Graalfs     return SIZE_BUFFER_VT220 - scon->iov_data_len;
50130c57c0SHeinz Graalfs }
51130c57c0SHeinz Graalfs 
52130c57c0SHeinz Graalfs /* Receive n bytes from character layer, save in iov buffer,
53130c57c0SHeinz Graalfs  * and set event pending */
54130c57c0SHeinz Graalfs static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf,
55130c57c0SHeinz Graalfs                                    int size)
56130c57c0SHeinz Graalfs {
57130c57c0SHeinz Graalfs     /* read data must fit into current buffer */
58130c57c0SHeinz Graalfs     assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
59130c57c0SHeinz Graalfs 
60130c57c0SHeinz Graalfs     /* put byte-stream from character layer into buffer */
61*ea9ad3e9SHeinz Graalfs     memcpy(&scon->iov[scon->iov_bs], buf, size);
62130c57c0SHeinz Graalfs     scon->iov_data_len += size;
63130c57c0SHeinz Graalfs     scon->iov_sclp_rest += size;
64130c57c0SHeinz Graalfs     scon->iov_bs += size;
65130c57c0SHeinz Graalfs     scon->event.event_pending = true;
66130c57c0SHeinz Graalfs }
67130c57c0SHeinz Graalfs 
68130c57c0SHeinz Graalfs /* Send data from a char device over to the guest */
69130c57c0SHeinz Graalfs static void chr_read(void *opaque, const uint8_t *buf, int size)
70130c57c0SHeinz Graalfs {
71130c57c0SHeinz Graalfs     SCLPConsole *scon = opaque;
72130c57c0SHeinz Graalfs 
73130c57c0SHeinz Graalfs     assert(scon);
74130c57c0SHeinz Graalfs 
75130c57c0SHeinz Graalfs     receive_from_chr_layer(scon, buf, size);
76130c57c0SHeinz Graalfs     /* trigger SCLP read operation */
77130c57c0SHeinz Graalfs     qemu_irq_raise(scon->irq_read_vt220);
78130c57c0SHeinz Graalfs }
79130c57c0SHeinz Graalfs 
80130c57c0SHeinz Graalfs /* functions to be called by event facility */
81130c57c0SHeinz Graalfs 
82130c57c0SHeinz Graalfs static int event_type(void)
83130c57c0SHeinz Graalfs {
84130c57c0SHeinz Graalfs     return SCLP_EVENT_ASCII_CONSOLE_DATA;
85130c57c0SHeinz Graalfs }
86130c57c0SHeinz Graalfs 
87130c57c0SHeinz Graalfs static unsigned int send_mask(void)
88130c57c0SHeinz Graalfs {
89130c57c0SHeinz Graalfs     return SCLP_EVENT_MASK_MSG_ASCII;
90130c57c0SHeinz Graalfs }
91130c57c0SHeinz Graalfs 
92130c57c0SHeinz Graalfs static unsigned int receive_mask(void)
93130c57c0SHeinz Graalfs {
94130c57c0SHeinz Graalfs     return SCLP_EVENT_MASK_MSG_ASCII;
95130c57c0SHeinz Graalfs }
96130c57c0SHeinz Graalfs 
97130c57c0SHeinz Graalfs /* triggered by SCLP's read_event_data -
98130c57c0SHeinz Graalfs  * copy console data byte-stream into provided (SCLP) buffer
99130c57c0SHeinz Graalfs  */
100130c57c0SHeinz Graalfs static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
101130c57c0SHeinz Graalfs                              int avail)
102130c57c0SHeinz Graalfs {
103130c57c0SHeinz Graalfs     SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event);
104130c57c0SHeinz Graalfs 
105130c57c0SHeinz Graalfs     /* first byte is hex 0 saying an ascii string follows */
106130c57c0SHeinz Graalfs     *buf++ = '\0';
107130c57c0SHeinz Graalfs     avail--;
108130c57c0SHeinz Graalfs     /* if all data fit into provided SCLP buffer */
109130c57c0SHeinz Graalfs     if (avail >= cons->iov_sclp_rest) {
110130c57c0SHeinz Graalfs         /* copy character byte-stream to SCLP buffer */
111*ea9ad3e9SHeinz Graalfs         memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest);
112130c57c0SHeinz Graalfs         *size = cons->iov_sclp_rest + 1;
113*ea9ad3e9SHeinz Graalfs         cons->iov_sclp = 0;
114*ea9ad3e9SHeinz Graalfs         cons->iov_bs = 0;
115130c57c0SHeinz Graalfs         cons->iov_data_len = 0;
116130c57c0SHeinz Graalfs         cons->iov_sclp_rest = 0;
117130c57c0SHeinz Graalfs         event->event_pending = false;
118130c57c0SHeinz Graalfs         /* data provided and no more data pending */
119130c57c0SHeinz Graalfs     } else {
120130c57c0SHeinz Graalfs         /* if provided buffer is too small, just copy part */
121*ea9ad3e9SHeinz Graalfs         memcpy(buf, &cons->iov[cons->iov_sclp], avail);
122130c57c0SHeinz Graalfs         *size = avail + 1;
123130c57c0SHeinz Graalfs         cons->iov_sclp_rest -= avail;
124130c57c0SHeinz Graalfs         cons->iov_sclp += avail;
125130c57c0SHeinz Graalfs         /* more data pending */
126130c57c0SHeinz Graalfs     }
127130c57c0SHeinz Graalfs }
128130c57c0SHeinz Graalfs 
129130c57c0SHeinz Graalfs static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
130130c57c0SHeinz Graalfs                            int *slen)
131130c57c0SHeinz Graalfs {
132130c57c0SHeinz Graalfs     int avail;
133130c57c0SHeinz Graalfs     size_t src_len;
134130c57c0SHeinz Graalfs     uint8_t *to;
135130c57c0SHeinz Graalfs     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
136130c57c0SHeinz Graalfs 
137130c57c0SHeinz Graalfs     if (!event->event_pending) {
138130c57c0SHeinz Graalfs         /* no data pending */
139130c57c0SHeinz Graalfs         return 0;
140130c57c0SHeinz Graalfs     }
141130c57c0SHeinz Graalfs 
142130c57c0SHeinz Graalfs     to = (uint8_t *)&acd->data;
143130c57c0SHeinz Graalfs     avail = *slen - sizeof(ASCIIConsoleData);
144130c57c0SHeinz Graalfs     get_console_data(event, to, &src_len, avail);
145130c57c0SHeinz Graalfs 
146130c57c0SHeinz Graalfs     acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
147130c57c0SHeinz Graalfs     acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
148130c57c0SHeinz Graalfs     acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
149130c57c0SHeinz Graalfs     *slen = avail - src_len;
150130c57c0SHeinz Graalfs 
151130c57c0SHeinz Graalfs     return 1;
152130c57c0SHeinz Graalfs }
153130c57c0SHeinz Graalfs 
154130c57c0SHeinz Graalfs /* triggered by SCLP's write_event_data
1558367a14fSStefan Weil  *  - write console data to character layer
1568367a14fSStefan Weil  *  returns < 0 if an error occurred
157130c57c0SHeinz Graalfs  */
158130c57c0SHeinz Graalfs static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
159130c57c0SHeinz Graalfs                                   size_t len)
160130c57c0SHeinz Graalfs {
161130c57c0SHeinz Graalfs     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
162130c57c0SHeinz Graalfs 
163130c57c0SHeinz Graalfs     if (!scon->chr) {
164130c57c0SHeinz Graalfs         /* If there's no backend, we can just say we consumed all data. */
165130c57c0SHeinz Graalfs         return len;
166130c57c0SHeinz Graalfs     }
167130c57c0SHeinz Graalfs 
1682e142114SHeinz Graalfs     return qemu_chr_fe_write_all(scon->chr, buf, len);
169130c57c0SHeinz Graalfs }
170130c57c0SHeinz Graalfs 
171130c57c0SHeinz Graalfs static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
172130c57c0SHeinz Graalfs {
173130c57c0SHeinz Graalfs     int rc;
174130c57c0SHeinz Graalfs     int length;
175130c57c0SHeinz Graalfs     ssize_t written;
176130c57c0SHeinz Graalfs     ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
177130c57c0SHeinz Graalfs 
178130c57c0SHeinz Graalfs     length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
179130c57c0SHeinz Graalfs     written = write_console_data(event, (uint8_t *)acd->data, length);
180130c57c0SHeinz Graalfs 
181130c57c0SHeinz Graalfs     rc = SCLP_RC_NORMAL_COMPLETION;
182130c57c0SHeinz Graalfs     /* set event buffer accepted flag */
183130c57c0SHeinz Graalfs     evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
184130c57c0SHeinz Graalfs 
185130c57c0SHeinz Graalfs     /* written will be zero if a pty is not connected - don't treat as error */
186130c57c0SHeinz Graalfs     if (written < 0) {
187130c57c0SHeinz Graalfs         /* event buffer not accepted due to error in character layer */
188130c57c0SHeinz Graalfs         evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
189130c57c0SHeinz Graalfs         rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
190130c57c0SHeinz Graalfs     }
191130c57c0SHeinz Graalfs 
192130c57c0SHeinz Graalfs     return rc;
193130c57c0SHeinz Graalfs }
194130c57c0SHeinz Graalfs 
1959944d320SPaolo Bonzini static void trigger_ascii_console_data(void *opaque, int n, int level)
196130c57c0SHeinz Graalfs {
197130c57c0SHeinz Graalfs     sclp_service_interrupt(0);
198130c57c0SHeinz Graalfs }
199130c57c0SHeinz Graalfs 
200130c57c0SHeinz Graalfs /* qemu object creation and initialization functions */
201130c57c0SHeinz Graalfs 
202130c57c0SHeinz Graalfs /* tell character layer our call-back functions */
203130c57c0SHeinz Graalfs static int console_init(SCLPEvent *event)
204130c57c0SHeinz Graalfs {
205130c57c0SHeinz Graalfs     static bool console_available;
206130c57c0SHeinz Graalfs 
207130c57c0SHeinz Graalfs     SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event);
208130c57c0SHeinz Graalfs 
209130c57c0SHeinz Graalfs     if (console_available) {
210130c57c0SHeinz Graalfs         error_report("Multiple VT220 operator consoles are not supported");
211130c57c0SHeinz Graalfs         return -1;
212130c57c0SHeinz Graalfs     }
213130c57c0SHeinz Graalfs     console_available = true;
214*ea9ad3e9SHeinz Graalfs     scon->iov_sclp = 0;
215*ea9ad3e9SHeinz Graalfs     scon->iov_bs = 0;
216*ea9ad3e9SHeinz Graalfs     scon->iov_data_len = 0;
217*ea9ad3e9SHeinz Graalfs     scon->iov_sclp_rest = 0;
218130c57c0SHeinz Graalfs     event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA;
219130c57c0SHeinz Graalfs     if (scon->chr) {
220130c57c0SHeinz Graalfs         qemu_chr_add_handlers(scon->chr, chr_can_read,
221*ea9ad3e9SHeinz Graalfs                               chr_read, NULL, scon);
222130c57c0SHeinz Graalfs     }
223130c57c0SHeinz Graalfs     scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data,
224130c57c0SHeinz Graalfs                                                NULL, 1);
225130c57c0SHeinz Graalfs 
226130c57c0SHeinz Graalfs     return 0;
227130c57c0SHeinz Graalfs }
228130c57c0SHeinz Graalfs 
229130c57c0SHeinz Graalfs static int console_exit(SCLPEvent *event)
230130c57c0SHeinz Graalfs {
231130c57c0SHeinz Graalfs     return 0;
232130c57c0SHeinz Graalfs }
233130c57c0SHeinz Graalfs 
234130c57c0SHeinz Graalfs static Property console_properties[] = {
235130c57c0SHeinz Graalfs     DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
236130c57c0SHeinz Graalfs     DEFINE_PROP_END_OF_LIST(),
237130c57c0SHeinz Graalfs };
238130c57c0SHeinz Graalfs 
239130c57c0SHeinz Graalfs static void console_class_init(ObjectClass *klass, void *data)
240130c57c0SHeinz Graalfs {
241130c57c0SHeinz Graalfs     DeviceClass *dc = DEVICE_CLASS(klass);
242130c57c0SHeinz Graalfs     SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
243130c57c0SHeinz Graalfs 
244130c57c0SHeinz Graalfs     dc->props = console_properties;
245130c57c0SHeinz Graalfs     ec->init = console_init;
246130c57c0SHeinz Graalfs     ec->exit = console_exit;
247130c57c0SHeinz Graalfs     ec->get_send_mask = send_mask;
248130c57c0SHeinz Graalfs     ec->get_receive_mask = receive_mask;
249130c57c0SHeinz Graalfs     ec->event_type = event_type;
250130c57c0SHeinz Graalfs     ec->read_event_data = read_event_data;
251130c57c0SHeinz Graalfs     ec->write_event_data = write_event_data;
252130c57c0SHeinz Graalfs }
253130c57c0SHeinz Graalfs 
2548c43a6f0SAndreas Färber static const TypeInfo sclp_console_info = {
255130c57c0SHeinz Graalfs     .name          = "sclpconsole",
256130c57c0SHeinz Graalfs     .parent        = TYPE_SCLP_EVENT,
257130c57c0SHeinz Graalfs     .instance_size = sizeof(SCLPConsole),
258130c57c0SHeinz Graalfs     .class_init    = console_class_init,
259130c57c0SHeinz Graalfs     .class_size    = sizeof(SCLPEventClass),
260130c57c0SHeinz Graalfs };
261130c57c0SHeinz Graalfs 
262130c57c0SHeinz Graalfs static void register_types(void)
263130c57c0SHeinz Graalfs {
264130c57c0SHeinz Graalfs     type_register_static(&sclp_console_info);
265130c57c0SHeinz Graalfs }
266130c57c0SHeinz Graalfs 
267130c57c0SHeinz Graalfs type_init(register_types)
268