19944d320SPaolo Bonzini /*
29944d320SPaolo Bonzini * SCLP event type
39944d320SPaolo Bonzini * Ascii Console Data (VT220 Console)
49944d320SPaolo Bonzini *
59944d320SPaolo Bonzini * Copyright IBM, Corp. 2012
69944d320SPaolo Bonzini *
79944d320SPaolo Bonzini * Authors:
89944d320SPaolo Bonzini * Heinz Graalfs <graalfs@de.ibm.com>
99944d320SPaolo Bonzini *
109944d320SPaolo Bonzini * This work is licensed under the terms of the GNU GPL, version 2 or (at your
119944d320SPaolo Bonzini * option) any later version. See the COPYING file in the top-level directory.
129944d320SPaolo Bonzini *
139944d320SPaolo Bonzini */
149944d320SPaolo Bonzini
159615495aSPeter Maydell #include "qemu/osdep.h"
169944d320SPaolo Bonzini #include "qemu/thread.h"
179944d320SPaolo Bonzini #include "qemu/error-report.h"
180b8fa32fSMarkus Armbruster #include "qemu/module.h"
199944d320SPaolo Bonzini
209944d320SPaolo Bonzini #include "hw/s390x/sclp.h"
21d6454270SMarkus Armbruster #include "migration/vmstate.h"
22a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
23ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
249944d320SPaolo Bonzini #include "hw/s390x/event-facility.h"
254d43a603SMarc-André Lureau #include "chardev/char-fe.h"
26db1015e9SEduardo Habkost #include "qom/object.h"
279944d320SPaolo Bonzini
289944d320SPaolo Bonzini typedef struct ASCIIConsoleData {
299944d320SPaolo Bonzini EventBufferHeader ebh;
30880a7817SPhilippe Mathieu-Daudé char data[];
319944d320SPaolo Bonzini } QEMU_PACKED ASCIIConsoleData;
329944d320SPaolo Bonzini
339944d320SPaolo Bonzini /* max size for ASCII data in 4K SCCB page */
349944d320SPaolo Bonzini #define SIZE_BUFFER_VT220 4080
359944d320SPaolo Bonzini
36db1015e9SEduardo Habkost struct SCLPConsole {
379944d320SPaolo Bonzini SCLPEvent event;
38becdfa00SMarc-André Lureau CharBackend chr;
39ea9ad3e9SHeinz Graalfs uint8_t iov[SIZE_BUFFER_VT220];
40ea9ad3e9SHeinz Graalfs uint32_t iov_sclp; /* offset in buf for SCLP read operation */
41ea9ad3e9SHeinz Graalfs uint32_t iov_bs; /* offset in buf for char layer read operation */
429944d320SPaolo Bonzini uint32_t iov_data_len; /* length of byte stream in buffer */
439944d320SPaolo Bonzini uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */
44bb3e9e1fSHeinz Graalfs bool notify; /* qemu_notify_event() req'd if true */
45db1015e9SEduardo Habkost };
46db1015e9SEduardo Habkost typedef struct SCLPConsole SCLPConsole;
479944d320SPaolo Bonzini
483f6ec642Sxiaoqiang zhao #define TYPE_SCLP_CONSOLE "sclpconsole"
DECLARE_INSTANCE_CHECKER(SCLPConsole,SCLP_CONSOLE,TYPE_SCLP_CONSOLE)498110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(SCLPConsole, SCLP_CONSOLE,
508110fa1dSEduardo Habkost TYPE_SCLP_CONSOLE)
513f6ec642Sxiaoqiang zhao
529944d320SPaolo Bonzini /* character layer call-back functions */
539944d320SPaolo Bonzini
549944d320SPaolo Bonzini /* Return number of bytes that fit into iov buffer */
559944d320SPaolo Bonzini static int chr_can_read(void *opaque)
569944d320SPaolo Bonzini {
579944d320SPaolo Bonzini SCLPConsole *scon = opaque;
58bb3e9e1fSHeinz Graalfs int avail = SIZE_BUFFER_VT220 - scon->iov_data_len;
599944d320SPaolo Bonzini
60bb3e9e1fSHeinz Graalfs if (avail == 0) {
61bb3e9e1fSHeinz Graalfs scon->notify = true;
62bb3e9e1fSHeinz Graalfs }
63bb3e9e1fSHeinz Graalfs return avail;
649944d320SPaolo Bonzini }
659944d320SPaolo Bonzini
66b074e622SChristian Borntraeger /* Send data from a char device over to the guest */
chr_read(void * opaque,const uint8_t * buf,int size)67b074e622SChristian Borntraeger static void chr_read(void *opaque, const uint8_t *buf, int size)
689944d320SPaolo Bonzini {
69b074e622SChristian Borntraeger SCLPConsole *scon = opaque;
70b074e622SChristian Borntraeger
71b074e622SChristian Borntraeger assert(scon);
729944d320SPaolo Bonzini /* read data must fit into current buffer */
739944d320SPaolo Bonzini assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len);
749944d320SPaolo Bonzini
759944d320SPaolo Bonzini /* put byte-stream from character layer into buffer */
76ea9ad3e9SHeinz Graalfs memcpy(&scon->iov[scon->iov_bs], buf, size);
779944d320SPaolo Bonzini scon->iov_data_len += size;
789944d320SPaolo Bonzini scon->iov_sclp_rest += size;
799944d320SPaolo Bonzini scon->iov_bs += size;
809944d320SPaolo Bonzini scon->event.event_pending = true;
81b074e622SChristian Borntraeger sclp_service_interrupt(0);
829944d320SPaolo Bonzini }
839944d320SPaolo Bonzini
849944d320SPaolo Bonzini /* functions to be called by event facility */
859944d320SPaolo Bonzini
can_handle_event(uint8_t type)86c3d9f24aSChristian Borntraeger static bool can_handle_event(uint8_t type)
879944d320SPaolo Bonzini {
88c3d9f24aSChristian Borntraeger return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
899944d320SPaolo Bonzini }
909944d320SPaolo Bonzini
send_mask(void)911ffed98fSClaudio Imbrenda static sccb_mask_t send_mask(void)
929944d320SPaolo Bonzini {
939944d320SPaolo Bonzini return SCLP_EVENT_MASK_MSG_ASCII;
949944d320SPaolo Bonzini }
959944d320SPaolo Bonzini
receive_mask(void)961ffed98fSClaudio Imbrenda static sccb_mask_t receive_mask(void)
979944d320SPaolo Bonzini {
989944d320SPaolo Bonzini return SCLP_EVENT_MASK_MSG_ASCII;
999944d320SPaolo Bonzini }
1009944d320SPaolo Bonzini
1019944d320SPaolo Bonzini /* triggered by SCLP's read_event_data -
1029944d320SPaolo Bonzini * copy console data byte-stream into provided (SCLP) buffer
1039944d320SPaolo Bonzini */
get_console_data(SCLPEvent * event,uint8_t * buf,size_t * size,int avail)1049944d320SPaolo Bonzini static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size,
1059944d320SPaolo Bonzini int avail)
1069944d320SPaolo Bonzini {
1073f6ec642Sxiaoqiang zhao SCLPConsole *cons = SCLP_CONSOLE(event);
1089944d320SPaolo Bonzini
1099944d320SPaolo Bonzini /* first byte is hex 0 saying an ascii string follows */
1109944d320SPaolo Bonzini *buf++ = '\0';
1119944d320SPaolo Bonzini avail--;
1129944d320SPaolo Bonzini /* if all data fit into provided SCLP buffer */
1139944d320SPaolo Bonzini if (avail >= cons->iov_sclp_rest) {
1149944d320SPaolo Bonzini /* copy character byte-stream to SCLP buffer */
115ea9ad3e9SHeinz Graalfs memcpy(buf, &cons->iov[cons->iov_sclp], cons->iov_sclp_rest);
1169944d320SPaolo Bonzini *size = cons->iov_sclp_rest + 1;
117ea9ad3e9SHeinz Graalfs cons->iov_sclp = 0;
118ea9ad3e9SHeinz Graalfs cons->iov_bs = 0;
1199944d320SPaolo Bonzini cons->iov_data_len = 0;
1209944d320SPaolo Bonzini cons->iov_sclp_rest = 0;
1219944d320SPaolo Bonzini event->event_pending = false;
1229944d320SPaolo Bonzini /* data provided and no more data pending */
1239944d320SPaolo Bonzini } else {
1249944d320SPaolo Bonzini /* if provided buffer is too small, just copy part */
125ea9ad3e9SHeinz Graalfs memcpy(buf, &cons->iov[cons->iov_sclp], avail);
1269944d320SPaolo Bonzini *size = avail + 1;
1279944d320SPaolo Bonzini cons->iov_sclp_rest -= avail;
1289944d320SPaolo Bonzini cons->iov_sclp += avail;
1299944d320SPaolo Bonzini /* more data pending */
1309944d320SPaolo Bonzini }
131bb3e9e1fSHeinz Graalfs if (cons->notify) {
132bb3e9e1fSHeinz Graalfs cons->notify = false;
133bb3e9e1fSHeinz Graalfs qemu_notify_event();
134bb3e9e1fSHeinz Graalfs }
1359944d320SPaolo Bonzini }
1369944d320SPaolo Bonzini
read_event_data(SCLPEvent * event,EventBufferHeader * evt_buf_hdr,int * slen)1379944d320SPaolo Bonzini static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
1389944d320SPaolo Bonzini int *slen)
1399944d320SPaolo Bonzini {
1409944d320SPaolo Bonzini int avail;
1419944d320SPaolo Bonzini size_t src_len;
1429944d320SPaolo Bonzini uint8_t *to;
1439944d320SPaolo Bonzini ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
1449944d320SPaolo Bonzini
1459944d320SPaolo Bonzini if (!event->event_pending) {
1469944d320SPaolo Bonzini /* no data pending */
1479944d320SPaolo Bonzini return 0;
1489944d320SPaolo Bonzini }
1499944d320SPaolo Bonzini
1509944d320SPaolo Bonzini to = (uint8_t *)&acd->data;
1519944d320SPaolo Bonzini avail = *slen - sizeof(ASCIIConsoleData);
1529944d320SPaolo Bonzini get_console_data(event, to, &src_len, avail);
1539944d320SPaolo Bonzini
1549944d320SPaolo Bonzini acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len);
1559944d320SPaolo Bonzini acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
1569944d320SPaolo Bonzini acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED;
1579944d320SPaolo Bonzini *slen = avail - src_len;
1589944d320SPaolo Bonzini
1599944d320SPaolo Bonzini return 1;
1609944d320SPaolo Bonzini }
1619944d320SPaolo Bonzini
1629944d320SPaolo Bonzini /* triggered by SCLP's write_event_data
1639944d320SPaolo Bonzini * - write console data to character layer
1649944d320SPaolo Bonzini * returns < 0 if an error occurred
1659944d320SPaolo Bonzini */
write_console_data(SCLPEvent * event,const uint8_t * buf,size_t len)1669944d320SPaolo Bonzini static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf,
1679944d320SPaolo Bonzini size_t len)
1689944d320SPaolo Bonzini {
1693f6ec642Sxiaoqiang zhao SCLPConsole *scon = SCLP_CONSOLE(event);
1709944d320SPaolo Bonzini
17130650701SAnton Nefedov if (!qemu_chr_fe_backend_connected(&scon->chr)) {
1729944d320SPaolo Bonzini /* If there's no backend, we can just say we consumed all data. */
1739944d320SPaolo Bonzini return len;
1749944d320SPaolo Bonzini }
1759944d320SPaolo Bonzini
1766ab3fc32SDaniel P. Berrange /* XXX this blocks entire thread. Rewrite to use
1776ab3fc32SDaniel P. Berrange * qemu_chr_fe_write and background I/O callbacks */
1785345fdb4SMarc-André Lureau return qemu_chr_fe_write_all(&scon->chr, buf, len);
1799944d320SPaolo Bonzini }
1809944d320SPaolo Bonzini
write_event_data(SCLPEvent * event,EventBufferHeader * evt_buf_hdr)1819944d320SPaolo Bonzini static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr)
1829944d320SPaolo Bonzini {
1839944d320SPaolo Bonzini int rc;
1849944d320SPaolo Bonzini int length;
1859944d320SPaolo Bonzini ssize_t written;
1869944d320SPaolo Bonzini ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr;
1879944d320SPaolo Bonzini
1889944d320SPaolo Bonzini length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader);
1899944d320SPaolo Bonzini written = write_console_data(event, (uint8_t *)acd->data, length);
1909944d320SPaolo Bonzini
1919944d320SPaolo Bonzini rc = SCLP_RC_NORMAL_COMPLETION;
1929944d320SPaolo Bonzini /* set event buffer accepted flag */
1939944d320SPaolo Bonzini evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED;
1949944d320SPaolo Bonzini
1959944d320SPaolo Bonzini /* written will be zero if a pty is not connected - don't treat as error */
1969944d320SPaolo Bonzini if (written < 0) {
1979944d320SPaolo Bonzini /* event buffer not accepted due to error in character layer */
1989944d320SPaolo Bonzini evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED);
1999944d320SPaolo Bonzini rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK;
2009944d320SPaolo Bonzini }
2019944d320SPaolo Bonzini
2029944d320SPaolo Bonzini return rc;
2039944d320SPaolo Bonzini }
2049944d320SPaolo Bonzini
205cb335bebSHeinz Graalfs static const VMStateDescription vmstate_sclpconsole = {
206cb335bebSHeinz Graalfs .name = "sclpconsole",
207cb335bebSHeinz Graalfs .version_id = 0,
208cb335bebSHeinz Graalfs .minimum_version_id = 0,
2092f6cab05SRichard Henderson .fields = (const VMStateField[]) {
210cb335bebSHeinz Graalfs VMSTATE_BOOL(event.event_pending, SCLPConsole),
211cb335bebSHeinz Graalfs VMSTATE_UINT8_ARRAY(iov, SCLPConsole, SIZE_BUFFER_VT220),
212cb335bebSHeinz Graalfs VMSTATE_UINT32(iov_sclp, SCLPConsole),
213cb335bebSHeinz Graalfs VMSTATE_UINT32(iov_bs, SCLPConsole),
214cb335bebSHeinz Graalfs VMSTATE_UINT32(iov_data_len, SCLPConsole),
215cb335bebSHeinz Graalfs VMSTATE_UINT32(iov_sclp_rest, SCLPConsole),
216cb335bebSHeinz Graalfs VMSTATE_END_OF_LIST()
217cb335bebSHeinz Graalfs }
218cb335bebSHeinz Graalfs };
219cb335bebSHeinz Graalfs
2209944d320SPaolo Bonzini /* qemu object creation and initialization functions */
2219944d320SPaolo Bonzini
2229944d320SPaolo Bonzini /* tell character layer our call-back functions */
223cb335bebSHeinz Graalfs
console_init(SCLPEvent * event)2249944d320SPaolo Bonzini static int console_init(SCLPEvent *event)
2259944d320SPaolo Bonzini {
2269944d320SPaolo Bonzini static bool console_available;
2279944d320SPaolo Bonzini
2283f6ec642Sxiaoqiang zhao SCLPConsole *scon = SCLP_CONSOLE(event);
2299944d320SPaolo Bonzini
2309944d320SPaolo Bonzini if (console_available) {
2319944d320SPaolo Bonzini error_report("Multiple VT220 operator consoles are not supported");
2329944d320SPaolo Bonzini return -1;
2339944d320SPaolo Bonzini }
2349944d320SPaolo Bonzini console_available = true;
2355345fdb4SMarc-André Lureau qemu_chr_fe_set_handlers(&scon->chr, chr_can_read,
23681517ba3SAnton Nefedov chr_read, NULL, NULL, scon, NULL, true);
2379944d320SPaolo Bonzini
2389944d320SPaolo Bonzini return 0;
2399944d320SPaolo Bonzini }
2409944d320SPaolo Bonzini
console_reset(DeviceState * dev)2413af6de32SHeinz Graalfs static void console_reset(DeviceState *dev)
2423af6de32SHeinz Graalfs {
2433af6de32SHeinz Graalfs SCLPEvent *event = SCLP_EVENT(dev);
2443f6ec642Sxiaoqiang zhao SCLPConsole *scon = SCLP_CONSOLE(event);
2453af6de32SHeinz Graalfs
2463af6de32SHeinz Graalfs event->event_pending = false;
2473af6de32SHeinz Graalfs scon->iov_sclp = 0;
2483af6de32SHeinz Graalfs scon->iov_bs = 0;
2493af6de32SHeinz Graalfs scon->iov_data_len = 0;
2503af6de32SHeinz Graalfs scon->iov_sclp_rest = 0;
251bb3e9e1fSHeinz Graalfs scon->notify = false;
2523af6de32SHeinz Graalfs }
2533af6de32SHeinz Graalfs
2549944d320SPaolo Bonzini static Property console_properties[] = {
2559944d320SPaolo Bonzini DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
2569944d320SPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
2579944d320SPaolo Bonzini };
2589944d320SPaolo Bonzini
console_class_init(ObjectClass * klass,void * data)2599944d320SPaolo Bonzini static void console_class_init(ObjectClass *klass, void *data)
2609944d320SPaolo Bonzini {
2619944d320SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
2629944d320SPaolo Bonzini SCLPEventClass *ec = SCLP_EVENT_CLASS(klass);
2639944d320SPaolo Bonzini
2644f67d30bSMarc-André Lureau device_class_set_props(dc, console_properties);
265*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, console_reset);
266cb335bebSHeinz Graalfs dc->vmsd = &vmstate_sclpconsole;
2679944d320SPaolo Bonzini ec->init = console_init;
2689944d320SPaolo Bonzini ec->get_send_mask = send_mask;
2699944d320SPaolo Bonzini ec->get_receive_mask = receive_mask;
270c3d9f24aSChristian Borntraeger ec->can_handle_event = can_handle_event;
2719944d320SPaolo Bonzini ec->read_event_data = read_event_data;
2729944d320SPaolo Bonzini ec->write_event_data = write_event_data;
273183f6b8dSCornelia Huck set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
2749944d320SPaolo Bonzini }
2759944d320SPaolo Bonzini
2769944d320SPaolo Bonzini static const TypeInfo sclp_console_info = {
2771a3bae79SEduardo Habkost .name = TYPE_SCLP_CONSOLE,
2789944d320SPaolo Bonzini .parent = TYPE_SCLP_EVENT,
2799944d320SPaolo Bonzini .instance_size = sizeof(SCLPConsole),
2809944d320SPaolo Bonzini .class_init = console_class_init,
2819944d320SPaolo Bonzini .class_size = sizeof(SCLPEventClass),
2829944d320SPaolo Bonzini };
2839944d320SPaolo Bonzini
register_types(void)2849944d320SPaolo Bonzini static void register_types(void)
2859944d320SPaolo Bonzini {
2869944d320SPaolo Bonzini type_register_static(&sclp_console_info);
2879944d320SPaolo Bonzini }
2889944d320SPaolo Bonzini
2899944d320SPaolo Bonzini type_init(register_types)
290