On 18.09.2013, at 05:19, Christian Borntraeger wrote: > From: Heinz Graalfs <graa...@linux.vnet.ibm.com> > > Add simple support for SCLP line-mode also known as operating > system messages. This can be added in addition to or instead of > the SCLP full screen console with -device sclplmconsole. > > Signed-off-by: Heinz Graalfs <graa...@linux.vnet.ibm.com> > Signed-off-by: Christian Borntraeger <borntrae...@de.ibm.com> > --- > hw/char/Makefile.objs | 2 +- > hw/char/sclpconsole-lm.c | 393 ++++++++++++++++++++++++++++++++++++++ > include/hw/s390x/event-facility.h | 78 ++++++++ > 3 files changed, 472 insertions(+), 1 deletion(-) > create mode 100644 hw/char/sclpconsole-lm.c > > diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs > index f8f3dbc..cbd6a00 100644 > --- a/hw/char/Makefile.objs > +++ b/hw/char/Makefile.objs > @@ -22,6 +22,6 @@ common-obj-$(CONFIG_IMX) += imx_serial.o > common-obj-$(CONFIG_LM32) += lm32_juart.o > common-obj-$(CONFIG_LM32) += lm32_uart.o > common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o > -common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o > +common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o > > obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o > diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c > new file mode 100644 > index 0000000..a0eb59b > --- /dev/null > +++ b/hw/char/sclpconsole-lm.c > @@ -0,0 +1,393 @@ > +/* > + * SCLP event types > + * Operations Command - Line Mode input > + * Message - Line Mode output > + * > + * Copyright IBM, Corp. 2013 > + * > + * Authors: > + * Heinz Graalfs <graa...@linux.vnet.ibm.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or (at > your > + * option) any later version. See the COPYING file in the top-level > directory. > + * > + */ > + > +#include "hw/qdev.h" > +#include "qemu/thread.h" > +#include "qemu/error-report.h" > +#include "sysemu/char.h" > + > +#include "hw/s390x/sclp.h" > +#include "hw/s390x/event-facility.h" > +#include "hw/s390x/ebcdic.h" > + > +typedef struct OprtnsCommand { > + EventBufferHeader header; > + MDMSU message_unit; > + char data[0]; > +} QEMU_PACKED OprtnsCommand; > + > +/* max size for line-mode data in 4K SCCB page */ > +#define SIZE_CONSOLE_BUFFER (SCCB_DATA_LEN - sizeof(OprtnsCommand)) > + > +typedef struct SCLPConsoleLM { > + SCLPEvent event; > + CharDriverState *chr; > + bool echo; /* immediate echo of input if true */ > + uint32_t write_errors; /* errors writing to char layer */ > + uint32_t length; /* length of byte stream in buffer */ > + uint8_t buf[SIZE_CONSOLE_BUFFER]; > + qemu_irq irq_console_read; > +} SCLPConsoleLM; > + > +/* > +* Character layer call-back functions > + * > + * Allow 1 character at a time > + * > + * Accumulate bytes from character layer in console buffer, > + * event_pending is set when a newline character is encountered > + * > + * The maximum command line length is limited by the maximum > + * space available in an SCCB > + */ > + > +static int chr_can_read(void *opaque) > +{ > + SCLPConsoleLM *scon = opaque; > + > + if (scon->event.event_pending) { > + return 0; > + } else if (SIZE_CONSOLE_BUFFER - scon->length) { > + return 1; > + } > + return 0; > +} > + > +static void receive_from_chr_layer(SCLPConsoleLM *scon, const uint8_t *buf, > + int size) > +{ > + assert(size == 1); > + > + if (*buf == '\r' || *buf == '\n') { > + scon->event.event_pending = true; > + return; > + } > + memcpy(&scon->buf[scon->length], buf, 1);
Why use memcpy here? > + scon->length += 1; > + if (scon->echo) { > + qemu_chr_fe_write(scon->chr, buf, size); > + } > +} > + > +/* > + * Send data from a char device over to the guest > + */ > +static void chr_read(void *opaque, const uint8_t *buf, int size) > +{ > + SCLPConsoleLM *scon = opaque; > + > + receive_from_chr_layer(scon, buf, size); > + if (scon->event.event_pending) { > + /* trigger SCLP read operation */ > + qemu_irq_raise(scon->irq_console_read); > + } > +} > + > +/* functions to be called by event facility */ > + > +static bool can_handle_event(uint8_t type) > +{ > + return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD; > +} > + > +static unsigned int send_mask(void) > +{ > + return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD; > +} > + > +static unsigned int receive_mask(void) > +{ > + return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD; > +} > + > +/* > + * Triggered by SCLP's read_event_data > + * - convert ASCII byte stream to EBCDIC and > + * - copy converted data into provided (SCLP) buffer > + */ > +static int get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, > + int avail) > +{ > + int len; > + > + SCLPConsoleLM *cons = DO_UPCAST(SCLPConsoleLM, event, event); > + > + len = cons->length; > + /* data need to fit into provided SCLP buffer */ > + if (len > avail) { > + return 1; > + } > + > + ebcdic_put(buf, (char *)&cons->buf, len); > + *size = len; > + cons->length = 0; > + /* data provided and no more data pending */ > + event->event_pending = false; > + return 0; > +} > + > +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, > + int *slen) > +{ > + int avail, rc; > + size_t src_len; > + uint8_t *to; > + OprtnsCommand *oc = (OprtnsCommand *) evt_buf_hdr; > + > + if (!event->event_pending) { > + /* no data pending */ > + return 0; > + } > + > + to = (uint8_t *)&oc->data; > + avail = *slen - sizeof(OprtnsCommand); > + rc = get_console_data(event, to, &src_len, avail); > + if (rc) { > + /* data didn't fit, try next SCCB */ > + return 1; > + } > + > + oc->message_unit.mdmsu.gds_id = GDS_ID_MDSMU; > + oc->message_unit.mdmsu.length = cpu_to_be16(sizeof(struct MDMSU)); > + > + oc->message_unit.cpmsu.gds_id = GDS_ID_CPMSU; > + oc->message_unit.cpmsu.length = > + cpu_to_be16(sizeof(struct MDMSU) - sizeof(GdsVector)); > + > + oc->message_unit.text_command.gds_id = GDS_ID_TEXTCMD; > + oc->message_unit.text_command.length = > + cpu_to_be16(sizeof(struct MDMSU) - (2 * sizeof(GdsVector))); > + > + oc->message_unit.self_def_text_message.key = GDS_KEY_SELFDEFTEXTMSG; > + oc->message_unit.self_def_text_message.length = > + cpu_to_be16(sizeof(struct MDMSU) - (3 * sizeof(GdsVector))); > + > + oc->message_unit.text_message.key = GDS_KEY_TEXTMSG; > + oc->message_unit.text_message.length = > + cpu_to_be16(sizeof(GdsSubvector) + src_len); > + > + oc->header.length = cpu_to_be16(sizeof(OprtnsCommand) + src_len); > + oc->header.type = SCLP_EVENT_OPRTNS_COMMAND; > + *slen = avail - src_len; > + > + return 1; > +} > + > +/* > + * Triggered by SCLP's write_event_data > + * - write console data to character layer > + * returns < 0 if an error occurred > + */ > +static int write_console_data(SCLPEvent *event, const uint8_t *buf, int len) > +{ > + int ret = 0; > + const uint8_t *buf_offset; > + > + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); > + > + if (!scon->chr) { > + /* If there's no backend, we can just say we consumed all data. */ > + return len; > + } > + > + buf_offset = buf; > + while (len > 0) { > + ret = qemu_chr_fe_write(scon->chr, buf, len); > + if (ret == 0) { > + /* a pty doesn't seem to be connected - no error */ > + len = 0; > + } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { > + len -= ret; > + buf_offset += ret; > + } else { > + len = 0; > + } > + } > + > + return ret; > +} > + > +#define SIZE_BUFFER 4096 > +#define NEWLINE "\n" Why are these in the middle of the file? > + > +static int process_mdb(SCLPEvent *event, MDBO *mdbo) > +{ > + int rc; > + int len; > + uint8_t buffer[SIZE_BUFFER]; > + > + len = be16_to_cpu(mdbo->length); > + len -= sizeof(mdbo->length) + sizeof(mdbo->type) > + + sizeof(mdbo->mto.line_type_flags) > + + sizeof(mdbo->mto.alarm_control) > + + sizeof(mdbo->mto._reserved); > + > + assert(len <= SIZE_BUFFER); > + > + /* convert EBCDIC SCLP contents to ASCII console message */ > + ascii_put(buffer, mdbo->mto.message, len); > + rc = write_console_data(event, (uint8_t *)NEWLINE, 1); > + if (rc < 0) { > + return rc; > + } > + return write_console_data(event, buffer, len); > +} > + > +static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) > +{ > + int len; > + int written; > + int errors = 0; > + MDBO *mdbo; > + SclpMsg *data = (SclpMsg *) ebh; > + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); > + > + len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); > + > + /* first check message buffers */ > + mdbo = data->mdb.mdbo; > + while (len > 0) { > + if (be16_to_cpu(mdbo->length) > len > + || be16_to_cpu(mdbo->length) == 0) { > + return SCLP_RC_INCONSISTENT_LENGTHS; > + } > + len -= be16_to_cpu(mdbo->length); > + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); > + } > + > + /* then execute */ > + len = be16_to_cpu(data->mdb.header.length) - sizeof(data->mdb.header); Can this overflow? > + mdbo = data->mdb.mdbo; > + while (len > 0) { > + switch (be16_to_cpu(mdbo->type)) { > + case 4: What is 4? This wants to be a #define. > + /* message text object */ > + written = process_mdb(event, mdbo); > + if (written < 0) { > + /* character layer error */ > + errors++; > + } > + break; > + default: /* ignore */ > + break; > + } > + len -= be16_to_cpu(mdbo->length); > + mdbo = (void *) mdbo + be16_to_cpu(mdbo->length); > + } > + if (errors) { > + scon->write_errors += errors; > + } > + data->header.flags = SCLP_EVENT_BUFFER_ACCEPTED; > + > + return SCLP_RC_NORMAL_COMPLETION; > +} > + > +static void trigger_console_data(void *opaque, int n, int level) > +{ > + sclp_service_interrupt(0); > +} > + > +/* functions for live migration */ > + > +static const VMStateDescription vmstate_sclplmconsole = { > + .name = "sclplmconsole", > + .version_id = 0, > + .minimum_version_id = 0, > + .minimum_version_id_old = 0, > + .fields = (VMStateField[]) { > + VMSTATE_BOOL(event.event_pending, SCLPConsoleLM), > + VMSTATE_UINT32(write_errors, SCLPConsoleLM), > + VMSTATE_UINT32(length, SCLPConsoleLM), > + VMSTATE_UINT8_ARRAY(buf, SCLPConsoleLM, SIZE_CONSOLE_BUFFER), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +/* qemu object creation and initialization functions */ > + > +/* tell character layer our call-back functions */ > + > +static int console_init(SCLPEvent *event) > +{ > + static bool console_available; > + > + SCLPConsoleLM *scon = DO_UPCAST(SCLPConsoleLM, event, event); > + > + if (console_available) { > + error_report("Multiple line-mode operator consoles are not > supported"); > + return -1; > + } > + console_available = true; > + > + if (scon->chr) { > + qemu_chr_add_handlers(scon->chr, chr_can_read, chr_read, NULL, scon); > + } > + scon->irq_console_read = *qemu_allocate_irqs(trigger_console_data, NULL, > 1); > + > + return vmstate_register(NULL, 0, &vmstate_sclplmconsole, scon); This should go into your QOM class. Alex