xref: /openbmc/qemu/hw/char/sclpconsole.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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