From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:41311) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Stl0w-0008T2-Qs for qemu-devel@nongnu.org; Tue, 24 Jul 2012 15:43:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Stl0u-0007nF-Mx for qemu-devel@nongnu.org; Tue, 24 Jul 2012 15:43:26 -0400 Received: from mail-yw0-f45.google.com ([209.85.213.45]:54528) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Stl0u-0007n9-GM for qemu-devel@nongnu.org; Tue, 24 Jul 2012 15:43:24 -0400 Received: by yhpp34 with SMTP id p34so7167559yhp.4 for ; Tue, 24 Jul 2012 12:43:24 -0700 (PDT) MIME-Version: 1.0 In-Reply-To: <1343115430-34285-7-git-send-email-borntraeger@de.ibm.com> References: <1343115430-34285-1-git-send-email-borntraeger@de.ibm.com> <1343115430-34285-7-git-send-email-borntraeger@de.ibm.com> From: Blue Swirl Date: Tue, 24 Jul 2012 19:42:59 +0000 Message-ID: Content-Type: text/plain; charset=UTF-8 Subject: Re: [Qemu-devel] [PATCH 6/7] s390: sclp ascii console support List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Christian Borntraeger Cc: qemu-devel , Heinz Graalfs , Alexander Graf , afaerber@suse.de, Jens Freimann On Tue, Jul 24, 2012 at 7:37 AM, Christian Borntraeger wrote: > From: Heinz Graalfs > > This code adds console support by implementing SCLP's ASCII Console > Data event. This is the same console as LPARs ASCII console or z/VMs > sysascii. > > The console can be specified manually with something like > -chardev stdio,id=charconsole0 -device sclpconsole,chardev=charconsole0,id=console0 > > Newer kernels will autodetect that console and prefer that over virtio > console. > > When data is received from the character layer it creates a service > interrupt to trigger a Read Event Data command from the guest that will > pick up the received character byte-stream. > When characters are echo'ed by the linux guest a Write Event Data occurs > which is forwarded by the Event Facility to the console that supports > a corresponding mask value. > Console resizing is not supported. > The character layer byte-stream is buffered using a fixed size iov > buffer. > > Signed-off-by: Heinz Graalfs > Signed-off-by: Christian Borntraeger > --- > hw/s390x/Makefile.objs | 2 +- > hw/s390x/sclpconsole.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 324 insertions(+), 1 deletions(-) > create mode 100644 hw/s390x/sclpconsole.c > > diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs > index ed4e61a..096dfcd 100644 > --- a/hw/s390x/Makefile.objs > +++ b/hw/s390x/Makefile.objs > @@ -3,4 +3,4 @@ obj-y = s390-virtio-bus.o s390-virtio.o > obj-y := $(addprefix ../,$(obj-y)) > obj-y += sclp.o > obj-y += event-facility.o > -obj-y += sclpquiesce.o > +obj-y += sclpquiesce.o sclpconsole.o > diff --git a/hw/s390x/sclpconsole.c b/hw/s390x/sclpconsole.c > new file mode 100644 > index 0000000..9a57032 > --- /dev/null > +++ b/hw/s390x/sclpconsole.c > @@ -0,0 +1,323 @@ > +/* > + * SCLP event type > + * Ascii Console Data (VT220 Console) > + * > + * Copyright IBM, Corp. 2012 > + * > + * Authors: > + * Heinz Graalfs > + * > + * 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 > +#include "qemu-thread.h" > + > +#include "sclp.h" > +#include "event-facility.h" > + > +typedef struct ASCIIConsoleData { > + EventBufferHeader ebh; > + char data[0]; > +} QEMU_PACKED ASCIIConsoleData; > + > +qemu_irq sclp_read_vt220; This should go into SCLPConsole. > + > +/* max size for ASCII data in 4K SCCB page */ > +#define SIZE_BUFFER_VT220 4080 > + > +typedef struct SCLPConsole { > + SCLPEvent event; > + CharDriverState *chr; > + /* io vector */ > + uint8_t *iov; /* iov buffer pointer */ > + uint8_t *iov_sclp; /* pointer to SCLP read offset */ > + uint8_t *iov_bs; /* pointer byte stream read offset */ > + uint32_t iov_data_len; /* length of byte stream in buffer */ > + uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ > +} SCLPConsole; > + > +/* character layer call-back functions */ > + > +/* Return number of bytes that fit into iov buffer */ > +static int chr_can_read(void *opaque) > +{ > + int can_read; > + SCLPConsole *scon = opaque; > + > + qemu_mutex_lock(&scon->event.lock); > + can_read = SIZE_BUFFER_VT220 - scon->iov_data_len; > + qemu_mutex_unlock(&scon->event.lock); > + > + return can_read; > +} > + > +/* Receive n bytes from character layer, save in iov buffer, > + * and set event pending */ > +static void receive_from_chr_layer(void *opaque, const uint8_t *buf, int size) > +{ > + SCLPConsole *scon = opaque; > + > + assert(scon->iov); > + > + qemu_mutex_lock(&scon->event.lock); > + > + /* if new data do not fit into current buffer */ > + if (scon->iov_data_len + size > SIZE_BUFFER_VT220) { > + /* character layer sent more than allowed */ > + qemu_mutex_unlock(&scon->event.lock); > + return; > + } > + /* put byte-stream from character layer into buffer */ > + memcpy(scon->iov_bs, buf, size); > + scon->iov_data_len += size; > + scon->iov_sclp_rest += size; > + scon->iov_bs += size; > + scon->event.event_pending = true; > + > + qemu_mutex_unlock(&scon->event.lock); > +} > + > +/* Send data from a char device over to the guest */ > +static void chr_read(void *opaque, const uint8_t *buf, int size) > +{ > + assert(opaque); > + I'd convert the opaque to SCLPConsole here and use that for receive_from_chr_layer() instead of opaque. > + receive_from_chr_layer(opaque, buf, size); > + /* trigger SCLP read operation */ > + qemu_irq_raise(sclp_read_vt220); > +} > + > +static void chr_event(void *opaque, int event) > +{ > + SCLPConsole *scon = opaque; > + > + switch (event) { > + case CHR_EVENT_OPENED: > + if (!scon->iov) { > + scon->iov = g_malloc0(SIZE_BUFFER_VT220); > + scon->iov_sclp = scon->iov; > + scon->iov_bs = scon->iov; > + scon->iov_data_len = 0; > + scon->iov_sclp_rest = 0; > + } > + break; > + case CHR_EVENT_CLOSED: > + if (scon->iov) { > + g_free(scon->iov); > + scon->iov = NULL; > + } > + break; > + } > +} > + > +/* functions to be called by event facility */ > + > +static int event_type(void) > +{ > + return SCLP_EVENT_ASCII_CONSOLE_DATA; > +} > + > +static unsigned int send_mask(void) > +{ > + return SCLP_EVENT_MASK_MSG_ASCII; > +} > + > +static unsigned int receive_mask(void) > +{ > + return SCLP_EVENT_MASK_MSG_ASCII; > +} > + > +/* triggered by SCLP's read_event_data - > + * copy console data byte-stream into provided (SCLP) buffer > + */ > +static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, > + int avail) > +{ > + SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); > + > + /* first byte is hex 0 saying an ascii string follows */ > + *buf++ = '\0'; > + avail--; > + /* if all data fit into provided SCLP buffer */ > + if (avail >= cons->iov_sclp_rest) { > + /* copy character byte-stream to SCLP buffer */ > + memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest); > + *size = cons->iov_sclp_rest + 1; > + cons->iov_sclp = cons->iov; > + cons->iov_bs = cons->iov; > + cons->iov_data_len = 0; > + cons->iov_sclp_rest = 0; > + event->event_pending = false; > + /* data provided and no more data pending */ > + } else { > + /* if provided buffer is too small, just copy part */ > + memcpy(buf, cons->iov_sclp, avail); > + *size = avail + 1; > + cons->iov_sclp_rest -= avail; > + cons->iov_sclp += avail; > + /* more data pending */ > + } > +} > + > +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, > + int *slen) > +{ > + int avail; > + size_t src_len; > + uint8_t *to; > + ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; > + > + qemu_mutex_lock(&event->lock); > + > + if (!event->event_pending) { > + /* no data pending */ > + qemu_mutex_unlock(&event->lock); > + return 0; > + } > + > + to = (uint8_t *)&acd->data; > + avail = *slen - sizeof(ASCIIConsoleData); > + get_console_data(event, to, &src_len, avail); > + > + acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len); > + acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; > + acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; > + *slen = avail - src_len; > + > + qemu_mutex_unlock(&event->lock); > + > + return 1; > +} > + > +/* triggered by SCLP's write_event_data > + * - write console data into character layer > + * returns < 0 if an error occured > + */ > +static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, > + size_t len) > +{ > + ssize_t ret = 0; > + const uint8_t *iov_offset; > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + > + if (!scon->chr) { > + /* If there's no backend, we can just say we consumed all data. */ > + return len; > + } > + > + iov_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; > + iov_offset += ret; > + } else { > + len = 0; > + } > + } > + > + return ret; > +} > + > +static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) > +{ > + int rc; > + int length; > + ssize_t written; > + ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; > + > + length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader); > + written = write_console_data(event, (uint8_t *)acd->data, length); > + > + rc = SCLP_RC_NORMAL_COMPLETION; > + /* set event buffer accepted flag */ > + evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED; > + > + /* written will be zero if a pty is not connected - don't treat as error */ > + if (written < 0) { > + /* event buffer not accepted due to error in character layer */ > + evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); > + rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK; > + } > + > + return rc; > +} > + > +static void trigger_ascii_console_data(void *env, int n, int level) > +{ > + sclp_service_interrupt(0); > +} > + > +/* qemu object creation and initialization functions */ > + > +/* tell character layer our call-back functions */ > +static int console_init(SCLPEvent *event) > +{ > + static bool console_available; > + > + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); > + > + if (console_available) { > + error_report("Multiple VT220 operator consoles are not supported"); > + return -1; > + } > + console_available = true; > + event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA; > + if (scon->chr) { > + qemu_chr_add_handlers(scon->chr, chr_can_read, > + chr_read, chr_event, scon); > + } > + sclp_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data, > + NULL, 1); > + > + qemu_mutex_init(&event->lock); > + > + return 0; > +} > + > +static int console_exit(SCLPEvent *event) > +{ > + qemu_mutex_destroy(&event->lock); > + > + return 0; > +} > + > +static Property console_properties[] = { > + DEFINE_PROP_CHR("chardev", SCLPConsole, chr), > + DEFINE_PROP_END_OF_LIST(), > +}; > + > +static void console_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); > + > + dc->props = console_properties; > + ec->init = console_init; > + ec->exit = console_exit; > + ec->get_send_mask = send_mask; > + ec->get_receive_mask = receive_mask; > + ec->event_type = event_type; > + ec->read_event_data = read_event_data; > + ec->write_event_data = write_event_data; > +} > + > +static TypeInfo sclp_console_info = { > + .name = "sclpconsole", > + .parent = TYPE_SCLP_EVENT, > + .instance_size = sizeof(SCLPConsole), > + .class_init = console_class_init, > + .class_size = sizeof(SCLPEventClass), > +}; > + > +static void register_types(void) > +{ > + type_register_static(&sclp_console_info); > +} > +type_init(register_types) > -- > 1.7.0.1 >